From 0533a20c3dff6f4ecc2d99b49693767575ed2727 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 22 Jun 2023 17:24:24 +0300 Subject: [PATCH 001/228] Proto is in progress --- detekt.yml | 2 +- .../src/main/kotlin/io/ksmt/expr/Bool.kt | 9 +- ksmt-maxsmt/build.gradle.kts | 19 ++ .../io/ksmt/solver/maxsmt/Constraint.kt | 8 + .../io/ksmt/solver/maxsmt/HardConstraint.kt | 6 + .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 232 ++++++++++++++++++ .../main/kotlin/io/ksmt/solver/maxsmt/Main.kt | 43 ++++ .../io/ksmt/solver/maxsmt/SoftConstraint.kt | 6 + .../io/ksmt/solver/maxsmt/UnitConstraint.kt | 34 +++ .../kotlin/io/ksmt/solver/maxsmt/Example.kt | 19 ++ settings.gradle.kts | 5 + 11 files changed, 378 insertions(+), 5 deletions(-) create mode 100644 ksmt-maxsmt/build.gradle.kts create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/UnitConstraint.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt diff --git a/detekt.yml b/detekt.yml index 07ba84ade..bd6911c9d 100644 --- a/detekt.yml +++ b/detekt.yml @@ -500,7 +500,7 @@ style: values: - 'FIXME:' - 'STOPSHIP:' - - 'TODO:' + #- 'TODO:' allowedPatterns: '' customMessage: '' ForbiddenImport: diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index b73de9362..9633ec6c4 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -64,7 +64,7 @@ abstract class KOrExpr(ctx: KContext) : KApp(ctx) { override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) } -class KOrBinaryExpr internal constructor( +class KOrBinaryExpr( ctx: KContext, val lhs: KExpr, val rhs: KExpr @@ -78,7 +78,7 @@ class KOrBinaryExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { lhs }, { rhs }) } -class KOrNaryExpr internal constructor( +class KOrNaryExpr( ctx: KContext, override val args: List> ) : KOrExpr(ctx) { @@ -92,7 +92,7 @@ class KOrNaryExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other) { args } } -class KNotExpr internal constructor( +class KNotExpr( ctx: KContext, val arg: KExpr ) : KApp(ctx) { @@ -108,6 +108,7 @@ class KNotExpr internal constructor( override fun internHashCode(): Int = hash(arg) override fun internEquals(other: Any): Boolean = structurallyEqual(other) { arg } + override fun toString(): String = "" } class KImpliesExpr internal constructor( @@ -148,7 +149,7 @@ class KXorExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { a }, { b }) } -class KEqExpr internal constructor( +class KEqExpr( ctx: KContext, val lhs: KExpr, val rhs: KExpr ) : KApp(ctx) { diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts new file mode 100644 index 000000000..3c504c7ec --- /dev/null +++ b/ksmt-maxsmt/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("io.ksmt.ksmt-base") +} + +group = "org.example" +version = "unspecified" + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":ksmt-core")) + implementation(project(":ksmt-z3")) + + testImplementation(project(":ksmt-core")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt new file mode 100644 index 000000000..9b97e1f22 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt @@ -0,0 +1,8 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.expr.KExpr +import io.ksmt.sort.KBoolSort + +interface Constraint { + val constraint: KExpr +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt new file mode 100644 index 000000000..bea1e5ba8 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.expr.KExpr +import io.ksmt.sort.KBoolSort + +class HardConstraint(override val constraint: KExpr) : Constraint diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt new file mode 100644 index 000000000..a58c12522 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -0,0 +1,232 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.expr.KEqExpr +import io.ksmt.expr.KExpr +import io.ksmt.expr.KNotExpr +import io.ksmt.expr.KOrBinaryExpr +import io.ksmt.expr.KOrNaryExpr +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KBoolSort +import io.ksmt.utils.mkConst +import kotlin.time.Duration + +// Why did I decide not to use inheritance? +// TODO: solver type must be KSolver but the code does not work with it +class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver { + private val softConstraints = mutableListOf() + + private val hardConstraints = mutableListOf() + + // Enum checking max SAT state (last check status, was not checked, invalid (soft assertions changed)) + // Should I support push/pop for soft constraints? + + fun assertSoft(expr: KExpr, weight: Int) { + softConstraints.add(SoftConstraint(expr, weight)) + } + + // TODO: what to return? + // TODO: add timeout? + fun checkMaxSMT(): Pair { + require(softConstraints.isNotEmpty()) { "Soft constraints list should not be empty" } + + // Should I check every time on satisfiability? + // У них в солверах есть last checked status. + // Should I add timeout? + val status = solver.check() + + if (status == KSolverStatus.UNSAT) { + error("Conjunction of asserted formulas is UNSAT") + } + else if (status == KSolverStatus.UNKNOWN) { + // TODO: handle this case + } + + // Formula with soft assertions is UNSAT (if SAT then we take all soft assertions): + // + // 1. soft constraints are already UNSAT (FIRST optimization): + // do while possible + // - get UNSAT core + // - while (!SAT) -> remove constraint with lower weight + // - check on SAT soft constraints + // + // 2. soft constraints are SAT + + // Possible case when cost of n soft constraints is equal to cost of n - 1 soft constraints + + var i = 0 + var formula = (hardConstraints + softConstraints).toMutableList() + + while (true) { + // TD: remove empty disjunction. + val (solverStatus, unsatCore, model) = solveSMT(softConstraints) + + if (solverStatus == KSolverStatus.SAT) { + return Pair(model, i) + } + else if (solverStatus == KSolverStatus.UNKNOWN) { + // TODO: implement + } + + val (formulaReified, reificationVariables) = + reifyCore(formula, getUnsatCoreOfConstraints(unsatCore), i) + + for (reificationVar in reificationVariables) { + val reificationVarConstraint = HardConstraint(reificationVar) + if (!formulaReified.contains(reificationVarConstraint)) { + // TODO: не уверена, что это сравнение работает корректно + formulaReified.add(reificationVarConstraint) + } + } + + formula = applyMaxRes(formulaReified, reificationVariables) + + ++i + } + } + + private fun applyMaxRes(formula: MutableList, reificationVariables: List>) + : MutableList { + for (i in reificationVariables.indices) { + // TODO: here we should use restrictions from the article for MaxRes + val reificationVar = reificationVariables[i] + formula.remove(SoftConstraint(KNotExpr(reificationVar.ctx, reificationVar), 1)) + + if (i < reificationVariables.size - 1) { + var reifiedLiteralsDisjunction = KOrNaryExpr(reificationVar.ctx, + reificationVariables.subList(i + 1, reificationVariables.size - 1)) + + val reifiedVar = reificationVar.ctx.boolSort.mkConst("d$i") + + formula.add(HardConstraint( + KEqExpr(reificationVar.ctx, + reifiedVar, reifiedLiteralsDisjunction) + )) + + formula.add(SoftConstraint( + KOrBinaryExpr(reificationVar.ctx, + KNotExpr(reificationVar.ctx, reificationVar), KNotExpr(reificationVar.ctx, reifiedVar)), 1)) + } + else { + // Здесь добавляем пустой дизъюнкт, но по факту это не нужно делать (т.к. потом его удалим) + } + } + + return formula + } + + private fun getUnsatCoreOfConstraints(unsatCore: List>): List { + val unsatCoreOfConstraints = mutableListOf() + + for (element in unsatCore) { + val softConstraint = softConstraints.find { x -> x.constraint == element } + if (softConstraint != null) { + unsatCoreOfConstraints.add(softConstraint) + } + else { + val hardConstraint = hardConstraints.find { x -> x.constraint == element } + unsatCoreOfConstraints.add(hardConstraint!!) + } + } + + return unsatCoreOfConstraints + } + + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int) + : Pair, List>> { + val unitConstraintExpressions = mutableListOf>() + + for (j in unsatCore.indices) { + val element = unsatCore[j] + // Может проверка element is SoftConstraint не нужна, так как использую checkWithAssumptions + // для soft constraints + if (element is SoftConstraint && element.weight == 1) { + formula.remove(element) + val constraint = element.constraint + // TODO: какой тут должен быть контекст? + // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? + val reificationVariable = constraint.ctx.boolSort.mkConst("b$i$j") + val reificationConstraint = KEqExpr(constraint.ctx, constraint, + KNotExpr(constraint.ctx, reificationVariable)) + // TODO: Переобозначить и остальные элементы в b_i_j + formula.add(HardConstraint(reificationConstraint)) + formula.add(SoftConstraint(reificationVariable, 1)) + + unitConstraintExpressions.add(reificationVariable) + + return Pair(formula, unitConstraintExpressions) + } + } + + error("reify core method, not implemented part") + } + + // Returns issat, unsat core (?) and assignment + private fun solveSMT(softConstraints: List): Triple>, KModel?> { + // Здесь нужно очистить и заполнить солвер assert-ами. + val solverStatus = solver.checkWithAssumptions(softConstraints.map { x -> x.constraint }) + + // Сейчас в unsatCore содержатся hard constraints + + if (solverStatus == KSolverStatus.SAT) { + return Triple(solverStatus, listOf(), solver.model()) + } + else if (solverStatus == KSolverStatus.UNSAT) { + return Triple(solverStatus, solver.unsatCore(), null) + } + + return Triple(solverStatus, listOf(), null) + } + + override fun configure(configurator: KSolverConfiguration.() -> Unit) { + solver.configure(configurator) + } + + override fun assert(expr: KExpr) { + hardConstraints.add(HardConstraint(expr)) + solver.assert(expr) + } + + override fun assertAndTrack(expr: KExpr) { + solver.assertAndTrack(expr) + } + + override fun push() { + solver.push() + } + + override fun pop(n: UInt) { + solver.pop(n) + } + + override fun check(timeout: Duration): KSolverStatus { + return solver.check(timeout) + } + + override fun checkWithAssumptions(assumptions: List>, timeout: Duration): KSolverStatus { + return solver.checkWithAssumptions(assumptions, timeout) + } + + override fun model(): KModel { + return solver.model() + } + + override fun unsatCore(): List> { + return solver.unsatCore() + } + + override fun reasonOfUnknown(): String { + return solver.reasonOfUnknown() + } + + override fun interrupt() { + solver.interrupt() + } + + override fun close() { + solver.close() + } +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt new file mode 100644 index 000000000..99bff7228 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt @@ -0,0 +1,43 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.KContext +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.utils.asExpr +import io.ksmt.utils.mkConst +import java.io.Console + +fun main() { + test() +} + +fun test() = with(KContext()) { + val z3Solver = KZ3Solver(this) + val maxSMTSolver = KMaxSMTSolver(z3Solver) + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + val c = boolSort.mkConst("c") + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(c) + maxSMTSolver.assertSoft(mkAnd(a, mkNot(c)), 1) + maxSMTSolver.assertSoft(mkNot(a), 1) + maxSMTSolver.checkMaxSMT() + +/* val aIsTrue = mkEq(a, mkTrue()) + val bIsTrue = mkEq(b, mkTrue()) + val cIsTrue = mkEq(c, mkTrue()) + + z3Solver.assert(aIsTrue) + z3Solver.assert(bIsTrue) + z3Solver.assert(cIsTrue) + z3Solver.assert(mkAnd(aIsTrue, mkNot(cIsTrue))) + z3Solver.assert(mkEq(a, mkFalse())) + val status = z3Solver.check() + if (status == KSolverStatus.UNSAT) { + println(status) + val unsatCore = z3Solver.unsatCore() + println("Unsat core length: ${unsatCore.size}") + unsatCore.forEach { x -> println(x) } + }*/ +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt new file mode 100644 index 000000000..2caea568d --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.expr.KExpr +import io.ksmt.sort.KBoolSort + +class SoftConstraint(override val constraint: KExpr, val weight: Int) : Constraint diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/UnitConstraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/UnitConstraint.kt new file mode 100644 index 000000000..236c974de --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/UnitConstraint.kt @@ -0,0 +1,34 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.cache.hash +import io.ksmt.cache.structurallyEqual +import io.ksmt.expr.KExpr +import io.ksmt.expr.printer.ExpressionPrinter +import io.ksmt.expr.transformer.KTransformerBase +import io.ksmt.sort.KBoolSort + +// TODO: do I correctly work with context? +class UnitConstraint(val i: Int, val j: Int, val formula: KExpr, override val sort: KBoolSort) + : KExpr(formula.ctx) { + override fun accept(transformer: KTransformerBase): KExpr { + TODO("Not yet implemented") + } + + override fun print(printer: ExpressionPrinter) { + TODO("Not yet implemented") + } + + override fun internEquals(other: Any): Boolean { + // Тут не учитываются i и j. + return structurallyEqual(other) { formula } + } + + override fun internHashCode(): Int { + // Is this hash ok for me? + return hash(i, j, formula) + } + + override fun toString(): String { + return "" + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt new file mode 100644 index 000000000..70381ac01 --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt @@ -0,0 +1,19 @@ +package io.ksmt.solver.maxsmt + +import org.junit.jupiter.api.Test +/*import io.ksmt.solver.KSolverStatus +import io.ksmt.sort.KArraySort +import io.ksmt.utils.mkConst*/ + +class Example { +/* @Test + fun test() = with(KContext()) { + val a = boolSort.mkConst("a") + val b = mkFuncDecl("b", boolSort, listOf(intSort)) + val c = intSort.mkConst("c") + val e = mkArraySort(intSort, intSort).mkConst("e") + val solver = KZ3Solver(this) + solver.assert(a) + + }*/ +} diff --git a/settings.gradle.kts b/settings.gradle.kts index c6d4d64de..29a91cdd3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,6 +7,7 @@ include("ksmt-runner") include("ksmt-runner:solver-generator") include("ksmt-test") include("ksmt-cvc5") +include("ksmt-maxsmt") pluginManagement { resolutionStrategy { @@ -17,3 +18,7 @@ pluginManagement { } } } +include("ksmt-maxsmt:src:test:kotlin") +findProject(":ksmt-maxsmt:src:test:kotlin")?.name = "kotlin" +include("ksmt-maxsmt:src:test") +findProject(":ksmt-maxsmt:src:test")?.name = "test" From 561afede29ea8ed0f094b988337eb8e55596357c Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 23 Jun 2023 13:12:22 +0300 Subject: [PATCH 002/228] Simple cases work --- .../src/main/kotlin/io/ksmt/expr/Bool.kt | 2 +- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 32 +++++++++++-------- .../main/kotlin/io/ksmt/solver/maxsmt/Main.kt | 12 ++++++- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index 9633ec6c4..5a802de47 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -108,7 +108,7 @@ class KNotExpr( override fun internHashCode(): Int = hash(arg) override fun internEquals(other: Any): Boolean = structurallyEqual(other) { arg } - override fun toString(): String = "" + //override fun toString(): String = "" } class KImpliesExpr internal constructor( diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index a58c12522..87d0250b0 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -28,7 +28,7 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver { require(softConstraints.isNotEmpty()) { "Soft constraints list should not be empty" } @@ -61,7 +61,6 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver x is SoftConstraint && x.constraint.internEquals(KNotExpr(reificationVar.ctx, reificationVar)) + && x.weight == 1 } + softConstraints.removeIf { x -> x.constraint.internEquals(KNotExpr(reificationVar.ctx, reificationVar)) + && x.weight == 1 } + + + // TODO: fix hard/soft constraints sets! if (i < reificationVariables.size - 1) { - var reifiedLiteralsDisjunction = KOrNaryExpr(reificationVar.ctx, + val reifiedLiteralsDisjunction = KOrNaryExpr(reificationVar.ctx, reificationVariables.subList(i + 1, reificationVariables.size - 1)) val reifiedVar = reificationVar.ctx.boolSort.mkConst("d$i") @@ -145,6 +147,7 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver x.constraint }) - // Сейчас в unsatCore содержатся hard constraints - if (solverStatus == KSolverStatus.SAT) { return Triple(solverStatus, listOf(), solver.model()) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt index 99bff7228..fa37fa6e1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt @@ -13,6 +13,14 @@ fun main() { fun test() = with(KContext()) { val z3Solver = KZ3Solver(this) + val a = boolSort.mkConst("a") + val notA = mkNot(a) + z3Solver.assert(a) + //z3Solver.assert(mkNot(a)) + println(z3Solver.checkWithAssumptions(listOf(notA))) + println(z3Solver.unsatCore()) + +/* val z3Solver = KZ3Solver(this) val maxSMTSolver = KMaxSMTSolver(z3Solver) val a = boolSort.mkConst("a") val b = boolSort.mkConst("b") @@ -22,7 +30,9 @@ fun test() = with(KContext()) { maxSMTSolver.assert(c) maxSMTSolver.assertSoft(mkAnd(a, mkNot(c)), 1) maxSMTSolver.assertSoft(mkNot(a), 1) - maxSMTSolver.checkMaxSMT() + val (model, iter) = maxSMTSolver.checkMaxSMT() + println("Model: $model") + println("Finished on $iter iteration")*/ /* val aIsTrue = mkEq(a, mkTrue()) val bIsTrue = mkEq(b, mkTrue()) From 9d02516ab77524bd86508982009aff44517ca793 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 23 Jun 2023 14:51:37 +0300 Subject: [PATCH 003/228] Refactor: getUnsatCoreOfConstraints, context --- ksmt-maxsmt/build.gradle.kts | 11 ++ .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 134 +++++++++--------- .../main/kotlin/io/ksmt/solver/maxsmt/Main.kt | 14 +- .../io/ksmt/solver/maxsmt/UnitConstraint.kt | 34 ----- 4 files changed, 79 insertions(+), 114 deletions(-) delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/UnitConstraint.kt diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index 3c504c7ec..38eb14756 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("io.ksmt.ksmt-base") + id("com.diffplug.spotless") version "5.10.0" } group = "org.example" @@ -17,3 +18,13 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") } + +spotless { + kotlin { + diktat() + } + + kotlinGradle { + diktat() + } +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index 87d0250b0..1b46c06b1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -1,5 +1,6 @@ package io.ksmt.solver.maxsmt +import io.ksmt.KContext import io.ksmt.expr.KEqExpr import io.ksmt.expr.KExpr import io.ksmt.expr.KNotExpr @@ -14,9 +15,8 @@ import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration -// Why did I decide not to use inheritance? // TODO: solver type must be KSolver but the code does not work with it -class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver { +class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KSolver { private val softConstraints = mutableListOf() private val hardConstraints = mutableListOf() @@ -40,23 +40,10 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver remove constraint with lower weight - // - check on SAT soft constraints - // - // 2. soft constraints are SAT - - // Possible case when cost of n soft constraints is equal to cost of n - 1 soft constraints - var i = 0 var formula = (hardConstraints + softConstraints).toMutableList() @@ -65,16 +52,15 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver, reificationVariables: List>) - : MutableList { + private fun applyMaxRes(formula: MutableList, reificationVariables: List>): MutableList { for (i in reificationVariables.indices) { // TODO: here we should use restrictions from the article for MaxRes val reificationVar = reificationVariables[i] - formula.removeIf { x -> x is SoftConstraint && x.constraint.internEquals(KNotExpr(reificationVar.ctx, reificationVar)) - && x.weight == 1 } - softConstraints.removeIf { x -> x.constraint.internEquals(KNotExpr(reificationVar.ctx, reificationVar)) - && x.weight == 1 } - + formula.removeIf { x -> + x is SoftConstraint && x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && + x.weight == 1 + } + softConstraints.removeIf { x -> + x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && + x.weight == 1 + } // TODO: fix hard/soft constraints sets! if (i < reificationVariables.size - 1) { - val reifiedLiteralsDisjunction = KOrNaryExpr(reificationVar.ctx, - reificationVariables.subList(i + 1, reificationVariables.size - 1)) - - val reifiedVar = reificationVar.ctx.boolSort.mkConst("d$i") - - formula.add(HardConstraint( - KEqExpr(reificationVar.ctx, - reifiedVar, reifiedLiteralsDisjunction) - )) - - formula.add(SoftConstraint( - KOrBinaryExpr(reificationVar.ctx, - KNotExpr(reificationVar.ctx, reificationVar), KNotExpr(reificationVar.ctx, reifiedVar)), 1)) - } - else { + val reifiedLiteralsDisjunction = KOrNaryExpr( + ctx, + reificationVariables.subList(i + 1, reificationVariables.size - 1), + ) + + val reifiedVar = ctx.boolSort.mkConst("d$i") + + formula.add( + HardConstraint( + KEqExpr( + ctx, + reifiedVar, + reifiedLiteralsDisjunction, + ), + ), + ) + + formula.add( + SoftConstraint( + KOrBinaryExpr( + ctx, + KNotExpr(ctx, reificationVar), + KNotExpr(ctx, reifiedVar), + ), + 1, + ), + ) + } else { // Здесь добавляем пустой дизъюнкт, но по факту это не нужно делать (т.к. потом его удалим) } } @@ -123,43 +124,39 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver>): List { val unsatCoreOfConstraints = mutableListOf() - for (element in unsatCore) { - val softConstraint = softConstraints.find { x -> x.constraint == element } - if (softConstraint != null) { - unsatCoreOfConstraints.add(softConstraint) - } - else { - val hardConstraint = hardConstraints.find { x -> x.constraint == element } - unsatCoreOfConstraints.add(hardConstraint!!) - } + for (coreElement in unsatCore) { + val softConstraint = softConstraints.find { x -> x.constraint == coreElement } + softConstraint?.let { unsatCoreOfConstraints.add(it) } } return unsatCoreOfConstraints } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int) - : Pair, List>> { + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { val unitConstraintExpressions = mutableListOf>() - for (j in unsatCore.indices) { - val element = unsatCore[j] - // Может проверка element is SoftConstraint не нужна, так как использую checkWithAssumptions - // для soft constraints - if (element is SoftConstraint && element.weight == 1) { - formula.remove(element) - softConstraints.remove(element) - val constraint = element.constraint + for (coreElement in unsatCore.withIndex()) { + if ((coreElement.value as SoftConstraint).weight == 1) { + formula.remove(coreElement.value) + softConstraints.remove(coreElement.value) + + val coreElementConstraint = coreElement.value.constraint // TODO: какой тут должен быть контекст? // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? - val reificationVariable = constraint.ctx.boolSort.mkConst("b$i$j") - val reificationConstraint = KEqExpr(constraint.ctx, constraint, - KNotExpr(constraint.ctx, reificationVariable)) + val reificationVariable = + ctx.boolSort.mkConst("b$i${coreElement.index}") + + val reificationConstraint = KEqExpr( + ctx, + coreElementConstraint, + KNotExpr(ctx, reificationVariable), + ) // TODO: Переобозначить и остальные элементы в b_i_j formula.add(HardConstraint(reificationConstraint)) this.assert(reificationConstraint) - formula.add(SoftConstraint(KNotExpr(constraint.ctx, reificationVariable), 1)) - softConstraints.add(SoftConstraint(KNotExpr(constraint.ctx, reificationVariable), 1)) + formula.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) + softConstraints.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) unitConstraintExpressions.add(reificationVariable) @@ -177,8 +174,7 @@ class KMaxSMTSolver(private val solver: KZ3Solver) : KSolver, override val sort: KBoolSort) - : KExpr(formula.ctx) { - override fun accept(transformer: KTransformerBase): KExpr { - TODO("Not yet implemented") - } - - override fun print(printer: ExpressionPrinter) { - TODO("Not yet implemented") - } - - override fun internEquals(other: Any): Boolean { - // Тут не учитываются i и j. - return structurallyEqual(other) { formula } - } - - override fun internHashCode(): Int { - // Is this hash ok for me? - return hash(i, j, formula) - } - - override fun toString(): String { - return "" - } -} From d8b4780122da9a5fe06059bde810c32b317f3f7a Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 23 Jun 2023 15:05:51 +0300 Subject: [PATCH 004/228] Do not keep and remove from formula hard constraints --- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index 1b46c06b1..b373538bb 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -19,8 +19,6 @@ import kotlin.time.Duration class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KSolver { private val softConstraints = mutableListOf() - private val hardConstraints = mutableListOf() - // Enum checking max SAT state (last check status, was not checked, invalid (soft assertions changed)) // Should I support push/pop for soft constraints? @@ -45,7 +43,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } var i = 0 - var formula = (hardConstraints + softConstraints).toMutableList() + var formula = softConstraints.toMutableList() while (true) { val (solverStatus, unsatCore, model) = solveSMT(softConstraints) @@ -61,7 +59,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : // TODO, FIX: Для одного странно использовать KOrNAryExpr val reifiedVariablesDisjunction = KOrNaryExpr(ctx, reificationVariables) - formulaReified.add(HardConstraint(reifiedVariablesDisjunction)) this.assert(reifiedVariablesDisjunction) formula = applyMaxRes(formulaReified, reificationVariables) @@ -70,13 +67,13 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } } - private fun applyMaxRes(formula: MutableList, reificationVariables: List>): MutableList { + private fun applyMaxRes(formula: MutableList, reificationVariables: List>): MutableList { for (i in reificationVariables.indices) { // TODO: here we should use restrictions from the article for MaxRes val reificationVar = reificationVariables[i] formula.removeIf { x -> - x is SoftConstraint && x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && + x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && x.weight == 1 } softConstraints.removeIf { x -> @@ -86,14 +83,18 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : // TODO: fix hard/soft constraints sets! if (i < reificationVariables.size - 1) { + // TODO: uncommented, commented in order to build +/* val reifiedLiteralsDisjunction = KOrNaryExpr( ctx, reificationVariables.subList(i + 1, reificationVariables.size - 1), ) +*/ val reifiedVar = ctx.boolSort.mkConst("d$i") - formula.add( + // TODO: assert it. +/* formula.add( HardConstraint( KEqExpr( ctx, @@ -101,7 +102,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : reifiedLiteralsDisjunction, ), ), - ) + )*/ formula.add( SoftConstraint( @@ -121,8 +122,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return formula } - private fun getUnsatCoreOfConstraints(unsatCore: List>): List { - val unsatCoreOfConstraints = mutableListOf() + private fun getUnsatCoreOfConstraints(unsatCore: List>): List { + val unsatCoreOfConstraints = mutableListOf() for (coreElement in unsatCore) { val softConstraint = softConstraints.find { x -> x.constraint == coreElement } @@ -132,11 +133,11 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return unsatCoreOfConstraints } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { val unitConstraintExpressions = mutableListOf>() for (coreElement in unsatCore.withIndex()) { - if ((coreElement.value as SoftConstraint).weight == 1) { + if (coreElement.value.weight == 1) { formula.remove(coreElement.value) softConstraints.remove(coreElement.value) @@ -152,7 +153,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KNotExpr(ctx, reificationVariable), ) // TODO: Переобозначить и остальные элементы в b_i_j - formula.add(HardConstraint(reificationConstraint)) this.assert(reificationConstraint) formula.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) @@ -168,7 +168,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } // Returns issat, unsat core (?) and assignment - private fun solveSMT(softConstraints: List): Triple>, KModel?> { + private fun solveSMT(softConstraints: List): Triple>, KModel?> { // Здесь нужно очистить и заполнить солвер assert-ами. val solverStatus = solver.checkWithAssumptions(softConstraints.map { x -> x.constraint }) @@ -186,7 +186,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } override fun assert(expr: KExpr) { - hardConstraints.add(HardConstraint(expr)) + // hardConstraints.add(HardConstraint(expr)) solver.assert(expr) } From c6351cd397eba3d4007648f04e271a2212b9102a Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 23 Jun 2023 15:49:01 +0300 Subject: [PATCH 005/228] Split formula and soft constraints meaning --- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index b373538bb..56bed9a9a 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -46,7 +46,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : var formula = softConstraints.toMutableList() while (true) { - val (solverStatus, unsatCore, model) = solveSMT(softConstraints) + val (solverStatus, unsatCore, model) = solveSMT(formula) if (solverStatus == KSolverStatus.SAT) { return Pair(model, i) @@ -55,11 +55,10 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } val (formulaReified, reificationVariables) = - reifyCore(formula, getUnsatCoreOfConstraints(unsatCore), i) + reifyCore(formula, getUnsatCoreOfConstraints(formula, unsatCore), i) - // TODO, FIX: Для одного странно использовать KOrNAryExpr - val reifiedVariablesDisjunction = KOrNaryExpr(ctx, reificationVariables) - this.assert(reifiedVariablesDisjunction) + // TODO, FIX: Для одного странно использовать KOrNaryExpr + this.assert(KOrNaryExpr(ctx, reificationVariables)) formula = applyMaxRes(formulaReified, reificationVariables) @@ -76,10 +75,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && x.weight == 1 } - softConstraints.removeIf { x -> - x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && - x.weight == 1 - } // TODO: fix hard/soft constraints sets! if (i < reificationVariables.size - 1) { @@ -122,11 +117,11 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return formula } - private fun getUnsatCoreOfConstraints(unsatCore: List>): List { + private fun getUnsatCoreOfConstraints(formula: MutableList, unsatCore: List>): List { val unsatCoreOfConstraints = mutableListOf() for (coreElement in unsatCore) { - val softConstraint = softConstraints.find { x -> x.constraint == coreElement } + val softConstraint = formula.find { x -> x.constraint == coreElement } softConstraint?.let { unsatCoreOfConstraints.add(it) } } @@ -139,10 +134,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : for (coreElement in unsatCore.withIndex()) { if (coreElement.value.weight == 1) { formula.remove(coreElement.value) - softConstraints.remove(coreElement.value) val coreElementConstraint = coreElement.value.constraint - // TODO: какой тут должен быть контекст? // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? val reificationVariable = ctx.boolSort.mkConst("b$i${coreElement.index}") @@ -156,7 +149,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : this.assert(reificationConstraint) formula.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) - softConstraints.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) unitConstraintExpressions.add(reificationVariable) @@ -168,9 +160,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } // Returns issat, unsat core (?) and assignment - private fun solveSMT(softConstraints: List): Triple>, KModel?> { - // Здесь нужно очистить и заполнить солвер assert-ами. - val solverStatus = solver.checkWithAssumptions(softConstraints.map { x -> x.constraint }) + private fun solveSMT(assumptions: List): Triple>, KModel?> { + val solverStatus = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) if (solverStatus == KSolverStatus.SAT) { return Triple(solverStatus, listOf(), solver.model()) @@ -186,7 +177,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } override fun assert(expr: KExpr) { - // hardConstraints.add(HardConstraint(expr)) solver.assert(expr) } From 72d2f97323ceb3e50af629a244eee03fcafcd039 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 26 Jun 2023 12:20:05 +0300 Subject: [PATCH 006/228] Improve wording in variable names --- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index 56bed9a9a..395892efa 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -66,45 +66,40 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } } - private fun applyMaxRes(formula: MutableList, reificationVariables: List>): MutableList { - for (i in reificationVariables.indices) { + private fun applyMaxRes(formula: MutableList, literalsToReify: List>): MutableList { + for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes - val reificationVar = reificationVariables[i] formula.removeIf { x -> - x.constraint.internEquals(KNotExpr(ctx, reificationVar)) && + x.constraint.internEquals(KNotExpr(ctx, indexedLiteral.value)) && x.weight == 1 } - // TODO: fix hard/soft constraints sets! - if (i < reificationVariables.size - 1) { - // TODO: uncommented, commented in order to build -/* - val reifiedLiteralsDisjunction = KOrNaryExpr( + val index = indexedLiteral.index + val indexLast = literalsToReify.size - 1 + + if (index < indexLast) { + val disjunction = KOrNaryExpr( ctx, - reificationVariables.subList(i + 1, reificationVariables.size - 1), + literalsToReify.subList(index + 1, indexLast), ) -*/ - val reifiedVar = ctx.boolSort.mkConst("d$i") + val literalToReifyDisjunction = ctx.boolSort.mkConst("d$indexedLiteral") - // TODO: assert it. -/* formula.add( - HardConstraint( - KEqExpr( - ctx, - reifiedVar, - reifiedLiteralsDisjunction, - ), + this.assert( + KEqExpr( + ctx, + literalToReifyDisjunction, + disjunction, ), - )*/ + ) formula.add( SoftConstraint( KOrBinaryExpr( ctx, - KNotExpr(ctx, reificationVar), - KNotExpr(ctx, reifiedVar), + KNotExpr(ctx, indexedLiteral.value), + KNotExpr(ctx, literalToReifyDisjunction), ), 1, ), @@ -129,7 +124,7 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { - val unitConstraintExpressions = mutableListOf>() + val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { if (coreElement.value.weight == 1) { @@ -137,39 +132,39 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : val coreElementConstraint = coreElement.value.constraint // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? - val reificationVariable = + val literalToReify = ctx.boolSort.mkConst("b$i${coreElement.index}") - val reificationConstraint = KEqExpr( + val constraintToReify = KEqExpr( ctx, coreElementConstraint, - KNotExpr(ctx, reificationVariable), + KNotExpr(ctx, literalToReify), ) // TODO: Переобозначить и остальные элементы в b_i_j - this.assert(reificationConstraint) + this.assert(constraintToReify) - formula.add(SoftConstraint(KNotExpr(ctx, reificationVariable), 1)) + formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), 1)) - unitConstraintExpressions.add(reificationVariable) + literalsToReify.add(literalToReify) - return Pair(formula, unitConstraintExpressions) + return Pair(formula, literalsToReify) } } - error("reify core method, not implemented part") + return Pair(formula, emptyList()) } // Returns issat, unsat core (?) and assignment private fun solveSMT(assumptions: List): Triple>, KModel?> { - val solverStatus = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) + val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) - if (solverStatus == KSolverStatus.SAT) { - return Triple(solverStatus, listOf(), solver.model()) - } else if (solverStatus == KSolverStatus.UNSAT) { - return Triple(solverStatus, solver.unsatCore(), null) + if (status == KSolverStatus.SAT) { + return Triple(status, listOf(), solver.model()) + } else if (status == KSolverStatus.UNSAT) { + return Triple(status, solver.unsatCore(), null) } - return Triple(solverStatus, listOf(), null) + return Triple(status, listOf(), null) } override fun configure(configurator: KSolverConfiguration.() -> Unit) { From 760d7d451135c4845ac0d8b2a11a1fd0bdd3df13 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 26 Jun 2023 12:22:45 +0300 Subject: [PATCH 007/228] Update methods' order --- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index 395892efa..8809ac562 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -66,6 +66,50 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : } } + // Returns issat, unsat core (?) and assignment + private fun solveSMT(assumptions: List): Triple>, KModel?> { + val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) + + if (status == KSolverStatus.SAT) { + return Triple(status, listOf(), solver.model()) + } else if (status == KSolverStatus.UNSAT) { + return Triple(status, solver.unsatCore(), null) + } + + return Triple(status, listOf(), null) + } + + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { + val literalsToReify = mutableListOf>() + + for (coreElement in unsatCore.withIndex()) { + if (coreElement.value.weight == 1) { + formula.remove(coreElement.value) + + val coreElementConstraint = coreElement.value.constraint + // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? + val literalToReify = + ctx.boolSort.mkConst("b$i${coreElement.index}") + + val constraintToReify = KEqExpr( + ctx, + coreElementConstraint, + KNotExpr(ctx, literalToReify), + ) + // TODO: Переобозначить и остальные элементы в b_i_j + this.assert(constraintToReify) + + formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), 1)) + + literalsToReify.add(literalToReify) + + return Pair(formula, literalsToReify) + } + } + + return Pair(formula, emptyList()) + } + private fun applyMaxRes(formula: MutableList, literalsToReify: List>): MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes @@ -123,50 +167,6 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return unsatCoreOfConstraints } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { - val literalsToReify = mutableListOf>() - - for (coreElement in unsatCore.withIndex()) { - if (coreElement.value.weight == 1) { - formula.remove(coreElement.value) - - val coreElementConstraint = coreElement.value.constraint - // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? - val literalToReify = - ctx.boolSort.mkConst("b$i${coreElement.index}") - - val constraintToReify = KEqExpr( - ctx, - coreElementConstraint, - KNotExpr(ctx, literalToReify), - ) - // TODO: Переобозначить и остальные элементы в b_i_j - this.assert(constraintToReify) - - formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), 1)) - - literalsToReify.add(literalToReify) - - return Pair(formula, literalsToReify) - } - } - - return Pair(formula, emptyList()) - } - - // Returns issat, unsat core (?) and assignment - private fun solveSMT(assumptions: List): Triple>, KModel?> { - val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) - - if (status == KSolverStatus.SAT) { - return Triple(status, listOf(), solver.model()) - } else if (status == KSolverStatus.UNSAT) { - return Triple(status, solver.unsatCore(), null) - } - - return Triple(status, listOf(), null) - } - override fun configure(configurator: KSolverConfiguration.() -> Unit) { solver.configure(configurator) } From 5ba890e6ac1ab8f4284e220c138c2be287e01aff Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 26 Jun 2023 14:40:32 +0300 Subject: [PATCH 008/228] Do not keep and remove from formula hard constraints --- ksmt-maxsmt/build.gradle.kts | 11 ----- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 9 ++-- .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 46 +++++++++++++++++++ settings.gradle.kts | 4 -- 4 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index 38eb14756..3c504c7ec 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("io.ksmt.ksmt-base") - id("com.diffplug.spotless") version "5.10.0" } group = "org.example" @@ -18,13 +17,3 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") } - -spotless { - kotlin { - diktat() - } - - kotlinGradle { - diktat() - } -} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index 8809ac562..b3a33d210 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -79,7 +79,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return Triple(status, listOf(), null) } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int): Pair, List>> { + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int) + : Pair, List>> { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { @@ -110,7 +111,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return Pair(formula, emptyList()) } - private fun applyMaxRes(formula: MutableList, literalsToReify: List>): MutableList { + private fun applyMaxRes(formula: MutableList, literalsToReify: List>) + : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes @@ -156,7 +158,8 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return formula } - private fun getUnsatCoreOfConstraints(formula: MutableList, unsatCore: List>): List { + private fun getUnsatCoreOfConstraints(formula: MutableList, unsatCore: List>) + : List { val unsatCoreOfConstraints = mutableListOf() for (coreElement in unsatCore) { diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt new file mode 100644 index 000000000..e38937f7d --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -0,0 +1,46 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.KContext +import io.ksmt.expr.KNotExpr +import io.ksmt.expr.KOrBinaryExpr +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.utils.mkConst +import org.junit.jupiter.api.Test + +class KMaxSMTSolverTest { + @Test + fun smokeTest() { + with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSMTSolver = KMaxSMTSolver(this, z3Solver) + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + val c = boolSort.mkConst("c") + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(c) + maxSMTSolver.assertSoft(mkAnd(a, mkNot(c)), 1) + maxSMTSolver.assertSoft(mkNot(a), 1) + val (model, iter) = maxSMTSolver.checkMaxSMT() + println("Model:\n$model") + println("Finished on $iter iteration") + } + } + + @Test + fun smokeTest2() { + with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSMTSolver = KMaxSMTSolver(this, z3Solver) + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + maxSMTSolver.assert(KOrBinaryExpr(this, a, b)) + maxSMTSolver.assert(KOrBinaryExpr(this, KNotExpr(this, a), b)) + maxSMTSolver.assertSoft(KOrBinaryExpr(this, a, KNotExpr(this, b)), 1) + maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)), 1) + val (model, iter) = maxSMTSolver.checkMaxSMT() + println("Model:\n$model") + println("Finished on $iter iteration") + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 29a91cdd3..8913bbb76 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,3 @@ pluginManagement { } } } -include("ksmt-maxsmt:src:test:kotlin") -findProject(":ksmt-maxsmt:src:test:kotlin")?.name = "kotlin" -include("ksmt-maxsmt:src:test") -findProject(":ksmt-maxsmt:src:test")?.name = "test" From 2efbc5b9734edc33587e98884027c9ad4e6a517b Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 26 Jun 2023 14:41:14 +0300 Subject: [PATCH 009/228] Add smoke tests --- .../kotlin/io/ksmt/solver/maxsmt/Example.kt | 19 - .../io/ksmt/test/RandomExpressionGenerator.kt | 773 ------------------ .../kotlin/io/ksmt/test/SmtLibParseError.kt | 3 - .../main/kotlin/io/ksmt/test/TestRunner.kt | 144 ---- .../main/kotlin/io/ksmt/test/TestWorker.kt | 12 - .../kotlin/io/ksmt/test/TestWorkerProcess.kt | 331 -------- .../io/ksmt/test/CheckWithAssumptionsTest.kt | 79 -- .../io/ksmt/test/ContextAstInterningTest.kt | 85 -- .../io/ksmt/test/MultiIndexedArrayTest.kt | 587 ------------- .../io/ksmt/test/TestBvOverflowChecks.kt | 390 --------- .../ksmt/test/UninterpretedSortValuesTest.kt | 353 -------- .../test/benchmarks/BenchmarksBasedTest.kt | 716 ---------------- .../benchmarks/BitwuzlaBenchmarksBasedTest.kt | 51 -- .../ContextMemoryUsageBenchmarksBasedTest.kt | 66 -- .../benchmarks/Cvc5BenchmarksBasedTest.kt | 315 ------- .../PortfolioBenchmarksBasedTest.kt | 59 -- .../SerializerBenchmarksBasedTest.kt | 94 --- .../SimplifierBenchmarksBasedTest.kt | 41 - .../benchmarks/YicesBenchmarksBasedTest.kt | 52 -- .../test/benchmarks/Z3BenchmarksBasedTest.kt | 43 - .../test/benchmarks/Z3ConverterBenchmark.kt | 253 ------ 21 files changed, 4466 deletions(-) delete mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt delete mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt delete mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt delete mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt delete mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt delete mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt delete mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt deleted file mode 100644 index 70381ac01..000000000 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/Example.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.ksmt.solver.maxsmt - -import org.junit.jupiter.api.Test -/*import io.ksmt.solver.KSolverStatus -import io.ksmt.sort.KArraySort -import io.ksmt.utils.mkConst*/ - -class Example { -/* @Test - fun test() = with(KContext()) { - val a = boolSort.mkConst("a") - val b = mkFuncDecl("b", boolSort, listOf(intSort)) - val c = intSort.mkConst("c") - val e = mkArraySort(intSort, intSort).mkConst("e") - val solver = KZ3Solver(this) - solver.assert(a) - - }*/ -} diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt deleted file mode 100644 index 78aa7cf40..000000000 --- a/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt +++ /dev/null @@ -1,773 +0,0 @@ -package io.ksmt.test - -import io.ksmt.KAst -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.expr.KFpRoundingMode -import io.ksmt.expr.KInterpretedValue -import io.ksmt.sort.KArray2Sort -import io.ksmt.sort.KArray3Sort -import io.ksmt.sort.KArrayNSort -import io.ksmt.sort.KArraySort -import io.ksmt.sort.KArraySortBase -import io.ksmt.sort.KBoolSort -import io.ksmt.sort.KBvSort -import io.ksmt.sort.KFp128Sort -import io.ksmt.sort.KFp64Sort -import io.ksmt.sort.KFpRoundingModeSort -import io.ksmt.sort.KFpSort -import io.ksmt.sort.KIntSort -import io.ksmt.sort.KRealSort -import io.ksmt.sort.KSort -import io.ksmt.sort.KSortVisitor -import io.ksmt.sort.KUninterpretedSort -import io.ksmt.utils.uncheckedCast -import java.util.SortedMap -import kotlin.random.Random -import kotlin.random.nextInt -import kotlin.random.nextUInt -import kotlin.random.nextULong -import kotlin.reflect.KClass -import kotlin.reflect.KClassifier -import kotlin.reflect.KFunction -import kotlin.reflect.KParameter -import kotlin.reflect.KType -import kotlin.reflect.KTypeParameter -import kotlin.reflect.KTypeProjection -import kotlin.reflect.KVisibility -import kotlin.reflect.full.allSupertypes -import kotlin.reflect.full.createType -import kotlin.reflect.full.isSubclassOf - - -/** - * Expression generation parameters. - * - * [seedExpressionsPerSort] -- initial expressions for each KSMT sort. - * - * [deepExpressionProbability] -- probability of picking the most deeply nested expression as an argument. - * Controls the depth of generated expressions. - * Default: 0.4. In our experiments, the probability value of 0.4 results - * in an average expression depth of about 0.0023 * #generated expressions, 0.3 -- 0.0017, 0.5 -- 0.0029. - * For example, if we generate 10000 expressions, with a probability of 0.4, the average expression depth will be 23, - * 0.3 -- 17 and with a probability of 0.5 -- 29. - * - * [generatedListSize] -- allowed sizes of generated lists. - * Default: 2-10, since lists are mostly used for expressions like And, Or, etc. - * This size range seems to be the most common for expressions of such kind. - * - * [generatedStringLength] -- allowed sizes of generated strings. - * Default: 7-10, since most of generated strings are names of constants, functions, etc. - * - * [possibleIntValues] -- allowed values for Int and UInt values. - * Default: 3-100, since such values are often used for Bv size or Fp size, - * and we don't want these values to be extremely large. - * - * [possibleStringChars] -- allowed symbols for generated strings. - * Default: a-z, since most of generated strings are names of constants, functions, etc. - * */ -data class GenerationParameters( - val seedExpressionsPerSort: Int = 10, - val deepExpressionProbability: Double = 0.4, - val generatedListSize: IntRange = 2..10, - val generatedStringLength: IntRange = 7..10, - val possibleIntValues: IntRange = 3..100, - val possibleStringChars: CharRange = 'a'..'z' -) - -class RandomExpressionGenerator { - private lateinit var generationContext: GenerationContext - - /** - * Generate [limit] random expressions with shared subexpressions. - * */ - fun generate( - limit: Int, - context: KContext, - random: Random = Random(42), - params: GenerationParameters = GenerationParameters(), - generatorFilter: (KFunction<*>) -> Boolean = { true } - ): List> { - generationContext = GenerationContext( - random = random, - context = context, - params = params, - expressionsAmountEstimation = limit, - sortsAmountEstimation = 1000 - ) - - generateInitialSeed(samplesPerSort = params.seedExpressionsPerSort) - - val filteredGenerators = generators.filter { generatorFilter(it.function) } - - while (generationContext.expressions.size < limit) { - val generator = filteredGenerators.random(random) - - nullIfGenerationFailed { - generator.generate(generationContext) - } - } - - return generationContext.expressions - } - - /** - * Recreate previously generated by [generate] expressions in a new [context]. - * */ - fun replay(context: KContext): List> { - val replayContext = GenerationContext( - random = Random(0), - context = context, - params = GenerationParameters(), - expressionsAmountEstimation = generationContext.expressions.size, - sortsAmountEstimation = generationContext.sorts.size - ) - - for (entry in generationContext.trace) { - val resolvedEntry = resolveTraceEntry(entry, replayContext) - val result = resolvedEntry.call(context) - when (result) { - // We don't care about expression depth since it is unused during replay - is KExpr<*> -> replayContext.registerExpr(result, depth = 0, inReplayMode = true) - is KSort -> replayContext.registerSort(result, inReplayMode = true) - } - } - - return replayContext.expressions - } - - private fun resolveTraceEntry( - entry: FunctionInvocation, - replayContext: GenerationContext - ): FunctionInvocation { - val args = entry.args.map { resolveArgument(it, replayContext) } - return FunctionInvocation(entry.function, args) - } - - private fun resolveArgument(argument: Argument, replayContext: GenerationContext): Argument = - when (argument) { - is SimpleArgument -> argument - is ListArgument -> ListArgument(argument.nested.map { resolveArgument(it, replayContext) }) - is ExprArgument -> ExprArgument(replayContext.expressions[argument.idx], argument.idx, argument.depth) - is SortArgument -> SortArgument(replayContext.sorts[argument.idx], argument.idx) - } - - private fun generateInitialSeed(samplesPerSort: Int) { - val (simpleGenerators, complexGenerators) = sortGenerators.partition { it.refSortProviders.isEmpty() } - - for (sortGenerator in simpleGenerators) { - repeat(samplesPerSort) { - nullIfGenerationFailed { sortGenerator.mkSeed(generationContext) } - } - } - for (sortGenerator in complexGenerators) { - repeat(samplesPerSort) { - nullIfGenerationFailed { sortGenerator.mkSeed(generationContext) } - } - } - } - - companion object { - - val noFreshConstants: (KFunction<*>) -> Boolean = { it != freshConstGen } - - private val ctxFunctions by lazy { - KContext::class.members - .filter { it.visibility == KVisibility.PUBLIC } - .filterIsInstance>() - } - - private val generators by lazy { - ctxFunctions - .asSequence() - .filter { it.returnType.isKExpr() } - .map { it.uncheckedCast, KFunction>>() } - .filterComplexStringGenerators() - .mapNotNull { - nullIfGenerationFailed { - it.mkGenerator()?.uncheckedCast, AstGenerator>() - } - } - .toList() - } - - private val sortGenerators by lazy { - ctxFunctions - .asSequence() - .filter { it.returnType.isKSort() } - .map { it.uncheckedCast, KFunction>() } - .mapNotNull { - nullIfGenerationFailed { - it.mkGenerator()?.uncheckedCast, AstGenerator>() - } - } - .toList() - } - - private val freshConstGen: KFunction> by lazy { KContext::mkFreshConst } - - /** - * Filter out generators that require a string with special format. - * E.g. binary string for Bv or numeric for IntNum. - * */ - private fun Sequence>>.filterComplexStringGenerators() = this - .filterNot { (it.name == "mkBv" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } - .filterNot { (it.name == "mkBvHex" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } - .filterNot { (it.name == "mkIntNum" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } - .filterNot { (it.name == "mkRealNum" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } - - private val boolGen by lazy { generators.single { it.function.match("mkBool", Boolean::class) } } - private val intGen by lazy { generators.single { it.function.match("mkIntNum", Int::class) } } - private val realGen by lazy { generators.single { it.function.match("mkRealNum", Int::class) } } - private val bvGen by lazy { generators.single { it.function.match("mkBv", Int::class, UInt::class) } } - private val fpGen by lazy { generators.single { it.function.match("mkFp", Double::class, KSort::class) } } - private val arrayGen by lazy { - generators.single { - it.function.match("mkArrayConst", KSort::class, KExpr::class) - } - } - private val fpRmGen by lazy { - generators.single { - it.function.match("mkFpRoundingModeExpr", KFpRoundingMode::class) - } - } - private val constGen by lazy { - generators.single { - it.function.match("mkConst", String::class, KSort::class) - } - } - private val arraySortGen by lazy { - sortGenerators.single { - it.function.match("mkArraySort", KSort::class, KSort::class) - } - } - - private fun KType.isSubclassOf(other: KClass<*>): Boolean = - when (val cls = classifier) { - is KClass<*> -> cls.isSubclassOf(other) - is KTypeParameter -> cls.upperBounds.all { it.isSubclassOf(other) } - else -> false - } - - private fun KType.isSimple(): Boolean = - simpleValueGenerators.keys.any { this.isSubclassOf(it) } - - private fun KType.isKExpr(): Boolean = isSubclassOf(KExpr::class) - private fun KType.isConst(): Boolean = isSubclassOf(KInterpretedValue::class) - private fun KType.isKSort(): Boolean = isSubclassOf(KSort::class) - private fun KType.isKContext(): Boolean = this == KContext::class.createType() - - private fun KFunction<*>.match(name: String, vararg valueParams: KClass<*>): Boolean { - if (this.name != name) return false - val actualValueParams = parameters.drop(1) - if (actualValueParams.size != valueParams.size) return false - return valueParams.zip(actualValueParams).all { (expect, actual) -> actual.type.isSubclassOf(expect) } - } - - private fun KFunction.mkGenerator(): AstGenerator<*>? { - if (!parametersAreCorrect()) return null - val valueParams = parameters.drop(1) - - val typeParametersProviders = hashMapOf() - typeParameters.forEach { - it.mkReferenceSortProvider(typeParametersProviders) - } - - val argumentProviders = valueParams.map { - it.mkArgProvider(typeParametersProviders) ?: return null - } - - return AstGenerator(this, typeParametersProviders, argumentProviders) { args -> - when (name) { - /** - * Bv repeat operation can enormously increase the size of bv. - * To avoid extremely large bvs, we move the repetitions count to the range 1..3. - * */ - "mkBvRepeatExpr", "mkBvRepeatExprNoSimplify" -> listOf( - SimpleArgument(((args[0] as SimpleArgument).value as Int) % 3 + 1), - args[1] - ) - - "mkFpRemExpr" -> { - val argSort = (args[0] as? ExprArgument)?.value?.sort as? KFpSort - if (argSort != null && argSort.exponentBits > KFp128Sort.exponentBits) { - generationFailed( - "Exponent size ${argSort.exponentBits} can result in slow fp.rem computation" - ) - } - args - } - - /** - * Avoid floating point with extremely large exponents since - * such expression creation may involve slow computations. - * */ - "mkFpSort" -> { - val exponentSize = (args[0] as SimpleArgument).value as UInt - val significandSize = (args[1] as SimpleArgument).value as UInt - listOf( - SimpleArgument(exponentSize.coerceAtMost(KFp128Sort.exponentBits)), - SimpleArgument(significandSize) - ) - } - - else -> args - } - } - } - - private fun KFunction<*>.parametersAreCorrect(): Boolean { - if (parameters.isEmpty()) return false - if (parameters.any { it.kind == KParameter.Kind.EXTENSION_RECEIVER }) return false - if (parameters[0].kind != KParameter.Kind.INSTANCE || !parameters[0].type.isKContext()) return false - if (parameters.drop(1).any { it.kind != KParameter.Kind.VALUE }) return false - return true - } - - private fun KParameter.mkArgProvider(typeParametersProviders: MutableMap): ArgumentProvider? { - if (type.isKExpr()) { - val sortProvider = type.mkKExprSortProvider(typeParametersProviders) - return if (type.isConst()) { - ConstExprProvider(sortProvider) - } else { - ExprProvider(sortProvider) - } - } - - if (type.isKSort()) { - return type.mkSortProvider(typeParametersProviders) - } - - if (type.isSubclassOf(List::class)) { - val elementType = type.arguments.single().type ?: return null - if (!elementType.isKExpr()) return null - val sortProvider = elementType.mkKExprSortProvider(typeParametersProviders) - return ListProvider(sortProvider) - } - - if (type.isSimple()) { - val cls = type.classifier as? KClass<*> ?: return null - return SimpleProvider(cls) - } - - val sortClass = type.classifier - if (sortClass is KTypeParameter && sortClass.name in typeParametersProviders) { - return type.mkSortProvider(typeParametersProviders) - } - - return null - } - - private fun KTypeParameter.mkReferenceSortProvider(references: MutableMap) { - val sortProvider = if (upperBounds.size == 1 && upperBounds.single().isKSort()) { - upperBounds.single().mkSortProvider(references) - } else { - generationFailed("Not a KSort type argument") - } - references[name] = sortProvider - } - - private fun KType.mkKExprSortProvider(references: MutableMap): SortProvider { - val expr = findKExprType() - val sort = expr.arguments.single() - if (sort == KTypeProjection.STAR) return SingleSortProvider(KSort::class.java) - val sortType = sort.type ?: generationFailed("No type available") - return sortType.mkSortProvider(references) - } - - private fun KType.findKExprType() = if (classifier == KExpr::class) { - this - } else { - (classifier as? KClass<*>)?.allSupertypes?.find { it.classifier == KExpr::class } - ?: generationFailed("No KExpr superclass found") - } - - private fun KType.mkSortProvider(references: MutableMap): SortProvider { - val sortClass = classifier - if (sortClass is KTypeParameter) { - if (sortClass.name !in references) { - sortClass.mkReferenceSortProvider(references) - } - return ReferenceSortProvider(sortClass.name) - } - - if (this.isSubclassOf(KArraySort::class)) { - val (domain, range) = arguments.map { - it.type?.mkSortProvider(references) - ?: generationFailed("Array sort type is not available") - } - return ArraySortProvider(domain, range) - } - - if (this.isKSort() && sortClass != null) { - return SingleSortProvider((sortClass.uncheckedCast>()).java) - } - - generationFailed("Unexpected type $this") - } - - - private class ConstExprGenerator( - val sortArgument: SortArgument, - val generationContext: GenerationContext - ) : KSortVisitor> { - override fun visit(sort: KBoolSort): AstGenerator = boolGen - override fun visit(sort: KIntSort): AstGenerator = intGen - override fun visit(sort: KRealSort): AstGenerator = realGen - override fun visit(sort: KFpRoundingModeSort): AstGenerator = fpRmGen - - override fun visit(sort: S): AstGenerator = - AstGenerator(bvGen.function, bvGen.refSortProviders, listOf(SimpleProvider(Int::class))) { args -> - args + SimpleArgument(sort.sizeBits) - } - - override fun visit(sort: S): AstGenerator = - AstGenerator(fpGen.function, fpGen.refSortProviders, listOf(SimpleProvider(Double::class))) { args -> - args + sortArgument - } - - override fun visit(sort: KUninterpretedSort): AstGenerator = - AstGenerator(constGen.function, constGen.refSortProviders, listOf(SimpleProvider(String::class))) { args -> - args + sortArgument - } - - private fun , R : KSort> generateArray(sort: A): AstGenerator { - val rangeSortArgument = SortArgument(sort.range, generationContext.findSortIdx(sort.range)) - val rangeExprGenerator = sort.range.accept(ConstExprGenerator(rangeSortArgument, generationContext)) - val rangeExpr = rangeExprGenerator.generate(generationContext) - return AstGenerator(arrayGen.function, arrayGen.refSortProviders, emptyList()) { - listOf(sortArgument, rangeExpr) - } - } - - override fun visit(sort: KArraySort): AstGenerator = - generateArray(sort) - - override fun visit( - sort: KArray2Sort - ): AstGenerator = generateArray(sort) - - override fun visit( - sort: KArray3Sort - ): AstGenerator = generateArray(sort) - - override fun visit(sort: KArrayNSort): AstGenerator = - generateArray(sort) - } - - sealed interface Argument { - val value: Any - val depth: Int - } - - class ListArgument(val nested: List) : Argument { - override val value: Any - get() = nested.map { it.value } - - override val depth: Int - get() = nested.maxOf { it.depth } - } - - class SimpleArgument(override val value: Any) : Argument { - override val depth: Int = 0 - } - - class SortArgument(override val value: KSort, val idx: Int) : Argument { - override val depth: Int = 0 - } - - class ExprArgument(override val value: KExpr<*>, val idx: Int, override val depth: Int) : Argument - - private sealed interface ArgumentProvider { - fun provide(generationContext: GenerationContext, references: Map): Argument - } - - private sealed interface SortProvider : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument = - resolve(generationContext, references) - - fun resolve(generationContext: GenerationContext, references: Map): SortArgument - } - - private class SingleSortProvider(val sort: Class) : SortProvider { - override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { - val candidates = generationContext.sortIndex[sort] ?: generationFailed("No sort matching $sort") - val idx = candidates.random(generationContext.random) - return SortArgument(generationContext.sorts[idx], idx) - } - } - - private class ReferenceSortProvider(val reference: String) : SortProvider { - override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { - return references[reference] ?: generationFailed("Unresolved sort reference $references") - } - } - - private class ArraySortProvider(val domain: SortProvider, val range: SortProvider) : SortProvider { - override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { - val generationParams = mapOf( - "D" to domain, - "R" to range - ) - val generator = AstGenerator( - arraySortGen.function, - generationParams, - generationParams.keys.map { ReferenceSortProvider(it) } - ) - val expr = generator.mkSeed(generationContext, references) - val sort = expr.value.sort - val sortIdx = generationContext.findSortIdx(sort) - return SortArgument(generationContext.sorts[sortIdx], sortIdx) - } - } - - private class SimpleProvider(val type: KClass<*>) : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument { - val value = type.generateSimpleValue(generationContext) - return SimpleArgument(value) - } - } - - private class ListProvider(val element: SortProvider) : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument { - val concreteSort = element.resolve(generationContext, references).value - val size = generationContext.random.nextInt(generationContext.params.generatedListSize) - val candidateExpressions = generationContext.expressionIndex[concreteSort] - ?: generationFailed("No expressions for sort $concreteSort") - - val nested = List(size) { - val (exprId, exprDepth) = selectRandomExpressionId(generationContext, candidateExpressions) - val expr = generationContext.expressions[exprId] - ExprArgument(expr, exprId, exprDepth) - } - return ListArgument(nested) - } - } - - private class ExprProvider(val sort: SortProvider) : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument { - val concreteSort = sort.resolve(generationContext, references).value - val candidateExpressions = generationContext.expressionIndex[concreteSort] - ?: generationFailed("No expressions for sort $concreteSort") - val (exprId, exprDepth) = selectRandomExpressionId(generationContext, candidateExpressions) - return ExprArgument(generationContext.expressions[exprId], exprId, exprDepth) - } - } - - private class ConstExprProvider(val sort: SortProvider) : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument { - val concreteSort = sort.resolve(generationContext, references) - val constants = generationContext.constantIndex[concreteSort.value] ?: emptyList() - - return if (constants.isNotEmpty()) { - val idx = constants.random(generationContext.random) - ExprArgument(generationContext.expressions[idx], idx, depth = 1) - } else { - val generator = concreteSort.value.accept(ConstExprGenerator(concreteSort, generationContext)) - generator.generate(generationContext, references) - } - } - } - - private class AstGenerator( - val function: KFunction<*>, - val refSortProviders: Map, - val argProviders: List, - val provideArguments: (List) -> List = { it } - ) : ArgumentProvider { - override fun provide(generationContext: GenerationContext, references: Map): Argument = - generate(generationContext, references) - - fun generate(generationContext: GenerationContext, context: Map = emptyMap()): T { - val resolvedRefProviders = refSortProviders.mapValues { - it.value.resolve(generationContext, context) - } - val baseArguments = argProviders.map { - it.provide(generationContext, context + resolvedRefProviders) - } - val arguments = provideArguments(baseArguments) - - val invocation = FunctionInvocation(function, arguments) - - val ast = try { - invocation.call(generationContext.context) - } catch (ex: Throwable) { - throw GenerationFailedException("Generator failed", ex) - } - - if (ast is KExpr<*>) { - if (!ast.isCorrect()) { - generationFailed("Incorrect ast generated") - } - val depth = (arguments.maxOfOrNull { it.depth } ?: 0) + 1 - val idx = generationContext.registerExpr(ast, depth) - generationContext.trace += invocation - return ExprArgument(ast, idx, depth).uncheckedCast() - } - - if (ast is KSort) { - var idx = generationContext.registerSort(ast) - if (idx == -1) { - idx = generationContext.findSortIdx(ast) - } - generationContext.trace += invocation - return SortArgument(ast, idx).uncheckedCast() - } - - generationFailed("Unexpected generation result: $ast") - } - - private fun KExpr<*>.isCorrect(): Boolean { - val sort = sort - if (sort is KBvSort && sort.sizeBits == 0u) return false - return true - } - } - - private fun AstGenerator.mkSeed( - generationContext: GenerationContext, - context: Map = emptyMap() - ): ExprArgument { - val exprGenerator = AstGenerator( - constGen.function, - emptyMap(), - listOf(SimpleProvider(String::class), this) - ) - return exprGenerator.generate(generationContext, context) - } - - private fun selectRandomExpressionId( - context: GenerationContext, - expressionIds: SortedMap> - ): Pair { - val expressionDepth = when (context.random.nextDouble()) { - in 0.0..context.params.deepExpressionProbability -> expressionIds.lastKey() - else -> expressionIds.keys.random(context.random) - } - val candidateExpressions = expressionIds.getValue(expressionDepth) - val exprId = candidateExpressions.random(context.random) - return exprId to expressionDepth - } - - private val simpleValueGenerators = mapOf, GenerationContext.(KClass<*>) -> Any>( - Boolean::class to { random.nextBoolean() }, - Byte::class to { random.nextInt().toByte() }, - UByte::class to { random.nextInt().toUByte() }, - Short::class to { random.nextInt().toShort() }, - UShort::class to { random.nextInt().toUShort() }, - Int::class to { random.nextInt(params.possibleIntValues) }, - UInt::class to { - val range = params.possibleIntValues.let { it.first.toUInt()..it.last.toUInt() } - random.nextUInt(range) - }, - Long::class to { random.nextLong() }, - ULong::class to { random.nextULong() }, - Float::class to { random.nextFloat() }, - Double::class to { random.nextDouble() }, - String::class to { - val stringLength = random.nextInt(params.generatedStringLength) - val chars = CharArray(stringLength) { params.possibleStringChars.random(random) } - String(chars) - }, - Enum::class to { it.java.enumConstants.random(random) } - ) - - private fun KClass<*>.generateSimpleValue(context: GenerationContext): Any { - val generator = simpleValueGenerators[this] - ?: (if (this.isSubclassOf(Enum::class)) simpleValueGenerators[Enum::class] else null) - ?: generationFailed("Unexpected simple type: $this") - return context.generator(this) - } - - private class FunctionInvocation( - val function: KFunction<*>, - val args: List - ) { - fun call(ctx: KContext): Any? { - val argumentValues = args.map { it.value } - return function.call(ctx, *argumentValues.toTypedArray()) - } - } - - private class GenerationContext( - val random: Random, - val context: KContext, - val params: GenerationParameters, - expressionsAmountEstimation: Int, - sortsAmountEstimation: Int - ) { - val expressions = ArrayList>(expressionsAmountEstimation) - val sorts = ArrayList(sortsAmountEstimation) - val registeredSorts = HashMap(sortsAmountEstimation) - val expressionIndex = hashMapOf>>() - val constantIndex = hashMapOf>() - val sortIndex = hashMapOf, MutableList>() - val trace = ArrayList(expressionsAmountEstimation + sortsAmountEstimation) - - fun registerSort(sort: KSort, inReplayMode: Boolean = false): Int { - val knownSortId = registeredSorts[sort] - - val sortId = if (knownSortId != null) { - knownSortId - } else { - val idx = sorts.size - sorts.add(sort) - registeredSorts[sort] = idx - idx - } - - if (knownSortId != null || inReplayMode) { - return sortId - } - - var sortCls: Class<*> = sort::class.java - while (sortCls != KSort::class.java) { - sortIndex.getOrPut(sortCls) { arrayListOf() }.add(sortId) - sortCls = sortCls.superclass - } - sortIndex.getOrPut(sortCls) { arrayListOf() }.add(sortId) - - return sortId - } - - fun registerExpr(expr: KExpr<*>, depth: Int, inReplayMode: Boolean = false): Int { - registerSort(expr.sort, inReplayMode) - - val exprId = expressions.size - expressions.add(expr) - - if (inReplayMode) { - return exprId - } - - val index = expressionIndex.getOrPut(expr.sort) { sortedMapOf() } - val expressionIds = index.getOrPut(depth) { arrayListOf() } - expressionIds.add(exprId) - - if (expr is KInterpretedValue<*>) { - constantIndex.getOrPut(expr.sort) { arrayListOf() }.add(exprId) - } - - return exprId - } - - fun findSortIdx(sort: KSort): Int = - registeredSorts[sort] ?: generationFailed("No idx for sort $sort") - } - } -} - -@Suppress("SwallowedException") -private inline fun nullIfGenerationFailed(body: () -> T): T? = try { - body() -} catch (ex: GenerationFailedException) { - null -} - -private class GenerationFailedException : Exception { - constructor(message: String) : super(message) - constructor(message: String, cause: Throwable) : super(message, cause) -} - -private fun generationFailed(message: String): Nothing = - throw GenerationFailedException(message) diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt deleted file mode 100644 index f2b9f0399..000000000 --- a/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.ksmt.test - -class SmtLibParseError(cause: Throwable) : Exception(cause) diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt deleted file mode 100644 index 55910c12f..000000000 --- a/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt +++ /dev/null @@ -1,144 +0,0 @@ -package io.ksmt.test - -import com.jetbrains.rd.util.reactive.RdFault -import kotlinx.coroutines.withTimeout -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.runner.core.KsmtWorkerSession -import io.ksmt.runner.generated.models.EqualityCheckAssumptionsParams -import io.ksmt.runner.generated.models.EqualityCheckParams -import io.ksmt.runner.generated.models.TestAssertParams -import io.ksmt.runner.generated.models.TestInternalizeAndConvertParams -import io.ksmt.runner.generated.models.TestProtocolModel -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.KSolverUnsupportedFeatureException -import io.ksmt.sort.KBoolSort -import java.nio.file.Path -import kotlin.time.Duration -import kotlin.time.DurationUnit - -class TestRunner( - val ctx: KContext, - private val hardTimeout: Duration, - private val worker: KsmtWorkerSession, -) { - suspend fun init() = withTimeoutAndExceptionHandling { - worker.protocolModel.create.startSuspending(worker.lifetime, Unit) - } - - suspend fun delete() = withTimeoutAndExceptionHandling { - if (!worker.isAlive) return@withTimeoutAndExceptionHandling - worker.protocolModel.delete.startSuspending(worker.lifetime, Unit) - } - - suspend fun parseFile(path: Path): List = withTimeoutAndExceptionHandling { - worker.protocolModel.parseFile.startSuspending(worker.lifetime, path.toFile().absolutePath) - } - - suspend fun convertAssertions(nativeAssertions: List): List> = - withTimeoutAndExceptionHandling { - val result = worker.protocolModel.convertAssertions.startSuspending(worker.lifetime, nativeAssertions) - result.expressions.map { - @Suppress("UNCHECKED_CAST") - it as KExpr - } - } - - suspend fun internalizeAndConvertBitwuzla(assertions: List>): List> = - withTimeoutAndExceptionHandling { - val params = TestInternalizeAndConvertParams(assertions) - val result = worker.protocolModel.internalizeAndConvertBitwuzla.startSuspending(worker.lifetime, params) - result.expressions.map { - @Suppress("UNCHECKED_CAST") - it as KExpr - } - } - - suspend fun internalizeAndConvertYices(assertions: List>): List> = - withTimeoutAndExceptionHandling { - val params = TestInternalizeAndConvertParams(assertions) - val result = worker.protocolModel.internalizeAndConvertYices.startSuspending(worker.lifetime, params) - result.expressions.map { - @Suppress("UNCHECKED_CAST") - it as KExpr - } - } - - suspend fun internalizeAndConvertCvc5(assertions: List>): List> = - withTimeoutAndExceptionHandling { - val params = TestInternalizeAndConvertParams(assertions) - val result = worker.protocolModel.internalizeAndConvertCvc5.startSuspending(worker.lifetime, params) - result.expressions.map { - @Suppress("UNCHECKED_CAST") - it as KExpr - } - } - - suspend fun createSolver(timeout: Duration): Int = withTimeoutAndExceptionHandling { - val timeoutValue = timeout.toInt(DurationUnit.MILLISECONDS) - worker.protocolModel.createSolver.startSuspending(worker.lifetime, timeoutValue) - } - - suspend fun assert(solver: Int, expr: Long) = withTimeoutAndExceptionHandling { - worker.protocolModel.assert.startSuspending(worker.lifetime, TestAssertParams(solver, expr)) - } - - suspend fun check(solver: Int): KSolverStatus = withTimeoutAndExceptionHandling { - worker.protocolModel.check.startSuspending(worker.lifetime, solver).status - } - - suspend fun addEqualityCheck(solver: Int, actual: KExpr<*>, expected: Long) = withTimeoutAndExceptionHandling { - val params = EqualityCheckParams(solver, actual, expected) - worker.protocolModel.addEqualityCheck.startSuspending(worker.lifetime, params) - } - - suspend fun addEqualityCheckAssumption(solver: Int, assumption: KExpr) = - withTimeoutAndExceptionHandling { - val params = EqualityCheckAssumptionsParams(solver, assumption) - worker.protocolModel.addEqualityCheckAssumption.startSuspending(worker.lifetime, params) - } - - suspend fun checkEqualities(solver: Int): KSolverStatus = withTimeoutAndExceptionHandling { - worker.protocolModel.checkEqualities.startSuspending(worker.lifetime, solver).status - } - - suspend fun findFirstFailedEquality(solver: Int): Int? = withTimeoutAndExceptionHandling { - worker.protocolModel.findFirstFailedEquality.startSuspending(worker.lifetime, solver) - } - - suspend fun exprToString(expr: Long): String = withTimeoutAndExceptionHandling { - worker.protocolModel.exprToString.startSuspending(worker.lifetime, expr) - } - - suspend fun getReasonUnknown(solver: Int): String = withTimeoutAndExceptionHandling { - worker.protocolModel.getReasonUnknown.startSuspending(worker.lifetime, solver) - } - - suspend fun mkTrueExpr(): Long = withTimeoutAndExceptionHandling { - worker.protocolModel.mkTrueExpr.startSuspending(worker.lifetime, Unit) - } - - @Suppress("TooGenericExceptionCaught", "SwallowedException", "ThrowsCount") - private suspend inline fun withTimeoutAndExceptionHandling(crossinline body: suspend () -> T): T { - try { - return withTimeout(hardTimeout) { - body() - } - } catch (ex: RdFault) { - throw rdExceptionCause(ex) ?: ex - } catch (ex: Exception) { - worker.terminate() - throw ex - } - } - - fun rdExceptionCause(ex: RdFault): Throwable? = when (ex.reasonTypeFqn) { - NotImplementedError::class.simpleName -> - NotImplementedError(ex.reasonMessage) - KSolverUnsupportedFeatureException::class.simpleName -> - KSolverUnsupportedFeatureException(ex.reasonMessage) - SmtLibParseError::class.simpleName -> - SmtLibParseError(ex) - else -> null - } -} diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt deleted file mode 100644 index 6ed7ab482..000000000 --- a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.ksmt.test - -import com.jetbrains.rd.framework.IProtocol -import io.ksmt.runner.core.KsmtWorkerBase -import io.ksmt.runner.core.RdServer -import io.ksmt.runner.generated.models.TestProtocolModel -import io.ksmt.runner.generated.models.testProtocolModel - -class TestWorker(id: Int, process: RdServer) : KsmtWorkerBase(id, process) { - override fun initProtocolModel(protocol: IProtocol): TestProtocolModel = - protocol.testProtocolModel -} diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt deleted file mode 100644 index d6308d7de..000000000 --- a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt +++ /dev/null @@ -1,331 +0,0 @@ -package io.ksmt.test - -import com.jetbrains.rd.framework.IProtocol -import com.microsoft.z3.AST -import com.microsoft.z3.BoolSort -import com.microsoft.z3.Context -import com.microsoft.z3.Expr -import com.microsoft.z3.Native -import com.microsoft.z3.Solver -import com.microsoft.z3.Status -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.runner.core.ChildProcessBase -import io.ksmt.runner.core.KsmtWorkerArgs -import io.ksmt.runner.generated.models.TestCheckResult -import io.ksmt.runner.generated.models.TestConversionResult -import io.ksmt.runner.generated.models.TestProtocolModel -import io.ksmt.runner.generated.models.testProtocolModel -import io.ksmt.runner.serializer.AstSerializationCtx -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.bitwuzla.KBitwuzlaContext -import io.ksmt.solver.bitwuzla.KBitwuzlaExprConverter -import io.ksmt.solver.bitwuzla.KBitwuzlaExprInternalizer -import io.ksmt.solver.cvc5.KCvc5Context -import io.ksmt.solver.cvc5.KCvc5ExprConverter -import io.ksmt.solver.cvc5.KCvc5ExprInternalizer -import io.ksmt.solver.cvc5.KCvc5Solver -import io.github.cvc5.Solver as Cvc5Solver -import io.ksmt.solver.z3.KZ3Context -import io.ksmt.solver.yices.* -import io.ksmt.solver.z3.KZ3ExprConverter -import io.ksmt.solver.z3.KZ3ExprInternalizer -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.sort.KBoolSort -import io.ksmt.utils.uncheckedCast - -class TestWorkerProcess : ChildProcessBase() { - private var workerCtx: KContext? = null - private var workerZ3Ctx: Context? = null - - private val ctx: KContext - get() = workerCtx ?: error("Solver is not initialized") - - private val z3Ctx: Context - get() = workerZ3Ctx ?: error("Solver is not initialized") - - private class OracleSolver( - val solver: Solver, - val solverCtx: KZ3Context - ) - - private val solvers = mutableListOf() - private val nativeAsts = mutableListOf() - private val equalityChecks = mutableMapOf>() - private val equalityCheckAssumptions = mutableMapOf>>() - - private fun create() { - workerCtx = KContext() - workerZ3Ctx = Context() - equalityChecks.clear() - equalityCheckAssumptions.clear() - solvers.clear() - nativeAsts.clear() - } - - private fun delete() { - equalityChecks.clear() - equalityCheckAssumptions.clear() - nativeAsts.clear() - solvers.clear() - workerCtx?.close() - workerZ3Ctx?.close() - workerCtx = null - workerZ3Ctx = null - } - - private fun parseFile(path: String): List = try { - val parsed = z3Ctx.parseSMTLIB2File( - path, - emptyArray(), - emptyArray(), - emptyArray(), - emptyArray() - ) - nativeAsts.addAll(parsed) - nativeAsts.map { z3Ctx.unwrapAST(it) } - } catch (ex: Exception) { - throw SmtLibParseError(ex) - } - - private fun convertAssertions(nativeAssertions: List): List> { - val converter = KZ3ExprConverter(ctx, KZ3Context(ctx, z3Ctx)) - return with(converter) { nativeAssertions.map { it.convertExpr() } } - } - - private fun internalizeAndConvertBitwuzla(assertions: List>): List> = - KBitwuzlaContext(ctx).use { bitwuzlaCtx -> - val internalizer = KBitwuzlaExprInternalizer(bitwuzlaCtx) - val bitwuzlaAssertions = with(internalizer) { - assertions.map { it.internalize() } - } - - val converter = KBitwuzlaExprConverter(ctx, bitwuzlaCtx) - with(converter) { - bitwuzlaAssertions.map { it.convertExpr(ctx.boolSort) } - } - } - - private fun internalizeAndConvertYices(assertions: List>): List> { - // Yices doesn't reverse cache internalized expressions (only interpreted values) - KYicesContext().use { internContext -> - val internalizer = KYicesExprInternalizer(internContext) - - val yicesAssertions = with(internalizer) { - assertions.map { it.internalize() } - } - - val converter = KYicesExprConverter(ctx, internContext) - - return with(converter) { - yicesAssertions.map { it.convert(ctx.boolSort) } - } - } - } - - private fun internalizeAndConvertCvc5(assertions: List>): List> { - KCvc5Solver(ctx).close() // ensure native libs loaded - - return Cvc5Solver().use { cvc5Solver -> - val cvc5Assertions = KCvc5Context(cvc5Solver, ctx).use { cvc5Ctx -> - val internalizer = KCvc5ExprInternalizer(cvc5Ctx) - with(internalizer) { assertions.map { it.internalizeExpr() } } - } - - KCvc5Context(cvc5Solver, ctx).use { cvc5Ctx -> - val converter = KCvc5ExprConverter(ctx, cvc5Ctx) - with(converter) { cvc5Assertions.map { it.convertExpr() } } - } - } - } - - private fun createSolver(timeout: Int): Int { - val solver = with(z3Ctx) { - mkSolver().apply { - val params = mkParams().apply { - add("timeout", timeout) - add("random_seed", SEED_FOR_RANDOM) - } - setParameters(params) - } - } - solvers.add(OracleSolver(solver, KZ3Context(ctx, z3Ctx))) - return solvers.lastIndex - } - - private fun assert(solver: Int, expr: Long) { - @Suppress("UNCHECKED_CAST") - solvers[solver].solver.add(z3Ctx.wrapAST(expr) as Expr) - } - - private fun check(solver: Int): KSolverStatus { - return solvers[solver].solver.check().processCheckResult() - } - - private fun exprToString(expr: Long): String { - return z3Ctx.wrapAST(expr).toString() - } - - private fun getReasonUnknown(solver: Int): String { - return solvers[solver].solver.reasonUnknown - } - - private fun addEqualityCheck(solver: Int, actual: KExpr<*>, expected: Long) { - val actualExpr = internalize(solver, actual) - val expectedExpr = z3Ctx.wrapAST(expected) as Expr<*> - val checks = equalityChecks.getOrPut(solver) { mutableListOf() } - checks += EqualityCheck(actual = actualExpr, expected = expectedExpr) - } - - private fun addEqualityCheckAssumption(solver: Int, assumption: KExpr<*>) { - val assumptionExpr = internalize(solver, assumption) - val assumptions = equalityCheckAssumptions.getOrPut(solver) { mutableListOf() } - assumptions.add(assumptionExpr.uncheckedCast()) - } - - private fun checkEqualities(solver: Int): KSolverStatus = with(z3Ctx) { - val checks = equalityChecks[solver] ?: emptyList() - val equalityBindings = checks.map { mkNot(mkEq(it.actual, it.expected)) } - equalityCheckAssumptions[solver]?.forEach { solvers[solver].solver.add(it) } - solvers[solver].solverCtx.assertPendingAxioms(solvers[solver].solver) - solvers[solver].solver.add(mkOr(*equalityBindings.toTypedArray())) - return check(solver) - } - - private fun findFirstFailedEquality(solver: Int): Int? = with(z3Ctx){ - val solverInstance = solvers[solver].solver - equalityCheckAssumptions[solver]?.forEach { solvers[solver].solver.add(it) } - solvers[solver].solverCtx.assertPendingAxioms(solvers[solver].solver) - val checks = equalityChecks[solver] ?: emptyList() - for ((idx, check) in checks.withIndex()) { - solverInstance.push() - val binding = mkNot(mkEq(check.actual, check.expected)) - solverInstance.add(binding) - val status = solverInstance.check() - solverInstance.pop() - if (status == Status.SATISFIABLE) - return idx - } - return null - } - - private fun mkTrueExpr(): Long = with(z3Ctx) { - val trueExpr = mkTrue().also { nativeAsts.add(it) } - unwrapAST(trueExpr) - } - - private fun Status?.processCheckResult() = when (this) { - Status.SATISFIABLE -> KSolverStatus.SAT - Status.UNSATISFIABLE -> KSolverStatus.UNSAT - Status.UNKNOWN -> KSolverStatus.UNKNOWN - null -> KSolverStatus.UNKNOWN - } - - private fun internalize(solver: Int, expr: KExpr<*>): Expr<*> { - val internCtx = solvers[solver].solverCtx - val internalizer = KZ3ExprInternalizer(ctx, internCtx) - val internalized = with(internalizer) { expr.internalizeExpr() } - return z3Ctx.wrapAST(internalized) as Expr<*> - } - - private data class EqualityCheck(val actual: Expr<*>, val expected: Expr<*>) - - override fun parseArgs(args: Array) = KsmtWorkerArgs.fromList(args.toList()) - - override fun initProtocolModel(protocol: IProtocol): TestProtocolModel = - protocol.testProtocolModel - - @Suppress("LongMethod") - override fun TestProtocolModel.setup(astSerializationCtx: AstSerializationCtx) { - // Limit z3 native memory usage to avoid OOM - Native.globalParamSet("memory_high_watermark_mb", "2048") // 2048 megabytes - - /** - * Memory usage hard limit. - * - * Normally z3 will throw an exception when used - * memory amount is slightly above `memory_high_watermark`. - * But `memory_high_watermark` check may be missed somewhere in Z3 and - * memory usage will become a way higher than limit. - * Therefore, we use hard limit to avoid OOM - */ - Native.globalParamSet("memory_max_size", "4096") // 4096 megabytes - - create.measureExecutionForTermination { - create() - astSerializationCtx.initCtx(ctx) - } - delete.measureExecutionForTermination { - astSerializationCtx.resetCtx() - delete() - } - parseFile.measureExecutionForTermination { path -> - parseFile(path) - } - convertAssertions.measureExecutionForTermination { assertions -> - val converted = convertAssertions(assertions) - TestConversionResult(converted) - } - internalizeAndConvertBitwuzla.measureExecutionForTermination { params -> - @Suppress("UNCHECKED_CAST") - val converted = internalizeAndConvertBitwuzla(params.expressions as List>) - TestConversionResult(converted) - } - internalizeAndConvertYices.measureExecutionForTermination { params -> - @Suppress("UNCHECKED_CAST") - val converted = internalizeAndConvertYices(params.expressions as List>) - TestConversionResult(converted) - } - internalizeAndConvertCvc5.measureExecutionForTermination { params -> - @Suppress("UNCHECKED_CAST") - val converted = internalizeAndConvertCvc5(params.expressions as List>) - TestConversionResult(converted) - } - createSolver.measureExecutionForTermination { timeout -> - createSolver(timeout) - } - assert.measureExecutionForTermination { params -> - assert(params.solver, params.expr) - } - check.measureExecutionForTermination { solver -> - val status = check(solver) - TestCheckResult(status) - } - exprToString.measureExecutionForTermination { solver -> - exprToString(solver) - } - getReasonUnknown.measureExecutionForTermination { solver -> - getReasonUnknown(solver) - } - addEqualityCheck.measureExecutionForTermination { params -> - addEqualityCheck(params.solver, params.actual as KExpr<*>, params.expected) - } - addEqualityCheckAssumption.measureExecutionForTermination { params -> - addEqualityCheckAssumption(params.solver, params.assumption as KExpr<*>) - } - checkEqualities.measureExecutionForTermination { solver -> - val status = checkEqualities(solver) - TestCheckResult(status) - } - findFirstFailedEquality.measureExecutionForTermination { solver -> - findFirstFailedEquality(solver) - } - mkTrueExpr.measureExecutionForTermination { - mkTrueExpr() - } - } - - companion object { - private const val SEED_FOR_RANDOM = 12345 - - init { - // force native library load - KZ3Solver(KContext()).close() - } - - @JvmStatic - fun main(args: Array) { - TestWorkerProcess().start(args) - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt deleted file mode 100644 index 162d82e05..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package io.ksmt.test - -import io.ksmt.KContext -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import io.ksmt.solver.cvc5.KCvc5Solver -import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.utils.getValue -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class CheckWithAssumptionsTest { - - @Test - fun testComplexAssumptionZ3() = testComplexAssumption { KZ3Solver(it) } - - @Test - fun testComplexAssumptionBitwuzla() = testComplexAssumption { KBitwuzlaSolver(it) } - - @Test - fun testComplexAssumptionYices() = testComplexAssumption { KYicesSolver(it) } - - @Test - fun testComplexAssumptionCvc() = testComplexAssumption { KCvc5Solver(it) } - - @Test - fun testUnsatCoreGenerationZ3() = testUnsatCoreGeneration { KZ3Solver(it) } - - @Test - fun testUnsatCoreGenerationBitwuzla() = testUnsatCoreGeneration { KBitwuzlaSolver(it) } - - @Test - fun testUnsatCoreGenerationYices() = testUnsatCoreGeneration { KYicesSolver(it) } - - @Test - fun testUnsatCoreGenerationCvc() = testUnsatCoreGeneration { KCvc5Solver(it) } - - private fun testComplexAssumption(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) { - mkSolver(this).use { solver -> - val a by bv32Sort - val b by bv32Sort - - solver.assert(a eq mkBv(0)) - solver.assert(b eq mkBv(0)) - - val expr = mkBvUnsignedGreaterExpr(a, b) - val status = solver.checkWithAssumptions(listOf(expr)) - - assertEquals(KSolverStatus.UNSAT, status) - } - } - - private fun testUnsatCoreGeneration(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) { - mkSolver(this).use { solver -> - val a by boolSort - val b by boolSort - val c by boolSort - - val e1 = (a and b) or c - val e2 = !(a and b) - val e3 = !c - - solver.assert(e1) - solver.assertAndTrack(e2) - - val status = solver.checkWithAssumptions(listOf(e3)) - assertEquals(KSolverStatus.UNSAT, status) - - val core = solver.unsatCore() - assertEquals(2, core.size) - - assertTrue(e2 in core) - assertTrue(e3 in core) - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt deleted file mode 100644 index 6f9fac035..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.ksmt.test - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import io.ksmt.KContext -import kotlin.random.Random -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertSame - -class ContextAstInterningTest { - - @Test - fun testSingleThreadNoGcContextInterning() = KContext( - operationMode = KContext.OperationMode.SINGLE_THREAD, - astManagementMode = KContext.AstManagementMode.NO_GC - ).runSingleThread(::testExpressionsEquality) - - @Test - fun testSingleThreadWithGcContextInterning() = KContext( - operationMode = KContext.OperationMode.SINGLE_THREAD, - astManagementMode = KContext.AstManagementMode.GC - ).runSingleThread(::testExpressionsEquality) - - @Test - fun testConcurrentNoGcContextInterning() = KContext( - operationMode = KContext.OperationMode.CONCURRENT, - astManagementMode = KContext.AstManagementMode.NO_GC - ).runParallel(::testExpressionsEquality) - - @Test - fun testConcurrentWithGcContextInterning() = KContext( - operationMode = KContext.OperationMode.CONCURRENT, - astManagementMode = KContext.AstManagementMode.GC - ).runParallel(::testExpressionsEquality) - - private fun KContext.runSingleThread(test: (KContext, RandomExpressionGenerator) -> Unit) = - generators.forEach { test(this, it) } - - private fun KContext.runParallel(test: (KContext, RandomExpressionGenerator) -> Unit) = runBlocking { - generators.map { - async(Dispatchers.Default) { test(this@runParallel, it) } - }.joinAll() - } - - private fun testExpressionsEquality(ctx: KContext, generator: RandomExpressionGenerator) { - val e1 = generator.replay(ctx) - val e2 = generator.replay(ctx) - - assertEquals(e1, e2) - e1.zip(e2).forEach { - assertSame(it.first, it.second) - } - } - - companion object { - private val generators = mutableListOf() - - @BeforeAll - @JvmStatic - fun setupGenerators() { - val generationCtx = KContext(KContext.OperationMode.SINGLE_THREAD, KContext.AstManagementMode.NO_GC) - val random = Random(42) - - repeat(20) { - generators += RandomExpressionGenerator().apply { - generate( - 10000, generationCtx, random, - generatorFilter = RandomExpressionGenerator.noFreshConstants - ) - } - } - } - - @AfterAll - @JvmStatic - fun cleanupGenerators() { - generators.clear() - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt deleted file mode 100644 index 923850844..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt +++ /dev/null @@ -1,587 +0,0 @@ -package io.ksmt.test - -import com.microsoft.z3.Context -import io.github.cvc5.Solver -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable -import io.ksmt.KContext -import io.ksmt.KContext.SimplificationMode.NO_SIMPLIFY -import io.ksmt.expr.KExpr -import io.ksmt.expr.rewrite.KExprUninterpretedDeclCollector -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.KSolverUnsupportedFeatureException -import io.ksmt.solver.bitwuzla.KBitwuzlaContext -import io.ksmt.solver.bitwuzla.KBitwuzlaExprConverter -import io.ksmt.solver.bitwuzla.KBitwuzlaExprInternalizer -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import io.ksmt.solver.cvc5.KCvc5Context -import io.ksmt.solver.cvc5.KCvc5ExprConverter -import io.ksmt.solver.cvc5.KCvc5ExprInternalizer -import io.ksmt.solver.cvc5.KCvc5Solver -import io.ksmt.solver.runner.KSolverRunnerManager -import io.ksmt.solver.yices.KYicesContext -import io.ksmt.solver.yices.KYicesExprConverter -import io.ksmt.solver.yices.KYicesExprInternalizer -import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.z3.KZ3Context -import io.ksmt.solver.z3.KZ3ExprConverter -import io.ksmt.solver.z3.KZ3ExprInternalizer -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.sort.KArray2Sort -import io.ksmt.sort.KArray3Sort -import io.ksmt.sort.KArraySort -import io.ksmt.sort.KArraySortBase -import io.ksmt.sort.KBv8Sort -import io.ksmt.sort.KBvSort -import io.ksmt.sort.KSort -import io.ksmt.utils.uncheckedCast -import kotlin.test.Test -import kotlin.test.assertTrue -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds - -@EnabledIfEnvironmentVariable( - named = "runMultiIndexedArrayTest", - matches = "true", - disabledReason = "Disable due to a very long runtime (about 6 hours) which is not applicable for usual CI runs" -) -class MultiIndexedArrayTest { - - @Test - fun testMultiIndexedArraysZ3WithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> - mkZ3Context(this).use { z3NativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertZ3(z3NativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysBitwuzlaWithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> - KBitwuzlaContext(this).use { bitwuzlaNativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertBitwuzla(bitwuzlaNativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysZ3WithBitwuzlaOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KBitwuzlaSolver::class).use { oracleSolver -> - mkZ3Context(this).use { z3NativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertZ3(z3NativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysBitwuzlaWithBitwuzlaOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KBitwuzlaSolver::class).use { oracleSolver -> - KBitwuzlaContext(this).use { bitwuzlaNativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertBitwuzla(bitwuzlaNativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysYicesWithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> - KYicesContext().use { yicesNativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertYices(yicesNativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysZ3WithYicesOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KYicesSolver::class).use { oracleSolver -> - mkZ3Context(this).use { z3NativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertZ3(z3NativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysYicesWithYicesOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KYicesSolver::class).use { oracleSolver -> - KYicesContext().use { yicesNativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertYices(yicesNativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysZ3WithCvc5Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KCvc5Solver::class).use { oracleSolver -> - - // Enable HO to test array lambda equalities - oracleSolver.configure { setCvc5Logic("HO_QF_ALL") } - - mkZ3Context(this).use { z3NativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertZ3(z3NativeCtx, expr) - } - } - } - } - - @Test - fun testMultiIndexedArraysCvc5WithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { - oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> - mkCvc5Context(this).use { cvc5NativeCtx -> - runMultiIndexedArraySamples(oracleSolver) { expr -> - internalizeAndConvertCvc5(cvc5NativeCtx, expr) - } - } - } - } - - private inline fun KContext.runMultiIndexedArraySamples( - oracle: KSolver<*>, - process: (KExpr) -> KExpr - ) { - val stats = TestStats() - val sorts = listOf( - mkArraySort(bv8Sort, bv8Sort), - mkArraySort(bv32Sort, bv16Sort, bv8Sort), - mkArraySort(bv32Sort, bv16Sort, bv8Sort, bv8Sort), - mkArrayNSort(listOf(bv32Sort, bv16Sort, bv8Sort, bv32Sort, bv8Sort), bv8Sort) - ) - - for (sort in sorts) { - val expressions = mkArrayExpressions(sort) - for (expr in expressions) { - stats.start() - stats.withErrorHandling { - val processed = process(expr) - assertEquals(stats, oracle, expr, processed) - } - } - } - - stats.result() - } - - private fun > KContext.mkArrayExpressions(sort: A): List> { - var arrayExpressions = listOfNotNull( - mkConst(sort), -// mkAsArray(sort), // disable as-array because it is too hard to check equality - mkArrayConst(sort) { mkConst("cv", bv8Sort) }, - mkLambda(sort) { mkConst("lv", bv8Sort) } - ) - - arrayExpressions = arrayExpressions + arrayExpressions.map { - mkStore(it) { mkConst("v", bv8Sort) } - } - - arrayExpressions = arrayExpressions + arrayExpressions.flatMap { first -> - arrayExpressions.map { second -> - mkIte(mkConst("cond", boolSort), first, second) - } - } - - val arrayEq = arrayExpressions.crossProduct { first, second -> first eq second } - - var arraySelects = arrayExpressions.map { mkSelect(it) } - - val arrayValues = arraySelects + listOf(mkConst("x", bv8Sort)) - - arrayExpressions = arrayExpressions + arrayExpressions.map { array -> - mkLambda(sort) { indices -> mkSelect(array) { indices.uncheckedCast() } } - } - - arrayExpressions = arrayExpressions + arrayValues.flatMap { value -> - listOf( - mkArrayConst(sort) { value }, - mkLambda(sort) { value }, - ) - } - - arrayExpressions = arrayExpressions + arrayExpressions.flatMap { array -> - arrayValues.map { value -> - mkStore(array) { value } - } - } - - arraySelects = arraySelects + arrayExpressions.map { mkSelect(it) } - - return listOf( - arrayExpressions, - arraySelects, - arrayEq - ).flatten().uncheckedCast() - } - - private inline fun List.crossProduct(transform: (T, T) -> R): List { - val result = mutableListOf() - for (i in indices) { - for (j in i until size) { - result += transform(get(i), get(j)) - } - } - return result - } - - private fun > KContext.mkConst(sort: A): KExpr = - mkFreshConst("c", sort) - - private fun > KContext.mkArrayConst( - sort: A, - value: () -> KExpr - ): KExpr = mkArrayConst(sort, value()) - - private fun > KContext.mkAsArray(sort: A): KExpr { - val function = mkFreshFuncDecl("f", sort.range, sort.domainSorts) - return mkFunctionAsArray(sort, function) - } - - private fun > KContext.mkLambda( - sort: A, - mkBody: (List>) -> KExpr - ): KExpr { - val indices = sort.domainSorts.map { mkFreshConst("i", it) } - val body = mkBody(indices.uncheckedCast()) - return when (indices.size) { - KArraySort.DOMAIN_SIZE -> mkArrayLambda(indices.single().decl, body) - KArray2Sort.DOMAIN_SIZE -> mkArrayLambda(indices.first().decl, indices.last().decl, body) - KArray3Sort.DOMAIN_SIZE -> { - val (i0, i1, i2) = indices - mkArrayLambda(i0.decl, i1.decl, i2.decl, body) - } - - else -> mkArrayNLambda(indices.map { it.decl }, body) - }.uncheckedCast() - } - - private fun > KContext.mkStore( - array: KExpr, - mkValue: () -> KExpr - ): KExpr { - val indices = array.sort.domainSorts.map { mkFreshConst("i", it) } - val value = mkValue() - return when (indices.size) { - KArraySort.DOMAIN_SIZE -> mkArrayStore(array.uncheckedCast(), indices.single(), value) - KArray2Sort.DOMAIN_SIZE -> mkArrayStore(array.uncheckedCast(), indices.first(), indices.last(), value) - KArray3Sort.DOMAIN_SIZE -> { - val (i0, i1, i2) = indices - mkArrayStore(array.uncheckedCast(), i0, i1, i2, value) - } - - else -> mkArrayNStore(array.uncheckedCast(), indices, value) - }.uncheckedCast() - } - - private fun > KContext.mkSelect( - array: KExpr, - mkIndices: KContext.(A) -> List> = { sort -> - sort.domainSorts.map { mkFreshConst("i", it) } - } - ): KExpr { - val indices = mkIndices(array.sort) - return when (indices.size) { - KArraySort.DOMAIN_SIZE -> mkArraySelect(array.uncheckedCast(), indices.single()) - KArray2Sort.DOMAIN_SIZE -> mkArraySelect(array.uncheckedCast(), indices.first(), indices.last()) - KArray3Sort.DOMAIN_SIZE -> { - val (i0, i1, i2) = indices - mkArraySelect(array.uncheckedCast(), i0, i1, i2) - } - - else -> mkArrayNSelect(array.uncheckedCast(), indices) - } - } - - private fun KContext.internalizeAndConvertBitwuzla( - nativeCtx: KBitwuzlaContext, expr: KExpr - ): KExpr { - val internalized = with(KBitwuzlaExprInternalizer(nativeCtx)) { - expr.internalizeExpr() - } - - val converted = with(KBitwuzlaExprConverter(this, nativeCtx)) { - internalized.convertExpr(expr.sort) - } - - return converted - } - - private fun KContext.internalizeAndConvertYices( - nativeCtx: KYicesContext, expr: KExpr - ): KExpr { - val internalized = with(KYicesExprInternalizer(nativeCtx)) { - expr.internalizeExpr() - } - - val converted = with(KYicesExprConverter(this, nativeCtx)) { - internalized.convert(expr.sort) - } - - return converted - } - - private fun KContext.internalizeAndConvertZ3(nativeCtx: Context, expr: KExpr): KExpr { - val z3InternCtx = KZ3Context(this, nativeCtx) - val z3ConvertCtx = KZ3Context(this, nativeCtx) - - val internalized = with(KZ3ExprInternalizer(this, z3InternCtx)) { - expr.internalizeExpr() - } - - // Copy declarations since we have fresh decls - val declarations = KExprUninterpretedDeclCollector.collectUninterpretedDeclarations(expr) - declarations.forEach { - val nativeDecl = z3InternCtx.findInternalizedDecl(it) - z3ConvertCtx.saveConvertedDecl(nativeDecl, it) - } - - val converted = with(KZ3ExprConverter(this, z3ConvertCtx)) { - internalized.convertExpr() - } - - return converted - } - - private fun KContext.internalizeAndConvertCvc5(nativeCtx: Solver, expr: KExpr): KExpr { - val internalizationCtx = KCvc5Context(nativeCtx, this) - val conversionCtx = KCvc5Context(nativeCtx, this) - - val internalizer = KCvc5ExprInternalizer(internalizationCtx) - val converter = KCvc5ExprConverter(this, conversionCtx) - - val internalized = with(internalizer) { - expr.internalizeExpr() - } - - // Copy declarations since we have fresh decls - val declarations = KExprUninterpretedDeclCollector.collectUninterpretedDeclarations(expr) - declarations.forEach { decl -> - internalizationCtx.findInternalizedDecl(decl)?.also { - conversionCtx.saveConvertedDecl(it, decl) - } - } - - val converted = with(converter) { - internalized.convertExpr() - } - - return converted - } - - private fun KContext.assertEquals( - stats: TestStats, - oracle: KSolver<*>, - expected: KExpr, - actual: KExpr - ) { - if (expected == actual) { - stats.passedFast() - return - } - - val satIsPossiblePassed = oracle.scoped { - assertPossibleToBeEqual(oracle, expected, actual) - oracle.checkSatAndReport(stats, expected, actual, KSolverStatus.SAT, "SAT is possible") - } - - val expressionEqualityPassed = oracle.scoped { - assertNotEqual(oracle, expected, actual) - - oracle.checkSatAndReport( - stats, expected, actual, KSolverStatus.UNSAT, "Expressions equal" - ) { - val model = oracle.model() - val actualValue = model.eval(actual, isComplete = false) - val expectedValue = model.eval(expected, isComplete = false) - - if (expectedValue == actualValue) { - stats.ignore(expected, actual, "Expressions equal: check incorrect") - return - } - } - } - - if (satIsPossiblePassed && expressionEqualityPassed) { - stats.passed() - } - } - - private fun KContext.assertNotEqual( - oracle: KSolver<*>, - expected: KExpr, - actual: KExpr - ) { - val sort = expected.sort - if (sort !is KArraySortBase<*>) { - oracle.assert(expected neq actual) - return - } - - val expectedArray: KExpr> = expected.uncheckedCast() - val actualArray: KExpr> = actual.uncheckedCast() - - // (exists (i) (/= (select expected i) (select actual i)) - val indices = sort.domainSorts.mapIndexed { i, srt -> mkFreshConst("i_${i}", srt) } - oracle.assert(mkSelect(expectedArray) { indices } neq mkSelect(actualArray) { indices }) - } - - private fun KContext.assertPossibleToBeEqual( - oracle: KSolver<*>, - expected: KExpr, - actual: KExpr - ) { - val sort = expected.sort - if (sort !is KArraySortBase<*>) { - oracle.assert(expected eq actual) - return - } - - val expectedArray: KExpr> = expected.uncheckedCast() - val actualArray: KExpr> = actual.uncheckedCast() - - // (exists (i) (= (select expected i) (select actual i)) - val indices = sort.domainSorts.mapIndexed { i, srt -> mkFreshConst("i_${i}", srt) } - oracle.assert(mkSelect(expectedArray) { indices } eq mkSelect(actualArray) { indices }) - } - - private inline fun KSolver<*>.checkSatAndReport( - stats: TestStats, - expected: KExpr<*>, - actual: KExpr<*>, - expectedStatus: KSolverStatus, - prefix: String, - onFailure: () -> Unit = {} - ): Boolean = when (check(CHECK_TIMEOUT)) { - expectedStatus -> true - KSolverStatus.UNKNOWN -> { - val message = "$prefix: ${reasonOfUnknown()}" - stats.ignore(expected, actual, message) - false - } - - else -> { - onFailure() - stats.fail(expected, actual, prefix) - false - } - } - - private inline fun KSolver<*>.scoped(block: () -> T): T { - push() - try { - return block() - } finally { - pop() - } - } - - private fun mkZ3Context(ctx: KContext): Context { - KZ3Solver(ctx).close() - return Context() - } - - private fun mkCvc5Context(ctx: KContext): Solver { - KCvc5Solver(ctx).close() - return Solver() - } - - private inline fun TestStats.withErrorHandling(body: () -> Unit) = try { - body() - } catch (ex: KSolverUnsupportedFeatureException) { - ignore(ex) - } catch (ex: Throwable) { - fail(ex) - } - - private data class TestCase( - val id: Int, - val message: String, - val expected: KExpr<*>?, - val actual: KExpr<*>? - ) - - private class TestStats( - private var total: Int = 0, - private var passed: Int = 0, - private var passedFast: Int = 0, - val ignored: MutableList = mutableListOf(), - val failed: MutableList = mutableListOf() - ) { - var testId = 0 - private set - - fun start() { - testId = total++ - } - - fun passed() { - passed++ - } - - fun passedFast() { - passedFast++ - passed() - } - - fun ignore(expected: KExpr<*>, actual: KExpr<*>, message: String) { - System.err.println("IGNORED ${testId}: $message") - ignored += TestCase(testId, message, expected, actual) - } - - fun fail(expected: KExpr<*>, actual: KExpr<*>, message: String) { - System.err.println("FAILED ${testId}: $message") - failed += TestCase(testId, message, expected, actual) - } - - fun fail(ex: Throwable) { - System.err.println("FAILED ${testId}: $ex") - failed += TestCase(testId, message = "$ex", expected = null, actual = null) - } - - fun ignore(ex: Throwable) { - System.err.println("IGNORED ${testId}: $ex") - ignored += TestCase(testId, message = "$ex", expected = null, actual = null) - } - - fun result() { - val stats = listOf( - "total=${total}", - "failed=${failed.size}", - "ignored=${ignored.size}", - "passed=${passed}", - "passedFast=${passedFast}" - ) - System.err.println("STATS: $stats") - - assertTrue(failed.isEmpty(), "Some tests failed") - } - } - - companion object { - private val CHECK_TIMEOUT = 10.seconds - - private val oracleManager = KSolverRunnerManager( - workerPoolSize = 2, - hardTimeout = 1.minutes - ) - - @AfterAll - @JvmStatic - fun cleanup() { - oracleManager.close() - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt deleted file mode 100644 index a5d6be478..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt +++ /dev/null @@ -1,390 +0,0 @@ -package io.ksmt.test - -import io.ksmt.KContext -import io.ksmt.KContext.SimplificationMode.NO_SIMPLIFY -import io.ksmt.expr.KExpr -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import io.ksmt.solver.cvc5.KCvc5Solver -import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.sort.KBoolSort -import io.ksmt.sort.KSort -import kotlin.test.Test -import kotlin.test.assertEquals - -class TestBvOverflowChecks { - - @Test - fun testBvAddNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvAddNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvSubNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoOverflowExpr(l, r) - } - } - - @Test - fun testBvSubNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvSubNoUnderflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvMulNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvMulNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvMulNoOverflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvDivNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { - bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvDivNoOverflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvAddNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvSubNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoOverflowExpr(l, r) - } - } - - @Test - fun testBvSubNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvSubNoUnderflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvMulNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvMulNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvMulNoOverflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvDivNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvDivNoOverflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvAddNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvSubNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoOverflowExpr(l, r) - } - } - - @Test - fun testBvSubNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvSubNoUnderflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvMulNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvMulNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvMulNoOverflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvDivNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvDivNoOverflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvAddNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvAddNoOverflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvAddNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvSubNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoOverflowExpr(l, r) - } - } - - @Test - fun testBvSubNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvSubNoUnderflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvSubNoUnderflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvMulNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = true) - } - } - - @Test - fun testBvMulNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoUnderflowExpr(l, r) - } - } - - @Test - fun testBvMulNoOverflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvMulNoOverflowExpr(l, r, isSigned = false) - } - } - - @Test - fun testBvDivNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { - z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> - mkBvDivNoOverflowExpr(l, r) - } - } - - private fun KContext.z3SampleBinaryBoolExprValues( - sort: T, - mkExpr: KContext.(KExpr, KExpr) -> KExpr - ): List> = KZ3Solver(this).use { solver -> - sampleBinaryBoolExprValues(solver, sort) { l, r -> mkExpr(l, r) } - } - - private fun KContext.bitwuzlaSampleBinaryBoolExprValues( - sort: T, - mkExpr: KContext.(KExpr, KExpr) -> KExpr - ): List> = KBitwuzlaSolver(this).use { solver -> - sampleBinaryBoolExprValues(solver, sort) { l, r -> mkExpr(l, r) } - } - - private fun KContext.sampleBinaryBoolExprValues( - solver: KSolver<*>, - sort: T, - mkExpr: KContext.(KExpr, KExpr) -> KExpr - ): List> { - val positive = sampleBinaryExprValues(solver, sort) { l, r -> - mkExpr(l, r) - } - val negative = sampleBinaryExprValues(solver, sort) { l, r -> - mkExpr(l, r).not() - } - return positive + negative - } - - private fun KContext.sampleBinaryExprValues( - solver: KSolver<*>, - sort: T, - mkExpr: KContext.(KExpr, KExpr) -> KExpr - ): List> { - val lhs = mkFreshConst("lhs", sort) - val rhs = mkFreshConst("rhs", sort) - val expr = mkExpr(lhs, rhs) - val result = mkFreshConst("result", expr.sort) - val samples = arrayListOf>() - - solver.assert(result eq expr) - - while (solver.check() == KSolverStatus.SAT && samples.size < NUM_SAMPLES) { - val model = solver.model() - - val lhsValue = model.eval(lhs) - val rhsValue = model.eval(rhs) - val resultValue = model.eval(result) - samples += BinaryExprSample(mkExpr, lhsValue, rhsValue, resultValue) - - solver.assert((lhs neq lhsValue) or (rhs neq rhsValue)) - } - - return samples - } - - private fun testBoolOperation( - mkSolver: (KContext) -> KSolver<*>, - mkSamples: KContext.() -> List> - ) = with(KContext(simplificationMode = NO_SIMPLIFY)) { - val samples = mkSamples() - mkSolver(this).use { solver -> - samples.forEach { sample -> - testOperationSample(solver, sample) - } - } - } - - private fun KContext.testOperationSample( - solver: KSolver<*>, - sample: BinaryExprSample - ) = try { - solver.push() - val operationValue = sample.operation(this, sample.lhs, sample.rhs) - solver.assert(operationValue neq sample.result) - - val status = solver.check() - assertEquals(KSolverStatus.UNSAT, status) - } finally { - solver.pop() - } - - data class BinaryExprSample( - val operation: KContext.(KExpr, KExpr) -> KExpr, - val lhs: KExpr, - val rhs: KExpr, - val result: KExpr - ) - - companion object { - private const val NUM_SAMPLES = 10 - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt deleted file mode 100644 index e516de726..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt +++ /dev/null @@ -1,353 +0,0 @@ -package io.ksmt.test - -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.solver.KModel -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import io.ksmt.solver.cvc5.KCvc5Solver -import io.ksmt.solver.runner.KSolverRunnerManager -import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.sort.KArraySort -import io.ksmt.sort.KBoolSort -import io.ksmt.sort.KSort -import io.ksmt.sort.KUninterpretedSort -import io.ksmt.utils.uncheckedCast -import kotlin.random.Random -import kotlin.random.nextInt -import kotlin.reflect.KClass -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import kotlin.time.Duration.Companion.seconds - -@EnabledIfEnvironmentVariable( - named = "runUninterpretedSortValuesTest", - matches = "true", - disabledReason = "Not suitable for usual CI runs" -) -class UninterpretedSortValuesTest { - - @Test - fun test(): Unit = with(KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)) { - val testData = generateTestData() - KSolverRunnerManager(workerPoolSize = 3).use { solverManager -> - solverManager.createSolver(this, KZ3Solver::class).use { oracleSover -> - val z3Results = solverManager.testSolver(this, KZ3Solver::class, testData) - validateResults(oracleSover, z3Results) - - val yicesResults = solverManager.testSolver(this, KYicesSolver::class, testData) - validateResults(oracleSover, yicesResults) - - val bitwuzlaResults = solverManager.testSolver(this, KBitwuzlaSolver::class, testData) - validateResults(oracleSover, bitwuzlaResults) - - val cvcResults = solverManager.testSolver(this, KCvc5Solver::class, testData) - validateResults(oracleSover, cvcResults) - } - } - } - - @Test - fun testIncremental(): Unit = with(KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)) { - val testData = generateTestData() - KSolverRunnerManager(workerPoolSize = 3).use { solverManager -> - solverManager.createSolver(this, KZ3Solver::class).use { oracleSover -> - val z3Results = solverManager.testSolverIncremental(this, KZ3Solver::class, testData) - validateResults(oracleSover, z3Results) - - val yicesResults = solverManager.testSolverIncremental(this, KYicesSolver::class, testData) - validateResults(oracleSover, yicesResults) - - val bitwuzlaResults = solverManager.testSolverIncremental(this, KBitwuzlaSolver::class, testData) - validateResults(oracleSover, bitwuzlaResults) - - val cvcResults = solverManager.testSolverIncremental(this, KCvc5Solver::class, testData) - validateResults(oracleSover, cvcResults) - } - } - } - - private fun KSolverRunnerManager.testSolver( - ctx: KContext, - solverType: KClass>, - data: List> - ) = createSolver(ctx, solverType).use { solver -> - val results = arrayListOf() - - for ((i, sample) in data.withIndex()) { - solver.push() - - solver.assert(sample) - val status = solver.check(SINGLE_CHECK_TIMEOUT) - - val model = if (status == KSolverStatus.SAT) solver.model() else null - - results += TestResult( - testId = i, - solverName = solverType.simpleName!!, - testSample = sample, - status = status, - model = model?.detach() - ) - - solver.pop() - } - - results - } - - private data class SolverTestDataSample( - val index: Int, - val expr: KExpr, - val variable: KExpr - ) - - private fun KSolverRunnerManager.testSolverIncremental( - ctx: KContext, - solverType: KClass>, - data: List> - ) = createSolver(ctx, solverType).use { solver -> - val results = arrayListOf() - - val chunks = data.mapIndexed { index, sample -> - val variable = ctx.mkFreshConst("t:$index", ctx.boolSort) - SolverTestDataSample(index, sample, variable) - }.chunked(TEST_SET_INCREMENTAL_CHUNK) - - ctx.pushAssertChunk(solver, chunks.first()) - - for (chunk in chunks.drop(1)) { - ctx.pushAssertChunk(solver, chunk) - popCheckSatChunk(solver, solverType.simpleName!!, chunk, results) - } - - popCheckSatChunk(solver, solverType.simpleName!!, chunks.first(), results) - - results - } - - private fun KContext.pushAssertChunk( - solver: KSolver<*>, - chunk: List - ) { - for (sample in chunk) { - solver.push() - solver.assert(sample.variable implies sample.expr) - } - } - - private fun popCheckSatChunk( - solver: KSolver<*>, - solverName: String, - chunk: List, - results: MutableList - ) { - for (sample in chunk.asReversed()) { - solver.push() - solver.assert(sample.variable) - val status = solver.check(SINGLE_CHECK_TIMEOUT) - - val model = if (status == KSolverStatus.SAT) solver.model() else null - - results += TestResult( - testId = sample.index, - solverName = solverName, - testSample = sample.expr, - status = status, - model = model?.detach() - ) - - solver.pop() - - // pop sample assertion - solver.pop() - } - } - - private fun KContext.validateResults(oracle: KSolver<*>, results: List) { - validateResultStatus(results) - - for (res in results) { - if (res.model == null) continue - validateModel(oracle, res, res.model) - } - } - - private fun KContext.validateModel(oracle: KSolver<*>, result: TestResult, model: KModel) { - val actualValue = model.eval(result.testSample, isComplete = false) - if (actualValue == trueExpr) return - - oracle.push() - - model.uninterpretedSorts.forEach { sort -> - val universe = model.uninterpretedSortUniverse(sort) - if (!universe.isNullOrEmpty()) { - val x = mkFreshConst("x", sort) - val constraint = mkOr(universe.map { x eq it }) - - oracle.assert(mkUniversalQuantifier(constraint, listOf(x.decl))) - } - } - - oracle.assert(actualValue neq trueExpr) - - val status = oracle.check() - assertEquals(KSolverStatus.UNSAT, status, result.solverName) - - oracle.pop() - } - - private fun validateResultStatus(results: List) { - val statuses = results - .groupBy({ it.testId }, { it.status }) - .mapValues { (_, status) -> status.filterNot { it == KSolverStatus.UNKNOWN }.toSet() } - - assertTrue(statuses.all { it.value.size <= 1 }, results.first().solverName) - } - - private fun KContext.generateTestData(): List> { - val sorts = (0 until NUMBER_OF_UNINTERPRETED_SORTS).map { - mkUninterpretedSort("T${it}") - } - val generationContext = GenerationContext(this, sorts) - return (0 until TEST_SET_SIZE).map { - generationContext.generate() - } - } - - private class GenerationContext( - val ctx: KContext, - val sorts: List - ) { - private val random = Random(RANDOM_SEED) - - private val sortMapOperations = sorts.associateWith { s1 -> - sorts.associateWith { s2 -> - ctx.mkFuncDecl("${s1}_${s2}", s2, listOf(s1)) - } - } - - private val basicExpressions = sorts.associateWithTo(hashMapOf>>()) { - arrayListOf(ctx.mkFreshConst("x", it)) - } - - private val expressions = hashMapOf>>() - - private fun newExpr() = when (random.nextDouble()) { - in 0.0..0.1 -> newArray() - in 0.1..0.3 -> newVar() - else -> newValue() - } - - private fun newArray() = with(ctx) { - val sort = mkArraySort(sorts.random(random), sorts.random(random)) - mkFreshConst("array", sort) - } - - private fun newVar() = with(ctx) { - mkFreshConst("v", sorts.random(random)) - } - - private fun newValue() = with(ctx) { - mkUninterpretedSortValue(sorts.random(random), random.nextInt(USORT_VALUE_RANGE)) - } - - private fun findExpr(sort: KSort): KExpr { - if (random.nextDouble() > 0.5) { - newExpr().also { - basicExpressions.getOrPut(it.sort) { arrayListOf() }.add(it) - } - } - - val expressionSet = when (random.nextDouble()) { - in 0.0..0.4 -> basicExpressions - else -> expressions - } - - val variants = expressionSet.getOrPut(sort) { - arrayListOf(ctx.mkFreshConst("stub", sort)) - } - - return variants.random(random).uncheckedCast() - } - - fun generate(): KExpr { - expressions.clear() - - val first = generateDeepExpr() - val second = generateDeepExpr() - - val transformer = sortMapOperations[first.sort]?.get(second.sort) - ?: error("Unexpected sorts: ${first.sort}, ${second.sort}") - val firstAsSecond = transformer.apply(listOf(first)) - - return ctx.mkEq(firstAsSecond, second.uncheckedCast()) - } - - private fun generateDeepExpr(): KExpr { - var current = findExpr(sorts.random(random)) - repeat(EXPRESSION_DEPTH) { - current = if (current.sort is KArraySort<*, *>) { - generateArrayOperation(current.uncheckedCast()) - } else { - generateOperation(current) - } - expressions.getOrPut(current.sort) { arrayListOf() }.add(current) - } - - val sort = current.sort - if (sort is KArraySort<*, *>) { - current = ctx.mkArraySelect(current.uncheckedCast(), findExpr(sort.domain)) - } - - return current - } - - private fun generateArrayOperation(expr: KExpr>): KExpr = with(ctx) { - when (random.nextDouble()) { - in 0.0..0.3 -> mkArraySelect(expr, findExpr(expr.sort.domain)) - else -> mkArrayStore(expr, findExpr(expr.sort.domain), findExpr(expr.sort.range)).uncheckedCast() - } - } - - private fun generateOperation(expr: KExpr): KExpr = with(ctx) { - when (random.nextDouble()) { - in 0.0..0.5 -> { - val otherSort = sorts.random(random) - val transformer = sortMapOperations[expr.sort]?.get(otherSort) - ?: error("Unexpected expr sort: ${expr.sort}") - transformer.apply(listOf(expr)).uncheckedCast() - } - - else -> { - val sort = mkArraySort(expr.sort, sorts.random(random)) - val array: KExpr> = findExpr(sort).uncheckedCast() - mkArrayStore(array, expr, findExpr(sort.range)).uncheckedCast() - } - } - } - } - - private data class TestResult( - val testId: Int, - val solverName: String, - val testSample: KExpr, - val status: KSolverStatus, - val model: KModel? - ) - - companion object { - private const val NUMBER_OF_UNINTERPRETED_SORTS = 3 - private const val RANDOM_SEED = 42 - private const val EXPRESSION_DEPTH = 30 - private const val TEST_SET_SIZE = 500 - private const val TEST_SET_INCREMENTAL_CHUNK = TEST_SET_SIZE / 50 - private val USORT_VALUE_RANGE = 0 until 25 - private val SINGLE_CHECK_TIMEOUT = 1.seconds - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt deleted file mode 100644 index 7e1831f60..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt +++ /dev/null @@ -1,716 +0,0 @@ -package io.ksmt.test.benchmarks - -import com.jetbrains.rd.util.reactive.RdFault -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assumptions -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.params.provider.Arguments -import io.ksmt.KContext -import io.ksmt.expr.KApp -import io.ksmt.expr.KDivArithExpr -import io.ksmt.expr.KExpr -import io.ksmt.expr.KFpMaxExpr -import io.ksmt.expr.KFpMinExpr -import io.ksmt.expr.KFpToBvExpr -import io.ksmt.expr.KFpToRealExpr -import io.ksmt.expr.KFpValue -import io.ksmt.expr.KFunctionAsArray -import io.ksmt.expr.KModIntExpr -import io.ksmt.expr.KPowerArithExpr -import io.ksmt.expr.KRemIntExpr -import io.ksmt.expr.transformer.KNonRecursiveTransformer -import io.ksmt.expr.transformer.KTransformer -import io.ksmt.runner.core.KsmtWorkerArgs -import io.ksmt.runner.core.KsmtWorkerFactory -import io.ksmt.runner.core.KsmtWorkerPool -import io.ksmt.runner.core.RdServer -import io.ksmt.runner.core.WorkerInitializationFailedException -import io.ksmt.runner.generated.models.TestProtocolModel -import io.ksmt.solver.KModel -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.KSolverException -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.KSolverUnsupportedFeatureException -import io.ksmt.solver.async.KAsyncSolver -import io.ksmt.solver.runner.KSolverRunnerManager -import io.ksmt.sort.KArithSort -import io.ksmt.sort.KArraySortBase -import io.ksmt.sort.KBoolSort -import io.ksmt.sort.KBvSort -import io.ksmt.sort.KFpSort -import io.ksmt.sort.KIntSort -import io.ksmt.sort.KRealSort -import io.ksmt.sort.KSort -import io.ksmt.test.SmtLibParseError -import io.ksmt.test.TestRunner -import io.ksmt.test.TestWorker -import io.ksmt.test.TestWorkerProcess -import io.ksmt.utils.FpUtils.isZero -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.Path -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.relativeTo -import kotlin.reflect.KClass -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertTrue -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.seconds - -@Suppress("UnnecessaryAbstractClass") -abstract class BenchmarksBasedTest { - - fun testConverter( - name: String, - samplePath: Path, - mkKsmtAssertions: suspend TestRunner.(List>) -> List> - ) = handleIgnoredTests("testConverter[$name]") { - ignoreNoTestDataStub(name) - val ctx = KContext() - testWorkers.withWorker(ctx) { worker -> - worker.skipBadTestCases { - val assertions = worker.parseFile(samplePath) - val convertedAssertions = worker.convertAssertions(assertions) - - val ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) - - ksmtAssertions.forEach { SortChecker(ctx).apply(it) } - - worker.performEqualityChecks { - for ((originalZ3Expr, ksmtExpr) in assertions.zip(ksmtAssertions)) { - areEqual(actual = ksmtExpr, expected = originalZ3Expr) - } - check { "expressions are not equal" } - } - } - } - } - - fun testModelConversion( - name: String, - samplePath: Path, - solverType: KClass> - ) = testModelConversion(name, samplePath) { ctx -> - solverManager.createSolver(ctx, solverType) - } - - fun testModelConversion( - name: String, - samplePath: Path, - solverProvider: (KContext) -> KAsyncSolver - ) = handleIgnoredTests("testModelConversion[$name]") { - ignoreNoTestDataStub(name) - val ctx = KContext() - testWorkers.withWorker(ctx) { worker -> - worker.skipBadTestCases { - val assertions = worker.parseFile(samplePath) - val ksmtAssertions = worker.convertAssertions(assertions) - - val model = solverProvider(ctx).use { testSolver -> - ksmtAssertions.forEach { testSolver.assertAsync(it) } - - val status = testSolver.checkAsync(SOLVER_CHECK_SAT_TIMEOUT) - if (status != KSolverStatus.SAT) { - ignoreTest { "No model to check" } - } - - testSolver.modelAsync() - } - - checkAsArrayDeclsPresentInModel(ctx, model) - - val evaluatedAssertions = ksmtAssertions.map { model.eval(it, isComplete = true) } - - val cardinalityConstraints = model.uninterpretedSorts.mapNotNull { sort -> - model.uninterpretedSortUniverse(sort)?.takeIf { it.isNotEmpty() }?.let { universe -> - with(ctx) { - val x = mkFreshConst("x", sort) - val variants = mkOr(universe.map { x eq it }) - mkUniversalQuantifier(variants, listOf(x.decl)) - } - } - } - - /** - * Evaluated assertion may contain some underspecified - * operations (e.g division by zero). Currently used test oracle (Z3) - * can define any interpretation for the underspecified operations - * and therefore (not (= a true)) check will always be SAT. - * We consider such cases as false positives. - * */ - val assertionsToCheck = evaluatedAssertions.filterNot { hasUnderspecifiedOperations(it) } - - // Samples are false UNSAT in Z3 because of incorrect FMA eval - if (name in KnownZ3Issues.z3FpFmaFalseUnsatSamples) { - ignoreTest { "Example is known to be false UNSAT in Z3 test oracle" } - } - - worker.performEqualityChecks { - cardinalityConstraints.forEach { assume(it) } - assertionsToCheck.forEach { isTrue(it) } - check { "assertions are not true in model" } - } - } - } - } - - fun testSolver( - name: String, - samplePath: Path, - solverType: KClass> - ) = testSolver(name, samplePath) { ctx -> - solverManager.createSolver(ctx, solverType) - } - - fun testSolver( - name: String, - samplePath: Path, - solverProvider: (KContext) -> KAsyncSolver - ) = handleIgnoredTests("testSolver[$name]") { - ignoreNoTestDataStub(name) - val ctx = KContext() - testWorkers.withWorker(ctx) { worker -> - worker.skipBadTestCases { - val assertions = worker.parseFile(samplePath) - val solver = worker.createSolver(TEST_WORKER_CHECK_SAT_TIMEOUT) - assertions.forEach { - worker.assert(solver, it) - } - - var expectedStatus = worker.check(solver) - - // Fix known Z3 satisfiability issues - if (expectedStatus == KSolverStatus.UNSAT && name in KnownZ3Issues.z3FpFmaFalseUnsatSamples) { - expectedStatus = KSolverStatus.SAT - } - if (expectedStatus == KSolverStatus.SAT && name in KnownZ3Issues.z3FpFmaFalseSatSamples) { - expectedStatus = KSolverStatus.UNSAT - } - - if (expectedStatus == KSolverStatus.UNKNOWN) { - ignoreTest { "Expected status is unknown" } - } - - val ksmtAssertions = worker.convertAssertions(assertions) - - val actualStatus = solverProvider(ctx).use { ksmtSolver -> - ksmtAssertions.forEach { ksmtSolver.assertAsync(it) } - - // use greater timeout to reduce false-positive unknowns - val status = ksmtSolver.check(SOLVER_CHECK_SAT_TIMEOUT) - if (status == KSolverStatus.UNKNOWN) { - ignoreTest { "Actual status is unknown: ${ksmtSolver.reasonOfUnknown()}" } - } - - status - } - assertEquals(expectedStatus, actualStatus, "solver check-sat mismatch") - } - } - } - - internal fun KsmtWorkerPool.withWorker( - ctx: KContext, - body: suspend (TestRunner) -> Unit - ) = runBlocking { - val worker = try { - getOrCreateFreeWorker() - } catch (ex: WorkerInitializationFailedException) { - ignoreTest { "worker initialization failed -- ${ex.message}" } - } - worker.astSerializationCtx.initCtx(ctx) - worker.lifetime.onTermination { - worker.astSerializationCtx.resetCtx() - } - try { - TestRunner(ctx, TEST_WORKER_SINGLE_OPERATION_TIMEOUT, worker).let { - try { - it.init() - body(it) - } finally { - it.delete() - } - } - } catch (ex: TimeoutCancellationException) { - ignoreTest { "worker timeout -- ${ex.message}" } - } finally { - worker.release() - } - } - - /** - * Check if expression contains underspecified operations: - * 1. division by zero - * 2. integer mod/rem with zero divisor - * 3. zero to the zero power - * 4. Fp to number conversions with NaN and Inf - * 5. Fp max/min with +/- zero - * */ - private fun hasUnderspecifiedOperations(expr: KExpr<*>): Boolean { - val detector = UnderspecifiedOperationDetector(expr.ctx) - detector.apply(expr) - return detector.hasUnderspecifiedOperation - } - - private class UnderspecifiedOperationDetector(ctx: KContext) : KNonRecursiveTransformer(ctx) { - var hasUnderspecifiedOperation = false - - override fun transform(expr: KDivArithExpr): KExpr = - super.transform(expr).also { checkDivisionByZero(expr.rhs) } - - override fun transform(expr: KModIntExpr): KExpr = - super.transform(expr).also { checkDivisionByZero(expr.rhs) } - - override fun transform(expr: KRemIntExpr): KExpr = - super.transform(expr).also { checkDivisionByZero(expr.rhs) } - - override fun transform(expr: KPowerArithExpr): KExpr = - super.transform(expr).also { checkZeroToZeroPower(expr.lhs, expr.rhs) } - - override fun transform(expr: KFpToBvExpr): KExpr = - super.transform(expr).also { checkFpNaNOrInf(expr.value) } - - override fun transform(expr: KFpToRealExpr): KExpr = - super.transform(expr).also { checkFpNaNOrInf(expr.value) } - - override fun transform(expr: KFpMinExpr): KExpr = - super.transform(expr).also { checkFpZeroWithDifferentSign(expr.arg0, expr.arg1) } - - override fun transform(expr: KFpMaxExpr): KExpr = - super.transform(expr).also { checkFpZeroWithDifferentSign(expr.arg0, expr.arg1) } - - private fun checkDivisionByZero(divisor: KExpr<*>) = with(ctx) { - if (divisor == 0.expr) { - hasUnderspecifiedOperation = true - } - } - - private fun checkZeroToZeroPower(base: KExpr<*>, power: KExpr<*>) = with(ctx) { - if (base == 0.expr && power == 0.expr) { - hasUnderspecifiedOperation = true - } - } - - private fun checkFpNaNOrInf(value: KExpr) = with(ctx) { - val underspecifiedValues = setOf( - mkFpNaN(value.sort), - mkFpInf(signBit = true, value.sort), - mkFpInf(signBit = false, value.sort), - ) - if (value in underspecifiedValues) { - hasUnderspecifiedOperation = true - } - } - - private fun checkFpZeroWithDifferentSign(lhs: KExpr, rhs: KExpr) { - if (lhs !is KFpValue || rhs !is KFpValue) return - if (lhs.isZero() && rhs.isZero() && lhs.signBit != rhs.signBit) { - hasUnderspecifiedOperation = true - } - } - } - - companion object { - val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 10.seconds - val TEST_WORKER_EQUALITY_CHECK_TIMEOUT = 3.seconds - val TEST_WORKER_CHECK_SAT_TIMEOUT = 1.seconds - - val SOLVER_SINGLE_OPERATION_TIMEOUT = 10.seconds - val SOLVER_CHECK_SAT_TIMEOUT = 3.seconds - - internal lateinit var solverManager: KSolverRunnerManager - internal lateinit var testWorkers: KsmtWorkerPool - - private val testDataChunkSize = System.getenv("benchmarkChunkMaxSize")?.toIntOrNull() ?: Int.MAX_VALUE - private val testDataChunk = System.getenv("benchmarkChunk")?.toIntOrNull() ?: 0 - - private val NO_TEST_DATA = BenchmarkTestArguments("__NO__TEST__DATA__", Path(".")) - - private fun testDataLocation(): Path = this::class.java.classLoader - .getResource("testData") - ?.toURI() - ?.let { Paths.get(it) } - ?: error("No test data") - - private fun prepareTestData(): List { - val testDataLocation = testDataLocation() - return testDataLocation - .listDirectoryEntries("*.smt2") - .sorted() - .drop(testDataChunk * testDataChunkSize) - .take(testDataChunkSize) - .map { BenchmarkTestArguments(it.relativeTo(testDataLocation).toString(), it) } - .skipBadTestCases() - .ensureNotEmpty() - } - - val testData by lazy { - prepareTestData() - } - - /** - * Parametrized tests require at least one argument. - * In some cases, we may filter out all provided test samples, - * which will cause JUnit failure. To overcome this problem, - * we use [NO_TEST_DATA] stub, which is handled - * by [ignoreNoTestDataStub] and results in a single ignored test. - * */ - fun List.ensureNotEmpty() = ifEmpty { listOf(NO_TEST_DATA) } - - fun ignoreNoTestDataStub(name: String) { - Assumptions.assumeTrue(name != NO_TEST_DATA.name) - } - - private fun List.skipBadTestCases(): List = - /** - * Contains a declaration with an empty name. - * Normally, such declarations have special name in Z3, - * but in this case it is not true. After internalization via API, - * resulting declaration has name as excepted. - * Therefore, declarations are not equal, but this is not our issue. - * */ - filterNot { it.name == "QF_BV_symbols.smt2" } - - @BeforeAll - @JvmStatic - fun initWorkerPools() { - solverManager = KSolverRunnerManager( - workerPoolSize = 4, - hardTimeout = SOLVER_SINGLE_OPERATION_TIMEOUT, - workerProcessIdleTimeout = 10.minutes - ) - testWorkers = KsmtWorkerPool( - maxWorkerPoolSize = 4, - workerProcessIdleTimeout = 10.minutes, - workerFactory = object : KsmtWorkerFactory { - override val childProcessEntrypoint = TestWorkerProcess::class - override fun updateArgs(args: KsmtWorkerArgs): KsmtWorkerArgs = args - override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) - } - ) - } - - @AfterAll - @JvmStatic - fun closeWorkerPools() { - solverManager.close() - testWorkers.terminate() - } - - // See [handleIgnoredTests] - inline fun ignoreTest(message: () -> String?): Nothing { - throw IgnoreTestException(message()) - } - } - - data class BenchmarkTestArguments( - val name: String, - val samplePath: Path - ) : Arguments { - override fun get() = arrayOf(name, samplePath) - } - - fun checkAsArrayDeclsPresentInModel(ctx: KContext, model: KModel) { - val checker = AsArrayDeclChecker(ctx, model) - model.declarations.forEach { decl -> - model.interpretation(decl)?.let { interpretation -> - interpretation.entries.forEach { it.value.accept(checker) } - interpretation.default?.accept(checker) - } - } - } - - class AsArrayDeclChecker(override val ctx: KContext, val model: KModel) : KTransformer { - override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { - assertNotNull(model.interpretation(expr.function), "no interpretation for as-array: $expr") - return expr - } - } - - class SortChecker(ctx: KContext) : KNonRecursiveTransformer(ctx) { - override fun transformApp(expr: KApp): KExpr = with(ctx) { - // apply internally check arguments sorts - expr.decl.apply(expr.args) - return super.transformApp(expr).also { - check(it.sort == expr.sort) { "sort mismatch" } - } - } - } - - suspend fun TestRunner.performEqualityChecks(checks: suspend EqualityChecker.() -> Unit) { - val solver = createSolver(TEST_WORKER_EQUALITY_CHECK_TIMEOUT) - val checker = EqualityChecker(this, solver) - checker.checks() - } - - private data class EqualityCheck(val actual: KExpr<*>, val expected: Long) - - class EqualityChecker( - private val worker: TestRunner, - private val solver: Int, - ) { - private val equalityChecks = mutableListOf() - private val workerTrueExpr: Long by lazy { runBlocking { worker.mkTrueExpr() } } - - suspend fun areEqual(actual: KExpr<*>, expected: Long) { - worker.addEqualityCheck(solver, actual, expected) - equalityChecks += EqualityCheck(actual, expected) - } - - suspend fun isTrue(actual: KExpr<*>) = areEqual(actual, workerTrueExpr) - - suspend fun assume(expr: KExpr) { - worker.addEqualityCheckAssumption(solver, expr) - } - - suspend fun check(message: () -> String) { - val status = worker.checkEqualities(solver) - when (status) { - KSolverStatus.UNSAT -> return - KSolverStatus.SAT -> { - val failedEqualityCheck = worker.findFirstFailedEquality(solver)?.let { equalityChecks[it] } - if (failedEqualityCheck != null) { - val expected = worker.exprToString(failedEqualityCheck.expected) - assertEquals(expected, "${failedEqualityCheck.actual}", message()) - } - assertTrue(false, message()) - } - - KSolverStatus.UNKNOWN -> ignoreTest { - "equality check: unknown -- ${worker.getReasonUnknown(solver)}" - } - } - } - } - - inline fun TestRunner.skipBadTestCases(body: () -> Unit) = try { - skipUnsupportedSolverFeatures { - body() - } - } catch (ex: SmtLibParseError) { - ignoreTest { "parse failed -- ${ex.message}" } - } catch (ex: TimeoutCancellationException) { - ignoreTest { "timeout -- ${ex.message}" } - } - - inline fun TestRunner.skipUnsupportedSolverFeatures(body: () -> Unit) = try { - handleWrappedSolverException { - body() - } - } catch (ex: NotImplementedError) { - // skip test with not implemented feature - ignoreTest { - val reducedStackTrace = ex.stackTrace.take(5).joinToString("\n") { it.toString() } - "${ex.message}\n$reducedStackTrace" - } - } catch (ex: KSolverUnsupportedFeatureException) { - ignoreTest { ex.message } - } - - inline fun TestRunner.handleWrappedSolverException(body: () -> T): T = try { - body() - } catch (ex: KSolverException) { - val unwrappedException = when (val cause = ex.cause) { - is RdFault -> rdExceptionCause(cause) ?: ex - is TimeoutCancellationException -> cause - else -> ex - } - throw unwrappedException - } - - class IgnoreTestException(message: String?) : Exception(message) - - /** - * When a test is ignored via JUnit assumption the reason (message) - * of the ignore is not shown in the test report. - * To keep some insight on the ignore reasons we use - * logging to stderr, since it is present in test reports. - * */ - fun handleIgnoredTests(testName: String, testBody: () -> Unit) { - try { - testBody() - } catch (ignore: IgnoreTestException) { - val testClassName = javaClass.canonicalName - System.err.println("IGNORE $testClassName.$testName: ${ignore.message}") - Assumptions.assumeTrue(false) - } - } - - object KnownZ3Issues { - /** - * These samples are known to be SAT according to the annotation - * in the source and according to the previous versions of Z3 (e.g. 4.8.15). - * Currently used z3 4.11.2 treat these samples as UNSAT. - * - * Todo: remove when this issue will be fixed in Z3. - * */ - val z3FpFmaFalseUnsatSamples = setOf( - "QF_FP_fma-has-solution-10232.smt2", - "QF_FP_fma-has-solution-10256.smt2", - "QF_FP_fma-has-solution-10601.smt2", - "QF_FP_fma-has-solution-10792.smt2", - "QF_FP_fma-has-solution-10834.smt2", - "QF_FP_fma-has-solution-10856.smt2", - "QF_FP_fma-has-solution-10867.smt2", - "QF_FP_fma-has-solution-10998.smt2", - "QF_FP_fma-has-solution-11152.smt2", - "QF_FP_fma-has-solution-11193.smt2", - "QF_FP_fma-has-solution-11245.smt2", - "QF_FP_fma-has-solution-11482.smt2", - "QF_FP_fma-has-solution-11503.smt2", - "QF_FP_fma-has-solution-12238.smt2", - "QF_FP_fma-has-solution-12329.smt2", - "QF_FP_fma-has-solution-1247.smt2", - "QF_FP_fma-has-solution-12600.smt2", - "QF_FP_fma-has-solution-12639.smt2", - "QF_FP_fma-has-solution-12682.smt2", - "QF_FP_fma-has-solution-12789.smt2", - "QF_FP_fma-has-solution-12840.smt2", - "QF_FP_fma-has-solution-12969.smt2", - "QF_FP_fma-has-solution-1325.smt2", - "QF_FP_fma-has-solution-13421.smt2", - "QF_FP_fma-has-solution-13786.smt2", - "QF_FP_fma-has-solution-14111.smt2", - "QF_FP_fma-has-solution-14346.smt2", - "QF_FP_fma-has-solution-14535.smt2", - "QF_FP_fma-has-solution-14613.smt2", - "QF_FP_fma-has-solution-14742.smt2", - "QF_FP_fma-has-solution-14799.smt2", - "QF_FP_fma-has-solution-14835.smt2", - "QF_FP_fma-has-solution-154.smt2", - "QF_FP_fma-has-solution-15774.smt2", - "QF_FP_fma-has-solution-15798.smt2", - "QF_FP_fma-has-solution-15963.smt2", - "QF_FP_fma-has-solution-15995.smt2", - "QF_FP_fma-has-solution-17127.smt2", - "QF_FP_fma-has-solution-17650.smt2", - "QF_FP_fma-has-solution-17915.smt2", - "QF_FP_fma-has-solution-17959.smt2", - "QF_FP_fma-has-solution-1809.smt2", - "QF_FP_fma-has-solution-18220.smt2", - "QF_FP_fma-has-solution-18700.smt2", - "QF_FP_fma-has-solution-19191.smt2", - "QF_FP_fma-has-solution-19593.smt2", - "QF_FP_fma-has-solution-2988.smt2", - "QF_FP_fma-has-solution-3042.smt2", - "QF_FP_fma-has-solution-3742.smt2", - "QF_FP_fma-has-solution-4281.smt2", - "QF_FP_fma-has-solution-457.smt2", - "QF_FP_fma-has-solution-4615.smt2", - "QF_FP_fma-has-solution-4981.smt2", - "QF_FP_fma-has-solution-4983.smt2", - "QF_FP_fma-has-solution-5056.smt2", - "QF_FP_fma-has-solution-5127.smt2", - "QF_FP_fma-has-solution-5213.smt2", - "QF_FP_fma-has-solution-5986.smt2", - "QF_FP_fma-has-solution-6211.smt2", - "QF_FP_fma-has-solution-6468.smt2", - "QF_FP_fma-has-solution-6573.smt2", - "QF_FP_fma-has-solution-6673.smt2", - "QF_FP_fma-has-solution-6822.smt2", - "QF_FP_fma-has-solution-7580.smt2", - "QF_FP_fma-has-solution-7736.smt2", - "QF_FP_fma-has-solution-7832.smt2", - "QF_FP_fma-has-solution-7920.smt2", - "QF_FP_fma-has-solution-80.smt2", - "QF_FP_fma-has-solution-8278.smt2", - "QF_FP_fma-has-solution-8475.smt2", - "QF_FP_fma-has-solution-8483.smt2", - "QF_FP_fma-has-solution-9132.smt2", - "QF_FP_fma-has-solution-9188.smt2", - "QF_FP_fma-has-solution-9455.smt2", - "QF_FP_fma-has-solution-9467.smt2", - "QF_FP_fma-has-solution-9517.smt2", - ) - - /** - * These samples are known to be UNSAT according to the annotation - * in the source and according to the previous versions of Z3 (e.g. 4.8.15). - * Currently used z3 4.11.2 treat these samples as SAT. - * - * Todo: remove when this issue will be fixed in Z3. - * */ - val z3FpFmaFalseSatSamples = setOf( - "QF_FP_fma-has-no-other-solution-10232.smt2", - "QF_FP_fma-has-no-other-solution-10256.smt2", - "QF_FP_fma-has-no-other-solution-10601.smt2", - "QF_FP_fma-has-no-other-solution-10856.smt2", - "QF_FP_fma-has-no-other-solution-10834.smt2", - "QF_FP_fma-has-no-other-solution-10792.smt2", - "QF_FP_fma-has-no-other-solution-10867.smt2", - "QF_FP_fma-has-no-other-solution-10998.smt2", - "QF_FP_fma-has-no-other-solution-11152.smt2", - "QF_FP_fma-has-no-other-solution-11193.smt2", - "QF_FP_fma-has-no-other-solution-11245.smt2", - "QF_FP_fma-has-no-other-solution-11482.smt2", - "QF_FP_fma-has-no-other-solution-11503.smt2", - "QF_FP_fma-has-no-other-solution-12238.smt2", - "QF_FP_fma-has-no-other-solution-12329.smt2", - "QF_FP_fma-has-no-other-solution-1247.smt2", - "QF_FP_fma-has-no-other-solution-12639.smt2", - "QF_FP_fma-has-no-other-solution-12600.smt2", - "QF_FP_fma-has-no-other-solution-12682.smt2", - "QF_FP_fma-has-no-other-solution-12789.smt2", - "QF_FP_fma-has-no-other-solution-12840.smt2", - "QF_FP_fma-has-no-other-solution-12969.smt2", - "QF_FP_fma-has-no-other-solution-1325.smt2", - "QF_FP_fma-has-no-other-solution-13421.smt2", - "QF_FP_fma-has-no-other-solution-13786.smt2", - "QF_FP_fma-has-no-other-solution-14111.smt2", - "QF_FP_fma-has-no-other-solution-14346.smt2", - "QF_FP_fma-has-no-other-solution-14613.smt2", - "QF_FP_fma-has-no-other-solution-14535.smt2", - "QF_FP_fma-has-no-other-solution-14742.smt2", - "QF_FP_fma-has-no-other-solution-14835.smt2", - "QF_FP_fma-has-no-other-solution-14799.smt2", - "QF_FP_fma-has-no-other-solution-154.smt2", - "QF_FP_fma-has-no-other-solution-15774.smt2", - "QF_FP_fma-has-no-other-solution-15798.smt2", - "QF_FP_fma-has-no-other-solution-15963.smt2", - "QF_FP_fma-has-no-other-solution-15995.smt2", - "QF_FP_fma-has-no-other-solution-17127.smt2", - "QF_FP_fma-has-no-other-solution-17650.smt2", - "QF_FP_fma-has-no-other-solution-17915.smt2", - "QF_FP_fma-has-no-other-solution-17959.smt2", - "QF_FP_fma-has-no-other-solution-1809.smt2", - "QF_FP_fma-has-no-other-solution-18220.smt2", - "QF_FP_fma-has-no-other-solution-18700.smt2", - "QF_FP_fma-has-no-other-solution-19191.smt2", - "QF_FP_fma-has-no-other-solution-19593.smt2", - "QF_FP_fma-has-no-other-solution-2988.smt2", - "QF_FP_fma-has-no-other-solution-3042.smt2", - "QF_FP_fma-has-no-other-solution-3742.smt2", - "QF_FP_fma-has-no-other-solution-4281.smt2", - "QF_FP_fma-has-no-other-solution-457.smt2", - "QF_FP_fma-has-no-other-solution-4615.smt2", - "QF_FP_fma-has-no-other-solution-4981.smt2", - "QF_FP_fma-has-no-other-solution-5056.smt2", - "QF_FP_fma-has-no-other-solution-4983.smt2", - "QF_FP_fma-has-no-other-solution-5213.smt2", - "QF_FP_fma-has-no-other-solution-5127.smt2", - "QF_FP_fma-has-no-other-solution-5986.smt2", - "QF_FP_fma-has-no-other-solution-6211.smt2", - "QF_FP_fma-has-no-other-solution-6468.smt2", - "QF_FP_fma-has-no-other-solution-6573.smt2", - "QF_FP_fma-has-no-other-solution-6673.smt2", - "QF_FP_fma-has-no-other-solution-6822.smt2", - "QF_FP_fma-has-no-other-solution-7580.smt2", - "QF_FP_fma-has-no-other-solution-7736.smt2", - "QF_FP_fma-has-no-other-solution-7832.smt2", - "QF_FP_fma-has-no-other-solution-7920.smt2", - "QF_FP_fma-has-no-other-solution-80.smt2", - "QF_FP_fma-has-no-other-solution-8278.smt2", - "QF_FP_fma-has-no-other-solution-8475.smt2", - "QF_FP_fma-has-no-other-solution-8483.smt2", - "QF_FP_fma-has-no-other-solution-9132.smt2", - "QF_FP_fma-has-no-other-solution-9188.smt2", - "QF_FP_fma-has-no-other-solution-9517.smt2", - "QF_FP_fma-has-no-other-solution-9455.smt2", - "QF_FP_fma-has-no-other-solution-9467.smt2", - ) - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt deleted file mode 100644 index a1c0d8066..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import java.nio.file.Path - - -class BitwuzlaBenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("bitwuzlaTestData") - fun testConverter(name: String, samplePath: Path) = - testConverter(name, samplePath) { assertions -> - internalizeAndConvertBitwuzla(assertions) - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("bitwuzlaModelConversionTestData") - fun testModelConversion(name: String, samplePath: Path) = - testModelConversion(name, samplePath, KBitwuzlaSolver::class) - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("bitwuzlaTestData") - fun testSolver(name: String, samplePath: Path) = - testSolver(name, samplePath, KBitwuzlaSolver::class) - - - companion object { - @JvmStatic - fun bitwuzlaTestData() = testData - .skipUnsupportedTheories() - .ensureNotEmpty() - - @JvmStatic - fun bitwuzlaModelConversionTestData() = - bitwuzlaTestData() - // Bitwuzla doesn't support models for quantified formulas - .filter { it.name.startsWith("QF_") } - .ensureNotEmpty() - - private fun List.skipUnsupportedTheories() = - filterNot { "LIA" in it.name || "LRA" in it.name || "LIRA" in it.name } - .filterNot { "NIA" in it.name || "NRA" in it.name || "NIRA" in it.name } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt deleted file mode 100644 index 078322fa0..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.KContext -import java.nio.file.Path - -class ContextMemoryUsageBenchmarksBasedTest : BenchmarksBasedTest() { - - @Disabled - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("testData") - fun testMemoryUsage( - name: String, - samplePath: Path, - ) = handleIgnoredTests("testMemoryUsage[$name]") { - ignoreNoTestDataStub(name) - testWorkers.withWorker(sharedCtx) { worker -> - worker.skipBadTestCases { - val assertions = worker.parseFile(samplePath) - val ksmtAssertions = worker.convertAssertions(assertions) - - ksmtAssertions.forEach { SortChecker(sharedCtx).apply(it) } - - worker.performEqualityChecks { - for ((originalZ3Expr, ksmtExpr) in assertions.zip(ksmtAssertions)) { - areEqual(actual = ksmtExpr, expected = originalZ3Expr) - } - check { "expressions are not equal" } - } - } - } - } - - companion object { - @JvmStatic - fun testData() = testData.filter { it.name.startsWith("QF_") } - - val sharedCtx: KContext - get() = sharedCtxField ?: error("Context is not initialized") - - private var sharedCtxField: KContext? = null - - @BeforeAll - @JvmStatic - fun initContext() { - sharedCtxField = KContext( - operationMode = KContext.OperationMode.CONCURRENT, - astManagementMode = KContext.AstManagementMode.GC - ) - } - - @AfterAll - @JvmStatic - fun clearContext() { - sharedCtxField?.close() - sharedCtxField = null - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt deleted file mode 100644 index a9db2606f..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt +++ /dev/null @@ -1,315 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.KContext -import io.ksmt.solver.cvc5.KCvc5Solver -import io.ksmt.solver.runner.KSolverRunnerManager -import java.nio.file.Path - -class Cvc5BenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("cvc5TestData") - fun testSolver(name: String, samplePath: Path) = testSolver(name, samplePath) { ctx -> - solverManager.createCvc5TestSolver(ctx) - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("cvc5TestData") - fun testModelConversion(name: String, samplePath: Path) = testModelConversion(name, samplePath) { ctx -> - solverManager.createCvc5TestSolver(ctx) - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("cvc5TestData") - fun testConverter(name: String, samplePath: Path) = testConverter(name, samplePath) { assertions -> - internalizeAndConvertCvc5(assertions) - } - - companion object { - @JvmStatic - fun cvc5TestData() = testData - .filter { it.name !in KnownCvc5Issues.fp64CrashSamples } - .ensureNotEmpty() - - /** - * Resource limit to prevent solver from consuming huge amounts of native memory. - * The value is measured in some abstract resource usage units and chosen to maintain - * a balance between resource usage and the number of unknown check-sat results. - * */ - const val RESOURCE_LIMIT = 4000 - - fun KSolverRunnerManager.createCvc5TestSolver(ctx: KContext) = - createSolver(ctx, KCvc5Solver::class).apply { - configure { setCvc5Option("rlimit", "$RESOURCE_LIMIT") } - } - } -} - -object KnownCvc5Issues { - /** - * On these benchmarks cvc5 crashes due to fp 64 rem operation (both Windows, and Linux, and using java-api) - * TODO: remove it after fix - */ - val fp64CrashSamples = setOf( - "FP_11_53_RNA_fp.rem_fp.geq0.smt2", - "FP_11_53_RNA_fp.rem_fp.isInfinite0.smt2", - "FP_11_53_RNA_fp.rem_fp.isInfinite1.smt2", - "FP_11_53_RNA_fp.rem_fp.isNegative0.smt2", - "FP_11_53_RNA_fp.rem_fp.isNormal1.smt2", - "FP_11_53_RNA_fp.rem_fp.isPositive0.smt2", - "FP_11_53_RNA_fp.rem_fp.isPositive1.smt2", - "FP_11_53_RNA_fp.rem_fp.isSubnormal1.smt2", - "FP_11_53_RNA_fp.rem_fp.isZero0.smt2", - "FP_11_53_RNA_fp.rem_fp.isZero1.smt2", - "FP_11_53_RNE_fp.rem_fp.isInfinite1.smt2", - "FP_11_53_RNE_fp.rem_fp.isNormal0.smt2", - "FP_11_53_RNE_fp.rem_fp.isNormal1.smt2", - "FP_11_53_RNE_fp.rem_fp.isPositive0.smt2", - "FP_11_53_RNE_fp.rem_fp.isPositive1.smt2", - "FP_11_53_RNE_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RNE_fp.rem_fp.isSubnormal1.smt2", - "FP_11_53_RNE_fp.rem_fp.isZero0.smt2", - "FP_11_53_RNE_fp.rem_fp.isZero1.smt2", - "FP_11_53_RNE_fp.rem_fp.leq0.smt2", - "FP_11_53_RTN_fp.rem_fp.isInfinite0.smt2", - "FP_11_53_RTN_fp.rem_fp.isNaN0.smt2", - "FP_11_53_RTN_fp.rem_fp.isPositive1.smt2", - "FP_11_53_RTN_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RTN_fp.rem_fp.isZero1.smt2", - "FP_11_53_RTN_fp.rem_fp.leq0.smt2", - "FP_11_53_RTP_fp.rem_fp.geq0.smt2", - "FP_11_53_RTP_fp.rem_fp.isNaN0.smt2", - "FP_11_53_RTP_fp.rem_fp.isNegative0.smt2", - "FP_11_53_RTP_fp.rem_fp.isNegative1.smt2", - "FP_11_53_RTP_fp.rem_fp.isNormal0.smt2", - "FP_11_53_RTP_fp.rem_fp.isNormal1.smt2", - "FP_11_53_RTP_fp.rem_fp.isPositive0.smt2", - "FP_11_53_RTP_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RTP_fp.rem_fp.isSubnormal1.smt2", - "FP_11_53_RTP_fp.rem_fp.isZero0.smt2", - "FP_11_53_RTZ_fp.rem_fp.geq0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isInfinite1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNaN0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNaN1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNegative0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNormal1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isPositive0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isPositive1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isSubnormal1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isZero0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isZero1.smt2", - "FP_11_53_RTZ_fp.rem_fp.leq0.smt2", - "FP_3_5_RNA_fp.rem_fp.geq0.smt2", - "FP_3_5_RNA_fp.rem_fp.isInfinite0.smt2", - "FP_3_5_RNA_fp.rem_fp.isNaN1.smt2", - "FP_3_5_RNA_fp.rem_fp.isNegative0.smt2", - "FP_3_5_RNA_fp.rem_fp.isNegative1.smt2", - "FP_3_5_RNA_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RNA_fp.rem_fp.isNormal1.smt2", - "FP_3_5_RNA_fp.rem_fp.isPositive0.smt2", - "FP_3_5_RNA_fp.rem_fp.isPositive1.smt2", - "FP_3_5_RNA_fp.rem_fp.isZero0.smt2", - "FP_3_5_RNA_fp.rem_fp.isZero1.smt2", - "FP_3_5_RNA_fp.rem_fp.leq0.smt2", - "FP_3_5_RNE_fp.rem_fp.geq0.smt2", - "FP_3_5_RNE_fp.rem_fp.isInfinite0.smt2", - "FP_3_5_RNE_fp.rem_fp.isNaN1.smt2", - "FP_3_5_RNE_fp.rem_fp.isNegative0.smt2", - "FP_3_5_RNE_fp.rem_fp.isNegative1.smt2", - "FP_3_5_RNE_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RNE_fp.rem_fp.isSubnormal1.smt2", - "FP_3_5_RNE_fp.rem_fp.isZero0.smt2", - "FP_3_5_RNE_fp.rem_fp.isZero1.smt2", - "FP_3_5_RTN_fp.rem_fp.geq0.smt2", - "FP_3_5_RTN_fp.rem_fp.isInfinite0.smt2", - "FP_3_5_RTN_fp.rem_fp.isInfinite1.smt2", - "FP_3_5_RTN_fp.rem_fp.isNaN0.smt2", - "FP_3_5_RTN_fp.rem_fp.isNaN1.smt2", - "FP_3_5_RTN_fp.rem_fp.isNegative0.smt2", - "FP_3_5_RTN_fp.rem_fp.isNegative1.smt2", - "FP_3_5_RTN_fp.rem_fp.isNormal1.smt2", - "FP_3_5_RTN_fp.rem_fp.isPositive0.smt2", - "FP_3_5_RTN_fp.rem_fp.isPositive1.smt2", - "FP_3_5_RTN_fp.rem_fp.isSubnormal0.smt2", - "FP_3_5_RTN_fp.rem_fp.isSubnormal1.smt2", - "FP_3_5_RTN_fp.rem_fp.isZero0.smt2", - "FP_3_5_RTN_fp.rem_fp.isZero1.smt2", - "FP_3_5_RTN_fp.rem_fp.leq0.smt2", - "FP_3_5_RTP_fp.rem_fp.geq0.smt2", - "FP_3_5_RTP_fp.rem_fp.isNaN1.smt2", - "FP_3_5_RTP_fp.rem_fp.isNegative0.smt2", - "FP_3_5_RTP_fp.rem_fp.isNegative1.smt2", - "FP_3_5_RTP_fp.rem_fp.isNormal1.smt2", - "FP_3_5_RTP_fp.rem_fp.isPositive0.smt2", - "FP_3_5_RTP_fp.rem_fp.isPositive1.smt2", - "FP_3_5_RTP_fp.rem_fp.isSubnormal0.smt2", - "FP_3_5_RTP_fp.rem_fp.isSubnormal1.smt2", - "FP_3_5_RTP_fp.rem_fp.isZero0.smt2", - "FP_3_5_RTZ_fp.rem_fp.geq0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNaN0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNegative0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isPositive1.smt2", - "FP_3_5_RTZ_fp.rem_fp.isSubnormal0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isSubnormal1.smt2", - "FP_3_5_RTZ_fp.rem_fp.leq0.smt2", - "FP_8_24_RNA_fp.rem_fp.geq0.smt2", - "FP_8_24_RNA_fp.rem_fp.isNaN0.smt2", - "FP_8_24_RNA_fp.rem_fp.isNaN1.smt2", - "FP_8_24_RNA_fp.rem_fp.isNegative0.smt2", - "FP_8_24_RNA_fp.rem_fp.isNegative1.smt2", - "FP_8_24_RNA_fp.rem_fp.isPositive0.smt2", - "FP_8_24_RNA_fp.rem_fp.isPositive1.smt2", - "FP_8_24_RNA_fp.rem_fp.isSubnormal1.smt2", - "FP_8_24_RNA_fp.rem_fp.isZero0.smt2", - "FP_8_24_RNA_fp.rem_fp.isZero1.smt2", - "FP_8_24_RNE_fp.rem_fp.geq0.smt2", - "FP_8_24_RNE_fp.rem_fp.isInfinite0.smt2", - "FP_8_24_RNE_fp.rem_fp.isInfinite1.smt2", - "FP_8_24_RNE_fp.rem_fp.isNaN0.smt2", - "FP_8_24_RNE_fp.rem_fp.isNaN1.smt2", - "FP_8_24_RNE_fp.rem_fp.isNegative0.smt2", - "FP_8_24_RNE_fp.rem_fp.isNegative1.smt2", - "FP_8_24_RNE_fp.rem_fp.isNormal1.smt2", - "FP_8_24_RNE_fp.rem_fp.isPositive0.smt2", - "FP_8_24_RNE_fp.rem_fp.isSubnormal1.smt2", - "FP_8_24_RTN_fp.rem_fp.isInfinite0.smt2", - "FP_8_24_RTN_fp.rem_fp.isNaN0.smt2", - "FP_8_24_RTN_fp.rem_fp.isNaN1.smt2", - "FP_8_24_RTN_fp.rem_fp.isNegative0.smt2", - "FP_8_24_RTN_fp.rem_fp.isNegative1.smt2", - "FP_8_24_RTN_fp.rem_fp.isNormal1.smt2", - "FP_8_24_RTN_fp.rem_fp.isPositive0.smt2", - "FP_8_24_RTN_fp.rem_fp.isSubnormal0.smt2", - "FP_8_24_RTN_fp.rem_fp.isSubnormal1.smt2", - "FP_8_24_RTN_fp.rem_fp.isZero1.smt2", - "FP_8_24_RTN_fp.rem_fp.leq0.smt2", - "FP_8_24_RTP_fp.rem_fp.isInfinite1.smt2", - "FP_8_24_RTP_fp.rem_fp.isNaN0.smt2", - "FP_8_24_RTP_fp.rem_fp.isNaN1.smt2", - "FP_8_24_RTP_fp.rem_fp.isNegative0.smt2", - "FP_8_24_RTP_fp.rem_fp.isNormal1.smt2", - "FP_8_24_RTP_fp.rem_fp.isPositive0.smt2", - "FP_8_24_RTP_fp.rem_fp.isPositive1.smt2", - "FP_8_24_RTP_fp.rem_fp.isSubnormal0.smt2", - "FP_8_24_RTP_fp.rem_fp.isSubnormal1.smt2", - "FP_8_24_RTP_fp.rem_fp.isZero1.smt2", - "FP_8_24_RTP_fp.rem_fp.leq0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isInfinite0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isInfinite1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNaN1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNegative0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNegative1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNormal0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isPositive1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isSubnormal1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isZero0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isZero1.smt2", - "FP_8_24_RTZ_fp.rem_fp.leq0.smt2", - "FP_11_53_RNA_fp.rem_fp.isNaN0.smt2", - "FP_11_53_RNA_fp.rem_fp.isNaN1.smt2", - "FP_11_53_RNA_fp.rem_fp.isNegative1.smt2", - "FP_11_53_RNA_fp.rem_fp.isNormal0.smt2", - "FP_11_53_RNA_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RNA_fp.rem_fp.leq0.smt2", - "FP_11_53_RNE_fp.rem_fp.geq0.smt2", - "FP_11_53_RNE_fp.rem_fp.isInfinite0.smt2", - "FP_11_53_RNE_fp.rem_fp.isNaN0.smt2", - "FP_11_53_RNE_fp.rem_fp.isNaN1.smt2", - "FP_11_53_RNE_fp.rem_fp.isNegative0.smt2", - "FP_11_53_RNE_fp.rem_fp.isNegative1.smt2", - "FP_11_53_RTN_fp.rem_fp.geq0.smt2", - "FP_11_53_RTN_fp.rem_fp.isInfinite1.smt2", - "FP_11_53_RTN_fp.rem_fp.isNaN1.smt2", - "FP_11_53_RTN_fp.rem_fp.isNegative0.smt2", - "FP_11_53_RTN_fp.rem_fp.isNegative1.smt2", - "FP_11_53_RTN_fp.rem_fp.isNormal0.smt2", - "FP_11_53_RTN_fp.rem_fp.isNormal1.smt2", - "FP_11_53_RTN_fp.rem_fp.isPositive0.smt2", - "FP_11_53_RTN_fp.rem_fp.isSubnormal1.smt2", - "FP_11_53_RTN_fp.rem_fp.isZero0.smt2", - "FP_11_53_RTP_fp.rem_fp.isInfinite0.smt2", - "FP_11_53_RTP_fp.rem_fp.isInfinite1.smt2", - "FP_11_53_RTP_fp.rem_fp.isNaN1.smt2", - "FP_11_53_RTP_fp.rem_fp.isPositive1.smt2", - "FP_11_53_RTP_fp.rem_fp.isZero1.smt2", - "FP_11_53_RTP_fp.rem_fp.leq0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isInfinite0.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNegative1.smt2", - "FP_11_53_RTZ_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RNA_fp.rem_fp.isInfinite1.smt2", - "FP_3_5_RNA_fp.rem_fp.isNaN0.smt2", - "FP_3_5_RNA_fp.rem_fp.isSubnormal0.smt2", - "FP_3_5_RNA_fp.rem_fp.isSubnormal1.smt2", - "FP_3_5_RNE_fp.rem_fp.isInfinite1.smt2", - "FP_3_5_RNE_fp.rem_fp.isNaN0.smt2", - "FP_3_5_RNE_fp.rem_fp.isNormal1.smt2", - "FP_3_5_RNE_fp.rem_fp.isPositive0.smt2", - "FP_3_5_RNE_fp.rem_fp.isPositive1.smt2", - "FP_3_5_RNE_fp.rem_fp.isSubnormal0.smt2", - "FP_3_5_RNE_fp.rem_fp.leq0.smt2", - "FP_3_5_RTN_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RTP_fp.rem_fp.isInfinite0.smt2", - "FP_3_5_RTP_fp.rem_fp.isInfinite1.smt2", - "FP_3_5_RTP_fp.rem_fp.isNaN0.smt2", - "FP_3_5_RTP_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RTP_fp.rem_fp.isZero1.smt2", - "FP_3_5_RTP_fp.rem_fp.leq0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isInfinite0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isInfinite1.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNaN1.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNegative1.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNormal0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isNormal1.smt2", - "FP_3_5_RTZ_fp.rem_fp.isPositive0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isZero0.smt2", - "FP_3_5_RTZ_fp.rem_fp.isZero1.smt2", - "FP_8_24_RNA_fp.rem_fp.isInfinite0.smt2", - "FP_8_24_RNA_fp.rem_fp.isInfinite1.smt2", - "FP_8_24_RNA_fp.rem_fp.isNormal0.smt2", - "FP_8_24_RNA_fp.rem_fp.isNormal1.smt2", - "FP_8_24_RNA_fp.rem_fp.isSubnormal0.smt2", - "FP_8_24_RNA_fp.rem_fp.leq0.smt2", - "FP_8_24_RNE_fp.rem_fp.isNormal0.smt2", - "FP_8_24_RNE_fp.rem_fp.isPositive1.smt2", - "FP_8_24_RNE_fp.rem_fp.isSubnormal0.smt2", - "FP_8_24_RNE_fp.rem_fp.isZero0.smt2", - "FP_8_24_RNE_fp.rem_fp.isZero1.smt2", - "FP_8_24_RNE_fp.rem_fp.leq0.smt2", - "FP_8_24_RTN_fp.rem_fp.geq0.smt2", - "FP_8_24_RTN_fp.rem_fp.isInfinite1.smt2", - "FP_8_24_RTN_fp.rem_fp.isNormal0.smt2", - "FP_8_24_RTN_fp.rem_fp.isPositive1.smt2", - "FP_8_24_RTN_fp.rem_fp.isZero0.smt2", - "FP_8_24_RTP_fp.rem_fp.geq0.smt2", - "FP_8_24_RTP_fp.rem_fp.isInfinite0.smt2", - "FP_8_24_RTP_fp.rem_fp.isNegative1.smt2", - "FP_8_24_RTP_fp.rem_fp.isNormal0.smt2", - "FP_8_24_RTP_fp.rem_fp.isZero0.smt2", - "FP_8_24_RTZ_fp.rem_fp.geq0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNaN0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isNormal1.smt2", - "FP_8_24_RTZ_fp.rem_fp.isPositive0.smt2", - "FP_8_24_RTZ_fp.rem_fp.isSubnormal0.smt2", - "FP_11_53_RNA_fp.rem_=0.smt2", - "FP_11_53_RNA_fp.rem_distinct0.smt2", - "FP_11_53_RNA_fp.rem_distinct1.smt2", - "FP_11_53_RNE_fp.rem_=0.smt2", - "FP_11_53_RNE_fp.rem_distinct0.smt2", - "FP_11_53_RNE_fp.rem_distinct1.smt2", - "FP_11_53_RTN_fp.rem_=0.smt2", - "FP_11_53_RTN_fp.rem_distinct0.smt2", - "FP_11_53_RTN_fp.rem_distinct1.smt2", - "FP_11_53_RTP_fp.rem_distinct0.smt2", - "FP_11_53_RTZ_fp.rem_=0.smt2", - "FP_11_53_RTZ_fp.rem_distinct0.smt2", - "FP_11_53_RTZ_fp.rem_distinct1.smt2", - ) -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt deleted file mode 100644 index 58ba89564..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.solver.bitwuzla.KBitwuzlaSolver -import io.ksmt.solver.portfolio.KPortfolioSolverManager -import io.ksmt.solver.z3.KZ3Solver -import java.nio.file.Path -import kotlin.time.Duration.Companion.seconds - -class PortfolioBenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("portfolioTestData") - fun testModelConversion(name: String, samplePath: Path) = - testModelConversion(name, samplePath) { ctx -> - portfolioSolverManager.createPortfolioSolver(ctx) - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("portfolioTestData") - fun testSolver(name: String, samplePath: Path) = - testSolver(name, samplePath) { ctx -> - portfolioSolverManager.createPortfolioSolver(ctx) - } - - companion object { - @JvmStatic - fun portfolioTestData() = testData - .filter { it.name.startsWith("QF_") } - .filter { "BV" in it.name } - .ensureNotEmpty() - - private lateinit var portfolioSolverManager: KPortfolioSolverManager - - @BeforeAll - @JvmStatic - fun initPortfolioPool() { - portfolioSolverManager = KPortfolioSolverManager( - solvers = listOf(KZ3Solver::class, KBitwuzlaSolver::class), - portfolioPoolSize = 4, - hardTimeout = 3.seconds, - workerProcessIdleTimeout = 50.seconds - ) - } - - @AfterAll - @JvmStatic - fun closePortfolioPool() { - portfolioSolverManager.close() - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt deleted file mode 100644 index 046b3b004..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.ksmt.test.benchmarks - -import com.jetbrains.rd.framework.IMarshaller -import com.jetbrains.rd.framework.SerializationCtx -import com.jetbrains.rd.framework.Serializers -import com.jetbrains.rd.framework.createAbstractBuffer -import com.jetbrains.rd.framework.readList -import com.jetbrains.rd.framework.writeList -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.KAst -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.runner.serializer.AstSerializationCtx -import java.nio.file.Path -import kotlin.test.assertEquals - -class SerializerBenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("serializerTestData") - fun testSerializer( - name: String, - samplePath: Path - ) = handleIgnoredTests("testSerializer[$name]") { - val ctx1 = KContext() - val ctx2 = KContext() - testWorkers.withWorker(ctx1) { worker -> - val assertions = worker.parseFile(samplePath) - val convertedAssertions = worker.convertAssertions(assertions) - - convertedAssertions.forEachIndexed { _, it -> - SortChecker(ctx1).apply(it) - } - - val (serialized, deserialized) = serializeAndDeserialize(ctx1, ctx2, convertedAssertions) - - assertEquals(convertedAssertions, deserialized) - - val (restored, _) = serializeAndDeserialize(ctx2, ctx1, serialized) - - assertEquals(convertedAssertions, restored) - } - } - - private fun serializeAndDeserialize( - sourceCtx: KContext, - targetCtx: KContext, - expressions: List>, - ): Pair>, List>> { - val srcSerializationCtx = AstSerializationCtx().apply { initCtx(sourceCtx) } - val srcMarshaller = AstSerializationCtx.marshaller(srcSerializationCtx) - - val targetSerializationCtx = AstSerializationCtx().apply { initCtx(targetCtx) } - val targetMarshaller = AstSerializationCtx.marshaller(targetSerializationCtx) - - val serialized = serializeAndDeserialize(expressions, srcMarshaller, targetMarshaller) - val deserialized = serializeAndDeserialize(serialized, targetMarshaller, srcMarshaller) - return serialized to deserialized - } - - - private fun serializeAndDeserialize( - expressions: List>, - ctx1Marshaller: IMarshaller, - ctx2Marshaller: IMarshaller - ): List> { - - val emptyRdSerializationCtx = SerializationCtx(Serializers()) - val buffer = createAbstractBuffer() - - buffer.writeList(expressions) { expr -> - ctx1Marshaller.write(emptyRdSerializationCtx, buffer, expr) - } - - buffer.rewind() - - val deserializedExpressions = buffer.readList { - ctx2Marshaller.read(emptyRdSerializationCtx, buffer) as KExpr<*> - } - - return deserializedExpressions - - } - - companion object { - - @JvmStatic - fun serializerTestData() = testData - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt deleted file mode 100644 index 720120b24..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.KContext -import io.ksmt.expr.KApp -import io.ksmt.expr.KExpr -import io.ksmt.expr.rewrite.simplify.KExprSimplifier -import io.ksmt.expr.transformer.KNonRecursiveTransformer -import io.ksmt.sort.KSort -import java.nio.file.Path - -class SimplifierBenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("simplifierTestData") - fun testSimplifier(name: String, samplePath: Path) = - testConverter(name, samplePath) { assertions -> - val simplifier = KExprSimplifier(ctx) - val simplified = assertions.map { simplifier.apply(it) } - simplified.forEach { ContextConsistencyChecker(ctx).apply(it) } - simplified - } - - companion object { - @JvmStatic - fun simplifierTestData() = testData - } - - class ContextConsistencyChecker(ctx: KContext) : KNonRecursiveTransformer(ctx) { - override fun transformApp(expr: KApp): KExpr = with(ctx) { - check(expr === expr.decl.apply(expr.args)) { - "Context is inconsistent" - } - return super.transformApp(expr) - } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt deleted file mode 100644 index 04d39846e..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.solver.yices.KYicesSolver -import java.nio.file.Path - -class YicesBenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("yicesTestData") - fun testConverter(name: String, samplePath: Path) = - testConverter(name, samplePath) { assertions -> - internalizeAndConvertYices(assertions) - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("yicesTestData") - fun testModelConversion(name: String, samplePath: Path) = - testModelConversion(name, samplePath, KYicesSolver::class) - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("yicesTestData") - fun testSolver(name: String, samplePath: Path) = - testSolver(name, samplePath, KYicesSolver::class) - - companion object { - @JvmStatic - fun yicesTestData() = testData - .skipUnsupportedTheories() - .skipBadTestCases() - .ensureNotEmpty() - - private fun List.skipUnsupportedTheories() = - filterNot { "QF" !in it.name || "FP" in it.name || "N" in it.name } - - private fun List.skipBadTestCases(): List = - /** - * Yices bug: returns an incorrect model - */ - filterNot { it.name == "QF_UFBV_QF_UFBV_bv8_bv_eq_sdp_v4_ab_cti_max.smt2" } - // Yices bug: incorrect bv-add after bv-and on 64 bit bv (63 and 65 are correct). - .filterNot { it.name == "QF_BV_countbits064.smt2" } - // Yices bug: same as in previous sample - .filterNot { it.name == "QF_BV_nextpoweroftwo064.smt2" } - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt deleted file mode 100644 index c8a6e8cb0..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.ksmt.test.benchmarks - -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.solver.z3.KZ3Solver -import java.nio.file.Path - -class Z3BenchmarksBasedTest : BenchmarksBasedTest() { - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun testConverter(name: String, samplePath: Path) = - testConverter(name, samplePath) { assertions -> - assertions - } - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun testModelConversion(name: String, samplePath: Path) = - testModelConversion(name, samplePath, KZ3Solver::class) - - @Execution(ExecutionMode.CONCURRENT) - @ParameterizedTest(name = "{0}") - @MethodSource("z3SolverTestData") - fun testSolver(name: String, samplePath: Path) = - testSolver(name, samplePath, KZ3Solver::class) - - - companion object { - @JvmStatic - fun z3TestData() = testData - - @JvmStatic - fun z3SolverTestData() = z3TestData() - .filter { it.name !in KnownZ3Issues.z3FpFmaFalseSatSamples } - .filter { it.name !in KnownZ3Issues.z3FpFmaFalseUnsatSamples } - .ensureNotEmpty() - } -} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt deleted file mode 100644 index b124d5768..000000000 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt +++ /dev/null @@ -1,253 +0,0 @@ -package io.ksmt.test.benchmarks - -import com.microsoft.z3.Context -import kotlinx.coroutines.async -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Timeout -import org.junit.jupiter.api.parallel.Execution -import org.junit.jupiter.api.parallel.ExecutionMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.expr.transformer.KNonRecursiveTransformer -import io.ksmt.solver.z3.KZ3SMTLibParser -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.sort.KSort -import java.nio.file.Path -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import kotlin.io.path.Path -import kotlin.io.path.absolutePathString -import kotlin.io.path.writeLines -import kotlin.system.measureNanoTime - -@Disabled -@Execution(ExecutionMode.SAME_THREAD) -@Timeout(10, unit = TimeUnit.SECONDS) -class Z3ConverterBenchmark : BenchmarksBasedTest() { - - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun measureFormulaSize(name: String, samplePath: Path) = ignoreExceptions { - with(KContext()) { - val assertions = KZ3SMTLibParser(this).parse(samplePath) - val size = assertions.sumOf { FormulaSizeCalculator.size(it) } - saveData(name, "size", "$size") - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun measureNativeAssertionTime(name: String, samplePath: Path) = ignoreExceptions { - Context().use { ctx -> - val assertions = ctx.parseSMTLIB2File( - samplePath.absolutePathString(), - emptyArray(), - emptyArray(), - emptyArray(), - emptyArray() - ) - val solver = ctx.mkSolver() - - // force solver initialization - solver.push() - - val assertTime = measureNanoTime { - assertions.forEach { solver.add(it) } - } - saveData(name, "native", "$assertTime") - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun measureNativeParsingAndAssertionTime(name: String, samplePath: Path) = ignoreExceptions { - Context().use { ctx -> - val solver = ctx.mkSolver() - - // force solver initialization - solver.push() - - val assertAndParseTime = measureNanoTime { - val assertions = ctx.parseSMTLIB2File( - samplePath.absolutePathString(), - emptyArray(), - emptyArray(), - emptyArray(), - emptyArray() - ) - assertions.forEach { solver.add(it) } - } - saveData(name, "native_parse", "$assertAndParseTime") - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun measureKsmtAssertionTime(name: String, samplePath: Path) = ignoreExceptions { - with(KContext()) { - val assertions = KZ3SMTLibParser(this).parse(samplePath) - KZ3Solver(this).use { solver -> - - // force solver initialization - solver.push() - - val internalizeAndAssert = measureNanoTime { - assertions.forEach { solver.assert(it) } - } - saveData(name, "ksmt", "$internalizeAndAssert") - } - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("z3TestData") - fun measureRunnerAssertionTime(name: String, samplePath: Path) = ignoreExceptions { - with(KContext()) { - val assertions = KZ3SMTLibParser(this).parse(samplePath) - solverManager.createSolver(this, KZ3Solver::class).use { solver -> - - // force solver initialization - solver.push() - - val internalizeAndAssert = measureNanoTime { - runBlocking { - assertions.map { expr -> - async { solver.assertAsync(expr) } - }.joinAll() - } - } - saveData(name, "runner", "$internalizeAndAssert") - } - } - } - - - private inline fun ignoreExceptions(block: () -> Unit) = try { - block() - } catch (t: Throwable) { - System.err.println(t.toString()) - } - - private class FormulaSizeCalculator(ctx: KContext) : KNonRecursiveTransformer(ctx) { - private var expressionCount = 0 - override fun transformExpr(expr: KExpr): KExpr { - expressionCount++ - return super.transformExpr(expr) - } - - companion object { - fun size(expr: KExpr<*>): Int = - FormulaSizeCalculator(expr.ctx) - .also { it.apply(expr) } - .expressionCount - } - } - - companion object { - private val data = ConcurrentHashMap>() - - private fun saveData(sample: String, type: String, value: String) { - data.getOrPut(sample) { ConcurrentHashMap() }[type] = value - } - - @AfterAll - @JvmStatic - fun saveData() { - val headerRow = data.values.firstOrNull()?.keys?.sorted() ?: return - val columns = listOf("Sample name") + headerRow - val orderedData = listOf(columns) + data.map { (name, sampleData) -> - val row = headerRow.map { sampleData[it] ?: "" } - listOf(name) + row - } - val csvData = orderedData.map { it.joinToString(separator = ",") } - Path("data.csv") - .writeLines(csvData) - } - - @JvmStatic - fun z3TestData() = testData.skipSlowSamples() - - // skip samples with slow native assert - private fun List.skipSlowSamples() = this - .filterNot { it.name.startsWith("QF_ABV_try3") } - .filterNot { it.name.startsWith("QF_ABV_try5") } - .filterNot { it.name.startsWith("QF_ABV_testcase") } - .filterNot { it.name in slowSamples } - - private val slowSamples = setOf( - "QF_ABV_ridecore-qf_abv-bug.smt2", - "QF_ABV_noregions-fullmemite.stp.smt2", - "QF_ABV_blaster-concrete.stp.smt2", - "QF_ABV_blaster-wp.ir.3.simplified13.stp.smt2", - "QF_ABV_blaster-wp.ir.3.simplified8.stp.smt2", - "QF_ABV_blaster.stp.smt2", - "QF_ABV_ff.stp.smt2", - "QF_ABV_grep0084.stp.smt2", - "QF_ABV_grep0095.stp.smt2", - "QF_ABV_grep0106.stp.smt2", - "QF_ABV_grep0117.stp.smt2", - "QF_ABV_grep0777.stp.smt2", - "AUFLIA_smt8061204852622600993.smt2", - "AUFLIRA_cl5_nebula_init_0222.fof.smt2", - "AUFLIRA_quaternion_ds1_inuse_0005.fof.smt2", - "AUFLIRA_quaternion_ds1_inuse_0222.fof.smt2", - "AUFLIRA_quaternion_ds1_symm_1198.fof.smt2", - "AUFLIRA_quaternion_ds1_symm_1558.fof.smt2", - "AUFLIRA_quaternion_ds1_symm_2906.fof.smt2", - "AUFLIRA_thruster_init_0967.fof.smt2", - "AUFLIRA_thruster_inuse_1134.fof.smt2", - "AUFLIRA_thruster_symm_0369.fof.smt2", - "AUFLIRA_thruster_symm_0946.fof.smt2", - "AUFLIRA_thruster_symm_2979.fof.smt2", - "LIA_ARI592=1.smt2", - "QF_ABV_egt-1334.smt2", - "QF_ABV_egt-2021.smt2", - "QF_ABV_egt-2319.smt2", - "QF_ABV_egt-4810.smt2", - "QF_ABV_egt-7600.smt2", - "QF_ABV_no_init_multi_member24.smt2", - "QF_ABVFP_query.01403.smt2", - "QF_ABVFP_query.03635.smt2", - "QF_ABVFP_query.05867.smt2", - "QF_ABVFP_query.06109.smt2", - "QF_ABVFP_query.07672.smt2", - "QF_ABVFP_query.08043.smt2", - "QF_ABVFP_query.08657.smt2", - "QF_BVFP_query.01217.smt2", - "QF_BVFP_query.03449.smt2", - "QF_BVFP_query.07486.smt2", - "QF_BVFP_query.08173.smt2", - "QF_FP_add-has-solution-15709.smt2", - "QF_FP_add-has-solution-8348.smt2", - "QF_FP_add-has-solution-8905.smt2", - "QF_FP_div-has-no-other-solution-14323.smt2", - "QF_FP_div-has-solution-12164.smt2", - "QF_FP_fma-has-no-other-solution-13136.smt2", - "QF_FP_gt-has-no-other-solution-6602.smt2", - "QF_FP_lt-has-no-other-solution-10072.smt2", - "QF_FP_lt-has-no-other-solution-10686.smt2", - "QF_FP_lt-has-no-other-solution-4316.smt2", - "QF_FP_lt-has-solution-5619.smt2", - "QF_FP_min-has-no-other-solution-18171.smt2", - "QF_FP_min-has-solution-10260.smt2", - "QF_FP_mul-has-no-other-solution-10919.smt2", - "QF_FP_rem-has-no-other-solution-5399.smt2", - "QF_FP_rem-has-no-other-solution-6643.smt2", - "QF_FP_rem-has-solution-5252.smt2", - "QF_FP_sqrt-has-no-other-solution-16567.smt2", - "QF_FP_sqrt-has-solution-1666.smt2", - "QF_FP_sub-has-no-other-solution-12726.smt2", - "QF_FP_sub-has-solution-19199.smt2", - "QF_FP_toIntegral-has-no-other-solution-1029.smt2", - "QF_FP_toIntegral-has-no-other-solution-1345.smt2", - "QF_FP_toIntegral-has-no-other-solution-264.smt2", - "QF_UFLIA_xs_28_38.smt2", - "QF_ABV_ndes.smt2" - ) - } -} From 00aa09a77d8d5290219ba2ab69e8b60c3b3099e8 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 26 Jun 2023 15:46:20 +0300 Subject: [PATCH 010/228] Fix bug with list subtraction im ApplyMaxRes, fix when styles, add more tests --- .../io/ksmt/solver/maxsmt/KMaxSMTSolver.kt | 25 +++++++++++++------ .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 17 +++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt index a0599cba1..1f56b4c8e 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt @@ -57,8 +57,12 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : val (formulaReified, reificationVariables) = reifyCore(formula, getUnsatCoreOfConstraints(formula, unsatCore), i) - // TODO, FIX: Для одного странно использовать KOrNaryExpr - this.assert(KOrNaryExpr(ctx, reificationVariables)) + // TODO: а если их 0, может ли быть такое? + when (reificationVariables.size) { + 1 -> this.assert(reificationVariables.first()) + 2 -> this.assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) + else -> this.assert(KOrNaryExpr(ctx, reificationVariables)) + } formula = applyMaxRes(formulaReified, reificationVariables) @@ -123,12 +127,19 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : val indexLast = literalsToReify.size - 1 if (index < indexLast) { - val disjunction = KOrNaryExpr( - ctx, - literalsToReify.subList(index + 1, indexLast), - ) + val disjunction = + // We do not take the current element (from the next to the last) + when (indexLast - index) { + 1 -> literalsToReify[index + 1] + 2 -> KOrBinaryExpr(ctx, literalsToReify[index + 1], literalsToReify[index + 2]) + else -> KOrNaryExpr( + ctx, + literalsToReify.subList(index + 1, indexLast + 1), + ) + + } - val literalToReifyDisjunction = ctx.boolSort.mkConst("d$indexedLiteral") + val literalToReifyDisjunction = ctx.boolSort.mkConst("d$index") this.assert( KEqExpr( diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index e38937f7d..dfa93f14b 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -43,4 +43,21 @@ class KMaxSMTSolverTest { println("Finished on $iter iteration") } } + + @Test + fun smokeTest3() { + with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSMTSolver = KMaxSMTSolver(this, z3Solver) + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + maxSMTSolver.assert(KOrBinaryExpr(this, a, b)) + maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), b), 1) + maxSMTSolver.assertSoft(KOrBinaryExpr(this, a, KNotExpr(this, b)), 1) + maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)), 1) + val (model, iter) = maxSMTSolver.checkMaxSMT() + println("Model:\n$model") + println("Finished on $iter iteration") + } + } } \ No newline at end of file From 1ab1638f33ad1b7f2defaf6e10023ea5912cffc3 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 27 Jun 2023 18:51:43 +0300 Subject: [PATCH 011/228] Support weighted soft constraints, soft constraints with duplicate expression , add more tests, rename to MaxSat --- .../src/main/kotlin/io/ksmt/expr/Bool.kt | 4 +- {ksmt-maxsmt => ksmt-maxsat}/build.gradle.kts | 0 .../io/ksmt/solver/maxsat}/Constraint.kt | 2 +- .../io/ksmt/solver/maxsat}/HardConstraint.kt | 2 +- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 126 +++++++------ .../kotlin/io/ksmt/solver/maxsat}/Main.kt | 19 +- .../io/ksmt/solver/maxsat}/SoftConstraint.kt | 2 +- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 165 ++++++++++++++++++ .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 63 ------- settings.gradle.kts | 2 +- 10 files changed, 252 insertions(+), 133 deletions(-) rename {ksmt-maxsmt => ksmt-maxsat}/build.gradle.kts (100%) rename {ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt => ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat}/Constraint.kt (80%) rename {ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt => ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat}/HardConstraint.kt (81%) rename ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt => ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt (58%) rename {ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt => ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat}/Main.kt (69%) rename {ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt => ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat}/SoftConstraint.kt (83%) create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt delete mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index 5a802de47..16aa2b35f 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -27,7 +27,7 @@ abstract class KAndExpr(ctx: KContext) : KApp(ctx) { override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) } -class KAndBinaryExpr internal constructor( +class KAndBinaryExpr( ctx: KContext, val lhs: KExpr, val rhs: KExpr @@ -41,7 +41,7 @@ class KAndBinaryExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { lhs }, { rhs }) } -class KAndNaryExpr internal constructor( +class KAndNaryExpr( ctx: KContext, override val args: List> ) : KAndExpr(ctx) { diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsat/build.gradle.kts similarity index 100% rename from ksmt-maxsmt/build.gradle.kts rename to ksmt-maxsat/build.gradle.kts diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt similarity index 80% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt index 9b97e1f22..96236f3e4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Constraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsmt +package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt similarity index 81% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt index bea1e5ba8..8ebe4a1d1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/HardConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsmt +package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt similarity index 58% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 1f56b4c8e..570741a78 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsmt +package io.ksmt.solver.maxsat import io.ksmt.KContext import io.ksmt.expr.KEqExpr @@ -6,6 +6,7 @@ import io.ksmt.expr.KExpr import io.ksmt.expr.KNotExpr import io.ksmt.expr.KOrBinaryExpr import io.ksmt.expr.KOrNaryExpr +import io.ksmt.expr.KTrue import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration @@ -16,79 +17,113 @@ import io.ksmt.utils.mkConst import kotlin.time.Duration // TODO: solver type must be KSolver but the code does not work with it -class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KSolver { +class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : KSolver { private val softConstraints = mutableListOf() - // Enum checking max SAT state (last check status, was not checked, invalid (soft assertions changed)) - // Should I support push/pop for soft constraints? - fun assertSoft(expr: KExpr, weight: Int) { + require(weight > 0) { "Soft constraint weight must be greater than 0" } softConstraints.add(SoftConstraint(expr, weight)) } - // TODO: return soft constraints // TODO: add timeout? - fun checkMaxSMT(): Pair { - require(softConstraints.isNotEmpty()) { "Soft constraints list should not be empty" } + fun checkMaxSMT(): Pair> { + if (softConstraints.isEmpty()) { + return Pair(solver.check(), listOf()) + } - // Should I check every time on satisfiability? - // У них в солверах есть last checked status. - // Should I add timeout? val status = solver.check() if (status == KSolverStatus.UNSAT) { error("Conjunction of asserted formulas is UNSAT") } else if (status == KSolverStatus.UNKNOWN) { - // TODO: handle this case + // TODO: implement } var i = 0 var formula = softConstraints.toMutableList() while (true) { - val (solverStatus, unsatCore, model) = solveSMT(formula) + val (solverStatus, unsatCore, model) = solveSAT(formula) if (solverStatus == KSolverStatus.SAT) { - return Pair(model, i) + // TODO: can I simplify this expression? + val satSoftConstraints = + softConstraints.filter { model?.eval(it.constraint)?.internEquals(KTrue(ctx)) == true } + return Pair(solverStatus, satSoftConstraints) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement } + val (weight, splitUnsatCore) = splitCoreSoftConstraints(formula, unsatCore) + val (formulaReified, reificationVariables) = - reifyCore(formula, getUnsatCoreOfConstraints(formula, unsatCore), i) + reifyCore(formula, splitUnsatCore, i, weight) // TODO: а если их 0, может ли быть такое? when (reificationVariables.size) { - 1 -> this.assert(reificationVariables.first()) - 2 -> this.assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) - else -> this.assert(KOrNaryExpr(ctx, reificationVariables)) + 1 -> assert(reificationVariables.first()) + 2 -> assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) + else -> assert(KOrNaryExpr(ctx, reificationVariables)) } - formula = applyMaxRes(formulaReified, reificationVariables) + formula = applyMaxRes(formulaReified, reificationVariables, weight) ++i } } - // Returns issat, unsat core (?) and assignment - private fun solveSMT(assumptions: List): Triple>, KModel?> { - val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint }) + private fun splitCoreSoftConstraints(formula: MutableList, unsatCore: List>) + : Pair> { + val coreSoftConstraints = formula.filter { x -> unsatCore.find { x.constraint.internEquals(it) } != null } + // Here we union soft constraints from core with the same expr + + val coreSoftConstraintsUnioned = coreSoftConstraints.toMutableList() + + var i = 0 + while (i < coreSoftConstraintsUnioned.size) { + val currentConstraint = coreSoftConstraintsUnioned[i].constraint + + val sameConstraints = + coreSoftConstraintsUnioned.filter { it.constraint.internEquals(currentConstraint) } + + if (sameConstraints.size > 1) { + val sameConstraintsWeightSum = sameConstraints.sumOf { it.weight } + coreSoftConstraintsUnioned.removeAll(sameConstraints) + coreSoftConstraintsUnioned.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) + + formula.removeAll(sameConstraints) + formula.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) + } - if (status == KSolverStatus.SAT) { - return Triple(status, listOf(), solver.model()) - } else if (status == KSolverStatus.UNSAT) { - return Triple(status, solver.unsatCore(), null) + ++i } - return Triple(status, listOf(), null) + val minWeight = coreSoftConstraintsUnioned.minBy { it.weight }.weight + + coreSoftConstraintsUnioned.forEach { x -> + if (x.weight > minWeight) { + formula.add(SoftConstraint(x.constraint, minWeight)) + formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) + formula.removeIf { it.weight == x.weight && it.constraint == x.constraint } + } + } + + return Pair(minWeight, formula.filter { x -> x.weight == minWeight && unsatCore.find { x.constraint.internEquals(it) } != null }) } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int) + private fun solveSAT(assumptions: List): Triple>, KModel?> = + when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint })) { + KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) + KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) + KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) + } + + private fun reifyCore(formula: MutableList, unsatCore: List, i: Int, weight: Int) : Pair, List>> { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { - if (coreElement.value.weight == 1) { + if (coreElement.value.weight == weight) { formula.remove(coreElement.value) val coreElementConstraint = coreElement.value.constraint @@ -102,9 +137,9 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KNotExpr(ctx, literalToReify), ) // TODO: Переобозначить и остальные элементы в b_i_j - this.assert(constraintToReify) + assert(constraintToReify) - formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), 1)) + formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), weight)) literalsToReify.add(literalToReify) } @@ -113,18 +148,17 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : return Pair(formula, literalsToReify) } - private fun applyMaxRes(formula: MutableList, literalsToReify: List>) + private fun applyMaxRes(formula: MutableList, literalsToReify: List>, weight: Int) : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes - formula.removeIf { x -> - x.constraint.internEquals(KNotExpr(ctx, indexedLiteral.value)) && - x.weight == 1 + formula.removeIf { it.constraint.internEquals(KNotExpr(ctx, indexedLiteral.value)) && + it.weight == weight } val index = indexedLiteral.index - val indexLast = literalsToReify.size - 1 + val indexLast = literalsToReify.lastIndex if (index < indexLast) { val disjunction = @@ -136,12 +170,12 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : ctx, literalsToReify.subList(index + 1, indexLast + 1), ) - } + // TODO, FIX: не будет ли коллизии при последующих запусках? val literalToReifyDisjunction = ctx.boolSort.mkConst("d$index") - this.assert( + assert( KEqExpr( ctx, literalToReifyDisjunction, @@ -156,29 +190,15 @@ class KMaxSMTSolver(private val ctx: KContext, private val solver: KZ3Solver) : KNotExpr(ctx, indexedLiteral.value), KNotExpr(ctx, literalToReifyDisjunction), ), - 1, + weight, ), ) - } else { - // Здесь добавляем пустой дизъюнкт, но по факту это не нужно делать (т.к. потом его удалим) } } return formula } - private fun getUnsatCoreOfConstraints(formula: MutableList, unsatCore: List>) - : List { - val unsatCoreOfConstraints = mutableListOf() - - for (coreElement in unsatCore) { - val softConstraint = formula.find { x -> x.constraint == coreElement } - softConstraint?.let { unsatCoreOfConstraints.add(it) } - } - - return unsatCoreOfConstraints - } - override fun configure(configurator: KSolverConfiguration.() -> Unit) { solver.configure(configurator) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt similarity index 69% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index a8b3eb0ab..100283729 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -1,11 +1,8 @@ -package io.ksmt.solver.maxsmt +package io.ksmt.solver.maxsat import io.ksmt.KContext -import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.utils.asExpr import io.ksmt.utils.mkConst -import java.io.Console fun main() { test() @@ -13,16 +10,16 @@ fun main() { fun test() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSMTSolver = KMaxSMTSolver(this, z3Solver) + val maxSATSolver = KMaxSATSolver(this, z3Solver) val a = boolSort.mkConst("a") val b = boolSort.mkConst("b") val c = boolSort.mkConst("c") - maxSMTSolver.assert(a) - maxSMTSolver.assert(b) - maxSMTSolver.assert(c) - maxSMTSolver.assertSoft(mkAnd(a, mkNot(c)), 1) - maxSMTSolver.assertSoft(mkNot(a), 1) - val (model, iter) = maxSMTSolver.checkMaxSMT() + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(c) + maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 1) + maxSATSolver.assertSoft(mkNot(a), 1) + val (model, iter) = maxSATSolver.checkMaxSMT() println("Model:\n$model") println("Finished on $iter iteration") diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt similarity index 83% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt index 2caea568d..eb1f8aa9b 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsmt +package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt new file mode 100644 index 000000000..d1e42f1e5 --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -0,0 +1,165 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.KContext +import io.ksmt.expr.KAndBinaryExpr +import io.ksmt.expr.KAndNaryExpr +import io.ksmt.expr.KNotExpr +import io.ksmt.expr.KOrBinaryExpr +import io.ksmt.expr.KOrNaryExpr +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.utils.getValue +import io.ksmt.utils.mkConst +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class KMaxSATSolverTest { + @Test + fun noSoftConstraintsSATTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a by boolSort + val b by boolSort + val c by boolSort + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(c) + maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 3) + maxSATSolver.assertSoft(mkNot(a), 5) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + assertTrue(satSoftConstraints.isEmpty()) + } + + @Test + fun oneOfTwoSoftConstraintsSATTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a by boolSort + val b by boolSort + + val notA = KNotExpr(this, a) + val notB = KNotExpr(this, b) + + maxSATSolver.assert(KOrBinaryExpr(this, a, b)) + maxSATSolver.assert(KOrBinaryExpr(this, notA, b)) + maxSATSolver.assertSoft(KOrBinaryExpr(this, a, notB), 2) + + val notAOrNotBExpr = KOrBinaryExpr(this, notA, notB) + maxSATSolver.assertSoft(notAOrNotBExpr, 3) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + assertTrue(satSoftConstraints.size == 1 && satSoftConstraints[0].weight == 3 && + satSoftConstraints[0].constraint == notAOrNotBExpr) + } + + @Test + fun twoOfThreeSoftConstraintsSATTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a by boolSort + val b by boolSort + + val notA = KNotExpr(this, a) + val notB = KNotExpr(this, b) + + maxSATSolver.assert(KOrBinaryExpr(this, a, b)) + + val notAOrBExpr = KOrBinaryExpr(this, notA, b) + maxSATSolver.assertSoft(notAOrBExpr, 4) + + val aOrNotBExpr = KOrBinaryExpr(this, a, notB) + maxSATSolver.assertSoft(aOrNotBExpr, 6) + + maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, notB), 2) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + + assertTrue(satSoftConstraints.size == 2) + } + + @Test + fun smokeTest4() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a by boolSort + val b by boolSort + + val notA = KNotExpr(this, a) + val notB = KNotExpr(this, b) + + maxSATSolver.assert(KOrBinaryExpr(this, a, b)) + maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, b)), 1) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, b), 1) + maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, b)), 1) + maxSATSolver.assertSoft(KOrBinaryExpr(this, a, notB), 1) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notA,notB), 1) + maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, notB)), 1) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + } + + @Test + fun smokeTest5() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val z = boolSort.mkConst("z") + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + val c = boolSort.mkConst("c") + + maxSATSolver.assert(z) + maxSATSolver.assertSoft(KAndBinaryExpr(this, a, b), 1) + val constr = KAndBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)) + maxSATSolver.assertSoft(constr, 5) + maxSATSolver.assertSoft(KAndNaryExpr(this, listOf(a, b, z)), 2) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + + assertTrue(satSoftConstraints.size == 1 && satSoftConstraints[0].weight == 5 && + satSoftConstraints[0].constraint == constr) + } + + @Test + fun sameExpressionSoftConstraintsSATTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + + val x by boolSort + val y by boolSort + + val notX = KNotExpr(this, x) + val notY = KNotExpr(this, y) + + maxSATSolver.assert(KOrBinaryExpr(this, x , y)) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, y), 6) + maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 4) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + + satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + } + + @Test + fun sameExpressionSoftConstraintsUNSATTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + + val x by boolSort + val y by boolSort + + val notX = KNotExpr(this, x) + val notY = KNotExpr(this, y) + + maxSATSolver.assert(KOrBinaryExpr(this, x , y)) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, y), 6) + maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 2) + + val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + + satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt deleted file mode 100644 index dfa93f14b..000000000 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package io.ksmt.solver.maxsmt - -import io.ksmt.KContext -import io.ksmt.expr.KNotExpr -import io.ksmt.expr.KOrBinaryExpr -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.utils.mkConst -import org.junit.jupiter.api.Test - -class KMaxSMTSolverTest { - @Test - fun smokeTest() { - with (KContext()) { - val z3Solver = KZ3Solver(this) - val maxSMTSolver = KMaxSMTSolver(this, z3Solver) - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - val c = boolSort.mkConst("c") - maxSMTSolver.assert(a) - maxSMTSolver.assert(b) - maxSMTSolver.assert(c) - maxSMTSolver.assertSoft(mkAnd(a, mkNot(c)), 1) - maxSMTSolver.assertSoft(mkNot(a), 1) - val (model, iter) = maxSMTSolver.checkMaxSMT() - println("Model:\n$model") - println("Finished on $iter iteration") - } - } - - @Test - fun smokeTest2() { - with (KContext()) { - val z3Solver = KZ3Solver(this) - val maxSMTSolver = KMaxSMTSolver(this, z3Solver) - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - maxSMTSolver.assert(KOrBinaryExpr(this, a, b)) - maxSMTSolver.assert(KOrBinaryExpr(this, KNotExpr(this, a), b)) - maxSMTSolver.assertSoft(KOrBinaryExpr(this, a, KNotExpr(this, b)), 1) - maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)), 1) - val (model, iter) = maxSMTSolver.checkMaxSMT() - println("Model:\n$model") - println("Finished on $iter iteration") - } - } - - @Test - fun smokeTest3() { - with (KContext()) { - val z3Solver = KZ3Solver(this) - val maxSMTSolver = KMaxSMTSolver(this, z3Solver) - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - maxSMTSolver.assert(KOrBinaryExpr(this, a, b)) - maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), b), 1) - maxSMTSolver.assertSoft(KOrBinaryExpr(this, a, KNotExpr(this, b)), 1) - maxSMTSolver.assertSoft(KOrBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)), 1) - val (model, iter) = maxSMTSolver.checkMaxSMT() - println("Model:\n$model") - println("Finished on $iter iteration") - } - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 8913bbb76..0bea54a97 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,7 @@ include("ksmt-runner") include("ksmt-runner:solver-generator") include("ksmt-test") include("ksmt-cvc5") -include("ksmt-maxsmt") +include("ksmt-maxsat") pluginManagement { resolutionStrategy { From b44203b664cda6ad5e870daa057086e21b6cea0a Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 12:39:36 +0300 Subject: [PATCH 012/228] Create MaxSATResult class, generalize KMaxSATSolver class --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 21 ++++----- .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 47 +++++-------------- .../io/ksmt/solver/maxsat/MaxSATResult.kt | 6 +++ .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 34 ++++++++------ 4 files changed, 47 insertions(+), 61 deletions(-) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 570741a78..c0166f3a9 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -11,13 +11,12 @@ import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration -// TODO: solver type must be KSolver but the code does not work with it -class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : KSolver { +class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver + where T : KSolverConfiguration { private val softConstraints = mutableListOf() fun assertSoft(expr: KExpr, weight: Int) { @@ -25,18 +24,18 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : softConstraints.add(SoftConstraint(expr, weight)) } - // TODO: add timeout? - fun checkMaxSMT(): Pair> { + // TODO: add timeout + fun checkMaxSAT(): MaxSATResult { if (softConstraints.isEmpty()) { - return Pair(solver.check(), listOf()) + return MaxSATResult(listOf(), solver.check(), true) } val status = solver.check() if (status == KSolverStatus.UNSAT) { - error("Conjunction of asserted formulas is UNSAT") + return MaxSATResult(listOf(), status, true) } else if (status == KSolverStatus.UNKNOWN) { - // TODO: implement + return MaxSATResult(listOf(), status, false) } var i = 0 @@ -49,7 +48,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : // TODO: can I simplify this expression? val satSoftConstraints = softConstraints.filter { model?.eval(it.constraint)?.internEquals(KTrue(ctx)) == true } - return Pair(solverStatus, satSoftConstraints) + return MaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement } @@ -59,7 +58,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : val (formulaReified, reificationVariables) = reifyCore(formula, splitUnsatCore, i, weight) - // TODO: а если их 0, может ли быть такое? when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) 2 -> assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) @@ -127,7 +125,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : formula.remove(coreElement.value) val coreElementConstraint = coreElement.value.constraint - // TODO: как реализовать переобозначение? Что если формула встречается как подформула в других формулах? val literalToReify = ctx.boolSort.mkConst("b$i${coreElement.index}") @@ -136,7 +133,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KZ3Solver) : coreElementConstraint, KNotExpr(ctx, literalToReify), ) - // TODO: Переобозначить и остальные элементы в b_i_j + assert(constraintToReify) formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), weight)) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index 100283729..944662c8d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -5,38 +5,17 @@ import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.mkConst fun main() { - test() -} - -fun test() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - val c = boolSort.mkConst("c") - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(c) - maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 1) - maxSATSolver.assertSoft(mkNot(a), 1) - val (model, iter) = maxSATSolver.checkMaxSMT() - println("Model:\n$model") - println("Finished on $iter iteration") - -/* val aIsTrue = mkEq(a, mkTrue()) - val bIsTrue = mkEq(b, mkTrue()) - val cIsTrue = mkEq(c, mkTrue()) - - z3Solver.assert(aIsTrue) - z3Solver.assert(bIsTrue) - z3Solver.assert(cIsTrue) - z3Solver.assert(mkAnd(aIsTrue, mkNot(cIsTrue))) - z3Solver.assert(mkEq(a, mkFalse())) - val status = z3Solver.check() - if (status == KSolverStatus.UNSAT) { - println(status) - val unsatCore = z3Solver.unsatCore() - println("Unsat core length: ${unsatCore.size}") - unsatCore.forEach { x -> println(x) } - }*/ + with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + val c = boolSort.mkConst("c") + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(c) + maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 1) + maxSATSolver.assertSoft(mkNot(a), 1) + //val maxSATResult = maxSATSolver.checkMaxSMT() + } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt new file mode 100644 index 000000000..d842fea73 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.KSolverStatus + +class MaxSATResult(val satSoftConstraints: List, + val hardConstraintsSATStatus: KSolverStatus, val maxSATSucceeded: Boolean) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index d1e42f1e5..cc2d74d32 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -6,6 +6,7 @@ import io.ksmt.expr.KAndNaryExpr import io.ksmt.expr.KNotExpr import io.ksmt.expr.KOrBinaryExpr import io.ksmt.expr.KOrNaryExpr +import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue import io.ksmt.utils.mkConst @@ -26,8 +27,11 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 3) maxSATSolver.assertSoft(mkNot(a), 5) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() - assertTrue(satSoftConstraints.isEmpty()) + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) } @Test @@ -47,9 +51,9 @@ class KMaxSATSolverTest { val notAOrNotBExpr = KOrBinaryExpr(this, notA, notB) maxSATSolver.assertSoft(notAOrNotBExpr, 3) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() - assertTrue(satSoftConstraints.size == 1 && satSoftConstraints[0].weight == 3 && - satSoftConstraints[0].constraint == notAOrNotBExpr) + val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 3 && + maxSATResult.satSoftConstraints[0].constraint == notAOrNotBExpr) } @Test @@ -72,9 +76,9 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, notB), 2) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(satSoftConstraints.size == 2) + assertTrue(maxSATResult.satSoftConstraints.size == 2) } @Test @@ -95,7 +99,7 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, notA,notB), 1) maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, notB)), 1) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + val maxSATResult = maxSATSolver.checkMaxSAT() } @Test @@ -113,10 +117,10 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(constr, 5) maxSATSolver.assertSoft(KAndNaryExpr(this, listOf(a, b, z)), 2) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(satSoftConstraints.size == 1 && satSoftConstraints[0].weight == 5 && - satSoftConstraints[0].constraint == constr) + assertTrue(maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 5 && + maxSATResult.satSoftConstraints[0].constraint == constr) } @Test @@ -136,9 +140,9 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 4) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + val maxSATResult = maxSATSolver.checkMaxSAT() - satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + maxSATResult.satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } } @Test @@ -158,8 +162,8 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 2) - val (status, satSoftConstraints) = maxSATSolver.checkMaxSMT() + val maxSATResult = maxSATSolver.checkMaxSAT() - satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + maxSATResult.satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } } } From 88bc4afcaee173e66d8d78973c721e9d65a72eb3 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 18:32:17 +0300 Subject: [PATCH 013/228] Improve tests, do unionSoftConstraintsWithSameExpressions when get formula, --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 66 +++++++---- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 107 +++++++++--------- 2 files changed, 92 insertions(+), 81 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index c0166f3a9..b06873b3d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -24,7 +24,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver softConstraints.add(SoftConstraint(expr, weight)) } - // TODO: add timeout + // TODO: add timeout support fun checkMaxSAT(): MaxSATResult { if (softConstraints.isEmpty()) { return MaxSATResult(listOf(), solver.check(), true) @@ -41,6 +41,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver var i = 0 var formula = softConstraints.toMutableList() + unionSoftConstraintsWithSameExpressions(formula) + while (true) { val (solverStatus, unsatCore, model) = solveSAT(formula) @@ -53,10 +55,10 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver // TODO: implement } - val (weight, splitUnsatCore) = splitCoreSoftConstraints(formula, unsatCore) + val (weight, splitUnsatCore) = splitUnsatCoreSoftConstraints(formula, unsatCore) val (formulaReified, reificationVariables) = - reifyCore(formula, splitUnsatCore, i, weight) + reifyCore(formula, splitUnsatCore, i, weight) when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) @@ -70,24 +72,50 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } } - private fun splitCoreSoftConstraints(formula: MutableList, unsatCore: List>) + /** + * Splits all soft constraints from the unsat core into two groups: + * - constraints with the weight equal to the minimum from the unsat core soft constraint weight + * - constraints with the weight equal to old weight - minimum weight + * + * Returns a pair of minimum weight and a list of unsat core soft constraints with minimum weight. + */ + private fun splitUnsatCoreSoftConstraints(formula: MutableList, unsatCore: List>) : Pair> { - val coreSoftConstraints = formula.filter { x -> unsatCore.find { x.constraint.internEquals(it) } != null } - // Here we union soft constraints from core with the same expr + // Filters soft constraints from the unsat core. + val unsatCoreSoftConstraints = + formula.filter { x -> unsatCore.any { x.constraint.internEquals(it) } }.toMutableList() + + val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight - val coreSoftConstraintsUnioned = coreSoftConstraints.toMutableList() + // Splits every soft constraint from the unsat core with the weight greater than + // minimum weight into two soft constraints with the same expression: with minimum weight and + // with the weight equal to old weight - minimum weight. + unsatCoreSoftConstraints.forEach { x -> + if (x.weight > minWeight) { + formula.add(SoftConstraint(x.constraint, minWeight)) + formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) + formula.removeIf { it.weight == x.weight && it.constraint.internEquals(x.constraint) } + } + } + + return Pair(minWeight, formula.filter { x -> x.weight == minWeight && unsatCore.any { x.constraint.internEquals(it) } }) + } + /** + * Unions soft constraints with same expressions into a single soft constraint. The soft constraint weight will be + * equal to the sum of old soft constraints weights. + */ + private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { var i = 0 - while (i < coreSoftConstraintsUnioned.size) { - val currentConstraint = coreSoftConstraintsUnioned[i].constraint - val sameConstraints = - coreSoftConstraintsUnioned.filter { it.constraint.internEquals(currentConstraint) } + while (i < formula.size) { + val currentConstraint = formula[i].constraint + val sameConstraints = formula.filter { it.constraint.internEquals(currentConstraint) } + + // Unions soft constraints with same expressions into a single soft constraint. if (sameConstraints.size > 1) { val sameConstraintsWeightSum = sameConstraints.sumOf { it.weight } - coreSoftConstraintsUnioned.removeAll(sameConstraints) - coreSoftConstraintsUnioned.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) formula.removeAll(sameConstraints) formula.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) @@ -95,18 +123,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver ++i } - - val minWeight = coreSoftConstraintsUnioned.minBy { it.weight }.weight - - coreSoftConstraintsUnioned.forEach { x -> - if (x.weight > minWeight) { - formula.add(SoftConstraint(x.constraint, minWeight)) - formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) - formula.removeIf { it.weight == x.weight && it.constraint == x.constraint } - } - } - - return Pair(minWeight, formula.filter { x -> x.weight == minWeight && unsatCore.find { x.constraint.internEquals(it) } != null }) } private fun solveSAT(assumptions: List): Triple>, KModel?> = diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index cc2d74d32..de4580061 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -1,15 +1,11 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext -import io.ksmt.expr.KAndBinaryExpr -import io.ksmt.expr.KAndNaryExpr import io.ksmt.expr.KNotExpr import io.ksmt.expr.KOrBinaryExpr -import io.ksmt.expr.KOrNaryExpr import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue -import io.ksmt.utils.mkConst import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -49,11 +45,15 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, a, notB), 2) val notAOrNotBExpr = KOrBinaryExpr(this, notA, notB) - maxSATSolver.assertSoft(notAOrNotBExpr, 3) + val notAOrNotBWeight = 3 + maxSATSolver.assertSoft(notAOrNotBExpr, notAOrNotBWeight) val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 3 && - maxSATResult.satSoftConstraints[0].constraint == notAOrNotBExpr) + + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSATSoftConstraints(listOf(SoftConstraint(notAOrNotBExpr, notAOrNotBWeight)), maxSATResult.satSoftConstraints) } @Test @@ -69,58 +69,23 @@ class KMaxSATSolverTest { maxSATSolver.assert(KOrBinaryExpr(this, a, b)) val notAOrBExpr = KOrBinaryExpr(this, notA, b) - maxSATSolver.assertSoft(notAOrBExpr, 4) + val notAOrBWeight = 4 + maxSATSolver.assertSoft(notAOrBExpr, notAOrBWeight) val aOrNotBExpr = KOrBinaryExpr(this, a, notB) - maxSATSolver.assertSoft(aOrNotBExpr, 6) + val aOrNotBWeight = 6 + maxSATSolver.assertSoft(aOrNotBExpr, aOrNotBWeight) maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, notB), 2) val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) - } - - @Test - fun smokeTest4() = with (KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) - val a by boolSort - val b by boolSort - - val notA = KNotExpr(this, a) - val notB = KNotExpr(this, b) - - maxSATSolver.assert(KOrBinaryExpr(this, a, b)) - maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, b)), 1) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, b), 1) - maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, b)), 1) - maxSATSolver.assertSoft(KOrBinaryExpr(this, a, notB), 1) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notA,notB), 1) - maxSATSolver.assertSoft(KOrNaryExpr(this, listOf(notA, notA, notB)), 1) - - val maxSATResult = maxSATSolver.checkMaxSAT() - } - - @Test - fun smokeTest5() = with (KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) - val z = boolSort.mkConst("z") - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - val c = boolSort.mkConst("c") - - maxSATSolver.assert(z) - maxSATSolver.assertSoft(KAndBinaryExpr(this, a, b), 1) - val constr = KAndBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)) - maxSATSolver.assertSoft(constr, 5) - maxSATSolver.assertSoft(KAndNaryExpr(this, listOf(a, b, z)), 2) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 5 && - maxSATResult.satSoftConstraints[0].constraint == constr) + val softConstraintsToAssertSAT = + listOf(SoftConstraint(notAOrBExpr, notAOrBWeight), SoftConstraint(aOrNotBExpr, aOrNotBWeight)) + assertSATSoftConstraints(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) } @Test @@ -135,14 +100,26 @@ class KMaxSATSolverTest { val notY = KNotExpr(this, y) maxSATSolver.assert(KOrBinaryExpr(this, x , y)) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, y), 6) + + val notXOrYExpr = KOrBinaryExpr(this, notX, y) + val notXOrYWeight = 7 + maxSATSolver.assertSoft(notXOrYExpr, notXOrYWeight) + maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 4) + // TODO: check and fixes about interning in solver then place it before the first usage of the expression + val notXOrNotYExpr = KOrBinaryExpr(this, notX, notY) + val maxSATResult = maxSATSolver.checkMaxSAT() - maxSATResult.satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 3) + assertSATSoftConstraints(listOf(SoftConstraint(notXOrYExpr, notXOrYWeight), SoftConstraint(notXOrNotYExpr, 3), + SoftConstraint(notXOrNotYExpr, 4)), maxSATResult.satSoftConstraints) } @Test @@ -157,13 +134,31 @@ class KMaxSATSolverTest { val notY = KNotExpr(this, y) maxSATSolver.assert(KOrBinaryExpr(this, x , y)) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, y), 6) - maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) + + val notXOrYExpr = KOrBinaryExpr(this, notX, y) + val notXOrYExprWeight = 6 + maxSATSolver.assertSoft(notXOrYExpr, notXOrYExprWeight) + + val xOrNotYExpr = KOrBinaryExpr(this, x, notY) + val xOrNotYWeight = 6 + maxSATSolver.assertSoft(xOrNotYExpr, xOrNotYWeight) + maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 2) val maxSATResult = maxSATSolver.checkMaxSAT() - maxSATResult.satSoftConstraints.forEach { println("constr: ${it.constraint}; weight: ${it.weight}") } + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSATSoftConstraints(listOf(SoftConstraint(notXOrYExpr, notXOrYExprWeight), SoftConstraint(xOrNotYExpr, xOrNotYWeight)), + maxSATResult.satSoftConstraints) + } + + private fun assertSATSoftConstraints(constraintsToAssert: List, + satConstraints: List) { + for (constraint in constraintsToAssert) { + assertTrue(satConstraints.any { constraint.constraint.internEquals(it.constraint) && constraint.weight == it.weight }) + } } } From cec69ed294abdf864a4e473a092faaa2524ee804 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 18:53:54 +0300 Subject: [PATCH 014/228] Remove TODO from tests, add docs to methods, update their names --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 22 ++++++++++++------- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 6 ++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index b06873b3d..a23d1e8cd 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -44,7 +44,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver unionSoftConstraintsWithSameExpressions(formula) while (true) { - val (solverStatus, unsatCore, model) = solveSAT(formula) + val (solverStatus, unsatCore, model) = checkSAT(formula) if (solverStatus == KSolverStatus.SAT) { // TODO: can I simplify this expression? @@ -58,7 +58,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (weight, splitUnsatCore) = splitUnsatCoreSoftConstraints(formula, unsatCore) val (formulaReified, reificationVariables) = - reifyCore(formula, splitUnsatCore, i, weight) + reifyUnsatCore(formula, splitUnsatCore, i, weight) when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) @@ -87,14 +87,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight - // Splits every soft constraint from the unsat core with the weight greater than - // minimum weight into two soft constraints with the same expression: with minimum weight and - // with the weight equal to old weight - minimum weight. unsatCoreSoftConstraints.forEach { x -> if (x.weight > minWeight) { formula.add(SoftConstraint(x.constraint, minWeight)) formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) - formula.removeIf { it.weight == x.weight && it.constraint.internEquals(x.constraint) } + formula.removeIf { it.weight == x.weight && it.constraint == x.constraint } } } @@ -125,14 +122,23 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } } - private fun solveSAT(assumptions: List): Triple>, KModel?> = + /** + * Checks on satisfiability hard constraints with assumed soft constraints. + * + * Returns a triple of solver status, unsat core (if exists, empty list otherwise) and model + * (if exists, otherwise null). + */ + private fun checkSAT(assumptions: List): Triple>, KModel?> = when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint })) { KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) } - private fun reifyCore(formula: MutableList, unsatCore: List, i: Int, weight: Int) + /** + * Reifies unsat core soft constraints with literals. + */ + private fun reifyUnsatCore(formula: MutableList, unsatCore: List, i: Int, weight: Int) : Pair, List>> { val literalsToReify = mutableListOf>() diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index de4580061..41adee6c0 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -107,11 +107,9 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 4) - - // TODO: check and fixes about interning in solver then place it before the first usage of the expression val notXOrNotYExpr = KOrBinaryExpr(this, notX, notY) + maxSATSolver.assertSoft(notXOrNotYExpr, 3) + maxSATSolver.assertSoft(notXOrNotYExpr, 4) val maxSATResult = maxSATSolver.checkMaxSAT() From 522c6a92a150cf2ae44e7f2832a562011adf830d Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 19:27:35 +0300 Subject: [PATCH 015/228] Simplify expression in checkMaxSAT (removes TODO) --- .../kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index a23d1e8cd..ca2efa63b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -47,9 +47,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (solverStatus, unsatCore, model) = checkSAT(formula) if (solverStatus == KSolverStatus.SAT) { - // TODO: can I simplify this expression? val satSoftConstraints = - softConstraints.filter { model?.eval(it.constraint)?.internEquals(KTrue(ctx)) == true } + softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } return MaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement @@ -138,17 +137,17 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Reifies unsat core soft constraints with literals. */ - private fun reifyUnsatCore(formula: MutableList, unsatCore: List, i: Int, weight: Int) + private fun reifyUnsatCore(formula: MutableList, unsatCore: List, iter: Int, weight: Int) : Pair, List>> { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { + // TODO: оба с минимальным весом? if (coreElement.value.weight == weight) { formula.remove(coreElement.value) val coreElementConstraint = coreElement.value.constraint - val literalToReify = - ctx.boolSort.mkConst("b$i${coreElement.index}") + val literalToReify = ctx.boolSort.mkConst("b$iter${coreElement.index}") val constraintToReify = KEqExpr( ctx, @@ -167,6 +166,9 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return Pair(formula, literalsToReify) } + /** + * Applies MaxRes rule. + */ private fun applyMaxRes(formula: MutableList, literalsToReify: List>, weight: Int) : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { From 7be0435e4527ccf691ebf1cec1c3962bcee55f93 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 20:05:04 +0300 Subject: [PATCH 016/228] Add more comments --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 34 ++++++++++++------- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 10 +++--- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index ca2efa63b..a6f918378 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -19,12 +19,20 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver where T : KSolverConfiguration { private val softConstraints = mutableListOf() + /** + * Assert softly an expression with weight (aka soft constraint) into solver. + * + * @see checkMaxSAT + * */ fun assertSoft(expr: KExpr, weight: Int) { require(weight > 0) { "Soft constraint weight must be greater than 0" } softConstraints.add(SoftConstraint(expr, weight)) } // TODO: add timeout support + /** + * Solve maximum satisfiability problem. + */ fun checkMaxSAT(): MaxSATResult { if (softConstraints.isEmpty()) { return MaxSATResult(listOf(), solver.check(), true) @@ -54,7 +62,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver // TODO: implement } - val (weight, splitUnsatCore) = splitUnsatCoreSoftConstraints(formula, unsatCore) + val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) val (formulaReified, reificationVariables) = reifyUnsatCore(formula, splitUnsatCore, i, weight) @@ -72,17 +80,17 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Splits all soft constraints from the unsat core into two groups: - * - constraints with the weight equal to the minimum from the unsat core soft constraint weight + * Split all soft constraints from the unsat core into two groups: + * - constraints with the weight equal to the minimum from the unsat core soft constraint weights * - constraints with the weight equal to old weight - minimum weight * * Returns a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ - private fun splitUnsatCoreSoftConstraints(formula: MutableList, unsatCore: List>) + private fun splitUnsatCore(formula: MutableList, unsatCore: List>) : Pair> { // Filters soft constraints from the unsat core. val unsatCoreSoftConstraints = - formula.filter { x -> unsatCore.any { x.constraint.internEquals(it) } }.toMutableList() + formula.filter { x -> unsatCore.any { x.constraint.internEquals(it) } } val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight @@ -94,11 +102,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } } - return Pair(minWeight, formula.filter { x -> x.weight == minWeight && unsatCore.any { x.constraint.internEquals(it) } }) + return Pair(minWeight, + formula.filter { x -> x.weight == minWeight && unsatCore.any { x.constraint.internEquals(it) } }) } /** - * Unions soft constraints with same expressions into a single soft constraint. The soft constraint weight will be + * Union soft constraints with same expressions into a single soft constraint. The soft constraint weight will be * equal to the sum of old soft constraints weights. */ private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { @@ -122,7 +131,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Checks on satisfiability hard constraints with assumed soft constraints. + * Check on satisfiability hard constraints with assumed soft constraints. * * Returns a triple of solver status, unsat core (if exists, empty list otherwise) and model * (if exists, otherwise null). @@ -135,14 +144,13 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Reifies unsat core soft constraints with literals. + * Reify unsat core soft constraints with literals. */ - private fun reifyUnsatCore(formula: MutableList, unsatCore: List, iter: Int, weight: Int) - : Pair, List>> { + private fun reifyUnsatCore(formula: MutableList, unsatCore: List, + iter: Int, weight: Int): Pair, List>> { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { - // TODO: оба с минимальным весом? if (coreElement.value.weight == weight) { formula.remove(coreElement.value) @@ -167,7 +175,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Applies MaxRes rule. + * Apply MaxRes rule. */ private fun applyMaxRes(formula: MutableList, literalsToReify: List>, weight: Int) : MutableList { diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 41adee6c0..18f716af9 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -53,7 +53,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSATSoftConstraints(listOf(SoftConstraint(notAOrNotBExpr, notAOrNotBWeight)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSAT(listOf(SoftConstraint(notAOrNotBExpr, notAOrNotBWeight)), maxSATResult.satSoftConstraints) } @Test @@ -85,7 +85,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.satSoftConstraints.size == 2) val softConstraintsToAssertSAT = listOf(SoftConstraint(notAOrBExpr, notAOrBWeight), SoftConstraint(aOrNotBExpr, aOrNotBWeight)) - assertSATSoftConstraints(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) + assertSoftConstraintsSAT(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) } @Test @@ -116,7 +116,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSATSoftConstraints(listOf(SoftConstraint(notXOrYExpr, notXOrYWeight), SoftConstraint(notXOrNotYExpr, 3), + assertSoftConstraintsSAT(listOf(SoftConstraint(notXOrYExpr, notXOrYWeight), SoftConstraint(notXOrNotYExpr, 3), SoftConstraint(notXOrNotYExpr, 4)), maxSATResult.satSoftConstraints) } @@ -149,11 +149,11 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSATSoftConstraints(listOf(SoftConstraint(notXOrYExpr, notXOrYExprWeight), SoftConstraint(xOrNotYExpr, xOrNotYWeight)), + assertSoftConstraintsSAT(listOf(SoftConstraint(notXOrYExpr, notXOrYExprWeight), SoftConstraint(xOrNotYExpr, xOrNotYWeight)), maxSATResult.satSoftConstraints) } - private fun assertSATSoftConstraints(constraintsToAssert: List, + private fun assertSoftConstraintsSAT(constraintsToAssert: List, satConstraints: List) { for (constraint in constraintsToAssert) { assertTrue(satConstraints.any { constraint.constraint.internEquals(it.constraint) && constraint.weight == it.weight }) From ffff3971439badcbf8d87c985f21a740d070de52 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 28 Jun 2023 20:14:53 +0300 Subject: [PATCH 017/228] Do hard constraints work with push/pop --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 4 +++ .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index a6f918378..e706926a1 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -51,15 +51,19 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver unionSoftConstraintsWithSameExpressions(formula) + solver.push() + while (true) { val (solverStatus, unsatCore, model) = checkSAT(formula) if (solverStatus == KSolverStatus.SAT) { + solver.pop() val satSoftConstraints = softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } return MaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement + solver.pop() } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 18f716af9..44edba679 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -1,11 +1,14 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext +import io.ksmt.expr.KAndBinaryExpr +import io.ksmt.expr.KAndNaryExpr import io.ksmt.expr.KNotExpr import io.ksmt.expr.KOrBinaryExpr import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue +import io.ksmt.utils.mkConst import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -153,6 +156,28 @@ class KMaxSATSolverTest { maxSATResult.satSoftConstraints) } + @Test + fun smokeTest5() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val z = boolSort.mkConst("z") + val a = boolSort.mkConst("a") + val b = boolSort.mkConst("b") + + maxSATSolver.assert(z) + maxSATSolver.assertSoft(KAndBinaryExpr(this, a, b), 1) + val constr = KAndBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)) + maxSATSolver.assertSoft(constr, 5) + maxSATSolver.assertSoft(KAndNaryExpr(this, listOf(a, b, z)), 2) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue( + maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 5 && + maxSATResult.satSoftConstraints[0].constraint == constr + ) + } + private fun assertSoftConstraintsSAT(constraintsToAssert: List, satConstraints: List) { for (constraint in constraintsToAssert) { From 14d8bddd9e32c5d1ed452a8d059f17bc50a5b0cc Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 29 Jun 2023 22:02:41 +0300 Subject: [PATCH 018/228] Support push/pop for soft constraints, use shorter expressions form in tests --- .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 6 + .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 35 +++-- .../io/ksmt/solver/maxsat/MaxSATResult.kt | 6 - .../io/ksmt/solver/maxsat/MaxSATScope.kt | 4 + .../ksmt/solver/maxsat/MaxSATScopeManager.kt | 50 ++++++ .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 143 +++++++++++------- 6 files changed, 167 insertions(+), 77 deletions(-) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt new file mode 100644 index 000000000..c0dfe9d8e --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.KSolverStatus + +class KMaxSATResult(val satSoftConstraints: List, + val hardConstraintsSATStatus: KSolverStatus, val maxSATSucceeded: Boolean) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index e706926a1..927c23587 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -17,7 +17,9 @@ import kotlin.time.Duration class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver where T : KSolverConfiguration { - private val softConstraints = mutableListOf() + private val scopeManager = MaxSATScopeManager() + + private var softConstraints = mutableListOf() /** * Assert softly an expression with weight (aka soft constraint) into solver. @@ -26,24 +28,27 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * */ fun assertSoft(expr: KExpr, weight: Int) { require(weight > 0) { "Soft constraint weight must be greater than 0" } - softConstraints.add(SoftConstraint(expr, weight)) + + val softConstraint = SoftConstraint(expr, weight) + softConstraints.add(softConstraint) + scopeManager.incrementSoft() } // TODO: add timeout support /** * Solve maximum satisfiability problem. */ - fun checkMaxSAT(): MaxSATResult { + fun checkMaxSAT(): KMaxSATResult { if (softConstraints.isEmpty()) { - return MaxSATResult(listOf(), solver.check(), true) + return KMaxSATResult(listOf(), solver.check(), true) } val status = solver.check() if (status == KSolverStatus.UNSAT) { - return MaxSATResult(listOf(), status, true) + return KMaxSATResult(listOf(), status, true) } else if (status == KSolverStatus.UNKNOWN) { - return MaxSATResult(listOf(), status, false) + return KMaxSATResult(listOf(), status, false) } var i = 0 @@ -60,7 +65,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver solver.pop() val satSoftConstraints = softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } - return MaxSATResult(satSoftConstraints, solverStatus, true) + return KMaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement solver.pop() @@ -77,9 +82,9 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver else -> assert(KOrNaryExpr(ctx, reificationVariables)) } - formula = applyMaxRes(formulaReified, reificationVariables, weight) + formula = applyMaxRes(formulaReified, reificationVariables, i, weight) - ++i + i++ } } @@ -130,7 +135,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver formula.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) } - ++i + i++ } } @@ -155,11 +160,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { + // TODO: FIX, удалим оба!!! if (coreElement.value.weight == weight) { formula.remove(coreElement.value) val coreElementConstraint = coreElement.value.constraint - val literalToReify = ctx.boolSort.mkConst("b$iter${coreElement.index}") + val literalToReify = coreElementConstraint.sort.mkConst("b$iter${coreElement.index}") val constraintToReify = KEqExpr( ctx, @@ -181,7 +187,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Apply MaxRes rule. */ - private fun applyMaxRes(formula: MutableList, literalsToReify: List>, weight: Int) + private fun applyMaxRes(formula: MutableList, literalsToReify: List>, + iter: Int, weight: Int) : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes @@ -206,7 +213,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } // TODO, FIX: не будет ли коллизии при последующих запусках? - val literalToReifyDisjunction = ctx.boolSort.mkConst("d$index") + val literalToReifyDisjunction = ctx.boolSort.mkConst("d$iter$index") assert( KEqExpr( @@ -246,10 +253,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver override fun push() { solver.push() + scopeManager.push() } override fun pop(n: UInt) { solver.pop(n) + softConstraints = scopeManager.pop(n, softConstraints) } override fun check(timeout: Duration): KSolverStatus { diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt deleted file mode 100644 index d842fea73..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.KSolverStatus - -class MaxSATResult(val satSoftConstraints: List, - val hardConstraintsSATStatus: KSolverStatus, val maxSATSucceeded: Boolean) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt new file mode 100644 index 000000000..b800b2814 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt @@ -0,0 +1,4 @@ +package io.ksmt.solver.maxsat + +// TODO: support info about MaxSATResult +class MaxSATScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt new file mode 100644 index 000000000..916ab63b2 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt @@ -0,0 +1,50 @@ +package io.ksmt.solver.maxsat + +class MaxSATScopeManager { + private var currentScope = 0u + + private val prevScopes = mutableListOf() + + private var scopeAddedSoftConstraints = 0 + + fun incrementSoft() { + if (currentScope != 0u) { + scopeAddedSoftConstraints++ + } + } + + fun push() { + if (currentScope != 0u) { + prevScopes.add(MaxSATScope(scopeAddedSoftConstraints)) + scopeAddedSoftConstraints = 0 + } + + currentScope++ + } + + fun pop(n: UInt, softConstraints: MutableList): MutableList { + require(n <= currentScope) { + "Can not pop $n scope levels because current scope level is $currentScope" + } + if (n == 0u) { + return softConstraints + } + + repeat(n.toInt()) { + val size = softConstraints.size + softConstraints.subList(size - scopeAddedSoftConstraints, size).clear() + + if (prevScopes.isNotEmpty()) { + scopeAddedSoftConstraints = prevScopes.last().scopeAddedSoftConstraints + prevScopes.removeLast() + } + else { + scopeAddedSoftConstraints = 0 + } + } + + currentScope -= n + + return softConstraints + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 44edba679..3e2c6ae54 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -4,7 +4,6 @@ import io.ksmt.KContext import io.ksmt.expr.KAndBinaryExpr import io.ksmt.expr.KAndNaryExpr import io.ksmt.expr.KNotExpr -import io.ksmt.expr.KOrBinaryExpr import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue @@ -23,8 +22,8 @@ class KMaxSATSolverTest { maxSATSolver.assert(a) maxSATSolver.assert(b) maxSATSolver.assert(c) - maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 3) - maxSATSolver.assertSoft(mkNot(a), 5) + maxSATSolver.assertSoft(a and !c, 3) + maxSATSolver.assertSoft(!a, 5) val maxSATResult = maxSATSolver.checkMaxSAT() @@ -40,23 +39,18 @@ class KMaxSATSolverTest { val a by boolSort val b by boolSort - val notA = KNotExpr(this, a) - val notB = KNotExpr(this, b) + maxSATSolver.assert(a or b) + maxSATSolver.assert(!a or b) - maxSATSolver.assert(KOrBinaryExpr(this, a, b)) - maxSATSolver.assert(KOrBinaryExpr(this, notA, b)) - maxSATSolver.assertSoft(KOrBinaryExpr(this, a, notB), 2) - - val notAOrNotBExpr = KOrBinaryExpr(this, notA, notB) - val notAOrNotBWeight = 3 - maxSATSolver.assertSoft(notAOrNotBExpr, notAOrNotBWeight) + maxSATSolver.assertSoft(a or !b, 2) + maxSATSolver.assertSoft(!a or !b, 3) val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSAT(listOf(SoftConstraint(notAOrNotBExpr, notAOrNotBWeight)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSAT(listOf(SoftConstraint(!a or !b, 3)), maxSATResult.satSoftConstraints) } @Test @@ -66,20 +60,11 @@ class KMaxSATSolverTest { val a by boolSort val b by boolSort - val notA = KNotExpr(this, a) - val notB = KNotExpr(this, b) - - maxSATSolver.assert(KOrBinaryExpr(this, a, b)) - - val notAOrBExpr = KOrBinaryExpr(this, notA, b) - val notAOrBWeight = 4 - maxSATSolver.assertSoft(notAOrBExpr, notAOrBWeight) + maxSATSolver.assert(a or b) - val aOrNotBExpr = KOrBinaryExpr(this, a, notB) - val aOrNotBWeight = 6 - maxSATSolver.assertSoft(aOrNotBExpr, aOrNotBWeight) - - maxSATSolver.assertSoft(KOrBinaryExpr(this, notA, notB), 2) + maxSATSolver.assertSoft(!a or b, 4) + maxSATSolver.assertSoft(a or !b, 6) + maxSATSolver.assertSoft(!a or !b, 2) val maxSATResult = maxSATSolver.checkMaxSAT() @@ -87,7 +72,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) val softConstraintsToAssertSAT = - listOf(SoftConstraint(notAOrBExpr, notAOrBWeight), SoftConstraint(aOrNotBExpr, aOrNotBWeight)) + listOf(SoftConstraint(!a or b, 4), SoftConstraint(a or !b, 6)) assertSoftConstraintsSAT(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) } @@ -99,28 +84,20 @@ class KMaxSATSolverTest { val x by boolSort val y by boolSort - val notX = KNotExpr(this, x) - val notY = KNotExpr(this, y) - - maxSATSolver.assert(KOrBinaryExpr(this, x , y)) - - val notXOrYExpr = KOrBinaryExpr(this, notX, y) - val notXOrYWeight = 7 - maxSATSolver.assertSoft(notXOrYExpr, notXOrYWeight) + maxSATSolver.assert(x or y) - maxSATSolver.assertSoft(KOrBinaryExpr(this, x, notY), 6) - - val notXOrNotYExpr = KOrBinaryExpr(this, notX, notY) - maxSATSolver.assertSoft(notXOrNotYExpr, 3) - maxSATSolver.assertSoft(notXOrNotYExpr, 4) + maxSATSolver.assertSoft(!x or y, 7) + maxSATSolver.assertSoft(x or !y, 6) + maxSATSolver.assertSoft(!x or !y, 3) + maxSATSolver.assertSoft(!x or !y, 4) val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSoftConstraintsSAT(listOf(SoftConstraint(notXOrYExpr, notXOrYWeight), SoftConstraint(notXOrNotYExpr, 3), - SoftConstraint(notXOrNotYExpr, 4)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSAT(listOf(SoftConstraint(!x or y, 7), SoftConstraint(!x or !y, 3), + SoftConstraint(!x or !y, 4)), maxSATResult.satSoftConstraints) } @Test @@ -131,28 +108,19 @@ class KMaxSATSolverTest { val x by boolSort val y by boolSort - val notX = KNotExpr(this, x) - val notY = KNotExpr(this, y) - - maxSATSolver.assert(KOrBinaryExpr(this, x , y)) - - val notXOrYExpr = KOrBinaryExpr(this, notX, y) - val notXOrYExprWeight = 6 - maxSATSolver.assertSoft(notXOrYExpr, notXOrYExprWeight) + maxSATSolver.assert(x or y) - val xOrNotYExpr = KOrBinaryExpr(this, x, notY) - val xOrNotYWeight = 6 - maxSATSolver.assertSoft(xOrNotYExpr, xOrNotYWeight) - - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 3) - maxSATSolver.assertSoft(KOrBinaryExpr(this, notX, notY), 2) + maxSATSolver.assertSoft(!x or y, 6) + maxSATSolver.assertSoft(x or !y, 6) + maxSATSolver.assertSoft(!x or !y, 3) + maxSATSolver.assertSoft(!x or !y, 2) val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSAT(listOf(SoftConstraint(notXOrYExpr, notXOrYExprWeight), SoftConstraint(xOrNotYExpr, xOrNotYWeight)), + assertSoftConstraintsSAT(listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), maxSATResult.satSoftConstraints) } @@ -178,10 +146,69 @@ class KMaxSATSolverTest { ) } + @Test + fun oneScopePushPopTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + val a by boolSort + val b by boolSort + + maxSATSolver.assert(a or b) + + maxSATSolver.assertSoft(!a or b, 1) + + val maxSATResult = maxSATSolver.checkMaxSAT() + assertSoftConstraintsSAT(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + + maxSATSolver.push() + + maxSATSolver.assertSoft(a or !b, 1) + maxSATSolver.assertSoft(!a or !b, 1) + val maxSATResultScoped = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) + + maxSATSolver.pop() + + assertSoftConstraintsSAT(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + } + + @Test + fun threeScopesPushPopTests() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + + val a by boolSort + val b by boolSort + val c by boolSort + + maxSATSolver.assert(a) + + maxSATSolver.push() + maxSATSolver.assertSoft(a or b, 1) + maxSATSolver.assertSoft(c or b, 1) + val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.satSoftConstraints.size == 2) + + maxSATSolver.push() + maxSATSolver.assertSoft(!b and !c, 2) + val maxSATResult2 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult2.satSoftConstraints.size == 1) + + maxSATSolver.push() + maxSATSolver.assertSoft(a or c, 1) + val maxSATResult3 = maxSATSolver.checkMaxSAT() + //assertTrue(maxSATResult3.satSoftConstraints) + + maxSATSolver.pop(2u) + + maxSATSolver.pop() + } + private fun assertSoftConstraintsSAT(constraintsToAssert: List, satConstraints: List) { for (constraint in constraintsToAssert) { - assertTrue(satConstraints.any { constraint.constraint.internEquals(it.constraint) && constraint.weight == it.weight }) + assertTrue(satConstraints.any { constraint.constraint.internEquals(it.constraint) && + constraint.weight == it.weight }) } } } From 6b680c4770b310e9c232a4f76d8178b5ad49107f Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 30 Jun 2023 17:14:15 +0300 Subject: [PATCH 019/228] Optimize algorithm (remove added that is later removed), fix a bug in split unsat core (if the same expressions weight was equal, both of them were added to the core). --- ksmt-maxsat/build.gradle.kts | 1 + .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 39 +++++++------------ .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 12 ++++-- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 21 +++++----- 4 files changed, 35 insertions(+), 38 deletions(-) diff --git a/ksmt-maxsat/build.gradle.kts b/ksmt-maxsat/build.gradle.kts index 3c504c7ec..6e282db3f 100644 --- a/ksmt-maxsat/build.gradle.kts +++ b/ksmt-maxsat/build.gradle.kts @@ -12,6 +12,7 @@ repositories { dependencies { implementation(project(":ksmt-core")) implementation(project(":ksmt-z3")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1") testImplementation(project(":ksmt-core")) testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 927c23587..146be4ba0 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -64,7 +64,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver if (solverStatus == KSolverStatus.SAT) { solver.pop() val satSoftConstraints = - softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } + softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } return KMaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement @@ -74,7 +74,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) val (formulaReified, reificationVariables) = - reifyUnsatCore(formula, splitUnsatCore, i, weight) + reifyUnsatCore(formula, splitUnsatCore, i, weight) when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) @@ -82,7 +82,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver else -> assert(KOrNaryExpr(ctx, reificationVariables)) } - formula = applyMaxRes(formulaReified, reificationVariables, i, weight) + formula = applyMaxRes(formulaReified, reificationVariables, i) i++ } @@ -103,16 +103,23 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight + val unsatCoreSoftConstraintsSplit = mutableListOf() + unsatCoreSoftConstraints.forEach { x -> if (x.weight > minWeight) { - formula.add(SoftConstraint(x.constraint, minWeight)) + val minWeightSoftConstraint = SoftConstraint(x.constraint, minWeight) + formula.add(minWeightSoftConstraint) formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) formula.removeIf { it.weight == x.weight && it.constraint == x.constraint } + + unsatCoreSoftConstraintsSplit.add(minWeightSoftConstraint) + } + else { + unsatCoreSoftConstraintsSplit.add(x) } } - return Pair(minWeight, - formula.filter { x -> x.weight == minWeight && unsatCore.any { x.constraint.internEquals(it) } }) + return Pair(minWeight, unsatCoreSoftConstraintsSplit) } /** @@ -175,8 +182,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver assert(constraintToReify) - formula.add(SoftConstraint(KNotExpr(ctx, literalToReify), weight)) - literalsToReify.add(literalToReify) } } @@ -188,15 +193,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * Apply MaxRes rule. */ private fun applyMaxRes(formula: MutableList, literalsToReify: List>, - iter: Int, weight: Int) + iter: Int) : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes - formula.removeIf { it.constraint.internEquals(KNotExpr(ctx, indexedLiteral.value)) && - it.weight == weight - } - val index = indexedLiteral.index val indexLast = literalsToReify.lastIndex @@ -212,7 +213,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver ) } - // TODO, FIX: не будет ли коллизии при последующих запусках? val literalToReifyDisjunction = ctx.boolSort.mkConst("d$iter$index") assert( @@ -222,17 +222,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver disjunction, ), ) - - formula.add( - SoftConstraint( - KOrBinaryExpr( - ctx, - KNotExpr(ctx, indexedLiteral.value), - KNotExpr(ctx, literalToReifyDisjunction), - ), - weight, - ), - ) } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index 944662c8d..4fbf54e10 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -3,9 +3,15 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.mkConst +import kotlinx.coroutines.isActive +import kotlinx.coroutines.withTimeout -fun main() { - with (KContext()) { +suspend fun main() { + withTimeout(100) { + while (isActive) println("Hi") + } + +/* with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a = boolSort.mkConst("a") @@ -17,5 +23,5 @@ fun main() { maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 1) maxSATSolver.assertSoft(mkNot(a), 1) //val maxSATResult = maxSATSolver.checkMaxSMT() - } + }*/ } diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 3e2c6ae54..e8b6a8a46 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -133,17 +133,14 @@ class KMaxSATSolverTest { val b = boolSort.mkConst("b") maxSATSolver.assert(z) - maxSATSolver.assertSoft(KAndBinaryExpr(this, a, b), 1) - val constr = KAndBinaryExpr(this, KNotExpr(this, a), KNotExpr(this, b)) - maxSATSolver.assertSoft(constr, 5) - maxSATSolver.assertSoft(KAndNaryExpr(this, listOf(a, b, z)), 2) + maxSATSolver.assertSoft(a and b, 1) + maxSATSolver.assertSoft(!a and !b, 5) + maxSATSolver.assertSoft(a and b and z, 2) val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue( - maxSATResult.satSoftConstraints.size == 1 && maxSATResult.satSoftConstraints[0].weight == 5 && - maxSATResult.satSoftConstraints[0].constraint == constr - ) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSoftConstraintsSAT(listOf(SoftConstraint(!a and !b, 5)), maxSATResult.satSoftConstraints) } @Test @@ -192,16 +189,20 @@ class KMaxSATSolverTest { maxSATSolver.push() maxSATSolver.assertSoft(!b and !c, 2) val maxSATResult2 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult2.satSoftConstraints.size == 1) + assertTrue(maxSATResult2.satSoftConstraints.size == 2) maxSATSolver.push() maxSATSolver.assertSoft(a or c, 1) val maxSATResult3 = maxSATSolver.checkMaxSAT() - //assertTrue(maxSATResult3.satSoftConstraints) + assertTrue(maxSATResult3.satSoftConstraints.size == 3) maxSATSolver.pop(2u) + val maxSATResult4 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult4.satSoftConstraints.size == 2) maxSATSolver.pop() + val maxSATResult5 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) } private fun assertSoftConstraintsSAT(constraintsToAssert: List, From 3e9afecaca47bd29e19da6daf1245ccab8c6ef70 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 30 Jun 2023 17:55:21 +0300 Subject: [PATCH 020/228] Add inequalities test --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 1 - .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 32 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 146be4ba0..304238ef4 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -167,7 +167,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { - // TODO: FIX, удалим оба!!! if (coreElement.value.weight == weight) { formula.remove(coreElement.value) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index e8b6a8a46..6826bc15c 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -1,9 +1,6 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext -import io.ksmt.expr.KAndBinaryExpr -import io.ksmt.expr.KAndNaryExpr -import io.ksmt.expr.KNotExpr import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue @@ -125,7 +122,7 @@ class KMaxSATSolverTest { } @Test - fun smokeTest5() = with (KContext()) { + fun chooseOneConstraintByWeightTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val z = boolSort.mkConst("z") @@ -143,6 +140,33 @@ class KMaxSATSolverTest { assertSoftConstraintsSAT(listOf(SoftConstraint(!a and !b, 5)), maxSATResult.satSoftConstraints) } + @Test + fun inequalitiesTest() = with (KContext()) { + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + + val x by intSort + val y by intSort + + val a1 = x gt 0.expr + val a2 = x lt y + val a3 = x + y le 0.expr + + maxSATSolver.assert(a3 eq a1) + maxSATSolver.assert(a3 or a2) + + maxSATSolver.assertSoft(a3, 3) + maxSATSolver.assertSoft(!a3, 5) + maxSATSolver.assertSoft(!a1, 10) + maxSATSolver.assertSoft(!a2, 3) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSAT(listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), + maxSATResult.satSoftConstraints) + } + @Test fun oneScopePushPopTest() = with (KContext()) { val z3Solver = KZ3Solver(this) From 3a14625c19139092d352dd77ea0b2e9e0db0bc31 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 30 Jun 2023 19:02:48 +0300 Subject: [PATCH 021/228] Rename soft constraint property: constraint -> expression --- .../io/ksmt/solver/maxsat/Constraint.kt | 2 +- .../io/ksmt/solver/maxsat/HardConstraint.kt | 2 +- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 43 ++++++++++--------- .../io/ksmt/solver/maxsat/SoftConstraint.kt | 2 +- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt index 96236f3e4..1f94cbd49 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt @@ -4,5 +4,5 @@ import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort interface Constraint { - val constraint: KExpr + val expression: KExpr } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt index 8ebe4a1d1..8216ec74b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt @@ -3,4 +3,4 @@ package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort -class HardConstraint(override val constraint: KExpr) : Constraint +class HardConstraint(override val expression: KExpr) : Constraint diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 304238ef4..ce71ddf0b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -61,10 +61,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver while (true) { val (solverStatus, unsatCore, model) = checkSAT(formula) + if (solverStatus == KSolverStatus.SAT) { solver.pop() val satSoftConstraints = - softConstraints.filter { model!!.eval(it.constraint).internEquals(KTrue(ctx)) } + softConstraints.filter { model!!.eval(it.expression).internEquals(KTrue(ctx)) } return KMaxSATResult(satSoftConstraints, solverStatus, true) } else if (solverStatus == KSolverStatus.UNKNOWN) { // TODO: implement @@ -74,7 +75,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) val (formulaReified, reificationVariables) = - reifyUnsatCore(formula, splitUnsatCore, i, weight) + reifyUnsatCore(formula, splitUnsatCore, i, weight) when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) @@ -90,7 +91,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Split all soft constraints from the unsat core into two groups: - * - constraints with the weight equal to the minimum from the unsat core soft constraint weights + * - constraints with the weight equal to the minimum of the unsat core soft constraint weights * - constraints with the weight equal to old weight - minimum weight * * Returns a pair of minimum weight and a list of unsat core soft constraints with minimum weight. @@ -99,7 +100,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver : Pair> { // Filters soft constraints from the unsat core. val unsatCoreSoftConstraints = - formula.filter { x -> unsatCore.any { x.constraint.internEquals(it) } } + formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight @@ -107,10 +108,10 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver unsatCoreSoftConstraints.forEach { x -> if (x.weight > minWeight) { - val minWeightSoftConstraint = SoftConstraint(x.constraint, minWeight) + val minWeightSoftConstraint = SoftConstraint(x.expression, minWeight) formula.add(minWeightSoftConstraint) - formula.add(SoftConstraint(x.constraint, x.weight - minWeight)) - formula.removeIf { it.weight == x.weight && it.constraint == x.constraint } + formula.add(SoftConstraint(x.expression, x.weight - minWeight)) + formula.removeIf { it.weight == x.weight && it.expression == x.expression } unsatCoreSoftConstraintsSplit.add(minWeightSoftConstraint) } @@ -123,23 +124,23 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Union soft constraints with same expressions into a single soft constraint. The soft constraint weight will be + * Union soft constraints with same expressions into a single soft constraint. The new soft constraint weight will be * equal to the sum of old soft constraints weights. */ private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { var i = 0 while (i < formula.size) { - val currentConstraint = formula[i].constraint + val currentExpr = formula[i].expression - val sameConstraints = formula.filter { it.constraint.internEquals(currentConstraint) } + val similarConstraints = formula.filter { it.expression.internEquals(currentExpr) } // Unions soft constraints with same expressions into a single soft constraint. - if (sameConstraints.size > 1) { - val sameConstraintsWeightSum = sameConstraints.sumOf { it.weight } + if (similarConstraints.size > 1) { + val similarConstraintsWeightsSum = similarConstraints.sumOf { it.weight } - formula.removeAll(sameConstraints) - formula.add(SoftConstraint(currentConstraint, sameConstraintsWeightSum)) + formula.removeAll(similarConstraints) + formula.add(SoftConstraint(currentExpr, similarConstraintsWeightsSum)) } i++ @@ -150,10 +151,10 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * Check on satisfiability hard constraints with assumed soft constraints. * * Returns a triple of solver status, unsat core (if exists, empty list otherwise) and model - * (if exists, otherwise null). + * (if exists, null otherwise). */ private fun checkSAT(assumptions: List): Triple>, KModel?> = - when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.constraint })) { + when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression })) { KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) @@ -170,12 +171,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver if (coreElement.value.weight == weight) { formula.remove(coreElement.value) - val coreElementConstraint = coreElement.value.constraint - val literalToReify = coreElementConstraint.sort.mkConst("b$iter${coreElement.index}") + val coreElementExpr = coreElement.value.expression + val literalToReify = coreElementExpr.sort.mkConst("*$iter${coreElement.index}") val constraintToReify = KEqExpr( ctx, - coreElementConstraint, + coreElementExpr, KNotExpr(ctx, literalToReify), ) @@ -202,7 +203,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver if (index < indexLast) { val disjunction = - // We do not take the current element (from the next to the last) + // We do not take the current literal to reify (from the next to the last) when (indexLast - index) { 1 -> literalsToReify[index + 1] 2 -> KOrBinaryExpr(ctx, literalsToReify[index + 1], literalsToReify[index + 2]) @@ -212,7 +213,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver ) } - val literalToReifyDisjunction = ctx.boolSort.mkConst("d$iter$index") + val literalToReifyDisjunction = ctx.boolSort.mkConst("#$iter$index") assert( KEqExpr( diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt index eb1f8aa9b..39c6b238f 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt @@ -3,4 +3,4 @@ package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort -class SoftConstraint(override val constraint: KExpr, val weight: Int) : Constraint +class SoftConstraint(override val expression: KExpr, val weight: Int) : Constraint diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 6826bc15c..a3910423d 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -232,7 +232,7 @@ class KMaxSATSolverTest { private fun assertSoftConstraintsSAT(constraintsToAssert: List, satConstraints: List) { for (constraint in constraintsToAssert) { - assertTrue(satConstraints.any { constraint.constraint.internEquals(it.constraint) && + assertTrue(satConstraints.any { constraint.expression.internEquals(it.expression) && constraint.weight == it.weight }) } } From e3c07f3c73310955eabf60087634a019454690dd Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 3 Jul 2023 19:45:49 +0300 Subject: [PATCH 022/228] Add timeout support, improve KMaxSATResult --- detekt.yml | 2 +- .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 4 +- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 122 ++++++++++----- .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 46 +++--- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 146 ++++++++++-------- 5 files changed, 200 insertions(+), 120 deletions(-) diff --git a/detekt.yml b/detekt.yml index bd6911c9d..ce79c1b5f 100644 --- a/detekt.yml +++ b/detekt.yml @@ -538,7 +538,7 @@ style: maxJumpCount: 1 MagicNumber: active: true - excludes: ['**/test/**', '**/example/**'] + excludes: ['**/test/**', '**/example/**', '**/Main.kt'] ignoreNumbers: - '-1' - '0' diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index c0dfe9d8e..33fa6e6ef 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -2,5 +2,5 @@ package io.ksmt.solver.maxsat import io.ksmt.solver.KSolverStatus -class KMaxSATResult(val satSoftConstraints: List, - val hardConstraintsSATStatus: KSolverStatus, val maxSATSucceeded: Boolean) +class KMaxSATResult(val satSoftConstraints: List, val hardConstraintsSATStatus: KSolverStatus, + val maxSATSucceeded: Boolean, val timeoutExceeded: Boolean) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index ce71ddf0b..9a8977a44 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -13,6 +13,7 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst +import kotlinx.coroutines.withTimeoutOrNull import kotlin.time.Duration class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver @@ -38,55 +39,78 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Solve maximum satisfiability problem. */ - fun checkMaxSAT(): KMaxSATResult { - if (softConstraints.isEmpty()) { - return KMaxSATResult(listOf(), solver.check(), true) - } + suspend fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { + var currentMaxSATResult: Triple>, KModel?> = + Triple(null, listOf(), null) - val status = solver.check() + solver.push() - if (status == KSolverStatus.UNSAT) { - return KMaxSATResult(listOf(), status, true) - } else if (status == KSolverStatus.UNKNOWN) { - return KMaxSATResult(listOf(), status, false) - } + val maxSATResult = withTimeoutOrNull(timeout.inWholeMilliseconds) { + if (softConstraints.isEmpty()) { + return@withTimeoutOrNull KMaxSATResult(listOf(), solver.check(), + maxSATSucceeded = true, + timeoutExceeded = false + ) + } - var i = 0 - var formula = softConstraints.toMutableList() + val status = solver.check() - unionSoftConstraintsWithSameExpressions(formula) + if (status == KSolverStatus.UNSAT) { + return@withTimeoutOrNull KMaxSATResult(listOf(), status, + maxSATSucceeded = true, + timeoutExceeded = false + ) + } else if (status == KSolverStatus.UNKNOWN) { + return@withTimeoutOrNull KMaxSATResult(listOf(), status, + maxSATSucceeded = false, + timeoutExceeded = false + ) + } - solver.push() + var i = 0 + var formula = softConstraints.toMutableList() - while (true) { - val (solverStatus, unsatCore, model) = checkSAT(formula) + unionSoftConstraintsWithSameExpressions(formula) + while (true) { + val (solverStatus, unsatCore, model) = checkSAT(formula) + if (solverStatus == KSolverStatus.UNKNOWN) { + return@withTimeoutOrNull handleUnknown(timeoutExceeded = false) + } - if (solverStatus == KSolverStatus.SAT) { - solver.pop() - val satSoftConstraints = - softConstraints.filter { model!!.eval(it.expression).internEquals(KTrue(ctx)) } - return KMaxSATResult(satSoftConstraints, solverStatus, true) - } else if (solverStatus == KSolverStatus.UNKNOWN) { - // TODO: implement - solver.pop() - } + currentMaxSATResult = Triple(solverStatus, unsatCore, model) + + if (solverStatus == KSolverStatus.SAT) { + return@withTimeoutOrNull handleSat(model!!) + } - val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) + val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) - val (formulaReified, reificationVariables) = - reifyUnsatCore(formula, splitUnsatCore, i, weight) + val (formulaReified, reificationVariables) = + reifyUnsatCore(formula, splitUnsatCore, i, weight) - when (reificationVariables.size) { - 1 -> assert(reificationVariables.first()) - 2 -> assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) - else -> assert(KOrNaryExpr(ctx, reificationVariables)) + unionReificationVariables(reificationVariables) + + formula = applyMaxRes(formulaReified, reificationVariables, i) + + i++ } + } - formula = applyMaxRes(formulaReified, reificationVariables, i) + solver.pop() - i++ + if (maxSATResult == null) { + val (solverStatus, unsatCore, model) = currentMaxSATResult + + return when (solverStatus) { + null -> KMaxSATResult(listOf(), KSolverStatus.UNKNOWN, maxSATSucceeded = false, timeoutExceeded = true) + KSolverStatus.SAT -> handleSat(model!!) + KSolverStatus.UNSAT -> handleUnsat(unsatCore) + KSolverStatus.UNKNOWN -> handleUnknown(timeoutExceeded = true) + } } + + return maxSATResult as KMaxSATResult } /** @@ -124,8 +148,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Union soft constraints with same expressions into a single soft constraint. The new soft constraint weight will be - * equal to the sum of old soft constraints weights. + * Union soft constraints with same expressions into a single soft constraint. The new soft constraint weight + * will be equal to the sum of old soft constraints weights. */ private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { var i = 0 @@ -189,6 +213,14 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return Pair(formula, literalsToReify) } + private fun unionReificationVariables(reificationVariables: List>) { + when (reificationVariables.size) { + 1 -> assert(reificationVariables.first()) + 2 -> assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) + else -> assert(KOrNaryExpr(ctx, reificationVariables)) + } + } + /** * Apply MaxRes rule. */ @@ -228,6 +260,24 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return formula } + private fun handleSat(model: KModel): KMaxSATResult { + val satSoftConstraints = getSatSoftConstraintsByModel(model) + return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = true, timeoutExceeded = false) + } + + private fun getSatSoftConstraintsByModel(model: KModel): List { + return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } + } + + private fun handleUnsat(unsatCore: List>): KMaxSATResult { + val satSoftConstraints = softConstraints.filter { x -> unsatCore.any { x.expression.internEquals(it) } } + return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = false, timeoutExceeded = true) + } + + private fun handleUnknown(timeoutExceeded: Boolean): KMaxSATResult { + return KMaxSATResult(listOf(), KSolverStatus.SAT, maxSATSucceeded = false, timeoutExceeded) + } + override fun configure(configurator: KSolverConfiguration.() -> Unit) { solver.configure(configurator) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index 4fbf54e10..099ac58bc 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -2,26 +2,36 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.utils.mkConst -import kotlinx.coroutines.isActive -import kotlinx.coroutines.withTimeout +import io.ksmt.utils.getValue +import kotlin.time.Duration.Companion.microseconds +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.nanoseconds suspend fun main() { - withTimeout(100) { - while (isActive) println("Hi") - } - -/* with (KContext()) { + with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") - val c = boolSort.mkConst("c") - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(c) - maxSATSolver.assertSoft(mkAnd(a, mkNot(c)), 1) - maxSATSolver.assertSoft(mkNot(a), 1) - //val maxSATResult = maxSATSolver.checkMaxSMT() - }*/ + + val x by intSort + val y by intSort + + val a1 = x gt 0.expr + val a2 = x lt y + val a3 = x + y le 0.expr + + maxSATSolver.assert(a3 eq a1) + maxSATSolver.assert(a3 or a2) + + maxSATSolver.assertSoft(a3, 3) + maxSATSolver.assertSoft(!a3, 5) + maxSATSolver.assertSoft(!a1, 10) + maxSATSolver.assertSoft(!a2, 3) + + val result = maxSATSolver.checkMaxSAT(1L.milliseconds) + println("Max SAT succeeded: ${result.maxSATSucceeded}") + println("Hard constraints SAT status: ${result.hardConstraintsSATStatus}") + println("Size SAT soft constraints: ${result.satSoftConstraints.size}") + println("Soft constraints:\n${result.satSoftConstraints.forEach { println(it.expression) }}") + println("Timeout exceeded:\n${result.timeoutExceeded}") + } } diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index a3910423d..ba69e6a45 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -5,12 +5,13 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue import io.ksmt.utils.mkConst +import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test class KMaxSATSolverTest { @Test - fun noSoftConstraintsSATTest() = with (KContext()) { + fun noSoftConstraintsSatTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -22,15 +23,17 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(a and !c, 3) maxSATSolver.assertSoft(!a, 5) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) + } } @Test - fun oneOfTwoSoftConstraintsSATTest() = with (KContext()) { + fun oneOfTwoSoftConstraintsSatTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -42,16 +45,18 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(a or !b, 2) maxSATSolver.assertSoft(!a or !b, 3) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSAT(listOf(SoftConstraint(!a or !b, 3)), maxSATResult.satSoftConstraints) + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3)), maxSATResult.satSoftConstraints) + } } @Test - fun twoOfThreeSoftConstraintsSATTest() = with (KContext()) { + fun twoOfThreeSoftConstraintsSatTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -63,18 +68,20 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(a or !b, 6) maxSATSolver.assertSoft(!a or !b, 2) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - val softConstraintsToAssertSAT = + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + val softConstraintsToAssertSAT = listOf(SoftConstraint(!a or b, 4), SoftConstraint(a or !b, 6)) - assertSoftConstraintsSAT(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) + } } @Test - fun sameExpressionSoftConstraintsSATTest() = with (KContext()) { + fun sameExpressionSoftConstraintsSatTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -88,17 +95,19 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(!x or !y, 3) maxSATSolver.assertSoft(!x or !y, 4) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSoftConstraintsSAT(listOf(SoftConstraint(!x or y, 7), SoftConstraint(!x or !y, 3), + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 3) + assertSoftConstraintsSat(listOf(SoftConstraint(!x or y, 7), SoftConstraint(!x or !y, 3), SoftConstraint(!x or !y, 4)), maxSATResult.satSoftConstraints) + } } @Test - fun sameExpressionSoftConstraintsUNSATTest() = with (KContext()) { + fun sameExpressionSoftConstraintsUnsatTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -112,13 +121,15 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(!x or !y, 3) maxSATSolver.assertSoft(!x or !y, 2) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSAT(listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), + assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat(listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), maxSATResult.satSoftConstraints) + } } @Test @@ -134,10 +145,12 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(!a and !b, 5) maxSATSolver.assertSoft(a and b and z, 2) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSAT(listOf(SoftConstraint(!a and !b, 5)), maxSATResult.satSoftConstraints) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5)), maxSATResult.satSoftConstraints) + } } @Test @@ -160,11 +173,13 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(!a1, 10) maxSATSolver.assertSoft(!a2, 3) - val maxSATResult = maxSATSolver.checkMaxSAT() + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSAT(listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat(listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), maxSATResult.satSoftConstraints) + } } @Test @@ -178,23 +193,25 @@ class KMaxSATSolverTest { maxSATSolver.assertSoft(!a or b, 1) - val maxSATResult = maxSATSolver.checkMaxSAT() - assertSoftConstraintsSAT(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) - maxSATSolver.push() + maxSATSolver.push() - maxSATSolver.assertSoft(a or !b, 1) - maxSATSolver.assertSoft(!a or !b, 1) - val maxSATResultScoped = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) + maxSATSolver.assertSoft(a or !b, 1) + maxSATSolver.assertSoft(!a or !b, 1) + val maxSATResultScoped = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) - maxSATSolver.pop() + maxSATSolver.pop() - assertSoftConstraintsSAT(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + } } @Test - fun threeScopesPushPopTests() = with (KContext()) { + fun threeScopesPushPopTest() = with (KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -207,29 +224,32 @@ class KMaxSATSolverTest { maxSATSolver.push() maxSATSolver.assertSoft(a or b, 1) maxSATSolver.assertSoft(c or b, 1) - val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 2) - maxSATSolver.push() - maxSATSolver.assertSoft(!b and !c, 2) - val maxSATResult2 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult2.satSoftConstraints.size == 2) + runBlocking { + val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.satSoftConstraints.size == 2) - maxSATSolver.push() - maxSATSolver.assertSoft(a or c, 1) - val maxSATResult3 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult3.satSoftConstraints.size == 3) + maxSATSolver.push() + maxSATSolver.assertSoft(!b and !c, 2) + val maxSATResult2 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult2.satSoftConstraints.size == 2) + + maxSATSolver.push() + maxSATSolver.assertSoft(a or c, 1) + val maxSATResult3 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult3.satSoftConstraints.size == 3) - maxSATSolver.pop(2u) - val maxSATResult4 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult4.satSoftConstraints.size == 2) + maxSATSolver.pop(2u) + val maxSATResult4 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult4.satSoftConstraints.size == 2) - maxSATSolver.pop() - val maxSATResult5 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) + maxSATSolver.pop() + val maxSATResult5 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) + } } - private fun assertSoftConstraintsSAT(constraintsToAssert: List, + private fun assertSoftConstraintsSat(constraintsToAssert: List, satConstraints: List) { for (constraint in constraintsToAssert) { assertTrue(satConstraints.any { constraint.expression.internEquals(it.expression) && From ae12b772e06c9c9c5052b74604af2c3eb30284a0 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 3 Jul 2023 19:57:24 +0300 Subject: [PATCH 023/228] Expression length growth: quadratic -> linear --- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 9a8977a44..f2eb1a493 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -35,7 +35,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver scopeManager.incrementSoft() } - // TODO: add timeout support /** * Solve maximum satisfiability problem. */ @@ -234,23 +233,20 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val indexLast = literalsToReify.lastIndex if (index < indexLast) { + val currentLiteralToReifyDisjunction = ctx.boolSort.mkConst("#$iter$index") + val nextLiteralToReifyDisjunction = ctx.boolSort.mkConst("#$iter${index + 1}") + val disjunction = - // We do not take the current literal to reify (from the next to the last) when (indexLast - index) { + // The second element is omitted as it is an empty disjunction. 1 -> literalsToReify[index + 1] - 2 -> KOrBinaryExpr(ctx, literalsToReify[index + 1], literalsToReify[index + 2]) - else -> KOrNaryExpr( - ctx, - literalsToReify.subList(index + 1, indexLast + 1), - ) + else -> KOrBinaryExpr(ctx, literalsToReify[index + 1], nextLiteralToReifyDisjunction) } - val literalToReifyDisjunction = ctx.boolSort.mkConst("#$iter$index") - assert( KEqExpr( ctx, - literalToReifyDisjunction, + currentLiteralToReifyDisjunction, disjunction, ), ) From 68ba4ed3e6c47eeb754e1be92611bf7f61f0b09c Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 3 Jul 2023 20:04:32 +0300 Subject: [PATCH 024/228] Return accidently removed --- .../src/main/kotlin/io/ksmt/expr/Bool.kt | 1 - .../io/ksmt/test/RandomExpressionGenerator.kt | 773 ++++++++++++++++++ .../kotlin/io/ksmt/test/SmtLibParseError.kt | 3 + .../main/kotlin/io/ksmt/test/TestRunner.kt | 144 ++++ .../main/kotlin/io/ksmt/test/TestWorker.kt | 12 + .../kotlin/io/ksmt/test/TestWorkerProcess.kt | 331 ++++++++ .../io/ksmt/test/ContextAstInterningTest.kt | 85 ++ .../io/ksmt/test/MultiIndexedArrayTest.kt | 587 +++++++++++++ .../io/ksmt/test/TestBvOverflowChecks.kt | 390 +++++++++ .../ksmt/test/UninterpretedSortValuesTest.kt | 353 ++++++++ .../test/benchmarks/BenchmarksBasedTest.kt | 716 ++++++++++++++++ .../benchmarks/BitwuzlaBenchmarksBasedTest.kt | 51 ++ .../ContextMemoryUsageBenchmarksBasedTest.kt | 66 ++ .../benchmarks/Cvc5BenchmarksBasedTest.kt | 315 +++++++ .../PortfolioBenchmarksBasedTest.kt | 59 ++ .../SerializerBenchmarksBasedTest.kt | 94 +++ .../SimplifierBenchmarksBasedTest.kt | 41 + .../benchmarks/YicesBenchmarksBasedTest.kt | 52 ++ .../test/benchmarks/Z3BenchmarksBasedTest.kt | 43 + .../test/benchmarks/Z3ConverterBenchmark.kt | 253 ++++++ 20 files changed, 4368 insertions(+), 1 deletion(-) create mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt create mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt create mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt create mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt create mode 100644 ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt create mode 100644 ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index 16aa2b35f..b96c78002 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -108,7 +108,6 @@ class KNotExpr( override fun internHashCode(): Int = hash(arg) override fun internEquals(other: Any): Boolean = structurallyEqual(other) { arg } - //override fun toString(): String = "" } class KImpliesExpr internal constructor( diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt new file mode 100644 index 000000000..78aa7cf40 --- /dev/null +++ b/ksmt-test/src/main/kotlin/io/ksmt/test/RandomExpressionGenerator.kt @@ -0,0 +1,773 @@ +package io.ksmt.test + +import io.ksmt.KAst +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.expr.KFpRoundingMode +import io.ksmt.expr.KInterpretedValue +import io.ksmt.sort.KArray2Sort +import io.ksmt.sort.KArray3Sort +import io.ksmt.sort.KArrayNSort +import io.ksmt.sort.KArraySort +import io.ksmt.sort.KArraySortBase +import io.ksmt.sort.KBoolSort +import io.ksmt.sort.KBvSort +import io.ksmt.sort.KFp128Sort +import io.ksmt.sort.KFp64Sort +import io.ksmt.sort.KFpRoundingModeSort +import io.ksmt.sort.KFpSort +import io.ksmt.sort.KIntSort +import io.ksmt.sort.KRealSort +import io.ksmt.sort.KSort +import io.ksmt.sort.KSortVisitor +import io.ksmt.sort.KUninterpretedSort +import io.ksmt.utils.uncheckedCast +import java.util.SortedMap +import kotlin.random.Random +import kotlin.random.nextInt +import kotlin.random.nextUInt +import kotlin.random.nextULong +import kotlin.reflect.KClass +import kotlin.reflect.KClassifier +import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.KType +import kotlin.reflect.KTypeParameter +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KVisibility +import kotlin.reflect.full.allSupertypes +import kotlin.reflect.full.createType +import kotlin.reflect.full.isSubclassOf + + +/** + * Expression generation parameters. + * + * [seedExpressionsPerSort] -- initial expressions for each KSMT sort. + * + * [deepExpressionProbability] -- probability of picking the most deeply nested expression as an argument. + * Controls the depth of generated expressions. + * Default: 0.4. In our experiments, the probability value of 0.4 results + * in an average expression depth of about 0.0023 * #generated expressions, 0.3 -- 0.0017, 0.5 -- 0.0029. + * For example, if we generate 10000 expressions, with a probability of 0.4, the average expression depth will be 23, + * 0.3 -- 17 and with a probability of 0.5 -- 29. + * + * [generatedListSize] -- allowed sizes of generated lists. + * Default: 2-10, since lists are mostly used for expressions like And, Or, etc. + * This size range seems to be the most common for expressions of such kind. + * + * [generatedStringLength] -- allowed sizes of generated strings. + * Default: 7-10, since most of generated strings are names of constants, functions, etc. + * + * [possibleIntValues] -- allowed values for Int and UInt values. + * Default: 3-100, since such values are often used for Bv size or Fp size, + * and we don't want these values to be extremely large. + * + * [possibleStringChars] -- allowed symbols for generated strings. + * Default: a-z, since most of generated strings are names of constants, functions, etc. + * */ +data class GenerationParameters( + val seedExpressionsPerSort: Int = 10, + val deepExpressionProbability: Double = 0.4, + val generatedListSize: IntRange = 2..10, + val generatedStringLength: IntRange = 7..10, + val possibleIntValues: IntRange = 3..100, + val possibleStringChars: CharRange = 'a'..'z' +) + +class RandomExpressionGenerator { + private lateinit var generationContext: GenerationContext + + /** + * Generate [limit] random expressions with shared subexpressions. + * */ + fun generate( + limit: Int, + context: KContext, + random: Random = Random(42), + params: GenerationParameters = GenerationParameters(), + generatorFilter: (KFunction<*>) -> Boolean = { true } + ): List> { + generationContext = GenerationContext( + random = random, + context = context, + params = params, + expressionsAmountEstimation = limit, + sortsAmountEstimation = 1000 + ) + + generateInitialSeed(samplesPerSort = params.seedExpressionsPerSort) + + val filteredGenerators = generators.filter { generatorFilter(it.function) } + + while (generationContext.expressions.size < limit) { + val generator = filteredGenerators.random(random) + + nullIfGenerationFailed { + generator.generate(generationContext) + } + } + + return generationContext.expressions + } + + /** + * Recreate previously generated by [generate] expressions in a new [context]. + * */ + fun replay(context: KContext): List> { + val replayContext = GenerationContext( + random = Random(0), + context = context, + params = GenerationParameters(), + expressionsAmountEstimation = generationContext.expressions.size, + sortsAmountEstimation = generationContext.sorts.size + ) + + for (entry in generationContext.trace) { + val resolvedEntry = resolveTraceEntry(entry, replayContext) + val result = resolvedEntry.call(context) + when (result) { + // We don't care about expression depth since it is unused during replay + is KExpr<*> -> replayContext.registerExpr(result, depth = 0, inReplayMode = true) + is KSort -> replayContext.registerSort(result, inReplayMode = true) + } + } + + return replayContext.expressions + } + + private fun resolveTraceEntry( + entry: FunctionInvocation, + replayContext: GenerationContext + ): FunctionInvocation { + val args = entry.args.map { resolveArgument(it, replayContext) } + return FunctionInvocation(entry.function, args) + } + + private fun resolveArgument(argument: Argument, replayContext: GenerationContext): Argument = + when (argument) { + is SimpleArgument -> argument + is ListArgument -> ListArgument(argument.nested.map { resolveArgument(it, replayContext) }) + is ExprArgument -> ExprArgument(replayContext.expressions[argument.idx], argument.idx, argument.depth) + is SortArgument -> SortArgument(replayContext.sorts[argument.idx], argument.idx) + } + + private fun generateInitialSeed(samplesPerSort: Int) { + val (simpleGenerators, complexGenerators) = sortGenerators.partition { it.refSortProviders.isEmpty() } + + for (sortGenerator in simpleGenerators) { + repeat(samplesPerSort) { + nullIfGenerationFailed { sortGenerator.mkSeed(generationContext) } + } + } + for (sortGenerator in complexGenerators) { + repeat(samplesPerSort) { + nullIfGenerationFailed { sortGenerator.mkSeed(generationContext) } + } + } + } + + companion object { + + val noFreshConstants: (KFunction<*>) -> Boolean = { it != freshConstGen } + + private val ctxFunctions by lazy { + KContext::class.members + .filter { it.visibility == KVisibility.PUBLIC } + .filterIsInstance>() + } + + private val generators by lazy { + ctxFunctions + .asSequence() + .filter { it.returnType.isKExpr() } + .map { it.uncheckedCast, KFunction>>() } + .filterComplexStringGenerators() + .mapNotNull { + nullIfGenerationFailed { + it.mkGenerator()?.uncheckedCast, AstGenerator>() + } + } + .toList() + } + + private val sortGenerators by lazy { + ctxFunctions + .asSequence() + .filter { it.returnType.isKSort() } + .map { it.uncheckedCast, KFunction>() } + .mapNotNull { + nullIfGenerationFailed { + it.mkGenerator()?.uncheckedCast, AstGenerator>() + } + } + .toList() + } + + private val freshConstGen: KFunction> by lazy { KContext::mkFreshConst } + + /** + * Filter out generators that require a string with special format. + * E.g. binary string for Bv or numeric for IntNum. + * */ + private fun Sequence>>.filterComplexStringGenerators() = this + .filterNot { (it.name == "mkBv" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } + .filterNot { (it.name == "mkBvHex" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } + .filterNot { (it.name == "mkIntNum" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } + .filterNot { (it.name == "mkRealNum" && it.parameters.any { p -> p.type.isSubclassOf(String::class) }) } + + private val boolGen by lazy { generators.single { it.function.match("mkBool", Boolean::class) } } + private val intGen by lazy { generators.single { it.function.match("mkIntNum", Int::class) } } + private val realGen by lazy { generators.single { it.function.match("mkRealNum", Int::class) } } + private val bvGen by lazy { generators.single { it.function.match("mkBv", Int::class, UInt::class) } } + private val fpGen by lazy { generators.single { it.function.match("mkFp", Double::class, KSort::class) } } + private val arrayGen by lazy { + generators.single { + it.function.match("mkArrayConst", KSort::class, KExpr::class) + } + } + private val fpRmGen by lazy { + generators.single { + it.function.match("mkFpRoundingModeExpr", KFpRoundingMode::class) + } + } + private val constGen by lazy { + generators.single { + it.function.match("mkConst", String::class, KSort::class) + } + } + private val arraySortGen by lazy { + sortGenerators.single { + it.function.match("mkArraySort", KSort::class, KSort::class) + } + } + + private fun KType.isSubclassOf(other: KClass<*>): Boolean = + when (val cls = classifier) { + is KClass<*> -> cls.isSubclassOf(other) + is KTypeParameter -> cls.upperBounds.all { it.isSubclassOf(other) } + else -> false + } + + private fun KType.isSimple(): Boolean = + simpleValueGenerators.keys.any { this.isSubclassOf(it) } + + private fun KType.isKExpr(): Boolean = isSubclassOf(KExpr::class) + private fun KType.isConst(): Boolean = isSubclassOf(KInterpretedValue::class) + private fun KType.isKSort(): Boolean = isSubclassOf(KSort::class) + private fun KType.isKContext(): Boolean = this == KContext::class.createType() + + private fun KFunction<*>.match(name: String, vararg valueParams: KClass<*>): Boolean { + if (this.name != name) return false + val actualValueParams = parameters.drop(1) + if (actualValueParams.size != valueParams.size) return false + return valueParams.zip(actualValueParams).all { (expect, actual) -> actual.type.isSubclassOf(expect) } + } + + private fun KFunction.mkGenerator(): AstGenerator<*>? { + if (!parametersAreCorrect()) return null + val valueParams = parameters.drop(1) + + val typeParametersProviders = hashMapOf() + typeParameters.forEach { + it.mkReferenceSortProvider(typeParametersProviders) + } + + val argumentProviders = valueParams.map { + it.mkArgProvider(typeParametersProviders) ?: return null + } + + return AstGenerator(this, typeParametersProviders, argumentProviders) { args -> + when (name) { + /** + * Bv repeat operation can enormously increase the size of bv. + * To avoid extremely large bvs, we move the repetitions count to the range 1..3. + * */ + "mkBvRepeatExpr", "mkBvRepeatExprNoSimplify" -> listOf( + SimpleArgument(((args[0] as SimpleArgument).value as Int) % 3 + 1), + args[1] + ) + + "mkFpRemExpr" -> { + val argSort = (args[0] as? ExprArgument)?.value?.sort as? KFpSort + if (argSort != null && argSort.exponentBits > KFp128Sort.exponentBits) { + generationFailed( + "Exponent size ${argSort.exponentBits} can result in slow fp.rem computation" + ) + } + args + } + + /** + * Avoid floating point with extremely large exponents since + * such expression creation may involve slow computations. + * */ + "mkFpSort" -> { + val exponentSize = (args[0] as SimpleArgument).value as UInt + val significandSize = (args[1] as SimpleArgument).value as UInt + listOf( + SimpleArgument(exponentSize.coerceAtMost(KFp128Sort.exponentBits)), + SimpleArgument(significandSize) + ) + } + + else -> args + } + } + } + + private fun KFunction<*>.parametersAreCorrect(): Boolean { + if (parameters.isEmpty()) return false + if (parameters.any { it.kind == KParameter.Kind.EXTENSION_RECEIVER }) return false + if (parameters[0].kind != KParameter.Kind.INSTANCE || !parameters[0].type.isKContext()) return false + if (parameters.drop(1).any { it.kind != KParameter.Kind.VALUE }) return false + return true + } + + private fun KParameter.mkArgProvider(typeParametersProviders: MutableMap): ArgumentProvider? { + if (type.isKExpr()) { + val sortProvider = type.mkKExprSortProvider(typeParametersProviders) + return if (type.isConst()) { + ConstExprProvider(sortProvider) + } else { + ExprProvider(sortProvider) + } + } + + if (type.isKSort()) { + return type.mkSortProvider(typeParametersProviders) + } + + if (type.isSubclassOf(List::class)) { + val elementType = type.arguments.single().type ?: return null + if (!elementType.isKExpr()) return null + val sortProvider = elementType.mkKExprSortProvider(typeParametersProviders) + return ListProvider(sortProvider) + } + + if (type.isSimple()) { + val cls = type.classifier as? KClass<*> ?: return null + return SimpleProvider(cls) + } + + val sortClass = type.classifier + if (sortClass is KTypeParameter && sortClass.name in typeParametersProviders) { + return type.mkSortProvider(typeParametersProviders) + } + + return null + } + + private fun KTypeParameter.mkReferenceSortProvider(references: MutableMap) { + val sortProvider = if (upperBounds.size == 1 && upperBounds.single().isKSort()) { + upperBounds.single().mkSortProvider(references) + } else { + generationFailed("Not a KSort type argument") + } + references[name] = sortProvider + } + + private fun KType.mkKExprSortProvider(references: MutableMap): SortProvider { + val expr = findKExprType() + val sort = expr.arguments.single() + if (sort == KTypeProjection.STAR) return SingleSortProvider(KSort::class.java) + val sortType = sort.type ?: generationFailed("No type available") + return sortType.mkSortProvider(references) + } + + private fun KType.findKExprType() = if (classifier == KExpr::class) { + this + } else { + (classifier as? KClass<*>)?.allSupertypes?.find { it.classifier == KExpr::class } + ?: generationFailed("No KExpr superclass found") + } + + private fun KType.mkSortProvider(references: MutableMap): SortProvider { + val sortClass = classifier + if (sortClass is KTypeParameter) { + if (sortClass.name !in references) { + sortClass.mkReferenceSortProvider(references) + } + return ReferenceSortProvider(sortClass.name) + } + + if (this.isSubclassOf(KArraySort::class)) { + val (domain, range) = arguments.map { + it.type?.mkSortProvider(references) + ?: generationFailed("Array sort type is not available") + } + return ArraySortProvider(domain, range) + } + + if (this.isKSort() && sortClass != null) { + return SingleSortProvider((sortClass.uncheckedCast>()).java) + } + + generationFailed("Unexpected type $this") + } + + + private class ConstExprGenerator( + val sortArgument: SortArgument, + val generationContext: GenerationContext + ) : KSortVisitor> { + override fun visit(sort: KBoolSort): AstGenerator = boolGen + override fun visit(sort: KIntSort): AstGenerator = intGen + override fun visit(sort: KRealSort): AstGenerator = realGen + override fun visit(sort: KFpRoundingModeSort): AstGenerator = fpRmGen + + override fun visit(sort: S): AstGenerator = + AstGenerator(bvGen.function, bvGen.refSortProviders, listOf(SimpleProvider(Int::class))) { args -> + args + SimpleArgument(sort.sizeBits) + } + + override fun visit(sort: S): AstGenerator = + AstGenerator(fpGen.function, fpGen.refSortProviders, listOf(SimpleProvider(Double::class))) { args -> + args + sortArgument + } + + override fun visit(sort: KUninterpretedSort): AstGenerator = + AstGenerator(constGen.function, constGen.refSortProviders, listOf(SimpleProvider(String::class))) { args -> + args + sortArgument + } + + private fun , R : KSort> generateArray(sort: A): AstGenerator { + val rangeSortArgument = SortArgument(sort.range, generationContext.findSortIdx(sort.range)) + val rangeExprGenerator = sort.range.accept(ConstExprGenerator(rangeSortArgument, generationContext)) + val rangeExpr = rangeExprGenerator.generate(generationContext) + return AstGenerator(arrayGen.function, arrayGen.refSortProviders, emptyList()) { + listOf(sortArgument, rangeExpr) + } + } + + override fun visit(sort: KArraySort): AstGenerator = + generateArray(sort) + + override fun visit( + sort: KArray2Sort + ): AstGenerator = generateArray(sort) + + override fun visit( + sort: KArray3Sort + ): AstGenerator = generateArray(sort) + + override fun visit(sort: KArrayNSort): AstGenerator = + generateArray(sort) + } + + sealed interface Argument { + val value: Any + val depth: Int + } + + class ListArgument(val nested: List) : Argument { + override val value: Any + get() = nested.map { it.value } + + override val depth: Int + get() = nested.maxOf { it.depth } + } + + class SimpleArgument(override val value: Any) : Argument { + override val depth: Int = 0 + } + + class SortArgument(override val value: KSort, val idx: Int) : Argument { + override val depth: Int = 0 + } + + class ExprArgument(override val value: KExpr<*>, val idx: Int, override val depth: Int) : Argument + + private sealed interface ArgumentProvider { + fun provide(generationContext: GenerationContext, references: Map): Argument + } + + private sealed interface SortProvider : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument = + resolve(generationContext, references) + + fun resolve(generationContext: GenerationContext, references: Map): SortArgument + } + + private class SingleSortProvider(val sort: Class) : SortProvider { + override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { + val candidates = generationContext.sortIndex[sort] ?: generationFailed("No sort matching $sort") + val idx = candidates.random(generationContext.random) + return SortArgument(generationContext.sorts[idx], idx) + } + } + + private class ReferenceSortProvider(val reference: String) : SortProvider { + override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { + return references[reference] ?: generationFailed("Unresolved sort reference $references") + } + } + + private class ArraySortProvider(val domain: SortProvider, val range: SortProvider) : SortProvider { + override fun resolve(generationContext: GenerationContext, references: Map): SortArgument { + val generationParams = mapOf( + "D" to domain, + "R" to range + ) + val generator = AstGenerator( + arraySortGen.function, + generationParams, + generationParams.keys.map { ReferenceSortProvider(it) } + ) + val expr = generator.mkSeed(generationContext, references) + val sort = expr.value.sort + val sortIdx = generationContext.findSortIdx(sort) + return SortArgument(generationContext.sorts[sortIdx], sortIdx) + } + } + + private class SimpleProvider(val type: KClass<*>) : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument { + val value = type.generateSimpleValue(generationContext) + return SimpleArgument(value) + } + } + + private class ListProvider(val element: SortProvider) : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument { + val concreteSort = element.resolve(generationContext, references).value + val size = generationContext.random.nextInt(generationContext.params.generatedListSize) + val candidateExpressions = generationContext.expressionIndex[concreteSort] + ?: generationFailed("No expressions for sort $concreteSort") + + val nested = List(size) { + val (exprId, exprDepth) = selectRandomExpressionId(generationContext, candidateExpressions) + val expr = generationContext.expressions[exprId] + ExprArgument(expr, exprId, exprDepth) + } + return ListArgument(nested) + } + } + + private class ExprProvider(val sort: SortProvider) : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument { + val concreteSort = sort.resolve(generationContext, references).value + val candidateExpressions = generationContext.expressionIndex[concreteSort] + ?: generationFailed("No expressions for sort $concreteSort") + val (exprId, exprDepth) = selectRandomExpressionId(generationContext, candidateExpressions) + return ExprArgument(generationContext.expressions[exprId], exprId, exprDepth) + } + } + + private class ConstExprProvider(val sort: SortProvider) : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument { + val concreteSort = sort.resolve(generationContext, references) + val constants = generationContext.constantIndex[concreteSort.value] ?: emptyList() + + return if (constants.isNotEmpty()) { + val idx = constants.random(generationContext.random) + ExprArgument(generationContext.expressions[idx], idx, depth = 1) + } else { + val generator = concreteSort.value.accept(ConstExprGenerator(concreteSort, generationContext)) + generator.generate(generationContext, references) + } + } + } + + private class AstGenerator( + val function: KFunction<*>, + val refSortProviders: Map, + val argProviders: List, + val provideArguments: (List) -> List = { it } + ) : ArgumentProvider { + override fun provide(generationContext: GenerationContext, references: Map): Argument = + generate(generationContext, references) + + fun generate(generationContext: GenerationContext, context: Map = emptyMap()): T { + val resolvedRefProviders = refSortProviders.mapValues { + it.value.resolve(generationContext, context) + } + val baseArguments = argProviders.map { + it.provide(generationContext, context + resolvedRefProviders) + } + val arguments = provideArguments(baseArguments) + + val invocation = FunctionInvocation(function, arguments) + + val ast = try { + invocation.call(generationContext.context) + } catch (ex: Throwable) { + throw GenerationFailedException("Generator failed", ex) + } + + if (ast is KExpr<*>) { + if (!ast.isCorrect()) { + generationFailed("Incorrect ast generated") + } + val depth = (arguments.maxOfOrNull { it.depth } ?: 0) + 1 + val idx = generationContext.registerExpr(ast, depth) + generationContext.trace += invocation + return ExprArgument(ast, idx, depth).uncheckedCast() + } + + if (ast is KSort) { + var idx = generationContext.registerSort(ast) + if (idx == -1) { + idx = generationContext.findSortIdx(ast) + } + generationContext.trace += invocation + return SortArgument(ast, idx).uncheckedCast() + } + + generationFailed("Unexpected generation result: $ast") + } + + private fun KExpr<*>.isCorrect(): Boolean { + val sort = sort + if (sort is KBvSort && sort.sizeBits == 0u) return false + return true + } + } + + private fun AstGenerator.mkSeed( + generationContext: GenerationContext, + context: Map = emptyMap() + ): ExprArgument { + val exprGenerator = AstGenerator( + constGen.function, + emptyMap(), + listOf(SimpleProvider(String::class), this) + ) + return exprGenerator.generate(generationContext, context) + } + + private fun selectRandomExpressionId( + context: GenerationContext, + expressionIds: SortedMap> + ): Pair { + val expressionDepth = when (context.random.nextDouble()) { + in 0.0..context.params.deepExpressionProbability -> expressionIds.lastKey() + else -> expressionIds.keys.random(context.random) + } + val candidateExpressions = expressionIds.getValue(expressionDepth) + val exprId = candidateExpressions.random(context.random) + return exprId to expressionDepth + } + + private val simpleValueGenerators = mapOf, GenerationContext.(KClass<*>) -> Any>( + Boolean::class to { random.nextBoolean() }, + Byte::class to { random.nextInt().toByte() }, + UByte::class to { random.nextInt().toUByte() }, + Short::class to { random.nextInt().toShort() }, + UShort::class to { random.nextInt().toUShort() }, + Int::class to { random.nextInt(params.possibleIntValues) }, + UInt::class to { + val range = params.possibleIntValues.let { it.first.toUInt()..it.last.toUInt() } + random.nextUInt(range) + }, + Long::class to { random.nextLong() }, + ULong::class to { random.nextULong() }, + Float::class to { random.nextFloat() }, + Double::class to { random.nextDouble() }, + String::class to { + val stringLength = random.nextInt(params.generatedStringLength) + val chars = CharArray(stringLength) { params.possibleStringChars.random(random) } + String(chars) + }, + Enum::class to { it.java.enumConstants.random(random) } + ) + + private fun KClass<*>.generateSimpleValue(context: GenerationContext): Any { + val generator = simpleValueGenerators[this] + ?: (if (this.isSubclassOf(Enum::class)) simpleValueGenerators[Enum::class] else null) + ?: generationFailed("Unexpected simple type: $this") + return context.generator(this) + } + + private class FunctionInvocation( + val function: KFunction<*>, + val args: List + ) { + fun call(ctx: KContext): Any? { + val argumentValues = args.map { it.value } + return function.call(ctx, *argumentValues.toTypedArray()) + } + } + + private class GenerationContext( + val random: Random, + val context: KContext, + val params: GenerationParameters, + expressionsAmountEstimation: Int, + sortsAmountEstimation: Int + ) { + val expressions = ArrayList>(expressionsAmountEstimation) + val sorts = ArrayList(sortsAmountEstimation) + val registeredSorts = HashMap(sortsAmountEstimation) + val expressionIndex = hashMapOf>>() + val constantIndex = hashMapOf>() + val sortIndex = hashMapOf, MutableList>() + val trace = ArrayList(expressionsAmountEstimation + sortsAmountEstimation) + + fun registerSort(sort: KSort, inReplayMode: Boolean = false): Int { + val knownSortId = registeredSorts[sort] + + val sortId = if (knownSortId != null) { + knownSortId + } else { + val idx = sorts.size + sorts.add(sort) + registeredSorts[sort] = idx + idx + } + + if (knownSortId != null || inReplayMode) { + return sortId + } + + var sortCls: Class<*> = sort::class.java + while (sortCls != KSort::class.java) { + sortIndex.getOrPut(sortCls) { arrayListOf() }.add(sortId) + sortCls = sortCls.superclass + } + sortIndex.getOrPut(sortCls) { arrayListOf() }.add(sortId) + + return sortId + } + + fun registerExpr(expr: KExpr<*>, depth: Int, inReplayMode: Boolean = false): Int { + registerSort(expr.sort, inReplayMode) + + val exprId = expressions.size + expressions.add(expr) + + if (inReplayMode) { + return exprId + } + + val index = expressionIndex.getOrPut(expr.sort) { sortedMapOf() } + val expressionIds = index.getOrPut(depth) { arrayListOf() } + expressionIds.add(exprId) + + if (expr is KInterpretedValue<*>) { + constantIndex.getOrPut(expr.sort) { arrayListOf() }.add(exprId) + } + + return exprId + } + + fun findSortIdx(sort: KSort): Int = + registeredSorts[sort] ?: generationFailed("No idx for sort $sort") + } + } +} + +@Suppress("SwallowedException") +private inline fun nullIfGenerationFailed(body: () -> T): T? = try { + body() +} catch (ex: GenerationFailedException) { + null +} + +private class GenerationFailedException : Exception { + constructor(message: String) : super(message) + constructor(message: String, cause: Throwable) : super(message, cause) +} + +private fun generationFailed(message: String): Nothing = + throw GenerationFailedException(message) diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt new file mode 100644 index 000000000..f2b9f0399 --- /dev/null +++ b/ksmt-test/src/main/kotlin/io/ksmt/test/SmtLibParseError.kt @@ -0,0 +1,3 @@ +package io.ksmt.test + +class SmtLibParseError(cause: Throwable) : Exception(cause) diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt new file mode 100644 index 000000000..55910c12f --- /dev/null +++ b/ksmt-test/src/main/kotlin/io/ksmt/test/TestRunner.kt @@ -0,0 +1,144 @@ +package io.ksmt.test + +import com.jetbrains.rd.util.reactive.RdFault +import kotlinx.coroutines.withTimeout +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.core.KsmtWorkerSession +import io.ksmt.runner.generated.models.EqualityCheckAssumptionsParams +import io.ksmt.runner.generated.models.EqualityCheckParams +import io.ksmt.runner.generated.models.TestAssertParams +import io.ksmt.runner.generated.models.TestInternalizeAndConvertParams +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverUnsupportedFeatureException +import io.ksmt.sort.KBoolSort +import java.nio.file.Path +import kotlin.time.Duration +import kotlin.time.DurationUnit + +class TestRunner( + val ctx: KContext, + private val hardTimeout: Duration, + private val worker: KsmtWorkerSession, +) { + suspend fun init() = withTimeoutAndExceptionHandling { + worker.protocolModel.create.startSuspending(worker.lifetime, Unit) + } + + suspend fun delete() = withTimeoutAndExceptionHandling { + if (!worker.isAlive) return@withTimeoutAndExceptionHandling + worker.protocolModel.delete.startSuspending(worker.lifetime, Unit) + } + + suspend fun parseFile(path: Path): List = withTimeoutAndExceptionHandling { + worker.protocolModel.parseFile.startSuspending(worker.lifetime, path.toFile().absolutePath) + } + + suspend fun convertAssertions(nativeAssertions: List): List> = + withTimeoutAndExceptionHandling { + val result = worker.protocolModel.convertAssertions.startSuspending(worker.lifetime, nativeAssertions) + result.expressions.map { + @Suppress("UNCHECKED_CAST") + it as KExpr + } + } + + suspend fun internalizeAndConvertBitwuzla(assertions: List>): List> = + withTimeoutAndExceptionHandling { + val params = TestInternalizeAndConvertParams(assertions) + val result = worker.protocolModel.internalizeAndConvertBitwuzla.startSuspending(worker.lifetime, params) + result.expressions.map { + @Suppress("UNCHECKED_CAST") + it as KExpr + } + } + + suspend fun internalizeAndConvertYices(assertions: List>): List> = + withTimeoutAndExceptionHandling { + val params = TestInternalizeAndConvertParams(assertions) + val result = worker.protocolModel.internalizeAndConvertYices.startSuspending(worker.lifetime, params) + result.expressions.map { + @Suppress("UNCHECKED_CAST") + it as KExpr + } + } + + suspend fun internalizeAndConvertCvc5(assertions: List>): List> = + withTimeoutAndExceptionHandling { + val params = TestInternalizeAndConvertParams(assertions) + val result = worker.protocolModel.internalizeAndConvertCvc5.startSuspending(worker.lifetime, params) + result.expressions.map { + @Suppress("UNCHECKED_CAST") + it as KExpr + } + } + + suspend fun createSolver(timeout: Duration): Int = withTimeoutAndExceptionHandling { + val timeoutValue = timeout.toInt(DurationUnit.MILLISECONDS) + worker.protocolModel.createSolver.startSuspending(worker.lifetime, timeoutValue) + } + + suspend fun assert(solver: Int, expr: Long) = withTimeoutAndExceptionHandling { + worker.protocolModel.assert.startSuspending(worker.lifetime, TestAssertParams(solver, expr)) + } + + suspend fun check(solver: Int): KSolverStatus = withTimeoutAndExceptionHandling { + worker.protocolModel.check.startSuspending(worker.lifetime, solver).status + } + + suspend fun addEqualityCheck(solver: Int, actual: KExpr<*>, expected: Long) = withTimeoutAndExceptionHandling { + val params = EqualityCheckParams(solver, actual, expected) + worker.protocolModel.addEqualityCheck.startSuspending(worker.lifetime, params) + } + + suspend fun addEqualityCheckAssumption(solver: Int, assumption: KExpr) = + withTimeoutAndExceptionHandling { + val params = EqualityCheckAssumptionsParams(solver, assumption) + worker.protocolModel.addEqualityCheckAssumption.startSuspending(worker.lifetime, params) + } + + suspend fun checkEqualities(solver: Int): KSolverStatus = withTimeoutAndExceptionHandling { + worker.protocolModel.checkEqualities.startSuspending(worker.lifetime, solver).status + } + + suspend fun findFirstFailedEquality(solver: Int): Int? = withTimeoutAndExceptionHandling { + worker.protocolModel.findFirstFailedEquality.startSuspending(worker.lifetime, solver) + } + + suspend fun exprToString(expr: Long): String = withTimeoutAndExceptionHandling { + worker.protocolModel.exprToString.startSuspending(worker.lifetime, expr) + } + + suspend fun getReasonUnknown(solver: Int): String = withTimeoutAndExceptionHandling { + worker.protocolModel.getReasonUnknown.startSuspending(worker.lifetime, solver) + } + + suspend fun mkTrueExpr(): Long = withTimeoutAndExceptionHandling { + worker.protocolModel.mkTrueExpr.startSuspending(worker.lifetime, Unit) + } + + @Suppress("TooGenericExceptionCaught", "SwallowedException", "ThrowsCount") + private suspend inline fun withTimeoutAndExceptionHandling(crossinline body: suspend () -> T): T { + try { + return withTimeout(hardTimeout) { + body() + } + } catch (ex: RdFault) { + throw rdExceptionCause(ex) ?: ex + } catch (ex: Exception) { + worker.terminate() + throw ex + } + } + + fun rdExceptionCause(ex: RdFault): Throwable? = when (ex.reasonTypeFqn) { + NotImplementedError::class.simpleName -> + NotImplementedError(ex.reasonMessage) + KSolverUnsupportedFeatureException::class.simpleName -> + KSolverUnsupportedFeatureException(ex.reasonMessage) + SmtLibParseError::class.simpleName -> + SmtLibParseError(ex) + else -> null + } +} diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt new file mode 100644 index 000000000..6ed7ab482 --- /dev/null +++ b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorker.kt @@ -0,0 +1,12 @@ +package io.ksmt.test + +import com.jetbrains.rd.framework.IProtocol +import io.ksmt.runner.core.KsmtWorkerBase +import io.ksmt.runner.core.RdServer +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.runner.generated.models.testProtocolModel + +class TestWorker(id: Int, process: RdServer) : KsmtWorkerBase(id, process) { + override fun initProtocolModel(protocol: IProtocol): TestProtocolModel = + protocol.testProtocolModel +} diff --git a/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt new file mode 100644 index 000000000..d6308d7de --- /dev/null +++ b/ksmt-test/src/main/kotlin/io/ksmt/test/TestWorkerProcess.kt @@ -0,0 +1,331 @@ +package io.ksmt.test + +import com.jetbrains.rd.framework.IProtocol +import com.microsoft.z3.AST +import com.microsoft.z3.BoolSort +import com.microsoft.z3.Context +import com.microsoft.z3.Expr +import com.microsoft.z3.Native +import com.microsoft.z3.Solver +import com.microsoft.z3.Status +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.core.ChildProcessBase +import io.ksmt.runner.core.KsmtWorkerArgs +import io.ksmt.runner.generated.models.TestCheckResult +import io.ksmt.runner.generated.models.TestConversionResult +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.runner.generated.models.testProtocolModel +import io.ksmt.runner.serializer.AstSerializationCtx +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.bitwuzla.KBitwuzlaContext +import io.ksmt.solver.bitwuzla.KBitwuzlaExprConverter +import io.ksmt.solver.bitwuzla.KBitwuzlaExprInternalizer +import io.ksmt.solver.cvc5.KCvc5Context +import io.ksmt.solver.cvc5.KCvc5ExprConverter +import io.ksmt.solver.cvc5.KCvc5ExprInternalizer +import io.ksmt.solver.cvc5.KCvc5Solver +import io.github.cvc5.Solver as Cvc5Solver +import io.ksmt.solver.z3.KZ3Context +import io.ksmt.solver.yices.* +import io.ksmt.solver.z3.KZ3ExprConverter +import io.ksmt.solver.z3.KZ3ExprInternalizer +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KBoolSort +import io.ksmt.utils.uncheckedCast + +class TestWorkerProcess : ChildProcessBase() { + private var workerCtx: KContext? = null + private var workerZ3Ctx: Context? = null + + private val ctx: KContext + get() = workerCtx ?: error("Solver is not initialized") + + private val z3Ctx: Context + get() = workerZ3Ctx ?: error("Solver is not initialized") + + private class OracleSolver( + val solver: Solver, + val solverCtx: KZ3Context + ) + + private val solvers = mutableListOf() + private val nativeAsts = mutableListOf() + private val equalityChecks = mutableMapOf>() + private val equalityCheckAssumptions = mutableMapOf>>() + + private fun create() { + workerCtx = KContext() + workerZ3Ctx = Context() + equalityChecks.clear() + equalityCheckAssumptions.clear() + solvers.clear() + nativeAsts.clear() + } + + private fun delete() { + equalityChecks.clear() + equalityCheckAssumptions.clear() + nativeAsts.clear() + solvers.clear() + workerCtx?.close() + workerZ3Ctx?.close() + workerCtx = null + workerZ3Ctx = null + } + + private fun parseFile(path: String): List = try { + val parsed = z3Ctx.parseSMTLIB2File( + path, + emptyArray(), + emptyArray(), + emptyArray(), + emptyArray() + ) + nativeAsts.addAll(parsed) + nativeAsts.map { z3Ctx.unwrapAST(it) } + } catch (ex: Exception) { + throw SmtLibParseError(ex) + } + + private fun convertAssertions(nativeAssertions: List): List> { + val converter = KZ3ExprConverter(ctx, KZ3Context(ctx, z3Ctx)) + return with(converter) { nativeAssertions.map { it.convertExpr() } } + } + + private fun internalizeAndConvertBitwuzla(assertions: List>): List> = + KBitwuzlaContext(ctx).use { bitwuzlaCtx -> + val internalizer = KBitwuzlaExprInternalizer(bitwuzlaCtx) + val bitwuzlaAssertions = with(internalizer) { + assertions.map { it.internalize() } + } + + val converter = KBitwuzlaExprConverter(ctx, bitwuzlaCtx) + with(converter) { + bitwuzlaAssertions.map { it.convertExpr(ctx.boolSort) } + } + } + + private fun internalizeAndConvertYices(assertions: List>): List> { + // Yices doesn't reverse cache internalized expressions (only interpreted values) + KYicesContext().use { internContext -> + val internalizer = KYicesExprInternalizer(internContext) + + val yicesAssertions = with(internalizer) { + assertions.map { it.internalize() } + } + + val converter = KYicesExprConverter(ctx, internContext) + + return with(converter) { + yicesAssertions.map { it.convert(ctx.boolSort) } + } + } + } + + private fun internalizeAndConvertCvc5(assertions: List>): List> { + KCvc5Solver(ctx).close() // ensure native libs loaded + + return Cvc5Solver().use { cvc5Solver -> + val cvc5Assertions = KCvc5Context(cvc5Solver, ctx).use { cvc5Ctx -> + val internalizer = KCvc5ExprInternalizer(cvc5Ctx) + with(internalizer) { assertions.map { it.internalizeExpr() } } + } + + KCvc5Context(cvc5Solver, ctx).use { cvc5Ctx -> + val converter = KCvc5ExprConverter(ctx, cvc5Ctx) + with(converter) { cvc5Assertions.map { it.convertExpr() } } + } + } + } + + private fun createSolver(timeout: Int): Int { + val solver = with(z3Ctx) { + mkSolver().apply { + val params = mkParams().apply { + add("timeout", timeout) + add("random_seed", SEED_FOR_RANDOM) + } + setParameters(params) + } + } + solvers.add(OracleSolver(solver, KZ3Context(ctx, z3Ctx))) + return solvers.lastIndex + } + + private fun assert(solver: Int, expr: Long) { + @Suppress("UNCHECKED_CAST") + solvers[solver].solver.add(z3Ctx.wrapAST(expr) as Expr) + } + + private fun check(solver: Int): KSolverStatus { + return solvers[solver].solver.check().processCheckResult() + } + + private fun exprToString(expr: Long): String { + return z3Ctx.wrapAST(expr).toString() + } + + private fun getReasonUnknown(solver: Int): String { + return solvers[solver].solver.reasonUnknown + } + + private fun addEqualityCheck(solver: Int, actual: KExpr<*>, expected: Long) { + val actualExpr = internalize(solver, actual) + val expectedExpr = z3Ctx.wrapAST(expected) as Expr<*> + val checks = equalityChecks.getOrPut(solver) { mutableListOf() } + checks += EqualityCheck(actual = actualExpr, expected = expectedExpr) + } + + private fun addEqualityCheckAssumption(solver: Int, assumption: KExpr<*>) { + val assumptionExpr = internalize(solver, assumption) + val assumptions = equalityCheckAssumptions.getOrPut(solver) { mutableListOf() } + assumptions.add(assumptionExpr.uncheckedCast()) + } + + private fun checkEqualities(solver: Int): KSolverStatus = with(z3Ctx) { + val checks = equalityChecks[solver] ?: emptyList() + val equalityBindings = checks.map { mkNot(mkEq(it.actual, it.expected)) } + equalityCheckAssumptions[solver]?.forEach { solvers[solver].solver.add(it) } + solvers[solver].solverCtx.assertPendingAxioms(solvers[solver].solver) + solvers[solver].solver.add(mkOr(*equalityBindings.toTypedArray())) + return check(solver) + } + + private fun findFirstFailedEquality(solver: Int): Int? = with(z3Ctx){ + val solverInstance = solvers[solver].solver + equalityCheckAssumptions[solver]?.forEach { solvers[solver].solver.add(it) } + solvers[solver].solverCtx.assertPendingAxioms(solvers[solver].solver) + val checks = equalityChecks[solver] ?: emptyList() + for ((idx, check) in checks.withIndex()) { + solverInstance.push() + val binding = mkNot(mkEq(check.actual, check.expected)) + solverInstance.add(binding) + val status = solverInstance.check() + solverInstance.pop() + if (status == Status.SATISFIABLE) + return idx + } + return null + } + + private fun mkTrueExpr(): Long = with(z3Ctx) { + val trueExpr = mkTrue().also { nativeAsts.add(it) } + unwrapAST(trueExpr) + } + + private fun Status?.processCheckResult() = when (this) { + Status.SATISFIABLE -> KSolverStatus.SAT + Status.UNSATISFIABLE -> KSolverStatus.UNSAT + Status.UNKNOWN -> KSolverStatus.UNKNOWN + null -> KSolverStatus.UNKNOWN + } + + private fun internalize(solver: Int, expr: KExpr<*>): Expr<*> { + val internCtx = solvers[solver].solverCtx + val internalizer = KZ3ExprInternalizer(ctx, internCtx) + val internalized = with(internalizer) { expr.internalizeExpr() } + return z3Ctx.wrapAST(internalized) as Expr<*> + } + + private data class EqualityCheck(val actual: Expr<*>, val expected: Expr<*>) + + override fun parseArgs(args: Array) = KsmtWorkerArgs.fromList(args.toList()) + + override fun initProtocolModel(protocol: IProtocol): TestProtocolModel = + protocol.testProtocolModel + + @Suppress("LongMethod") + override fun TestProtocolModel.setup(astSerializationCtx: AstSerializationCtx) { + // Limit z3 native memory usage to avoid OOM + Native.globalParamSet("memory_high_watermark_mb", "2048") // 2048 megabytes + + /** + * Memory usage hard limit. + * + * Normally z3 will throw an exception when used + * memory amount is slightly above `memory_high_watermark`. + * But `memory_high_watermark` check may be missed somewhere in Z3 and + * memory usage will become a way higher than limit. + * Therefore, we use hard limit to avoid OOM + */ + Native.globalParamSet("memory_max_size", "4096") // 4096 megabytes + + create.measureExecutionForTermination { + create() + astSerializationCtx.initCtx(ctx) + } + delete.measureExecutionForTermination { + astSerializationCtx.resetCtx() + delete() + } + parseFile.measureExecutionForTermination { path -> + parseFile(path) + } + convertAssertions.measureExecutionForTermination { assertions -> + val converted = convertAssertions(assertions) + TestConversionResult(converted) + } + internalizeAndConvertBitwuzla.measureExecutionForTermination { params -> + @Suppress("UNCHECKED_CAST") + val converted = internalizeAndConvertBitwuzla(params.expressions as List>) + TestConversionResult(converted) + } + internalizeAndConvertYices.measureExecutionForTermination { params -> + @Suppress("UNCHECKED_CAST") + val converted = internalizeAndConvertYices(params.expressions as List>) + TestConversionResult(converted) + } + internalizeAndConvertCvc5.measureExecutionForTermination { params -> + @Suppress("UNCHECKED_CAST") + val converted = internalizeAndConvertCvc5(params.expressions as List>) + TestConversionResult(converted) + } + createSolver.measureExecutionForTermination { timeout -> + createSolver(timeout) + } + assert.measureExecutionForTermination { params -> + assert(params.solver, params.expr) + } + check.measureExecutionForTermination { solver -> + val status = check(solver) + TestCheckResult(status) + } + exprToString.measureExecutionForTermination { solver -> + exprToString(solver) + } + getReasonUnknown.measureExecutionForTermination { solver -> + getReasonUnknown(solver) + } + addEqualityCheck.measureExecutionForTermination { params -> + addEqualityCheck(params.solver, params.actual as KExpr<*>, params.expected) + } + addEqualityCheckAssumption.measureExecutionForTermination { params -> + addEqualityCheckAssumption(params.solver, params.assumption as KExpr<*>) + } + checkEqualities.measureExecutionForTermination { solver -> + val status = checkEqualities(solver) + TestCheckResult(status) + } + findFirstFailedEquality.measureExecutionForTermination { solver -> + findFirstFailedEquality(solver) + } + mkTrueExpr.measureExecutionForTermination { + mkTrueExpr() + } + } + + companion object { + private const val SEED_FOR_RANDOM = 12345 + + init { + // force native library load + KZ3Solver(KContext()).close() + } + + @JvmStatic + fun main(args: Array) { + TestWorkerProcess().start(args) + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt new file mode 100644 index 000000000..6f9fac035 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/ContextAstInterningTest.kt @@ -0,0 +1,85 @@ +package io.ksmt.test + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import io.ksmt.KContext +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +class ContextAstInterningTest { + + @Test + fun testSingleThreadNoGcContextInterning() = KContext( + operationMode = KContext.OperationMode.SINGLE_THREAD, + astManagementMode = KContext.AstManagementMode.NO_GC + ).runSingleThread(::testExpressionsEquality) + + @Test + fun testSingleThreadWithGcContextInterning() = KContext( + operationMode = KContext.OperationMode.SINGLE_THREAD, + astManagementMode = KContext.AstManagementMode.GC + ).runSingleThread(::testExpressionsEquality) + + @Test + fun testConcurrentNoGcContextInterning() = KContext( + operationMode = KContext.OperationMode.CONCURRENT, + astManagementMode = KContext.AstManagementMode.NO_GC + ).runParallel(::testExpressionsEquality) + + @Test + fun testConcurrentWithGcContextInterning() = KContext( + operationMode = KContext.OperationMode.CONCURRENT, + astManagementMode = KContext.AstManagementMode.GC + ).runParallel(::testExpressionsEquality) + + private fun KContext.runSingleThread(test: (KContext, RandomExpressionGenerator) -> Unit) = + generators.forEach { test(this, it) } + + private fun KContext.runParallel(test: (KContext, RandomExpressionGenerator) -> Unit) = runBlocking { + generators.map { + async(Dispatchers.Default) { test(this@runParallel, it) } + }.joinAll() + } + + private fun testExpressionsEquality(ctx: KContext, generator: RandomExpressionGenerator) { + val e1 = generator.replay(ctx) + val e2 = generator.replay(ctx) + + assertEquals(e1, e2) + e1.zip(e2).forEach { + assertSame(it.first, it.second) + } + } + + companion object { + private val generators = mutableListOf() + + @BeforeAll + @JvmStatic + fun setupGenerators() { + val generationCtx = KContext(KContext.OperationMode.SINGLE_THREAD, KContext.AstManagementMode.NO_GC) + val random = Random(42) + + repeat(20) { + generators += RandomExpressionGenerator().apply { + generate( + 10000, generationCtx, random, + generatorFilter = RandomExpressionGenerator.noFreshConstants + ) + } + } + } + + @AfterAll + @JvmStatic + fun cleanupGenerators() { + generators.clear() + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt new file mode 100644 index 000000000..923850844 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/MultiIndexedArrayTest.kt @@ -0,0 +1,587 @@ +package io.ksmt.test + +import com.microsoft.z3.Context +import io.github.cvc5.Solver +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable +import io.ksmt.KContext +import io.ksmt.KContext.SimplificationMode.NO_SIMPLIFY +import io.ksmt.expr.KExpr +import io.ksmt.expr.rewrite.KExprUninterpretedDeclCollector +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverUnsupportedFeatureException +import io.ksmt.solver.bitwuzla.KBitwuzlaContext +import io.ksmt.solver.bitwuzla.KBitwuzlaExprConverter +import io.ksmt.solver.bitwuzla.KBitwuzlaExprInternalizer +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Context +import io.ksmt.solver.cvc5.KCvc5ExprConverter +import io.ksmt.solver.cvc5.KCvc5ExprInternalizer +import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.runner.KSolverRunnerManager +import io.ksmt.solver.yices.KYicesContext +import io.ksmt.solver.yices.KYicesExprConverter +import io.ksmt.solver.yices.KYicesExprInternalizer +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Context +import io.ksmt.solver.z3.KZ3ExprConverter +import io.ksmt.solver.z3.KZ3ExprInternalizer +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KArray2Sort +import io.ksmt.sort.KArray3Sort +import io.ksmt.sort.KArraySort +import io.ksmt.sort.KArraySortBase +import io.ksmt.sort.KBv8Sort +import io.ksmt.sort.KBvSort +import io.ksmt.sort.KSort +import io.ksmt.utils.uncheckedCast +import kotlin.test.Test +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +@EnabledIfEnvironmentVariable( + named = "runMultiIndexedArrayTest", + matches = "true", + disabledReason = "Disable due to a very long runtime (about 6 hours) which is not applicable for usual CI runs" +) +class MultiIndexedArrayTest { + + @Test + fun testMultiIndexedArraysZ3WithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> + mkZ3Context(this).use { z3NativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertZ3(z3NativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysBitwuzlaWithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> + KBitwuzlaContext(this).use { bitwuzlaNativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertBitwuzla(bitwuzlaNativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysZ3WithBitwuzlaOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KBitwuzlaSolver::class).use { oracleSolver -> + mkZ3Context(this).use { z3NativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertZ3(z3NativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysBitwuzlaWithBitwuzlaOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KBitwuzlaSolver::class).use { oracleSolver -> + KBitwuzlaContext(this).use { bitwuzlaNativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertBitwuzla(bitwuzlaNativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysYicesWithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> + KYicesContext().use { yicesNativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertYices(yicesNativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysZ3WithYicesOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KYicesSolver::class).use { oracleSolver -> + mkZ3Context(this).use { z3NativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertZ3(z3NativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysYicesWithYicesOracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KYicesSolver::class).use { oracleSolver -> + KYicesContext().use { yicesNativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertYices(yicesNativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysZ3WithCvc5Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KCvc5Solver::class).use { oracleSolver -> + + // Enable HO to test array lambda equalities + oracleSolver.configure { setCvc5Logic("HO_QF_ALL") } + + mkZ3Context(this).use { z3NativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertZ3(z3NativeCtx, expr) + } + } + } + } + + @Test + fun testMultiIndexedArraysCvc5WithZ3Oracle(): Unit = with(KContext(simplificationMode = NO_SIMPLIFY)) { + oracleManager.createSolver(this, KZ3Solver::class).use { oracleSolver -> + mkCvc5Context(this).use { cvc5NativeCtx -> + runMultiIndexedArraySamples(oracleSolver) { expr -> + internalizeAndConvertCvc5(cvc5NativeCtx, expr) + } + } + } + } + + private inline fun KContext.runMultiIndexedArraySamples( + oracle: KSolver<*>, + process: (KExpr) -> KExpr + ) { + val stats = TestStats() + val sorts = listOf( + mkArraySort(bv8Sort, bv8Sort), + mkArraySort(bv32Sort, bv16Sort, bv8Sort), + mkArraySort(bv32Sort, bv16Sort, bv8Sort, bv8Sort), + mkArrayNSort(listOf(bv32Sort, bv16Sort, bv8Sort, bv32Sort, bv8Sort), bv8Sort) + ) + + for (sort in sorts) { + val expressions = mkArrayExpressions(sort) + for (expr in expressions) { + stats.start() + stats.withErrorHandling { + val processed = process(expr) + assertEquals(stats, oracle, expr, processed) + } + } + } + + stats.result() + } + + private fun > KContext.mkArrayExpressions(sort: A): List> { + var arrayExpressions = listOfNotNull( + mkConst(sort), +// mkAsArray(sort), // disable as-array because it is too hard to check equality + mkArrayConst(sort) { mkConst("cv", bv8Sort) }, + mkLambda(sort) { mkConst("lv", bv8Sort) } + ) + + arrayExpressions = arrayExpressions + arrayExpressions.map { + mkStore(it) { mkConst("v", bv8Sort) } + } + + arrayExpressions = arrayExpressions + arrayExpressions.flatMap { first -> + arrayExpressions.map { second -> + mkIte(mkConst("cond", boolSort), first, second) + } + } + + val arrayEq = arrayExpressions.crossProduct { first, second -> first eq second } + + var arraySelects = arrayExpressions.map { mkSelect(it) } + + val arrayValues = arraySelects + listOf(mkConst("x", bv8Sort)) + + arrayExpressions = arrayExpressions + arrayExpressions.map { array -> + mkLambda(sort) { indices -> mkSelect(array) { indices.uncheckedCast() } } + } + + arrayExpressions = arrayExpressions + arrayValues.flatMap { value -> + listOf( + mkArrayConst(sort) { value }, + mkLambda(sort) { value }, + ) + } + + arrayExpressions = arrayExpressions + arrayExpressions.flatMap { array -> + arrayValues.map { value -> + mkStore(array) { value } + } + } + + arraySelects = arraySelects + arrayExpressions.map { mkSelect(it) } + + return listOf( + arrayExpressions, + arraySelects, + arrayEq + ).flatten().uncheckedCast() + } + + private inline fun List.crossProduct(transform: (T, T) -> R): List { + val result = mutableListOf() + for (i in indices) { + for (j in i until size) { + result += transform(get(i), get(j)) + } + } + return result + } + + private fun > KContext.mkConst(sort: A): KExpr = + mkFreshConst("c", sort) + + private fun > KContext.mkArrayConst( + sort: A, + value: () -> KExpr + ): KExpr = mkArrayConst(sort, value()) + + private fun > KContext.mkAsArray(sort: A): KExpr { + val function = mkFreshFuncDecl("f", sort.range, sort.domainSorts) + return mkFunctionAsArray(sort, function) + } + + private fun > KContext.mkLambda( + sort: A, + mkBody: (List>) -> KExpr + ): KExpr { + val indices = sort.domainSorts.map { mkFreshConst("i", it) } + val body = mkBody(indices.uncheckedCast()) + return when (indices.size) { + KArraySort.DOMAIN_SIZE -> mkArrayLambda(indices.single().decl, body) + KArray2Sort.DOMAIN_SIZE -> mkArrayLambda(indices.first().decl, indices.last().decl, body) + KArray3Sort.DOMAIN_SIZE -> { + val (i0, i1, i2) = indices + mkArrayLambda(i0.decl, i1.decl, i2.decl, body) + } + + else -> mkArrayNLambda(indices.map { it.decl }, body) + }.uncheckedCast() + } + + private fun > KContext.mkStore( + array: KExpr, + mkValue: () -> KExpr + ): KExpr { + val indices = array.sort.domainSorts.map { mkFreshConst("i", it) } + val value = mkValue() + return when (indices.size) { + KArraySort.DOMAIN_SIZE -> mkArrayStore(array.uncheckedCast(), indices.single(), value) + KArray2Sort.DOMAIN_SIZE -> mkArrayStore(array.uncheckedCast(), indices.first(), indices.last(), value) + KArray3Sort.DOMAIN_SIZE -> { + val (i0, i1, i2) = indices + mkArrayStore(array.uncheckedCast(), i0, i1, i2, value) + } + + else -> mkArrayNStore(array.uncheckedCast(), indices, value) + }.uncheckedCast() + } + + private fun > KContext.mkSelect( + array: KExpr, + mkIndices: KContext.(A) -> List> = { sort -> + sort.domainSorts.map { mkFreshConst("i", it) } + } + ): KExpr { + val indices = mkIndices(array.sort) + return when (indices.size) { + KArraySort.DOMAIN_SIZE -> mkArraySelect(array.uncheckedCast(), indices.single()) + KArray2Sort.DOMAIN_SIZE -> mkArraySelect(array.uncheckedCast(), indices.first(), indices.last()) + KArray3Sort.DOMAIN_SIZE -> { + val (i0, i1, i2) = indices + mkArraySelect(array.uncheckedCast(), i0, i1, i2) + } + + else -> mkArrayNSelect(array.uncheckedCast(), indices) + } + } + + private fun KContext.internalizeAndConvertBitwuzla( + nativeCtx: KBitwuzlaContext, expr: KExpr + ): KExpr { + val internalized = with(KBitwuzlaExprInternalizer(nativeCtx)) { + expr.internalizeExpr() + } + + val converted = with(KBitwuzlaExprConverter(this, nativeCtx)) { + internalized.convertExpr(expr.sort) + } + + return converted + } + + private fun KContext.internalizeAndConvertYices( + nativeCtx: KYicesContext, expr: KExpr + ): KExpr { + val internalized = with(KYicesExprInternalizer(nativeCtx)) { + expr.internalizeExpr() + } + + val converted = with(KYicesExprConverter(this, nativeCtx)) { + internalized.convert(expr.sort) + } + + return converted + } + + private fun KContext.internalizeAndConvertZ3(nativeCtx: Context, expr: KExpr): KExpr { + val z3InternCtx = KZ3Context(this, nativeCtx) + val z3ConvertCtx = KZ3Context(this, nativeCtx) + + val internalized = with(KZ3ExprInternalizer(this, z3InternCtx)) { + expr.internalizeExpr() + } + + // Copy declarations since we have fresh decls + val declarations = KExprUninterpretedDeclCollector.collectUninterpretedDeclarations(expr) + declarations.forEach { + val nativeDecl = z3InternCtx.findInternalizedDecl(it) + z3ConvertCtx.saveConvertedDecl(nativeDecl, it) + } + + val converted = with(KZ3ExprConverter(this, z3ConvertCtx)) { + internalized.convertExpr() + } + + return converted + } + + private fun KContext.internalizeAndConvertCvc5(nativeCtx: Solver, expr: KExpr): KExpr { + val internalizationCtx = KCvc5Context(nativeCtx, this) + val conversionCtx = KCvc5Context(nativeCtx, this) + + val internalizer = KCvc5ExprInternalizer(internalizationCtx) + val converter = KCvc5ExprConverter(this, conversionCtx) + + val internalized = with(internalizer) { + expr.internalizeExpr() + } + + // Copy declarations since we have fresh decls + val declarations = KExprUninterpretedDeclCollector.collectUninterpretedDeclarations(expr) + declarations.forEach { decl -> + internalizationCtx.findInternalizedDecl(decl)?.also { + conversionCtx.saveConvertedDecl(it, decl) + } + } + + val converted = with(converter) { + internalized.convertExpr() + } + + return converted + } + + private fun KContext.assertEquals( + stats: TestStats, + oracle: KSolver<*>, + expected: KExpr, + actual: KExpr + ) { + if (expected == actual) { + stats.passedFast() + return + } + + val satIsPossiblePassed = oracle.scoped { + assertPossibleToBeEqual(oracle, expected, actual) + oracle.checkSatAndReport(stats, expected, actual, KSolverStatus.SAT, "SAT is possible") + } + + val expressionEqualityPassed = oracle.scoped { + assertNotEqual(oracle, expected, actual) + + oracle.checkSatAndReport( + stats, expected, actual, KSolverStatus.UNSAT, "Expressions equal" + ) { + val model = oracle.model() + val actualValue = model.eval(actual, isComplete = false) + val expectedValue = model.eval(expected, isComplete = false) + + if (expectedValue == actualValue) { + stats.ignore(expected, actual, "Expressions equal: check incorrect") + return + } + } + } + + if (satIsPossiblePassed && expressionEqualityPassed) { + stats.passed() + } + } + + private fun KContext.assertNotEqual( + oracle: KSolver<*>, + expected: KExpr, + actual: KExpr + ) { + val sort = expected.sort + if (sort !is KArraySortBase<*>) { + oracle.assert(expected neq actual) + return + } + + val expectedArray: KExpr> = expected.uncheckedCast() + val actualArray: KExpr> = actual.uncheckedCast() + + // (exists (i) (/= (select expected i) (select actual i)) + val indices = sort.domainSorts.mapIndexed { i, srt -> mkFreshConst("i_${i}", srt) } + oracle.assert(mkSelect(expectedArray) { indices } neq mkSelect(actualArray) { indices }) + } + + private fun KContext.assertPossibleToBeEqual( + oracle: KSolver<*>, + expected: KExpr, + actual: KExpr + ) { + val sort = expected.sort + if (sort !is KArraySortBase<*>) { + oracle.assert(expected eq actual) + return + } + + val expectedArray: KExpr> = expected.uncheckedCast() + val actualArray: KExpr> = actual.uncheckedCast() + + // (exists (i) (= (select expected i) (select actual i)) + val indices = sort.domainSorts.mapIndexed { i, srt -> mkFreshConst("i_${i}", srt) } + oracle.assert(mkSelect(expectedArray) { indices } eq mkSelect(actualArray) { indices }) + } + + private inline fun KSolver<*>.checkSatAndReport( + stats: TestStats, + expected: KExpr<*>, + actual: KExpr<*>, + expectedStatus: KSolverStatus, + prefix: String, + onFailure: () -> Unit = {} + ): Boolean = when (check(CHECK_TIMEOUT)) { + expectedStatus -> true + KSolverStatus.UNKNOWN -> { + val message = "$prefix: ${reasonOfUnknown()}" + stats.ignore(expected, actual, message) + false + } + + else -> { + onFailure() + stats.fail(expected, actual, prefix) + false + } + } + + private inline fun KSolver<*>.scoped(block: () -> T): T { + push() + try { + return block() + } finally { + pop() + } + } + + private fun mkZ3Context(ctx: KContext): Context { + KZ3Solver(ctx).close() + return Context() + } + + private fun mkCvc5Context(ctx: KContext): Solver { + KCvc5Solver(ctx).close() + return Solver() + } + + private inline fun TestStats.withErrorHandling(body: () -> Unit) = try { + body() + } catch (ex: KSolverUnsupportedFeatureException) { + ignore(ex) + } catch (ex: Throwable) { + fail(ex) + } + + private data class TestCase( + val id: Int, + val message: String, + val expected: KExpr<*>?, + val actual: KExpr<*>? + ) + + private class TestStats( + private var total: Int = 0, + private var passed: Int = 0, + private var passedFast: Int = 0, + val ignored: MutableList = mutableListOf(), + val failed: MutableList = mutableListOf() + ) { + var testId = 0 + private set + + fun start() { + testId = total++ + } + + fun passed() { + passed++ + } + + fun passedFast() { + passedFast++ + passed() + } + + fun ignore(expected: KExpr<*>, actual: KExpr<*>, message: String) { + System.err.println("IGNORED ${testId}: $message") + ignored += TestCase(testId, message, expected, actual) + } + + fun fail(expected: KExpr<*>, actual: KExpr<*>, message: String) { + System.err.println("FAILED ${testId}: $message") + failed += TestCase(testId, message, expected, actual) + } + + fun fail(ex: Throwable) { + System.err.println("FAILED ${testId}: $ex") + failed += TestCase(testId, message = "$ex", expected = null, actual = null) + } + + fun ignore(ex: Throwable) { + System.err.println("IGNORED ${testId}: $ex") + ignored += TestCase(testId, message = "$ex", expected = null, actual = null) + } + + fun result() { + val stats = listOf( + "total=${total}", + "failed=${failed.size}", + "ignored=${ignored.size}", + "passed=${passed}", + "passedFast=${passedFast}" + ) + System.err.println("STATS: $stats") + + assertTrue(failed.isEmpty(), "Some tests failed") + } + } + + companion object { + private val CHECK_TIMEOUT = 10.seconds + + private val oracleManager = KSolverRunnerManager( + workerPoolSize = 2, + hardTimeout = 1.minutes + ) + + @AfterAll + @JvmStatic + fun cleanup() { + oracleManager.close() + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt new file mode 100644 index 000000000..a5d6be478 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/TestBvOverflowChecks.kt @@ -0,0 +1,390 @@ +package io.ksmt.test + +import io.ksmt.KContext +import io.ksmt.KContext.SimplificationMode.NO_SIMPLIFY +import io.ksmt.expr.KExpr +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KBoolSort +import io.ksmt.sort.KSort +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestBvOverflowChecks { + + @Test + fun testBvAddNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvAddNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvSubNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoOverflowExpr(l, r) + } + } + + @Test + fun testBvSubNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvSubNoUnderflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvMulNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvMulNoUnderflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvMulNoOverflowUnsignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvDivNoOverflowSignedZ3() = testBoolOperation({ KZ3Solver(it) }) { + bitwuzlaSampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvDivNoOverflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvAddNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvSubNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoOverflowExpr(l, r) + } + } + + @Test + fun testBvSubNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvSubNoUnderflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvMulNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvMulNoUnderflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvMulNoOverflowUnsignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvDivNoOverflowSignedBitwuzla() = testBoolOperation({ KBitwuzlaSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvDivNoOverflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvAddNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvSubNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoOverflowExpr(l, r) + } + } + + @Test + fun testBvSubNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvSubNoUnderflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvMulNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvMulNoUnderflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvMulNoOverflowUnsignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvDivNoOverflowSignedYices() = testBoolOperation({ KYicesSolver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvDivNoOverflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvAddNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvAddNoOverflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvAddNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvSubNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoOverflowExpr(l, r) + } + } + + @Test + fun testBvSubNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvSubNoUnderflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvSubNoUnderflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvMulNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = true) + } + } + + @Test + fun testBvMulNoUnderflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoUnderflowExpr(l, r) + } + } + + @Test + fun testBvMulNoOverflowUnsignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvMulNoOverflowExpr(l, r, isSigned = false) + } + } + + @Test + fun testBvDivNoOverflowSignedCvc() = testBoolOperation({ KCvc5Solver(it) }) { + z3SampleBinaryBoolExprValues(bv32Sort) { l, r -> + mkBvDivNoOverflowExpr(l, r) + } + } + + private fun KContext.z3SampleBinaryBoolExprValues( + sort: T, + mkExpr: KContext.(KExpr, KExpr) -> KExpr + ): List> = KZ3Solver(this).use { solver -> + sampleBinaryBoolExprValues(solver, sort) { l, r -> mkExpr(l, r) } + } + + private fun KContext.bitwuzlaSampleBinaryBoolExprValues( + sort: T, + mkExpr: KContext.(KExpr, KExpr) -> KExpr + ): List> = KBitwuzlaSolver(this).use { solver -> + sampleBinaryBoolExprValues(solver, sort) { l, r -> mkExpr(l, r) } + } + + private fun KContext.sampleBinaryBoolExprValues( + solver: KSolver<*>, + sort: T, + mkExpr: KContext.(KExpr, KExpr) -> KExpr + ): List> { + val positive = sampleBinaryExprValues(solver, sort) { l, r -> + mkExpr(l, r) + } + val negative = sampleBinaryExprValues(solver, sort) { l, r -> + mkExpr(l, r).not() + } + return positive + negative + } + + private fun KContext.sampleBinaryExprValues( + solver: KSolver<*>, + sort: T, + mkExpr: KContext.(KExpr, KExpr) -> KExpr + ): List> { + val lhs = mkFreshConst("lhs", sort) + val rhs = mkFreshConst("rhs", sort) + val expr = mkExpr(lhs, rhs) + val result = mkFreshConst("result", expr.sort) + val samples = arrayListOf>() + + solver.assert(result eq expr) + + while (solver.check() == KSolverStatus.SAT && samples.size < NUM_SAMPLES) { + val model = solver.model() + + val lhsValue = model.eval(lhs) + val rhsValue = model.eval(rhs) + val resultValue = model.eval(result) + samples += BinaryExprSample(mkExpr, lhsValue, rhsValue, resultValue) + + solver.assert((lhs neq lhsValue) or (rhs neq rhsValue)) + } + + return samples + } + + private fun testBoolOperation( + mkSolver: (KContext) -> KSolver<*>, + mkSamples: KContext.() -> List> + ) = with(KContext(simplificationMode = NO_SIMPLIFY)) { + val samples = mkSamples() + mkSolver(this).use { solver -> + samples.forEach { sample -> + testOperationSample(solver, sample) + } + } + } + + private fun KContext.testOperationSample( + solver: KSolver<*>, + sample: BinaryExprSample + ) = try { + solver.push() + val operationValue = sample.operation(this, sample.lhs, sample.rhs) + solver.assert(operationValue neq sample.result) + + val status = solver.check() + assertEquals(KSolverStatus.UNSAT, status) + } finally { + solver.pop() + } + + data class BinaryExprSample( + val operation: KContext.(KExpr, KExpr) -> KExpr, + val lhs: KExpr, + val rhs: KExpr, + val result: KExpr + ) + + companion object { + private const val NUM_SAMPLES = 10 + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt new file mode 100644 index 000000000..e516de726 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/UninterpretedSortValuesTest.kt @@ -0,0 +1,353 @@ +package io.ksmt.test + +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.runner.KSolverRunnerManager +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KArraySort +import io.ksmt.sort.KBoolSort +import io.ksmt.sort.KSort +import io.ksmt.sort.KUninterpretedSort +import io.ksmt.utils.uncheckedCast +import kotlin.random.Random +import kotlin.random.nextInt +import kotlin.reflect.KClass +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.seconds + +@EnabledIfEnvironmentVariable( + named = "runUninterpretedSortValuesTest", + matches = "true", + disabledReason = "Not suitable for usual CI runs" +) +class UninterpretedSortValuesTest { + + @Test + fun test(): Unit = with(KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)) { + val testData = generateTestData() + KSolverRunnerManager(workerPoolSize = 3).use { solverManager -> + solverManager.createSolver(this, KZ3Solver::class).use { oracleSover -> + val z3Results = solverManager.testSolver(this, KZ3Solver::class, testData) + validateResults(oracleSover, z3Results) + + val yicesResults = solverManager.testSolver(this, KYicesSolver::class, testData) + validateResults(oracleSover, yicesResults) + + val bitwuzlaResults = solverManager.testSolver(this, KBitwuzlaSolver::class, testData) + validateResults(oracleSover, bitwuzlaResults) + + val cvcResults = solverManager.testSolver(this, KCvc5Solver::class, testData) + validateResults(oracleSover, cvcResults) + } + } + } + + @Test + fun testIncremental(): Unit = with(KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)) { + val testData = generateTestData() + KSolverRunnerManager(workerPoolSize = 3).use { solverManager -> + solverManager.createSolver(this, KZ3Solver::class).use { oracleSover -> + val z3Results = solverManager.testSolverIncremental(this, KZ3Solver::class, testData) + validateResults(oracleSover, z3Results) + + val yicesResults = solverManager.testSolverIncremental(this, KYicesSolver::class, testData) + validateResults(oracleSover, yicesResults) + + val bitwuzlaResults = solverManager.testSolverIncremental(this, KBitwuzlaSolver::class, testData) + validateResults(oracleSover, bitwuzlaResults) + + val cvcResults = solverManager.testSolverIncremental(this, KCvc5Solver::class, testData) + validateResults(oracleSover, cvcResults) + } + } + } + + private fun KSolverRunnerManager.testSolver( + ctx: KContext, + solverType: KClass>, + data: List> + ) = createSolver(ctx, solverType).use { solver -> + val results = arrayListOf() + + for ((i, sample) in data.withIndex()) { + solver.push() + + solver.assert(sample) + val status = solver.check(SINGLE_CHECK_TIMEOUT) + + val model = if (status == KSolverStatus.SAT) solver.model() else null + + results += TestResult( + testId = i, + solverName = solverType.simpleName!!, + testSample = sample, + status = status, + model = model?.detach() + ) + + solver.pop() + } + + results + } + + private data class SolverTestDataSample( + val index: Int, + val expr: KExpr, + val variable: KExpr + ) + + private fun KSolverRunnerManager.testSolverIncremental( + ctx: KContext, + solverType: KClass>, + data: List> + ) = createSolver(ctx, solverType).use { solver -> + val results = arrayListOf() + + val chunks = data.mapIndexed { index, sample -> + val variable = ctx.mkFreshConst("t:$index", ctx.boolSort) + SolverTestDataSample(index, sample, variable) + }.chunked(TEST_SET_INCREMENTAL_CHUNK) + + ctx.pushAssertChunk(solver, chunks.first()) + + for (chunk in chunks.drop(1)) { + ctx.pushAssertChunk(solver, chunk) + popCheckSatChunk(solver, solverType.simpleName!!, chunk, results) + } + + popCheckSatChunk(solver, solverType.simpleName!!, chunks.first(), results) + + results + } + + private fun KContext.pushAssertChunk( + solver: KSolver<*>, + chunk: List + ) { + for (sample in chunk) { + solver.push() + solver.assert(sample.variable implies sample.expr) + } + } + + private fun popCheckSatChunk( + solver: KSolver<*>, + solverName: String, + chunk: List, + results: MutableList + ) { + for (sample in chunk.asReversed()) { + solver.push() + solver.assert(sample.variable) + val status = solver.check(SINGLE_CHECK_TIMEOUT) + + val model = if (status == KSolverStatus.SAT) solver.model() else null + + results += TestResult( + testId = sample.index, + solverName = solverName, + testSample = sample.expr, + status = status, + model = model?.detach() + ) + + solver.pop() + + // pop sample assertion + solver.pop() + } + } + + private fun KContext.validateResults(oracle: KSolver<*>, results: List) { + validateResultStatus(results) + + for (res in results) { + if (res.model == null) continue + validateModel(oracle, res, res.model) + } + } + + private fun KContext.validateModel(oracle: KSolver<*>, result: TestResult, model: KModel) { + val actualValue = model.eval(result.testSample, isComplete = false) + if (actualValue == trueExpr) return + + oracle.push() + + model.uninterpretedSorts.forEach { sort -> + val universe = model.uninterpretedSortUniverse(sort) + if (!universe.isNullOrEmpty()) { + val x = mkFreshConst("x", sort) + val constraint = mkOr(universe.map { x eq it }) + + oracle.assert(mkUniversalQuantifier(constraint, listOf(x.decl))) + } + } + + oracle.assert(actualValue neq trueExpr) + + val status = oracle.check() + assertEquals(KSolverStatus.UNSAT, status, result.solverName) + + oracle.pop() + } + + private fun validateResultStatus(results: List) { + val statuses = results + .groupBy({ it.testId }, { it.status }) + .mapValues { (_, status) -> status.filterNot { it == KSolverStatus.UNKNOWN }.toSet() } + + assertTrue(statuses.all { it.value.size <= 1 }, results.first().solverName) + } + + private fun KContext.generateTestData(): List> { + val sorts = (0 until NUMBER_OF_UNINTERPRETED_SORTS).map { + mkUninterpretedSort("T${it}") + } + val generationContext = GenerationContext(this, sorts) + return (0 until TEST_SET_SIZE).map { + generationContext.generate() + } + } + + private class GenerationContext( + val ctx: KContext, + val sorts: List + ) { + private val random = Random(RANDOM_SEED) + + private val sortMapOperations = sorts.associateWith { s1 -> + sorts.associateWith { s2 -> + ctx.mkFuncDecl("${s1}_${s2}", s2, listOf(s1)) + } + } + + private val basicExpressions = sorts.associateWithTo(hashMapOf>>()) { + arrayListOf(ctx.mkFreshConst("x", it)) + } + + private val expressions = hashMapOf>>() + + private fun newExpr() = when (random.nextDouble()) { + in 0.0..0.1 -> newArray() + in 0.1..0.3 -> newVar() + else -> newValue() + } + + private fun newArray() = with(ctx) { + val sort = mkArraySort(sorts.random(random), sorts.random(random)) + mkFreshConst("array", sort) + } + + private fun newVar() = with(ctx) { + mkFreshConst("v", sorts.random(random)) + } + + private fun newValue() = with(ctx) { + mkUninterpretedSortValue(sorts.random(random), random.nextInt(USORT_VALUE_RANGE)) + } + + private fun findExpr(sort: KSort): KExpr { + if (random.nextDouble() > 0.5) { + newExpr().also { + basicExpressions.getOrPut(it.sort) { arrayListOf() }.add(it) + } + } + + val expressionSet = when (random.nextDouble()) { + in 0.0..0.4 -> basicExpressions + else -> expressions + } + + val variants = expressionSet.getOrPut(sort) { + arrayListOf(ctx.mkFreshConst("stub", sort)) + } + + return variants.random(random).uncheckedCast() + } + + fun generate(): KExpr { + expressions.clear() + + val first = generateDeepExpr() + val second = generateDeepExpr() + + val transformer = sortMapOperations[first.sort]?.get(second.sort) + ?: error("Unexpected sorts: ${first.sort}, ${second.sort}") + val firstAsSecond = transformer.apply(listOf(first)) + + return ctx.mkEq(firstAsSecond, second.uncheckedCast()) + } + + private fun generateDeepExpr(): KExpr { + var current = findExpr(sorts.random(random)) + repeat(EXPRESSION_DEPTH) { + current = if (current.sort is KArraySort<*, *>) { + generateArrayOperation(current.uncheckedCast()) + } else { + generateOperation(current) + } + expressions.getOrPut(current.sort) { arrayListOf() }.add(current) + } + + val sort = current.sort + if (sort is KArraySort<*, *>) { + current = ctx.mkArraySelect(current.uncheckedCast(), findExpr(sort.domain)) + } + + return current + } + + private fun generateArrayOperation(expr: KExpr>): KExpr = with(ctx) { + when (random.nextDouble()) { + in 0.0..0.3 -> mkArraySelect(expr, findExpr(expr.sort.domain)) + else -> mkArrayStore(expr, findExpr(expr.sort.domain), findExpr(expr.sort.range)).uncheckedCast() + } + } + + private fun generateOperation(expr: KExpr): KExpr = with(ctx) { + when (random.nextDouble()) { + in 0.0..0.5 -> { + val otherSort = sorts.random(random) + val transformer = sortMapOperations[expr.sort]?.get(otherSort) + ?: error("Unexpected expr sort: ${expr.sort}") + transformer.apply(listOf(expr)).uncheckedCast() + } + + else -> { + val sort = mkArraySort(expr.sort, sorts.random(random)) + val array: KExpr> = findExpr(sort).uncheckedCast() + mkArrayStore(array, expr, findExpr(sort.range)).uncheckedCast() + } + } + } + } + + private data class TestResult( + val testId: Int, + val solverName: String, + val testSample: KExpr, + val status: KSolverStatus, + val model: KModel? + ) + + companion object { + private const val NUMBER_OF_UNINTERPRETED_SORTS = 3 + private const val RANDOM_SEED = 42 + private const val EXPRESSION_DEPTH = 30 + private const val TEST_SET_SIZE = 500 + private const val TEST_SET_INCREMENTAL_CHUNK = TEST_SET_SIZE / 50 + private val USORT_VALUE_RANGE = 0 until 25 + private val SINGLE_CHECK_TIMEOUT = 1.seconds + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt new file mode 100644 index 000000000..7e1831f60 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BenchmarksBasedTest.kt @@ -0,0 +1,716 @@ +package io.ksmt.test.benchmarks + +import com.jetbrains.rd.util.reactive.RdFault +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assumptions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.params.provider.Arguments +import io.ksmt.KContext +import io.ksmt.expr.KApp +import io.ksmt.expr.KDivArithExpr +import io.ksmt.expr.KExpr +import io.ksmt.expr.KFpMaxExpr +import io.ksmt.expr.KFpMinExpr +import io.ksmt.expr.KFpToBvExpr +import io.ksmt.expr.KFpToRealExpr +import io.ksmt.expr.KFpValue +import io.ksmt.expr.KFunctionAsArray +import io.ksmt.expr.KModIntExpr +import io.ksmt.expr.KPowerArithExpr +import io.ksmt.expr.KRemIntExpr +import io.ksmt.expr.transformer.KNonRecursiveTransformer +import io.ksmt.expr.transformer.KTransformer +import io.ksmt.runner.core.KsmtWorkerArgs +import io.ksmt.runner.core.KsmtWorkerFactory +import io.ksmt.runner.core.KsmtWorkerPool +import io.ksmt.runner.core.RdServer +import io.ksmt.runner.core.WorkerInitializationFailedException +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverException +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverUnsupportedFeatureException +import io.ksmt.solver.async.KAsyncSolver +import io.ksmt.solver.runner.KSolverRunnerManager +import io.ksmt.sort.KArithSort +import io.ksmt.sort.KArraySortBase +import io.ksmt.sort.KBoolSort +import io.ksmt.sort.KBvSort +import io.ksmt.sort.KFpSort +import io.ksmt.sort.KIntSort +import io.ksmt.sort.KRealSort +import io.ksmt.sort.KSort +import io.ksmt.test.SmtLibParseError +import io.ksmt.test.TestRunner +import io.ksmt.test.TestWorker +import io.ksmt.test.TestWorkerProcess +import io.ksmt.utils.FpUtils.isZero +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.Path +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.relativeTo +import kotlin.reflect.KClass +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +@Suppress("UnnecessaryAbstractClass") +abstract class BenchmarksBasedTest { + + fun testConverter( + name: String, + samplePath: Path, + mkKsmtAssertions: suspend TestRunner.(List>) -> List> + ) = handleIgnoredTests("testConverter[$name]") { + ignoreNoTestDataStub(name) + val ctx = KContext() + testWorkers.withWorker(ctx) { worker -> + worker.skipBadTestCases { + val assertions = worker.parseFile(samplePath) + val convertedAssertions = worker.convertAssertions(assertions) + + val ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + + ksmtAssertions.forEach { SortChecker(ctx).apply(it) } + + worker.performEqualityChecks { + for ((originalZ3Expr, ksmtExpr) in assertions.zip(ksmtAssertions)) { + areEqual(actual = ksmtExpr, expected = originalZ3Expr) + } + check { "expressions are not equal" } + } + } + } + } + + fun testModelConversion( + name: String, + samplePath: Path, + solverType: KClass> + ) = testModelConversion(name, samplePath) { ctx -> + solverManager.createSolver(ctx, solverType) + } + + fun testModelConversion( + name: String, + samplePath: Path, + solverProvider: (KContext) -> KAsyncSolver + ) = handleIgnoredTests("testModelConversion[$name]") { + ignoreNoTestDataStub(name) + val ctx = KContext() + testWorkers.withWorker(ctx) { worker -> + worker.skipBadTestCases { + val assertions = worker.parseFile(samplePath) + val ksmtAssertions = worker.convertAssertions(assertions) + + val model = solverProvider(ctx).use { testSolver -> + ksmtAssertions.forEach { testSolver.assertAsync(it) } + + val status = testSolver.checkAsync(SOLVER_CHECK_SAT_TIMEOUT) + if (status != KSolverStatus.SAT) { + ignoreTest { "No model to check" } + } + + testSolver.modelAsync() + } + + checkAsArrayDeclsPresentInModel(ctx, model) + + val evaluatedAssertions = ksmtAssertions.map { model.eval(it, isComplete = true) } + + val cardinalityConstraints = model.uninterpretedSorts.mapNotNull { sort -> + model.uninterpretedSortUniverse(sort)?.takeIf { it.isNotEmpty() }?.let { universe -> + with(ctx) { + val x = mkFreshConst("x", sort) + val variants = mkOr(universe.map { x eq it }) + mkUniversalQuantifier(variants, listOf(x.decl)) + } + } + } + + /** + * Evaluated assertion may contain some underspecified + * operations (e.g division by zero). Currently used test oracle (Z3) + * can define any interpretation for the underspecified operations + * and therefore (not (= a true)) check will always be SAT. + * We consider such cases as false positives. + * */ + val assertionsToCheck = evaluatedAssertions.filterNot { hasUnderspecifiedOperations(it) } + + // Samples are false UNSAT in Z3 because of incorrect FMA eval + if (name in KnownZ3Issues.z3FpFmaFalseUnsatSamples) { + ignoreTest { "Example is known to be false UNSAT in Z3 test oracle" } + } + + worker.performEqualityChecks { + cardinalityConstraints.forEach { assume(it) } + assertionsToCheck.forEach { isTrue(it) } + check { "assertions are not true in model" } + } + } + } + } + + fun testSolver( + name: String, + samplePath: Path, + solverType: KClass> + ) = testSolver(name, samplePath) { ctx -> + solverManager.createSolver(ctx, solverType) + } + + fun testSolver( + name: String, + samplePath: Path, + solverProvider: (KContext) -> KAsyncSolver + ) = handleIgnoredTests("testSolver[$name]") { + ignoreNoTestDataStub(name) + val ctx = KContext() + testWorkers.withWorker(ctx) { worker -> + worker.skipBadTestCases { + val assertions = worker.parseFile(samplePath) + val solver = worker.createSolver(TEST_WORKER_CHECK_SAT_TIMEOUT) + assertions.forEach { + worker.assert(solver, it) + } + + var expectedStatus = worker.check(solver) + + // Fix known Z3 satisfiability issues + if (expectedStatus == KSolverStatus.UNSAT && name in KnownZ3Issues.z3FpFmaFalseUnsatSamples) { + expectedStatus = KSolverStatus.SAT + } + if (expectedStatus == KSolverStatus.SAT && name in KnownZ3Issues.z3FpFmaFalseSatSamples) { + expectedStatus = KSolverStatus.UNSAT + } + + if (expectedStatus == KSolverStatus.UNKNOWN) { + ignoreTest { "Expected status is unknown" } + } + + val ksmtAssertions = worker.convertAssertions(assertions) + + val actualStatus = solverProvider(ctx).use { ksmtSolver -> + ksmtAssertions.forEach { ksmtSolver.assertAsync(it) } + + // use greater timeout to reduce false-positive unknowns + val status = ksmtSolver.check(SOLVER_CHECK_SAT_TIMEOUT) + if (status == KSolverStatus.UNKNOWN) { + ignoreTest { "Actual status is unknown: ${ksmtSolver.reasonOfUnknown()}" } + } + + status + } + assertEquals(expectedStatus, actualStatus, "solver check-sat mismatch") + } + } + } + + internal fun KsmtWorkerPool.withWorker( + ctx: KContext, + body: suspend (TestRunner) -> Unit + ) = runBlocking { + val worker = try { + getOrCreateFreeWorker() + } catch (ex: WorkerInitializationFailedException) { + ignoreTest { "worker initialization failed -- ${ex.message}" } + } + worker.astSerializationCtx.initCtx(ctx) + worker.lifetime.onTermination { + worker.astSerializationCtx.resetCtx() + } + try { + TestRunner(ctx, TEST_WORKER_SINGLE_OPERATION_TIMEOUT, worker).let { + try { + it.init() + body(it) + } finally { + it.delete() + } + } + } catch (ex: TimeoutCancellationException) { + ignoreTest { "worker timeout -- ${ex.message}" } + } finally { + worker.release() + } + } + + /** + * Check if expression contains underspecified operations: + * 1. division by zero + * 2. integer mod/rem with zero divisor + * 3. zero to the zero power + * 4. Fp to number conversions with NaN and Inf + * 5. Fp max/min with +/- zero + * */ + private fun hasUnderspecifiedOperations(expr: KExpr<*>): Boolean { + val detector = UnderspecifiedOperationDetector(expr.ctx) + detector.apply(expr) + return detector.hasUnderspecifiedOperation + } + + private class UnderspecifiedOperationDetector(ctx: KContext) : KNonRecursiveTransformer(ctx) { + var hasUnderspecifiedOperation = false + + override fun transform(expr: KDivArithExpr): KExpr = + super.transform(expr).also { checkDivisionByZero(expr.rhs) } + + override fun transform(expr: KModIntExpr): KExpr = + super.transform(expr).also { checkDivisionByZero(expr.rhs) } + + override fun transform(expr: KRemIntExpr): KExpr = + super.transform(expr).also { checkDivisionByZero(expr.rhs) } + + override fun transform(expr: KPowerArithExpr): KExpr = + super.transform(expr).also { checkZeroToZeroPower(expr.lhs, expr.rhs) } + + override fun transform(expr: KFpToBvExpr): KExpr = + super.transform(expr).also { checkFpNaNOrInf(expr.value) } + + override fun transform(expr: KFpToRealExpr): KExpr = + super.transform(expr).also { checkFpNaNOrInf(expr.value) } + + override fun transform(expr: KFpMinExpr): KExpr = + super.transform(expr).also { checkFpZeroWithDifferentSign(expr.arg0, expr.arg1) } + + override fun transform(expr: KFpMaxExpr): KExpr = + super.transform(expr).also { checkFpZeroWithDifferentSign(expr.arg0, expr.arg1) } + + private fun checkDivisionByZero(divisor: KExpr<*>) = with(ctx) { + if (divisor == 0.expr) { + hasUnderspecifiedOperation = true + } + } + + private fun checkZeroToZeroPower(base: KExpr<*>, power: KExpr<*>) = with(ctx) { + if (base == 0.expr && power == 0.expr) { + hasUnderspecifiedOperation = true + } + } + + private fun checkFpNaNOrInf(value: KExpr) = with(ctx) { + val underspecifiedValues = setOf( + mkFpNaN(value.sort), + mkFpInf(signBit = true, value.sort), + mkFpInf(signBit = false, value.sort), + ) + if (value in underspecifiedValues) { + hasUnderspecifiedOperation = true + } + } + + private fun checkFpZeroWithDifferentSign(lhs: KExpr, rhs: KExpr) { + if (lhs !is KFpValue || rhs !is KFpValue) return + if (lhs.isZero() && rhs.isZero() && lhs.signBit != rhs.signBit) { + hasUnderspecifiedOperation = true + } + } + } + + companion object { + val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 10.seconds + val TEST_WORKER_EQUALITY_CHECK_TIMEOUT = 3.seconds + val TEST_WORKER_CHECK_SAT_TIMEOUT = 1.seconds + + val SOLVER_SINGLE_OPERATION_TIMEOUT = 10.seconds + val SOLVER_CHECK_SAT_TIMEOUT = 3.seconds + + internal lateinit var solverManager: KSolverRunnerManager + internal lateinit var testWorkers: KsmtWorkerPool + + private val testDataChunkSize = System.getenv("benchmarkChunkMaxSize")?.toIntOrNull() ?: Int.MAX_VALUE + private val testDataChunk = System.getenv("benchmarkChunk")?.toIntOrNull() ?: 0 + + private val NO_TEST_DATA = BenchmarkTestArguments("__NO__TEST__DATA__", Path(".")) + + private fun testDataLocation(): Path = this::class.java.classLoader + .getResource("testData") + ?.toURI() + ?.let { Paths.get(it) } + ?: error("No test data") + + private fun prepareTestData(): List { + val testDataLocation = testDataLocation() + return testDataLocation + .listDirectoryEntries("*.smt2") + .sorted() + .drop(testDataChunk * testDataChunkSize) + .take(testDataChunkSize) + .map { BenchmarkTestArguments(it.relativeTo(testDataLocation).toString(), it) } + .skipBadTestCases() + .ensureNotEmpty() + } + + val testData by lazy { + prepareTestData() + } + + /** + * Parametrized tests require at least one argument. + * In some cases, we may filter out all provided test samples, + * which will cause JUnit failure. To overcome this problem, + * we use [NO_TEST_DATA] stub, which is handled + * by [ignoreNoTestDataStub] and results in a single ignored test. + * */ + fun List.ensureNotEmpty() = ifEmpty { listOf(NO_TEST_DATA) } + + fun ignoreNoTestDataStub(name: String) { + Assumptions.assumeTrue(name != NO_TEST_DATA.name) + } + + private fun List.skipBadTestCases(): List = + /** + * Contains a declaration with an empty name. + * Normally, such declarations have special name in Z3, + * but in this case it is not true. After internalization via API, + * resulting declaration has name as excepted. + * Therefore, declarations are not equal, but this is not our issue. + * */ + filterNot { it.name == "QF_BV_symbols.smt2" } + + @BeforeAll + @JvmStatic + fun initWorkerPools() { + solverManager = KSolverRunnerManager( + workerPoolSize = 4, + hardTimeout = SOLVER_SINGLE_OPERATION_TIMEOUT, + workerProcessIdleTimeout = 10.minutes + ) + testWorkers = KsmtWorkerPool( + maxWorkerPoolSize = 4, + workerProcessIdleTimeout = 10.minutes, + workerFactory = object : KsmtWorkerFactory { + override val childProcessEntrypoint = TestWorkerProcess::class + override fun updateArgs(args: KsmtWorkerArgs): KsmtWorkerArgs = args + override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) + } + ) + } + + @AfterAll + @JvmStatic + fun closeWorkerPools() { + solverManager.close() + testWorkers.terminate() + } + + // See [handleIgnoredTests] + inline fun ignoreTest(message: () -> String?): Nothing { + throw IgnoreTestException(message()) + } + } + + data class BenchmarkTestArguments( + val name: String, + val samplePath: Path + ) : Arguments { + override fun get() = arrayOf(name, samplePath) + } + + fun checkAsArrayDeclsPresentInModel(ctx: KContext, model: KModel) { + val checker = AsArrayDeclChecker(ctx, model) + model.declarations.forEach { decl -> + model.interpretation(decl)?.let { interpretation -> + interpretation.entries.forEach { it.value.accept(checker) } + interpretation.default?.accept(checker) + } + } + } + + class AsArrayDeclChecker(override val ctx: KContext, val model: KModel) : KTransformer { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { + assertNotNull(model.interpretation(expr.function), "no interpretation for as-array: $expr") + return expr + } + } + + class SortChecker(ctx: KContext) : KNonRecursiveTransformer(ctx) { + override fun transformApp(expr: KApp): KExpr = with(ctx) { + // apply internally check arguments sorts + expr.decl.apply(expr.args) + return super.transformApp(expr).also { + check(it.sort == expr.sort) { "sort mismatch" } + } + } + } + + suspend fun TestRunner.performEqualityChecks(checks: suspend EqualityChecker.() -> Unit) { + val solver = createSolver(TEST_WORKER_EQUALITY_CHECK_TIMEOUT) + val checker = EqualityChecker(this, solver) + checker.checks() + } + + private data class EqualityCheck(val actual: KExpr<*>, val expected: Long) + + class EqualityChecker( + private val worker: TestRunner, + private val solver: Int, + ) { + private val equalityChecks = mutableListOf() + private val workerTrueExpr: Long by lazy { runBlocking { worker.mkTrueExpr() } } + + suspend fun areEqual(actual: KExpr<*>, expected: Long) { + worker.addEqualityCheck(solver, actual, expected) + equalityChecks += EqualityCheck(actual, expected) + } + + suspend fun isTrue(actual: KExpr<*>) = areEqual(actual, workerTrueExpr) + + suspend fun assume(expr: KExpr) { + worker.addEqualityCheckAssumption(solver, expr) + } + + suspend fun check(message: () -> String) { + val status = worker.checkEqualities(solver) + when (status) { + KSolverStatus.UNSAT -> return + KSolverStatus.SAT -> { + val failedEqualityCheck = worker.findFirstFailedEquality(solver)?.let { equalityChecks[it] } + if (failedEqualityCheck != null) { + val expected = worker.exprToString(failedEqualityCheck.expected) + assertEquals(expected, "${failedEqualityCheck.actual}", message()) + } + assertTrue(false, message()) + } + + KSolverStatus.UNKNOWN -> ignoreTest { + "equality check: unknown -- ${worker.getReasonUnknown(solver)}" + } + } + } + } + + inline fun TestRunner.skipBadTestCases(body: () -> Unit) = try { + skipUnsupportedSolverFeatures { + body() + } + } catch (ex: SmtLibParseError) { + ignoreTest { "parse failed -- ${ex.message}" } + } catch (ex: TimeoutCancellationException) { + ignoreTest { "timeout -- ${ex.message}" } + } + + inline fun TestRunner.skipUnsupportedSolverFeatures(body: () -> Unit) = try { + handleWrappedSolverException { + body() + } + } catch (ex: NotImplementedError) { + // skip test with not implemented feature + ignoreTest { + val reducedStackTrace = ex.stackTrace.take(5).joinToString("\n") { it.toString() } + "${ex.message}\n$reducedStackTrace" + } + } catch (ex: KSolverUnsupportedFeatureException) { + ignoreTest { ex.message } + } + + inline fun TestRunner.handleWrappedSolverException(body: () -> T): T = try { + body() + } catch (ex: KSolverException) { + val unwrappedException = when (val cause = ex.cause) { + is RdFault -> rdExceptionCause(cause) ?: ex + is TimeoutCancellationException -> cause + else -> ex + } + throw unwrappedException + } + + class IgnoreTestException(message: String?) : Exception(message) + + /** + * When a test is ignored via JUnit assumption the reason (message) + * of the ignore is not shown in the test report. + * To keep some insight on the ignore reasons we use + * logging to stderr, since it is present in test reports. + * */ + fun handleIgnoredTests(testName: String, testBody: () -> Unit) { + try { + testBody() + } catch (ignore: IgnoreTestException) { + val testClassName = javaClass.canonicalName + System.err.println("IGNORE $testClassName.$testName: ${ignore.message}") + Assumptions.assumeTrue(false) + } + } + + object KnownZ3Issues { + /** + * These samples are known to be SAT according to the annotation + * in the source and according to the previous versions of Z3 (e.g. 4.8.15). + * Currently used z3 4.11.2 treat these samples as UNSAT. + * + * Todo: remove when this issue will be fixed in Z3. + * */ + val z3FpFmaFalseUnsatSamples = setOf( + "QF_FP_fma-has-solution-10232.smt2", + "QF_FP_fma-has-solution-10256.smt2", + "QF_FP_fma-has-solution-10601.smt2", + "QF_FP_fma-has-solution-10792.smt2", + "QF_FP_fma-has-solution-10834.smt2", + "QF_FP_fma-has-solution-10856.smt2", + "QF_FP_fma-has-solution-10867.smt2", + "QF_FP_fma-has-solution-10998.smt2", + "QF_FP_fma-has-solution-11152.smt2", + "QF_FP_fma-has-solution-11193.smt2", + "QF_FP_fma-has-solution-11245.smt2", + "QF_FP_fma-has-solution-11482.smt2", + "QF_FP_fma-has-solution-11503.smt2", + "QF_FP_fma-has-solution-12238.smt2", + "QF_FP_fma-has-solution-12329.smt2", + "QF_FP_fma-has-solution-1247.smt2", + "QF_FP_fma-has-solution-12600.smt2", + "QF_FP_fma-has-solution-12639.smt2", + "QF_FP_fma-has-solution-12682.smt2", + "QF_FP_fma-has-solution-12789.smt2", + "QF_FP_fma-has-solution-12840.smt2", + "QF_FP_fma-has-solution-12969.smt2", + "QF_FP_fma-has-solution-1325.smt2", + "QF_FP_fma-has-solution-13421.smt2", + "QF_FP_fma-has-solution-13786.smt2", + "QF_FP_fma-has-solution-14111.smt2", + "QF_FP_fma-has-solution-14346.smt2", + "QF_FP_fma-has-solution-14535.smt2", + "QF_FP_fma-has-solution-14613.smt2", + "QF_FP_fma-has-solution-14742.smt2", + "QF_FP_fma-has-solution-14799.smt2", + "QF_FP_fma-has-solution-14835.smt2", + "QF_FP_fma-has-solution-154.smt2", + "QF_FP_fma-has-solution-15774.smt2", + "QF_FP_fma-has-solution-15798.smt2", + "QF_FP_fma-has-solution-15963.smt2", + "QF_FP_fma-has-solution-15995.smt2", + "QF_FP_fma-has-solution-17127.smt2", + "QF_FP_fma-has-solution-17650.smt2", + "QF_FP_fma-has-solution-17915.smt2", + "QF_FP_fma-has-solution-17959.smt2", + "QF_FP_fma-has-solution-1809.smt2", + "QF_FP_fma-has-solution-18220.smt2", + "QF_FP_fma-has-solution-18700.smt2", + "QF_FP_fma-has-solution-19191.smt2", + "QF_FP_fma-has-solution-19593.smt2", + "QF_FP_fma-has-solution-2988.smt2", + "QF_FP_fma-has-solution-3042.smt2", + "QF_FP_fma-has-solution-3742.smt2", + "QF_FP_fma-has-solution-4281.smt2", + "QF_FP_fma-has-solution-457.smt2", + "QF_FP_fma-has-solution-4615.smt2", + "QF_FP_fma-has-solution-4981.smt2", + "QF_FP_fma-has-solution-4983.smt2", + "QF_FP_fma-has-solution-5056.smt2", + "QF_FP_fma-has-solution-5127.smt2", + "QF_FP_fma-has-solution-5213.smt2", + "QF_FP_fma-has-solution-5986.smt2", + "QF_FP_fma-has-solution-6211.smt2", + "QF_FP_fma-has-solution-6468.smt2", + "QF_FP_fma-has-solution-6573.smt2", + "QF_FP_fma-has-solution-6673.smt2", + "QF_FP_fma-has-solution-6822.smt2", + "QF_FP_fma-has-solution-7580.smt2", + "QF_FP_fma-has-solution-7736.smt2", + "QF_FP_fma-has-solution-7832.smt2", + "QF_FP_fma-has-solution-7920.smt2", + "QF_FP_fma-has-solution-80.smt2", + "QF_FP_fma-has-solution-8278.smt2", + "QF_FP_fma-has-solution-8475.smt2", + "QF_FP_fma-has-solution-8483.smt2", + "QF_FP_fma-has-solution-9132.smt2", + "QF_FP_fma-has-solution-9188.smt2", + "QF_FP_fma-has-solution-9455.smt2", + "QF_FP_fma-has-solution-9467.smt2", + "QF_FP_fma-has-solution-9517.smt2", + ) + + /** + * These samples are known to be UNSAT according to the annotation + * in the source and according to the previous versions of Z3 (e.g. 4.8.15). + * Currently used z3 4.11.2 treat these samples as SAT. + * + * Todo: remove when this issue will be fixed in Z3. + * */ + val z3FpFmaFalseSatSamples = setOf( + "QF_FP_fma-has-no-other-solution-10232.smt2", + "QF_FP_fma-has-no-other-solution-10256.smt2", + "QF_FP_fma-has-no-other-solution-10601.smt2", + "QF_FP_fma-has-no-other-solution-10856.smt2", + "QF_FP_fma-has-no-other-solution-10834.smt2", + "QF_FP_fma-has-no-other-solution-10792.smt2", + "QF_FP_fma-has-no-other-solution-10867.smt2", + "QF_FP_fma-has-no-other-solution-10998.smt2", + "QF_FP_fma-has-no-other-solution-11152.smt2", + "QF_FP_fma-has-no-other-solution-11193.smt2", + "QF_FP_fma-has-no-other-solution-11245.smt2", + "QF_FP_fma-has-no-other-solution-11482.smt2", + "QF_FP_fma-has-no-other-solution-11503.smt2", + "QF_FP_fma-has-no-other-solution-12238.smt2", + "QF_FP_fma-has-no-other-solution-12329.smt2", + "QF_FP_fma-has-no-other-solution-1247.smt2", + "QF_FP_fma-has-no-other-solution-12639.smt2", + "QF_FP_fma-has-no-other-solution-12600.smt2", + "QF_FP_fma-has-no-other-solution-12682.smt2", + "QF_FP_fma-has-no-other-solution-12789.smt2", + "QF_FP_fma-has-no-other-solution-12840.smt2", + "QF_FP_fma-has-no-other-solution-12969.smt2", + "QF_FP_fma-has-no-other-solution-1325.smt2", + "QF_FP_fma-has-no-other-solution-13421.smt2", + "QF_FP_fma-has-no-other-solution-13786.smt2", + "QF_FP_fma-has-no-other-solution-14111.smt2", + "QF_FP_fma-has-no-other-solution-14346.smt2", + "QF_FP_fma-has-no-other-solution-14613.smt2", + "QF_FP_fma-has-no-other-solution-14535.smt2", + "QF_FP_fma-has-no-other-solution-14742.smt2", + "QF_FP_fma-has-no-other-solution-14835.smt2", + "QF_FP_fma-has-no-other-solution-14799.smt2", + "QF_FP_fma-has-no-other-solution-154.smt2", + "QF_FP_fma-has-no-other-solution-15774.smt2", + "QF_FP_fma-has-no-other-solution-15798.smt2", + "QF_FP_fma-has-no-other-solution-15963.smt2", + "QF_FP_fma-has-no-other-solution-15995.smt2", + "QF_FP_fma-has-no-other-solution-17127.smt2", + "QF_FP_fma-has-no-other-solution-17650.smt2", + "QF_FP_fma-has-no-other-solution-17915.smt2", + "QF_FP_fma-has-no-other-solution-17959.smt2", + "QF_FP_fma-has-no-other-solution-1809.smt2", + "QF_FP_fma-has-no-other-solution-18220.smt2", + "QF_FP_fma-has-no-other-solution-18700.smt2", + "QF_FP_fma-has-no-other-solution-19191.smt2", + "QF_FP_fma-has-no-other-solution-19593.smt2", + "QF_FP_fma-has-no-other-solution-2988.smt2", + "QF_FP_fma-has-no-other-solution-3042.smt2", + "QF_FP_fma-has-no-other-solution-3742.smt2", + "QF_FP_fma-has-no-other-solution-4281.smt2", + "QF_FP_fma-has-no-other-solution-457.smt2", + "QF_FP_fma-has-no-other-solution-4615.smt2", + "QF_FP_fma-has-no-other-solution-4981.smt2", + "QF_FP_fma-has-no-other-solution-5056.smt2", + "QF_FP_fma-has-no-other-solution-4983.smt2", + "QF_FP_fma-has-no-other-solution-5213.smt2", + "QF_FP_fma-has-no-other-solution-5127.smt2", + "QF_FP_fma-has-no-other-solution-5986.smt2", + "QF_FP_fma-has-no-other-solution-6211.smt2", + "QF_FP_fma-has-no-other-solution-6468.smt2", + "QF_FP_fma-has-no-other-solution-6573.smt2", + "QF_FP_fma-has-no-other-solution-6673.smt2", + "QF_FP_fma-has-no-other-solution-6822.smt2", + "QF_FP_fma-has-no-other-solution-7580.smt2", + "QF_FP_fma-has-no-other-solution-7736.smt2", + "QF_FP_fma-has-no-other-solution-7832.smt2", + "QF_FP_fma-has-no-other-solution-7920.smt2", + "QF_FP_fma-has-no-other-solution-80.smt2", + "QF_FP_fma-has-no-other-solution-8278.smt2", + "QF_FP_fma-has-no-other-solution-8475.smt2", + "QF_FP_fma-has-no-other-solution-8483.smt2", + "QF_FP_fma-has-no-other-solution-9132.smt2", + "QF_FP_fma-has-no-other-solution-9188.smt2", + "QF_FP_fma-has-no-other-solution-9517.smt2", + "QF_FP_fma-has-no-other-solution-9455.smt2", + "QF_FP_fma-has-no-other-solution-9467.smt2", + ) + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt new file mode 100644 index 000000000..a1c0d8066 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/BitwuzlaBenchmarksBasedTest.kt @@ -0,0 +1,51 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import java.nio.file.Path + + +class BitwuzlaBenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("bitwuzlaTestData") + fun testConverter(name: String, samplePath: Path) = + testConverter(name, samplePath) { assertions -> + internalizeAndConvertBitwuzla(assertions) + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("bitwuzlaModelConversionTestData") + fun testModelConversion(name: String, samplePath: Path) = + testModelConversion(name, samplePath, KBitwuzlaSolver::class) + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("bitwuzlaTestData") + fun testSolver(name: String, samplePath: Path) = + testSolver(name, samplePath, KBitwuzlaSolver::class) + + + companion object { + @JvmStatic + fun bitwuzlaTestData() = testData + .skipUnsupportedTheories() + .ensureNotEmpty() + + @JvmStatic + fun bitwuzlaModelConversionTestData() = + bitwuzlaTestData() + // Bitwuzla doesn't support models for quantified formulas + .filter { it.name.startsWith("QF_") } + .ensureNotEmpty() + + private fun List.skipUnsupportedTheories() = + filterNot { "LIA" in it.name || "LRA" in it.name || "LIRA" in it.name } + .filterNot { "NIA" in it.name || "NRA" in it.name || "NIRA" in it.name } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt new file mode 100644 index 000000000..078322fa0 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/ContextMemoryUsageBenchmarksBasedTest.kt @@ -0,0 +1,66 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.KContext +import java.nio.file.Path + +class ContextMemoryUsageBenchmarksBasedTest : BenchmarksBasedTest() { + + @Disabled + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("testData") + fun testMemoryUsage( + name: String, + samplePath: Path, + ) = handleIgnoredTests("testMemoryUsage[$name]") { + ignoreNoTestDataStub(name) + testWorkers.withWorker(sharedCtx) { worker -> + worker.skipBadTestCases { + val assertions = worker.parseFile(samplePath) + val ksmtAssertions = worker.convertAssertions(assertions) + + ksmtAssertions.forEach { SortChecker(sharedCtx).apply(it) } + + worker.performEqualityChecks { + for ((originalZ3Expr, ksmtExpr) in assertions.zip(ksmtAssertions)) { + areEqual(actual = ksmtExpr, expected = originalZ3Expr) + } + check { "expressions are not equal" } + } + } + } + } + + companion object { + @JvmStatic + fun testData() = testData.filter { it.name.startsWith("QF_") } + + val sharedCtx: KContext + get() = sharedCtxField ?: error("Context is not initialized") + + private var sharedCtxField: KContext? = null + + @BeforeAll + @JvmStatic + fun initContext() { + sharedCtxField = KContext( + operationMode = KContext.OperationMode.CONCURRENT, + astManagementMode = KContext.AstManagementMode.GC + ) + } + + @AfterAll + @JvmStatic + fun clearContext() { + sharedCtxField?.close() + sharedCtxField = null + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt new file mode 100644 index 000000000..a9db2606f --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Cvc5BenchmarksBasedTest.kt @@ -0,0 +1,315 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.KContext +import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.runner.KSolverRunnerManager +import java.nio.file.Path + +class Cvc5BenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("cvc5TestData") + fun testSolver(name: String, samplePath: Path) = testSolver(name, samplePath) { ctx -> + solverManager.createCvc5TestSolver(ctx) + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("cvc5TestData") + fun testModelConversion(name: String, samplePath: Path) = testModelConversion(name, samplePath) { ctx -> + solverManager.createCvc5TestSolver(ctx) + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("cvc5TestData") + fun testConverter(name: String, samplePath: Path) = testConverter(name, samplePath) { assertions -> + internalizeAndConvertCvc5(assertions) + } + + companion object { + @JvmStatic + fun cvc5TestData() = testData + .filter { it.name !in KnownCvc5Issues.fp64CrashSamples } + .ensureNotEmpty() + + /** + * Resource limit to prevent solver from consuming huge amounts of native memory. + * The value is measured in some abstract resource usage units and chosen to maintain + * a balance between resource usage and the number of unknown check-sat results. + * */ + const val RESOURCE_LIMIT = 4000 + + fun KSolverRunnerManager.createCvc5TestSolver(ctx: KContext) = + createSolver(ctx, KCvc5Solver::class).apply { + configure { setCvc5Option("rlimit", "$RESOURCE_LIMIT") } + } + } +} + +object KnownCvc5Issues { + /** + * On these benchmarks cvc5 crashes due to fp 64 rem operation (both Windows, and Linux, and using java-api) + * TODO: remove it after fix + */ + val fp64CrashSamples = setOf( + "FP_11_53_RNA_fp.rem_fp.geq0.smt2", + "FP_11_53_RNA_fp.rem_fp.isInfinite0.smt2", + "FP_11_53_RNA_fp.rem_fp.isInfinite1.smt2", + "FP_11_53_RNA_fp.rem_fp.isNegative0.smt2", + "FP_11_53_RNA_fp.rem_fp.isNormal1.smt2", + "FP_11_53_RNA_fp.rem_fp.isPositive0.smt2", + "FP_11_53_RNA_fp.rem_fp.isPositive1.smt2", + "FP_11_53_RNA_fp.rem_fp.isSubnormal1.smt2", + "FP_11_53_RNA_fp.rem_fp.isZero0.smt2", + "FP_11_53_RNA_fp.rem_fp.isZero1.smt2", + "FP_11_53_RNE_fp.rem_fp.isInfinite1.smt2", + "FP_11_53_RNE_fp.rem_fp.isNormal0.smt2", + "FP_11_53_RNE_fp.rem_fp.isNormal1.smt2", + "FP_11_53_RNE_fp.rem_fp.isPositive0.smt2", + "FP_11_53_RNE_fp.rem_fp.isPositive1.smt2", + "FP_11_53_RNE_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RNE_fp.rem_fp.isSubnormal1.smt2", + "FP_11_53_RNE_fp.rem_fp.isZero0.smt2", + "FP_11_53_RNE_fp.rem_fp.isZero1.smt2", + "FP_11_53_RNE_fp.rem_fp.leq0.smt2", + "FP_11_53_RTN_fp.rem_fp.isInfinite0.smt2", + "FP_11_53_RTN_fp.rem_fp.isNaN0.smt2", + "FP_11_53_RTN_fp.rem_fp.isPositive1.smt2", + "FP_11_53_RTN_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RTN_fp.rem_fp.isZero1.smt2", + "FP_11_53_RTN_fp.rem_fp.leq0.smt2", + "FP_11_53_RTP_fp.rem_fp.geq0.smt2", + "FP_11_53_RTP_fp.rem_fp.isNaN0.smt2", + "FP_11_53_RTP_fp.rem_fp.isNegative0.smt2", + "FP_11_53_RTP_fp.rem_fp.isNegative1.smt2", + "FP_11_53_RTP_fp.rem_fp.isNormal0.smt2", + "FP_11_53_RTP_fp.rem_fp.isNormal1.smt2", + "FP_11_53_RTP_fp.rem_fp.isPositive0.smt2", + "FP_11_53_RTP_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RTP_fp.rem_fp.isSubnormal1.smt2", + "FP_11_53_RTP_fp.rem_fp.isZero0.smt2", + "FP_11_53_RTZ_fp.rem_fp.geq0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isInfinite1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNaN0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNaN1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNegative0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNormal1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isPositive0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isPositive1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isSubnormal1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isZero0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isZero1.smt2", + "FP_11_53_RTZ_fp.rem_fp.leq0.smt2", + "FP_3_5_RNA_fp.rem_fp.geq0.smt2", + "FP_3_5_RNA_fp.rem_fp.isInfinite0.smt2", + "FP_3_5_RNA_fp.rem_fp.isNaN1.smt2", + "FP_3_5_RNA_fp.rem_fp.isNegative0.smt2", + "FP_3_5_RNA_fp.rem_fp.isNegative1.smt2", + "FP_3_5_RNA_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RNA_fp.rem_fp.isNormal1.smt2", + "FP_3_5_RNA_fp.rem_fp.isPositive0.smt2", + "FP_3_5_RNA_fp.rem_fp.isPositive1.smt2", + "FP_3_5_RNA_fp.rem_fp.isZero0.smt2", + "FP_3_5_RNA_fp.rem_fp.isZero1.smt2", + "FP_3_5_RNA_fp.rem_fp.leq0.smt2", + "FP_3_5_RNE_fp.rem_fp.geq0.smt2", + "FP_3_5_RNE_fp.rem_fp.isInfinite0.smt2", + "FP_3_5_RNE_fp.rem_fp.isNaN1.smt2", + "FP_3_5_RNE_fp.rem_fp.isNegative0.smt2", + "FP_3_5_RNE_fp.rem_fp.isNegative1.smt2", + "FP_3_5_RNE_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RNE_fp.rem_fp.isSubnormal1.smt2", + "FP_3_5_RNE_fp.rem_fp.isZero0.smt2", + "FP_3_5_RNE_fp.rem_fp.isZero1.smt2", + "FP_3_5_RTN_fp.rem_fp.geq0.smt2", + "FP_3_5_RTN_fp.rem_fp.isInfinite0.smt2", + "FP_3_5_RTN_fp.rem_fp.isInfinite1.smt2", + "FP_3_5_RTN_fp.rem_fp.isNaN0.smt2", + "FP_3_5_RTN_fp.rem_fp.isNaN1.smt2", + "FP_3_5_RTN_fp.rem_fp.isNegative0.smt2", + "FP_3_5_RTN_fp.rem_fp.isNegative1.smt2", + "FP_3_5_RTN_fp.rem_fp.isNormal1.smt2", + "FP_3_5_RTN_fp.rem_fp.isPositive0.smt2", + "FP_3_5_RTN_fp.rem_fp.isPositive1.smt2", + "FP_3_5_RTN_fp.rem_fp.isSubnormal0.smt2", + "FP_3_5_RTN_fp.rem_fp.isSubnormal1.smt2", + "FP_3_5_RTN_fp.rem_fp.isZero0.smt2", + "FP_3_5_RTN_fp.rem_fp.isZero1.smt2", + "FP_3_5_RTN_fp.rem_fp.leq0.smt2", + "FP_3_5_RTP_fp.rem_fp.geq0.smt2", + "FP_3_5_RTP_fp.rem_fp.isNaN1.smt2", + "FP_3_5_RTP_fp.rem_fp.isNegative0.smt2", + "FP_3_5_RTP_fp.rem_fp.isNegative1.smt2", + "FP_3_5_RTP_fp.rem_fp.isNormal1.smt2", + "FP_3_5_RTP_fp.rem_fp.isPositive0.smt2", + "FP_3_5_RTP_fp.rem_fp.isPositive1.smt2", + "FP_3_5_RTP_fp.rem_fp.isSubnormal0.smt2", + "FP_3_5_RTP_fp.rem_fp.isSubnormal1.smt2", + "FP_3_5_RTP_fp.rem_fp.isZero0.smt2", + "FP_3_5_RTZ_fp.rem_fp.geq0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNaN0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNegative0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isPositive1.smt2", + "FP_3_5_RTZ_fp.rem_fp.isSubnormal0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isSubnormal1.smt2", + "FP_3_5_RTZ_fp.rem_fp.leq0.smt2", + "FP_8_24_RNA_fp.rem_fp.geq0.smt2", + "FP_8_24_RNA_fp.rem_fp.isNaN0.smt2", + "FP_8_24_RNA_fp.rem_fp.isNaN1.smt2", + "FP_8_24_RNA_fp.rem_fp.isNegative0.smt2", + "FP_8_24_RNA_fp.rem_fp.isNegative1.smt2", + "FP_8_24_RNA_fp.rem_fp.isPositive0.smt2", + "FP_8_24_RNA_fp.rem_fp.isPositive1.smt2", + "FP_8_24_RNA_fp.rem_fp.isSubnormal1.smt2", + "FP_8_24_RNA_fp.rem_fp.isZero0.smt2", + "FP_8_24_RNA_fp.rem_fp.isZero1.smt2", + "FP_8_24_RNE_fp.rem_fp.geq0.smt2", + "FP_8_24_RNE_fp.rem_fp.isInfinite0.smt2", + "FP_8_24_RNE_fp.rem_fp.isInfinite1.smt2", + "FP_8_24_RNE_fp.rem_fp.isNaN0.smt2", + "FP_8_24_RNE_fp.rem_fp.isNaN1.smt2", + "FP_8_24_RNE_fp.rem_fp.isNegative0.smt2", + "FP_8_24_RNE_fp.rem_fp.isNegative1.smt2", + "FP_8_24_RNE_fp.rem_fp.isNormal1.smt2", + "FP_8_24_RNE_fp.rem_fp.isPositive0.smt2", + "FP_8_24_RNE_fp.rem_fp.isSubnormal1.smt2", + "FP_8_24_RTN_fp.rem_fp.isInfinite0.smt2", + "FP_8_24_RTN_fp.rem_fp.isNaN0.smt2", + "FP_8_24_RTN_fp.rem_fp.isNaN1.smt2", + "FP_8_24_RTN_fp.rem_fp.isNegative0.smt2", + "FP_8_24_RTN_fp.rem_fp.isNegative1.smt2", + "FP_8_24_RTN_fp.rem_fp.isNormal1.smt2", + "FP_8_24_RTN_fp.rem_fp.isPositive0.smt2", + "FP_8_24_RTN_fp.rem_fp.isSubnormal0.smt2", + "FP_8_24_RTN_fp.rem_fp.isSubnormal1.smt2", + "FP_8_24_RTN_fp.rem_fp.isZero1.smt2", + "FP_8_24_RTN_fp.rem_fp.leq0.smt2", + "FP_8_24_RTP_fp.rem_fp.isInfinite1.smt2", + "FP_8_24_RTP_fp.rem_fp.isNaN0.smt2", + "FP_8_24_RTP_fp.rem_fp.isNaN1.smt2", + "FP_8_24_RTP_fp.rem_fp.isNegative0.smt2", + "FP_8_24_RTP_fp.rem_fp.isNormal1.smt2", + "FP_8_24_RTP_fp.rem_fp.isPositive0.smt2", + "FP_8_24_RTP_fp.rem_fp.isPositive1.smt2", + "FP_8_24_RTP_fp.rem_fp.isSubnormal0.smt2", + "FP_8_24_RTP_fp.rem_fp.isSubnormal1.smt2", + "FP_8_24_RTP_fp.rem_fp.isZero1.smt2", + "FP_8_24_RTP_fp.rem_fp.leq0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isInfinite0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isInfinite1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNaN1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNegative0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNegative1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNormal0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isPositive1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isSubnormal1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isZero0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isZero1.smt2", + "FP_8_24_RTZ_fp.rem_fp.leq0.smt2", + "FP_11_53_RNA_fp.rem_fp.isNaN0.smt2", + "FP_11_53_RNA_fp.rem_fp.isNaN1.smt2", + "FP_11_53_RNA_fp.rem_fp.isNegative1.smt2", + "FP_11_53_RNA_fp.rem_fp.isNormal0.smt2", + "FP_11_53_RNA_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RNA_fp.rem_fp.leq0.smt2", + "FP_11_53_RNE_fp.rem_fp.geq0.smt2", + "FP_11_53_RNE_fp.rem_fp.isInfinite0.smt2", + "FP_11_53_RNE_fp.rem_fp.isNaN0.smt2", + "FP_11_53_RNE_fp.rem_fp.isNaN1.smt2", + "FP_11_53_RNE_fp.rem_fp.isNegative0.smt2", + "FP_11_53_RNE_fp.rem_fp.isNegative1.smt2", + "FP_11_53_RTN_fp.rem_fp.geq0.smt2", + "FP_11_53_RTN_fp.rem_fp.isInfinite1.smt2", + "FP_11_53_RTN_fp.rem_fp.isNaN1.smt2", + "FP_11_53_RTN_fp.rem_fp.isNegative0.smt2", + "FP_11_53_RTN_fp.rem_fp.isNegative1.smt2", + "FP_11_53_RTN_fp.rem_fp.isNormal0.smt2", + "FP_11_53_RTN_fp.rem_fp.isNormal1.smt2", + "FP_11_53_RTN_fp.rem_fp.isPositive0.smt2", + "FP_11_53_RTN_fp.rem_fp.isSubnormal1.smt2", + "FP_11_53_RTN_fp.rem_fp.isZero0.smt2", + "FP_11_53_RTP_fp.rem_fp.isInfinite0.smt2", + "FP_11_53_RTP_fp.rem_fp.isInfinite1.smt2", + "FP_11_53_RTP_fp.rem_fp.isNaN1.smt2", + "FP_11_53_RTP_fp.rem_fp.isPositive1.smt2", + "FP_11_53_RTP_fp.rem_fp.isZero1.smt2", + "FP_11_53_RTP_fp.rem_fp.leq0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isInfinite0.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNegative1.smt2", + "FP_11_53_RTZ_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RNA_fp.rem_fp.isInfinite1.smt2", + "FP_3_5_RNA_fp.rem_fp.isNaN0.smt2", + "FP_3_5_RNA_fp.rem_fp.isSubnormal0.smt2", + "FP_3_5_RNA_fp.rem_fp.isSubnormal1.smt2", + "FP_3_5_RNE_fp.rem_fp.isInfinite1.smt2", + "FP_3_5_RNE_fp.rem_fp.isNaN0.smt2", + "FP_3_5_RNE_fp.rem_fp.isNormal1.smt2", + "FP_3_5_RNE_fp.rem_fp.isPositive0.smt2", + "FP_3_5_RNE_fp.rem_fp.isPositive1.smt2", + "FP_3_5_RNE_fp.rem_fp.isSubnormal0.smt2", + "FP_3_5_RNE_fp.rem_fp.leq0.smt2", + "FP_3_5_RTN_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RTP_fp.rem_fp.isInfinite0.smt2", + "FP_3_5_RTP_fp.rem_fp.isInfinite1.smt2", + "FP_3_5_RTP_fp.rem_fp.isNaN0.smt2", + "FP_3_5_RTP_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RTP_fp.rem_fp.isZero1.smt2", + "FP_3_5_RTP_fp.rem_fp.leq0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isInfinite0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isInfinite1.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNaN1.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNegative1.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNormal0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isNormal1.smt2", + "FP_3_5_RTZ_fp.rem_fp.isPositive0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isZero0.smt2", + "FP_3_5_RTZ_fp.rem_fp.isZero1.smt2", + "FP_8_24_RNA_fp.rem_fp.isInfinite0.smt2", + "FP_8_24_RNA_fp.rem_fp.isInfinite1.smt2", + "FP_8_24_RNA_fp.rem_fp.isNormal0.smt2", + "FP_8_24_RNA_fp.rem_fp.isNormal1.smt2", + "FP_8_24_RNA_fp.rem_fp.isSubnormal0.smt2", + "FP_8_24_RNA_fp.rem_fp.leq0.smt2", + "FP_8_24_RNE_fp.rem_fp.isNormal0.smt2", + "FP_8_24_RNE_fp.rem_fp.isPositive1.smt2", + "FP_8_24_RNE_fp.rem_fp.isSubnormal0.smt2", + "FP_8_24_RNE_fp.rem_fp.isZero0.smt2", + "FP_8_24_RNE_fp.rem_fp.isZero1.smt2", + "FP_8_24_RNE_fp.rem_fp.leq0.smt2", + "FP_8_24_RTN_fp.rem_fp.geq0.smt2", + "FP_8_24_RTN_fp.rem_fp.isInfinite1.smt2", + "FP_8_24_RTN_fp.rem_fp.isNormal0.smt2", + "FP_8_24_RTN_fp.rem_fp.isPositive1.smt2", + "FP_8_24_RTN_fp.rem_fp.isZero0.smt2", + "FP_8_24_RTP_fp.rem_fp.geq0.smt2", + "FP_8_24_RTP_fp.rem_fp.isInfinite0.smt2", + "FP_8_24_RTP_fp.rem_fp.isNegative1.smt2", + "FP_8_24_RTP_fp.rem_fp.isNormal0.smt2", + "FP_8_24_RTP_fp.rem_fp.isZero0.smt2", + "FP_8_24_RTZ_fp.rem_fp.geq0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNaN0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isNormal1.smt2", + "FP_8_24_RTZ_fp.rem_fp.isPositive0.smt2", + "FP_8_24_RTZ_fp.rem_fp.isSubnormal0.smt2", + "FP_11_53_RNA_fp.rem_=0.smt2", + "FP_11_53_RNA_fp.rem_distinct0.smt2", + "FP_11_53_RNA_fp.rem_distinct1.smt2", + "FP_11_53_RNE_fp.rem_=0.smt2", + "FP_11_53_RNE_fp.rem_distinct0.smt2", + "FP_11_53_RNE_fp.rem_distinct1.smt2", + "FP_11_53_RTN_fp.rem_=0.smt2", + "FP_11_53_RTN_fp.rem_distinct0.smt2", + "FP_11_53_RTN_fp.rem_distinct1.smt2", + "FP_11_53_RTP_fp.rem_distinct0.smt2", + "FP_11_53_RTZ_fp.rem_=0.smt2", + "FP_11_53_RTZ_fp.rem_distinct0.smt2", + "FP_11_53_RTZ_fp.rem_distinct1.smt2", + ) +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt new file mode 100644 index 000000000..58ba89564 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/PortfolioBenchmarksBasedTest.kt @@ -0,0 +1,59 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.portfolio.KPortfolioSolverManager +import io.ksmt.solver.z3.KZ3Solver +import java.nio.file.Path +import kotlin.time.Duration.Companion.seconds + +class PortfolioBenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("portfolioTestData") + fun testModelConversion(name: String, samplePath: Path) = + testModelConversion(name, samplePath) { ctx -> + portfolioSolverManager.createPortfolioSolver(ctx) + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("portfolioTestData") + fun testSolver(name: String, samplePath: Path) = + testSolver(name, samplePath) { ctx -> + portfolioSolverManager.createPortfolioSolver(ctx) + } + + companion object { + @JvmStatic + fun portfolioTestData() = testData + .filter { it.name.startsWith("QF_") } + .filter { "BV" in it.name } + .ensureNotEmpty() + + private lateinit var portfolioSolverManager: KPortfolioSolverManager + + @BeforeAll + @JvmStatic + fun initPortfolioPool() { + portfolioSolverManager = KPortfolioSolverManager( + solvers = listOf(KZ3Solver::class, KBitwuzlaSolver::class), + portfolioPoolSize = 4, + hardTimeout = 3.seconds, + workerProcessIdleTimeout = 50.seconds + ) + } + + @AfterAll + @JvmStatic + fun closePortfolioPool() { + portfolioSolverManager.close() + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt new file mode 100644 index 000000000..046b3b004 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SerializerBenchmarksBasedTest.kt @@ -0,0 +1,94 @@ +package io.ksmt.test.benchmarks + +import com.jetbrains.rd.framework.IMarshaller +import com.jetbrains.rd.framework.SerializationCtx +import com.jetbrains.rd.framework.Serializers +import com.jetbrains.rd.framework.createAbstractBuffer +import com.jetbrains.rd.framework.readList +import com.jetbrains.rd.framework.writeList +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.KAst +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.serializer.AstSerializationCtx +import java.nio.file.Path +import kotlin.test.assertEquals + +class SerializerBenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("serializerTestData") + fun testSerializer( + name: String, + samplePath: Path + ) = handleIgnoredTests("testSerializer[$name]") { + val ctx1 = KContext() + val ctx2 = KContext() + testWorkers.withWorker(ctx1) { worker -> + val assertions = worker.parseFile(samplePath) + val convertedAssertions = worker.convertAssertions(assertions) + + convertedAssertions.forEachIndexed { _, it -> + SortChecker(ctx1).apply(it) + } + + val (serialized, deserialized) = serializeAndDeserialize(ctx1, ctx2, convertedAssertions) + + assertEquals(convertedAssertions, deserialized) + + val (restored, _) = serializeAndDeserialize(ctx2, ctx1, serialized) + + assertEquals(convertedAssertions, restored) + } + } + + private fun serializeAndDeserialize( + sourceCtx: KContext, + targetCtx: KContext, + expressions: List>, + ): Pair>, List>> { + val srcSerializationCtx = AstSerializationCtx().apply { initCtx(sourceCtx) } + val srcMarshaller = AstSerializationCtx.marshaller(srcSerializationCtx) + + val targetSerializationCtx = AstSerializationCtx().apply { initCtx(targetCtx) } + val targetMarshaller = AstSerializationCtx.marshaller(targetSerializationCtx) + + val serialized = serializeAndDeserialize(expressions, srcMarshaller, targetMarshaller) + val deserialized = serializeAndDeserialize(serialized, targetMarshaller, srcMarshaller) + return serialized to deserialized + } + + + private fun serializeAndDeserialize( + expressions: List>, + ctx1Marshaller: IMarshaller, + ctx2Marshaller: IMarshaller + ): List> { + + val emptyRdSerializationCtx = SerializationCtx(Serializers()) + val buffer = createAbstractBuffer() + + buffer.writeList(expressions) { expr -> + ctx1Marshaller.write(emptyRdSerializationCtx, buffer, expr) + } + + buffer.rewind() + + val deserializedExpressions = buffer.readList { + ctx2Marshaller.read(emptyRdSerializationCtx, buffer) as KExpr<*> + } + + return deserializedExpressions + + } + + companion object { + + @JvmStatic + fun serializerTestData() = testData + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt new file mode 100644 index 000000000..720120b24 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/SimplifierBenchmarksBasedTest.kt @@ -0,0 +1,41 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.KContext +import io.ksmt.expr.KApp +import io.ksmt.expr.KExpr +import io.ksmt.expr.rewrite.simplify.KExprSimplifier +import io.ksmt.expr.transformer.KNonRecursiveTransformer +import io.ksmt.sort.KSort +import java.nio.file.Path + +class SimplifierBenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("simplifierTestData") + fun testSimplifier(name: String, samplePath: Path) = + testConverter(name, samplePath) { assertions -> + val simplifier = KExprSimplifier(ctx) + val simplified = assertions.map { simplifier.apply(it) } + simplified.forEach { ContextConsistencyChecker(ctx).apply(it) } + simplified + } + + companion object { + @JvmStatic + fun simplifierTestData() = testData + } + + class ContextConsistencyChecker(ctx: KContext) : KNonRecursiveTransformer(ctx) { + override fun transformApp(expr: KApp): KExpr = with(ctx) { + check(expr === expr.decl.apply(expr.args)) { + "Context is inconsistent" + } + return super.transformApp(expr) + } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt new file mode 100644 index 000000000..04d39846e --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/YicesBenchmarksBasedTest.kt @@ -0,0 +1,52 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.solver.yices.KYicesSolver +import java.nio.file.Path + +class YicesBenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("yicesTestData") + fun testConverter(name: String, samplePath: Path) = + testConverter(name, samplePath) { assertions -> + internalizeAndConvertYices(assertions) + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("yicesTestData") + fun testModelConversion(name: String, samplePath: Path) = + testModelConversion(name, samplePath, KYicesSolver::class) + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("yicesTestData") + fun testSolver(name: String, samplePath: Path) = + testSolver(name, samplePath, KYicesSolver::class) + + companion object { + @JvmStatic + fun yicesTestData() = testData + .skipUnsupportedTheories() + .skipBadTestCases() + .ensureNotEmpty() + + private fun List.skipUnsupportedTheories() = + filterNot { "QF" !in it.name || "FP" in it.name || "N" in it.name } + + private fun List.skipBadTestCases(): List = + /** + * Yices bug: returns an incorrect model + */ + filterNot { it.name == "QF_UFBV_QF_UFBV_bv8_bv_eq_sdp_v4_ab_cti_max.smt2" } + // Yices bug: incorrect bv-add after bv-and on 64 bit bv (63 and 65 are correct). + .filterNot { it.name == "QF_BV_countbits064.smt2" } + // Yices bug: same as in previous sample + .filterNot { it.name == "QF_BV_nextpoweroftwo064.smt2" } + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt new file mode 100644 index 000000000..c8a6e8cb0 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3BenchmarksBasedTest.kt @@ -0,0 +1,43 @@ +package io.ksmt.test.benchmarks + +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.solver.z3.KZ3Solver +import java.nio.file.Path + +class Z3BenchmarksBasedTest : BenchmarksBasedTest() { + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun testConverter(name: String, samplePath: Path) = + testConverter(name, samplePath) { assertions -> + assertions + } + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun testModelConversion(name: String, samplePath: Path) = + testModelConversion(name, samplePath, KZ3Solver::class) + + @Execution(ExecutionMode.CONCURRENT) + @ParameterizedTest(name = "{0}") + @MethodSource("z3SolverTestData") + fun testSolver(name: String, samplePath: Path) = + testSolver(name, samplePath, KZ3Solver::class) + + + companion object { + @JvmStatic + fun z3TestData() = testData + + @JvmStatic + fun z3SolverTestData() = z3TestData() + .filter { it.name !in KnownZ3Issues.z3FpFmaFalseSatSamples } + .filter { it.name !in KnownZ3Issues.z3FpFmaFalseUnsatSamples } + .ensureNotEmpty() + } +} diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt new file mode 100644 index 000000000..b124d5768 --- /dev/null +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/benchmarks/Z3ConverterBenchmark.kt @@ -0,0 +1,253 @@ +package io.ksmt.test.benchmarks + +import com.microsoft.z3.Context +import kotlinx.coroutines.async +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Timeout +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.expr.transformer.KNonRecursiveTransformer +import io.ksmt.solver.z3.KZ3SMTLibParser +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KSort +import java.nio.file.Path +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.writeLines +import kotlin.system.measureNanoTime + +@Disabled +@Execution(ExecutionMode.SAME_THREAD) +@Timeout(10, unit = TimeUnit.SECONDS) +class Z3ConverterBenchmark : BenchmarksBasedTest() { + + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun measureFormulaSize(name: String, samplePath: Path) = ignoreExceptions { + with(KContext()) { + val assertions = KZ3SMTLibParser(this).parse(samplePath) + val size = assertions.sumOf { FormulaSizeCalculator.size(it) } + saveData(name, "size", "$size") + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun measureNativeAssertionTime(name: String, samplePath: Path) = ignoreExceptions { + Context().use { ctx -> + val assertions = ctx.parseSMTLIB2File( + samplePath.absolutePathString(), + emptyArray(), + emptyArray(), + emptyArray(), + emptyArray() + ) + val solver = ctx.mkSolver() + + // force solver initialization + solver.push() + + val assertTime = measureNanoTime { + assertions.forEach { solver.add(it) } + } + saveData(name, "native", "$assertTime") + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun measureNativeParsingAndAssertionTime(name: String, samplePath: Path) = ignoreExceptions { + Context().use { ctx -> + val solver = ctx.mkSolver() + + // force solver initialization + solver.push() + + val assertAndParseTime = measureNanoTime { + val assertions = ctx.parseSMTLIB2File( + samplePath.absolutePathString(), + emptyArray(), + emptyArray(), + emptyArray(), + emptyArray() + ) + assertions.forEach { solver.add(it) } + } + saveData(name, "native_parse", "$assertAndParseTime") + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun measureKsmtAssertionTime(name: String, samplePath: Path) = ignoreExceptions { + with(KContext()) { + val assertions = KZ3SMTLibParser(this).parse(samplePath) + KZ3Solver(this).use { solver -> + + // force solver initialization + solver.push() + + val internalizeAndAssert = measureNanoTime { + assertions.forEach { solver.assert(it) } + } + saveData(name, "ksmt", "$internalizeAndAssert") + } + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("z3TestData") + fun measureRunnerAssertionTime(name: String, samplePath: Path) = ignoreExceptions { + with(KContext()) { + val assertions = KZ3SMTLibParser(this).parse(samplePath) + solverManager.createSolver(this, KZ3Solver::class).use { solver -> + + // force solver initialization + solver.push() + + val internalizeAndAssert = measureNanoTime { + runBlocking { + assertions.map { expr -> + async { solver.assertAsync(expr) } + }.joinAll() + } + } + saveData(name, "runner", "$internalizeAndAssert") + } + } + } + + + private inline fun ignoreExceptions(block: () -> Unit) = try { + block() + } catch (t: Throwable) { + System.err.println(t.toString()) + } + + private class FormulaSizeCalculator(ctx: KContext) : KNonRecursiveTransformer(ctx) { + private var expressionCount = 0 + override fun transformExpr(expr: KExpr): KExpr { + expressionCount++ + return super.transformExpr(expr) + } + + companion object { + fun size(expr: KExpr<*>): Int = + FormulaSizeCalculator(expr.ctx) + .also { it.apply(expr) } + .expressionCount + } + } + + companion object { + private val data = ConcurrentHashMap>() + + private fun saveData(sample: String, type: String, value: String) { + data.getOrPut(sample) { ConcurrentHashMap() }[type] = value + } + + @AfterAll + @JvmStatic + fun saveData() { + val headerRow = data.values.firstOrNull()?.keys?.sorted() ?: return + val columns = listOf("Sample name") + headerRow + val orderedData = listOf(columns) + data.map { (name, sampleData) -> + val row = headerRow.map { sampleData[it] ?: "" } + listOf(name) + row + } + val csvData = orderedData.map { it.joinToString(separator = ",") } + Path("data.csv") + .writeLines(csvData) + } + + @JvmStatic + fun z3TestData() = testData.skipSlowSamples() + + // skip samples with slow native assert + private fun List.skipSlowSamples() = this + .filterNot { it.name.startsWith("QF_ABV_try3") } + .filterNot { it.name.startsWith("QF_ABV_try5") } + .filterNot { it.name.startsWith("QF_ABV_testcase") } + .filterNot { it.name in slowSamples } + + private val slowSamples = setOf( + "QF_ABV_ridecore-qf_abv-bug.smt2", + "QF_ABV_noregions-fullmemite.stp.smt2", + "QF_ABV_blaster-concrete.stp.smt2", + "QF_ABV_blaster-wp.ir.3.simplified13.stp.smt2", + "QF_ABV_blaster-wp.ir.3.simplified8.stp.smt2", + "QF_ABV_blaster.stp.smt2", + "QF_ABV_ff.stp.smt2", + "QF_ABV_grep0084.stp.smt2", + "QF_ABV_grep0095.stp.smt2", + "QF_ABV_grep0106.stp.smt2", + "QF_ABV_grep0117.stp.smt2", + "QF_ABV_grep0777.stp.smt2", + "AUFLIA_smt8061204852622600993.smt2", + "AUFLIRA_cl5_nebula_init_0222.fof.smt2", + "AUFLIRA_quaternion_ds1_inuse_0005.fof.smt2", + "AUFLIRA_quaternion_ds1_inuse_0222.fof.smt2", + "AUFLIRA_quaternion_ds1_symm_1198.fof.smt2", + "AUFLIRA_quaternion_ds1_symm_1558.fof.smt2", + "AUFLIRA_quaternion_ds1_symm_2906.fof.smt2", + "AUFLIRA_thruster_init_0967.fof.smt2", + "AUFLIRA_thruster_inuse_1134.fof.smt2", + "AUFLIRA_thruster_symm_0369.fof.smt2", + "AUFLIRA_thruster_symm_0946.fof.smt2", + "AUFLIRA_thruster_symm_2979.fof.smt2", + "LIA_ARI592=1.smt2", + "QF_ABV_egt-1334.smt2", + "QF_ABV_egt-2021.smt2", + "QF_ABV_egt-2319.smt2", + "QF_ABV_egt-4810.smt2", + "QF_ABV_egt-7600.smt2", + "QF_ABV_no_init_multi_member24.smt2", + "QF_ABVFP_query.01403.smt2", + "QF_ABVFP_query.03635.smt2", + "QF_ABVFP_query.05867.smt2", + "QF_ABVFP_query.06109.smt2", + "QF_ABVFP_query.07672.smt2", + "QF_ABVFP_query.08043.smt2", + "QF_ABVFP_query.08657.smt2", + "QF_BVFP_query.01217.smt2", + "QF_BVFP_query.03449.smt2", + "QF_BVFP_query.07486.smt2", + "QF_BVFP_query.08173.smt2", + "QF_FP_add-has-solution-15709.smt2", + "QF_FP_add-has-solution-8348.smt2", + "QF_FP_add-has-solution-8905.smt2", + "QF_FP_div-has-no-other-solution-14323.smt2", + "QF_FP_div-has-solution-12164.smt2", + "QF_FP_fma-has-no-other-solution-13136.smt2", + "QF_FP_gt-has-no-other-solution-6602.smt2", + "QF_FP_lt-has-no-other-solution-10072.smt2", + "QF_FP_lt-has-no-other-solution-10686.smt2", + "QF_FP_lt-has-no-other-solution-4316.smt2", + "QF_FP_lt-has-solution-5619.smt2", + "QF_FP_min-has-no-other-solution-18171.smt2", + "QF_FP_min-has-solution-10260.smt2", + "QF_FP_mul-has-no-other-solution-10919.smt2", + "QF_FP_rem-has-no-other-solution-5399.smt2", + "QF_FP_rem-has-no-other-solution-6643.smt2", + "QF_FP_rem-has-solution-5252.smt2", + "QF_FP_sqrt-has-no-other-solution-16567.smt2", + "QF_FP_sqrt-has-solution-1666.smt2", + "QF_FP_sub-has-no-other-solution-12726.smt2", + "QF_FP_sub-has-solution-19199.smt2", + "QF_FP_toIntegral-has-no-other-solution-1029.smt2", + "QF_FP_toIntegral-has-no-other-solution-1345.smt2", + "QF_FP_toIntegral-has-no-other-solution-264.smt2", + "QF_UFLIA_xs_28_38.smt2", + "QF_ABV_ndes.smt2" + ) + } +} From c3ccb577f8a1daa33190692306f6fd7ae2639b1d Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 3 Jul 2023 20:36:52 +0300 Subject: [PATCH 025/228] Update build.gradle.kts for max-sat module --- ksmt-maxsat/build.gradle.kts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ksmt-maxsat/build.gradle.kts b/ksmt-maxsat/build.gradle.kts index 6e282db3f..e7beb90a0 100644 --- a/ksmt-maxsat/build.gradle.kts +++ b/ksmt-maxsat/build.gradle.kts @@ -2,9 +2,6 @@ plugins { id("io.ksmt.ksmt-base") } -group = "org.example" -version = "unspecified" - repositories { mavenCentral() } @@ -12,9 +9,7 @@ repositories { dependencies { implementation(project(":ksmt-core")) implementation(project(":ksmt-z3")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2") - testImplementation(project(":ksmt-core")) - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") } From b44279fc73cf2b59665e27bba83ccc8d725ac059 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 12:33:22 +0300 Subject: [PATCH 026/228] applyMaxRes: add lost soft constraint, format code --- .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 8 ++- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 65 +++++++++++++------ .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 4 +- .../ksmt/solver/maxsat/MaxSATScopeManager.kt | 3 +- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 54 +++++++++------ 5 files changed, 88 insertions(+), 46 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index 33fa6e6ef..28308707c 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -2,5 +2,9 @@ package io.ksmt.solver.maxsat import io.ksmt.solver.KSolverStatus -class KMaxSATResult(val satSoftConstraints: List, val hardConstraintsSATStatus: KSolverStatus, - val maxSATSucceeded: Boolean, val timeoutExceeded: Boolean) +class KMaxSATResult( + val satSoftConstraints: List, + val hardConstraintsSATStatus: KSolverStatus, + val maxSATSucceeded: Boolean, + val timeoutExceeded: Boolean, +) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index f2eb1a493..3c7206cb9 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -17,7 +17,7 @@ import kotlinx.coroutines.withTimeoutOrNull import kotlin.time.Duration class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver - where T : KSolverConfiguration { + where T : KSolverConfiguration { private val scopeManager = MaxSATScopeManager() private var softConstraints = mutableListOf() @@ -40,29 +40,35 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver */ suspend fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { var currentMaxSATResult: Triple>, KModel?> = - Triple(null, listOf(), null) + Triple(null, listOf(), null) solver.push() val maxSATResult = withTimeoutOrNull(timeout.inWholeMilliseconds) { if (softConstraints.isEmpty()) { - return@withTimeoutOrNull KMaxSATResult(listOf(), solver.check(), + return@withTimeoutOrNull KMaxSATResult( + listOf(), + solver.check(), maxSATSucceeded = true, - timeoutExceeded = false + timeoutExceeded = false, ) } val status = solver.check() if (status == KSolverStatus.UNSAT) { - return@withTimeoutOrNull KMaxSATResult(listOf(), status, + return@withTimeoutOrNull KMaxSATResult( + listOf(), + status, maxSATSucceeded = true, - timeoutExceeded = false + timeoutExceeded = false, ) } else if (status == KSolverStatus.UNKNOWN) { - return@withTimeoutOrNull KMaxSATResult(listOf(), status, + return@withTimeoutOrNull KMaxSATResult( + listOf(), + status, maxSATSucceeded = false, - timeoutExceeded = false + timeoutExceeded = false, ) } @@ -86,11 +92,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) val (formulaReified, reificationVariables) = - reifyUnsatCore(formula, splitUnsatCore, i, weight) + reifyUnsatCore(formula, splitUnsatCore, i, weight) unionReificationVariables(reificationVariables) - formula = applyMaxRes(formulaReified, reificationVariables, i) + formula = applyMaxRes(formulaReified, reificationVariables, i, weight) i++ } @@ -120,10 +126,10 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * Returns a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ private fun splitUnsatCore(formula: MutableList, unsatCore: List>) - : Pair> { + : Pair> { // Filters soft constraints from the unsat core. - val unsatCoreSoftConstraints = - formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } + val unsatCoreSoftConstraints = + formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight @@ -137,8 +143,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver formula.removeIf { it.weight == x.weight && it.expression == x.expression } unsatCoreSoftConstraintsSplit.add(minWeightSoftConstraint) - } - else { + } else { unsatCoreSoftConstraintsSplit.add(x) } } @@ -186,8 +191,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Reify unsat core soft constraints with literals. */ - private fun reifyUnsatCore(formula: MutableList, unsatCore: List, - iter: Int, weight: Int): Pair, List>> { + private fun reifyUnsatCore( + formula: MutableList, + unsatCore: List, + iter: Int, + weight: Int, + ): Pair, List>> { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { @@ -223,9 +232,13 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Apply MaxRes rule. */ - private fun applyMaxRes(formula: MutableList, literalsToReify: List>, - iter: Int) - : MutableList { + private fun applyMaxRes( + formula: MutableList, + literalsToReify: List>, + iter: Int, + weight: Int, + ) + : MutableList { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes @@ -250,6 +263,18 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver disjunction, ), ) + + // What weight? + formula.add( + SoftConstraint( + KOrBinaryExpr( + ctx, + KNotExpr(ctx, currentLiteralToReifyDisjunction), + KNotExpr(ctx, indexedLiteral.value), + ), + weight, + ), + ) } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index 099ac58bc..0be2dd7c4 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -3,12 +3,10 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue -import kotlin.time.Duration.Companion.microseconds import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.nanoseconds suspend fun main() { - with (KContext()) { + with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt index 916ab63b2..74ad5d634 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt @@ -37,8 +37,7 @@ class MaxSATScopeManager { if (prevScopes.isNotEmpty()) { scopeAddedSoftConstraints = prevScopes.last().scopeAddedSoftConstraints prevScopes.removeLast() - } - else { + } else { scopeAddedSoftConstraints = 0 } } diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index ba69e6a45..315710a25 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test class KMaxSATSolverTest { @Test - fun noSoftConstraintsSatTest() = with (KContext()) { + fun noSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -33,7 +33,7 @@ class KMaxSATSolverTest { } @Test - fun oneOfTwoSoftConstraintsSatTest() = with (KContext()) { + fun oneOfTwoSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -56,7 +56,7 @@ class KMaxSATSolverTest { } @Test - fun twoOfThreeSoftConstraintsSatTest() = with (KContext()) { + fun twoOfThreeSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -81,7 +81,7 @@ class KMaxSATSolverTest { } @Test - fun sameExpressionSoftConstraintsSatTest() = with (KContext()) { + fun sameExpressionSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -101,13 +101,19 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSoftConstraintsSat(listOf(SoftConstraint(!x or y, 7), SoftConstraint(!x or !y, 3), - SoftConstraint(!x or !y, 4)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat( + listOf( + SoftConstraint(!x or y, 7), + SoftConstraint(!x or !y, 3), + SoftConstraint(!x or !y, 4), + ), + maxSATResult.satSoftConstraints, + ) } } @Test - fun sameExpressionSoftConstraintsUnsatTest() = with (KContext()) { + fun sameExpressionSoftConstraintsUnsatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -127,13 +133,15 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat(listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), - maxSATResult.satSoftConstraints) + assertSoftConstraintsSat( + listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), + maxSATResult.satSoftConstraints, + ) } } @Test - fun chooseOneConstraintByWeightTest() = with (KContext()) { + fun chooseOneConstraintByWeightTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val z = boolSort.mkConst("z") @@ -154,7 +162,7 @@ class KMaxSATSolverTest { } @Test - fun inequalitiesTest() = with (KContext()) { + fun inequalitiesTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -177,13 +185,15 @@ class KMaxSATSolverTest { val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat(listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), - maxSATResult.satSoftConstraints) + assertSoftConstraintsSat( + listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), + maxSATResult.satSoftConstraints, + ) } } @Test - fun oneScopePushPopTest() = with (KContext()) { + fun oneScopePushPopTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) val a by boolSort @@ -211,7 +221,7 @@ class KMaxSATSolverTest { } @Test - fun threeScopesPushPopTest() = with (KContext()) { + fun threeScopesPushPopTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) @@ -249,11 +259,17 @@ class KMaxSATSolverTest { } } - private fun assertSoftConstraintsSat(constraintsToAssert: List, - satConstraints: List) { + private fun assertSoftConstraintsSat( + constraintsToAssert: List, + satConstraints: List, + ) { for (constraint in constraintsToAssert) { - assertTrue(satConstraints.any { constraint.expression.internEquals(it.expression) && - constraint.weight == it.weight }) + assertTrue( + satConstraints.any { + constraint.expression.internEquals(it.expression) && + constraint.weight == it.weight + }, + ) } } } From 51739d60f9846571f9464fe09348849a51cf5698 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 15:20:20 +0300 Subject: [PATCH 027/228] Use with(ctx) in solver for more readable expressions --- .../src/main/kotlin/io/ksmt/expr/Bool.kt | 8 ++-- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 47 +++++-------------- .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 2 +- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index b96c78002..46905e30a 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -27,7 +27,7 @@ abstract class KAndExpr(ctx: KContext) : KApp(ctx) { override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) } -class KAndBinaryExpr( +class KAndBinaryExpr internal constructor( ctx: KContext, val lhs: KExpr, val rhs: KExpr @@ -64,7 +64,7 @@ abstract class KOrExpr(ctx: KContext) : KApp(ctx) { override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) } -class KOrBinaryExpr( +class KOrBinaryExpr internal constructor( ctx: KContext, val lhs: KExpr, val rhs: KExpr @@ -92,7 +92,7 @@ class KOrNaryExpr( override fun internEquals(other: Any): Boolean = structurallyEqual(other) { args } } -class KNotExpr( +class KNotExpr internal constructor( ctx: KContext, val arg: KExpr ) : KApp(ctx) { @@ -148,7 +148,7 @@ class KXorExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { a }, { b }) } -class KEqExpr( +class KEqExpr internal constructor( ctx: KContext, val lhs: KExpr, val rhs: KExpr ) : KApp(ctx) { diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 3c7206cb9..dcd4acd0a 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -1,10 +1,7 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext -import io.ksmt.expr.KEqExpr import io.ksmt.expr.KExpr -import io.ksmt.expr.KNotExpr -import io.ksmt.expr.KOrBinaryExpr import io.ksmt.expr.KOrNaryExpr import io.ksmt.expr.KTrue import io.ksmt.solver.KModel @@ -44,7 +41,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver solver.push() - val maxSATResult = withTimeoutOrNull(timeout.inWholeMilliseconds) { + val maxSATResult = withTimeoutOrNull(timeout) { if (softConstraints.isEmpty()) { return@withTimeoutOrNull KMaxSATResult( listOf(), @@ -196,7 +193,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver unsatCore: List, iter: Int, weight: Int, - ): Pair, List>> { + ): Pair, List>> = with(ctx) { val literalsToReify = mutableListOf>() for (coreElement in unsatCore.withIndex()) { @@ -206,11 +203,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val coreElementExpr = coreElement.value.expression val literalToReify = coreElementExpr.sort.mkConst("*$iter${coreElement.index}") - val constraintToReify = KEqExpr( - ctx, - coreElementExpr, - KNotExpr(ctx, literalToReify), - ) + val constraintToReify = coreElementExpr eq !literalToReify assert(constraintToReify) @@ -221,11 +214,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return Pair(formula, literalsToReify) } - private fun unionReificationVariables(reificationVariables: List>) { + private fun unionReificationVariables(reificationVariables: List>) = with(ctx) { when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) - 2 -> assert(KOrBinaryExpr(ctx, reificationVariables[0], reificationVariables[1])) - else -> assert(KOrNaryExpr(ctx, reificationVariables)) + 2 -> assert(reificationVariables[0] or reificationVariables[1]) + else -> assert(KOrNaryExpr(this, reificationVariables)) } } @@ -238,7 +231,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver iter: Int, weight: Int, ) - : MutableList { + : MutableList = with(ctx) { for (indexedLiteral in literalsToReify.withIndex()) { // TODO: here we should use restrictions from the article for MaxRes @@ -246,35 +239,21 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val indexLast = literalsToReify.lastIndex if (index < indexLast) { - val currentLiteralToReifyDisjunction = ctx.boolSort.mkConst("#$iter$index") - val nextLiteralToReifyDisjunction = ctx.boolSort.mkConst("#$iter${index + 1}") + val sort = indexedLiteral.value.sort + val currentLiteralToReifyDisjunction = sort.mkConst("#$iter$index") + val nextLiteralToReifyDisjunction = sort.mkConst("#$iter${index + 1}") val disjunction = when (indexLast - index) { // The second element is omitted as it is an empty disjunction. 1 -> literalsToReify[index + 1] - else -> KOrBinaryExpr(ctx, literalsToReify[index + 1], nextLiteralToReifyDisjunction) + else -> literalsToReify[index + 1] or nextLiteralToReifyDisjunction } - assert( - KEqExpr( - ctx, - currentLiteralToReifyDisjunction, - disjunction, - ), - ) + assert(currentLiteralToReifyDisjunction eq disjunction) // What weight? - formula.add( - SoftConstraint( - KOrBinaryExpr( - ctx, - KNotExpr(ctx, currentLiteralToReifyDisjunction), - KNotExpr(ctx, indexedLiteral.value), - ), - weight, - ), - ) + formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !indexedLiteral.value, weight)) } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt index 0be2dd7c4..c0c7f809a 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt @@ -25,7 +25,7 @@ suspend fun main() { maxSATSolver.assertSoft(!a1, 10) maxSATSolver.assertSoft(!a2, 3) - val result = maxSATSolver.checkMaxSAT(1L.milliseconds) + val result = maxSATSolver.checkMaxSAT(50.milliseconds) println("Max SAT succeeded: ${result.maxSATSucceeded}") println("Hard constraints SAT status: ${result.hardConstraintsSATStatus}") println("Size SAT soft constraints: ${result.satSoftConstraints.size}") From 908175dc508265d84e310722c63f6f77f0c93fcf Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 16:29:24 +0300 Subject: [PATCH 028/228] Rewrite KOrNaryExpr through reduce --- ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt | 4 ++-- .../src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt | 6 +----- .../test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt | 7 +++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt index 46905e30a..b73de9362 100644 --- a/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt +++ b/ksmt-core/src/main/kotlin/io/ksmt/expr/Bool.kt @@ -41,7 +41,7 @@ class KAndBinaryExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { lhs }, { rhs }) } -class KAndNaryExpr( +class KAndNaryExpr internal constructor( ctx: KContext, override val args: List> ) : KAndExpr(ctx) { @@ -78,7 +78,7 @@ class KOrBinaryExpr internal constructor( override fun internEquals(other: Any): Boolean = structurallyEqual(other, { lhs }, { rhs }) } -class KOrNaryExpr( +class KOrNaryExpr internal constructor( ctx: KContext, override val args: List> ) : KOrExpr(ctx) { diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index dcd4acd0a..58563049b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -2,7 +2,6 @@ package io.ksmt.solver.maxsat import io.ksmt.KContext import io.ksmt.expr.KExpr -import io.ksmt.expr.KOrNaryExpr import io.ksmt.expr.KTrue import io.ksmt.solver.KModel import io.ksmt.solver.KSolver @@ -218,7 +217,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver when (reificationVariables.size) { 1 -> assert(reificationVariables.first()) 2 -> assert(reificationVariables[0] or reificationVariables[1]) - else -> assert(KOrNaryExpr(this, reificationVariables)) + else -> assert(reificationVariables.reduce { x, y -> x or y }) } } @@ -233,8 +232,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver ) : MutableList = with(ctx) { for (indexedLiteral in literalsToReify.withIndex()) { - // TODO: here we should use restrictions from the article for MaxRes - val index = indexedLiteral.index val indexLast = literalsToReify.lastIndex @@ -252,7 +249,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver assert(currentLiteralToReifyDisjunction eq disjunction) - // What weight? formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !indexedLiteral.value, weight)) } } diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 315710a25..0487531ea 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -4,7 +4,6 @@ import io.ksmt.KContext import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver import io.ksmt.utils.getValue -import io.ksmt.utils.mkConst import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -144,9 +143,9 @@ class KMaxSATSolverTest { fun chooseOneConstraintByWeightTest() = with(KContext()) { val z3Solver = KZ3Solver(this) val maxSATSolver = KMaxSATSolver(this, z3Solver) - val z = boolSort.mkConst("z") - val a = boolSort.mkConst("a") - val b = boolSort.mkConst("b") + val z by boolSort + val a by boolSort + val b by boolSort maxSATSolver.assert(z) maxSATSolver.assertSoft(a and b, 1) From 6aa4c5078ff3e1295a5a9b194764624791d64c86 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 16:43:31 +0300 Subject: [PATCH 029/228] Throw NotImplementedError when unsat and timeout exceeded --- .../main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 58563049b..19a2ad563 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -101,12 +101,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver solver.pop() if (maxSATResult == null) { - val (solverStatus, unsatCore, model) = currentMaxSATResult + val (solverStatus, _, model) = currentMaxSATResult return when (solverStatus) { null -> KMaxSATResult(listOf(), KSolverStatus.UNKNOWN, maxSATSucceeded = false, timeoutExceeded = true) KSolverStatus.SAT -> handleSat(model!!) - KSolverStatus.UNSAT -> handleUnsat(unsatCore) + KSolverStatus.UNSAT -> throw NotImplementedError() KSolverStatus.UNKNOWN -> handleUnknown(timeoutExceeded = true) } } @@ -265,11 +265,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } } - private fun handleUnsat(unsatCore: List>): KMaxSATResult { - val satSoftConstraints = softConstraints.filter { x -> unsatCore.any { x.expression.internEquals(it) } } - return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = false, timeoutExceeded = true) - } - private fun handleUnknown(timeoutExceeded: Boolean): KMaxSATResult { return KMaxSATResult(listOf(), KSolverStatus.SAT, maxSATSucceeded = false, timeoutExceeded) } From 9d68212accadf7a7ca8245cc2e93f54c7b97b4d3 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 17:12:37 +0300 Subject: [PATCH 030/228] Add comments to MaxSATScopeManager, remove redundant classes --- .../main/kotlin/io/ksmt/solver/maxsat/Constraint.kt | 8 -------- .../kotlin/io/ksmt/solver/maxsat/HardConstraint.kt | 6 ------ .../main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt | 3 +-- .../io/ksmt/solver/maxsat/MaxSATScopeManager.kt | 11 ++++++++++- .../kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt deleted file mode 100644 index 1f94cbd49..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.expr.KExpr -import io.ksmt.sort.KBoolSort - -interface Constraint { - val expression: KExpr -} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt deleted file mode 100644 index 8216ec74b..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.expr.KExpr -import io.ksmt.sort.KBoolSort - -class HardConstraint(override val expression: KExpr) : Constraint diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt index b800b2814..c54b6301d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt @@ -1,4 +1,3 @@ package io.ksmt.solver.maxsat -// TODO: support info about MaxSATResult -class MaxSATScope(val scopeAddedSoftConstraints: Int) +internal class MaxSATScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt index 74ad5d634..bfc5c0f33 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt @@ -1,18 +1,24 @@ package io.ksmt.solver.maxsat -class MaxSATScopeManager { +internal class MaxSATScopeManager { private var currentScope = 0u private val prevScopes = mutableListOf() private var scopeAddedSoftConstraints = 0 + /** + * Increment scope added soft constraints number. + */ fun incrementSoft() { if (currentScope != 0u) { scopeAddedSoftConstraints++ } } + /** + * Push a new scope. + */ fun push() { if (currentScope != 0u) { prevScopes.add(MaxSATScope(scopeAddedSoftConstraints)) @@ -22,6 +28,9 @@ class MaxSATScopeManager { currentScope++ } + /** + * Pop n scopes removing soft constraints added in these scopes. + */ fun pop(n: UInt, softConstraints: MutableList): MutableList { require(n <= currentScope) { "Can not pop $n scope levels because current scope level is $currentScope" diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt index 39c6b238f..3833a54af 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt @@ -3,4 +3,4 @@ package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort -class SoftConstraint(override val expression: KExpr, val weight: Int) : Constraint +class SoftConstraint(val expression: KExpr, val weight: Int) From 972a5212399e7088e8e07fa3e3bca50f6022b875 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 18:38:50 +0300 Subject: [PATCH 031/228] Comment KMaxSATResult, add default value to pop in MaxSATScopeManager --- .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 17 ++++++++++++ .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 26 +++++++++---------- .../ksmt/solver/maxsat/MaxSATScopeManager.kt | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index 28308707c..bf377a8ad 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -2,6 +2,23 @@ package io.ksmt.solver.maxsat import io.ksmt.solver.KSolverStatus +/** + * @property satSoftConstraints + * - MaxSAT has succeeded -> contains soft constraints from MaxSAT solution. + * - MaxSAT has not succeeded -> contains soft constraints algorithm considered as satisfiable (incomplete solution). + * + * @property hardConstraintsSATStatus + * Shows satisfiability status of hardly asserted constraints' conjunction. + * + * @property maxSATSucceeded + * Shows whether MaxSAT calculation has succeeded or not. + * + * It may end without success in case of exceeding the timeout or in case solver started returning UNKNOWN during + * MaxSAT calculation. + * + * @property timeoutExceeded + * Shows whether timeout has been exceeded or not. + */ class KMaxSATResult( val satSoftConstraints: List, val hardConstraintsSATStatus: KSolverStatus, diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 19a2ad563..8070e9de2 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -15,7 +15,6 @@ import kotlin.time.Duration class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver where T : KSolverConfiguration { private val scopeManager = MaxSATScopeManager() - private var softConstraints = mutableListOf() /** @@ -87,12 +86,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) - val (formulaReified, reificationVariables) = + val (formulaReified, reificationLiterals) = reifyUnsatCore(formula, splitUnsatCore, i, weight) - unionReificationVariables(reificationVariables) + unionReificationLiterals(reificationLiterals) - formula = applyMaxRes(formulaReified, reificationVariables, i, weight) + formula = applyMaxRes(formulaReified, reificationLiterals, i, weight) i++ } @@ -119,7 +118,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * - constraints with the weight equal to the minimum of the unsat core soft constraint weights * - constraints with the weight equal to old weight - minimum weight * - * Returns a pair of minimum weight and a list of unsat core soft constraints with minimum weight. + * @return a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ private fun splitUnsatCore(formula: MutableList, unsatCore: List>) : Pair> { @@ -148,8 +147,9 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } /** - * Union soft constraints with same expressions into a single soft constraint. The new soft constraint weight - * will be equal to the sum of old soft constraints weights. + * Union soft constraints with same expressions into a single soft constraint. + * + * The new soft constraint weight will be equal to the sum of old soft constraints weights. */ private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { var i = 0 @@ -174,7 +174,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Check on satisfiability hard constraints with assumed soft constraints. * - * Returns a triple of solver status, unsat core (if exists, empty list otherwise) and model + * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model * (if exists, null otherwise). */ private fun checkSAT(assumptions: List): Triple>, KModel?> = @@ -213,11 +213,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return Pair(formula, literalsToReify) } - private fun unionReificationVariables(reificationVariables: List>) = with(ctx) { - when (reificationVariables.size) { - 1 -> assert(reificationVariables.first()) - 2 -> assert(reificationVariables[0] or reificationVariables[1]) - else -> assert(reificationVariables.reduce { x, y -> x or y }) + private fun unionReificationLiterals(reificationLiterals: List>) = with(ctx) { + when (reificationLiterals.size) { + 1 -> assert(reificationLiterals.first()) + 2 -> assert(reificationLiterals[0] or reificationLiterals[1]) + else -> assert(reificationLiterals.reduce { x, y -> x or y }) } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt index bfc5c0f33..c3810169e 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt @@ -31,7 +31,7 @@ internal class MaxSATScopeManager { /** * Pop n scopes removing soft constraints added in these scopes. */ - fun pop(n: UInt, softConstraints: MutableList): MutableList { + fun pop(n: UInt = 1u, softConstraints: MutableList): MutableList { require(n <= currentScope) { "Can not pop $n scope levels because current scope level is $currentScope" } From da70d6ed6e413eaf535bfd578fd70720ef331d37 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 4 Jul 2023 20:54:41 +0300 Subject: [PATCH 032/228] Refactor: more Kotlin style in MaxSATSolver --- detekt.yml | 4 +- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 71 ++++++++++--------- .../ksmt/solver/maxsat/MaxSATScopeManager.kt | 8 ++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/detekt.yml b/detekt.yml index ce79c1b5f..07ba84ade 100644 --- a/detekt.yml +++ b/detekt.yml @@ -500,7 +500,7 @@ style: values: - 'FIXME:' - 'STOPSHIP:' - #- 'TODO:' + - 'TODO:' allowedPatterns: '' customMessage: '' ForbiddenImport: @@ -538,7 +538,7 @@ style: maxJumpCount: 1 MagicNumber: active: true - excludes: ['**/test/**', '**/example/**', '**/Main.kt'] + excludes: ['**/test/**', '**/example/**'] ignoreNumbers: - '-1' - '0' diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 8070e9de2..46b454610 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -23,7 +23,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * @see checkMaxSAT * */ fun assertSoft(expr: KExpr, weight: Int) { - require(weight > 0) { "Soft constraint weight must be greater than 0" } + require(weight > 0) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } val softConstraint = SoftConstraint(expr, weight) softConstraints.add(softConstraint) @@ -32,8 +32,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver /** * Solve maximum satisfiability problem. + * + * @throws NotImplementedError */ suspend fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { + var hardConstraintsSatStatus = KSolverStatus.UNKNOWN var currentMaxSATResult: Triple>, KModel?> = Triple(null, listOf(), null) @@ -50,6 +53,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } val status = solver.check() + hardConstraintsSatStatus = status if (status == KSolverStatus.UNSAT) { return@withTimeoutOrNull KMaxSATResult( @@ -74,8 +78,9 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver while (true) { val (solverStatus, unsatCore, model) = checkSAT(formula) + if (solverStatus == KSolverStatus.UNKNOWN) { - return@withTimeoutOrNull handleUnknown(timeoutExceeded = false) + throw NotImplementedError() } currentMaxSATResult = Triple(solverStatus, unsatCore, model) @@ -103,10 +108,15 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val (solverStatus, _, model) = currentMaxSATResult return when (solverStatus) { - null -> KMaxSATResult(listOf(), KSolverStatus.UNKNOWN, maxSATSucceeded = false, timeoutExceeded = true) + null -> KMaxSATResult( + listOf(), + hardConstraintsSatStatus, + maxSATSucceeded = false, + timeoutExceeded = true, + ) KSolverStatus.SAT -> handleSat(model!!) KSolverStatus.UNSAT -> throw NotImplementedError() - KSolverStatus.UNKNOWN -> handleUnknown(timeoutExceeded = true) + KSolverStatus.UNKNOWN -> throw NotImplementedError() } } @@ -132,12 +142,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver unsatCoreSoftConstraints.forEach { x -> if (x.weight > minWeight) { - val minWeightSoftConstraint = SoftConstraint(x.expression, minWeight) - formula.add(minWeightSoftConstraint) + val minWeightedSoftConstraint = SoftConstraint(x.expression, minWeight) + formula.add(minWeightedSoftConstraint) formula.add(SoftConstraint(x.expression, x.weight - minWeight)) formula.removeIf { it.weight == x.weight && it.expression == x.expression } - unsatCoreSoftConstraintsSplit.add(minWeightSoftConstraint) + unsatCoreSoftConstraintsSplit.add(minWeightedSoftConstraint) } else { unsatCoreSoftConstraintsSplit.add(x) } @@ -152,22 +162,24 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * The new soft constraint weight will be equal to the sum of old soft constraints weights. */ private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { - var i = 0 - - while (i < formula.size) { - val currentExpr = formula[i].expression + val exprToRepetitionsMap = mutableMapOf, Int>() - val similarConstraints = formula.filter { it.expression.internEquals(currentExpr) } + formula.forEach { + if (exprToRepetitionsMap.containsKey(it.expression)) { + exprToRepetitionsMap[it.expression] = exprToRepetitionsMap[it.expression]!! + 1 + } else { + exprToRepetitionsMap[it.expression] = 1 + } + } - // Unions soft constraints with same expressions into a single soft constraint. - if (similarConstraints.size > 1) { - val similarConstraintsWeightsSum = similarConstraints.sumOf { it.weight } + exprToRepetitionsMap.forEach { (expr, repetitions) -> + if (repetitions > 1) { + val repeatedExpressions = formula.filter { it.expression == expr } - formula.removeAll(similarConstraints) - formula.add(SoftConstraint(currentExpr, similarConstraintsWeightsSum)) + formula.removeAll(repeatedExpressions) + val repeatedExpressionsWeightsSum = repeatedExpressions.sumOf { it.weight } + formula.add(SoftConstraint(expr, repeatedExpressionsWeightsSum)) } - - i++ } } @@ -195,12 +207,12 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver ): Pair, List>> = with(ctx) { val literalsToReify = mutableListOf>() - for (coreElement in unsatCore.withIndex()) { - if (coreElement.value.weight == weight) { - formula.remove(coreElement.value) + unsatCore.forEachIndexed { index, coreElement -> + if (coreElement.weight == weight) { + formula.remove(coreElement) - val coreElementExpr = coreElement.value.expression - val literalToReify = coreElementExpr.sort.mkConst("*$iter${coreElement.index}") + val coreElementExpr = coreElement.expression + val literalToReify = coreElementExpr.sort.mkConst("*$iter$index") val constraintToReify = coreElementExpr eq !literalToReify @@ -231,12 +243,11 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver weight: Int, ) : MutableList = with(ctx) { - for (indexedLiteral in literalsToReify.withIndex()) { - val index = indexedLiteral.index + literalsToReify.forEachIndexed { index, literal -> val indexLast = literalsToReify.lastIndex if (index < indexLast) { - val sort = indexedLiteral.value.sort + val sort = literal.sort val currentLiteralToReifyDisjunction = sort.mkConst("#$iter$index") val nextLiteralToReifyDisjunction = sort.mkConst("#$iter${index + 1}") @@ -249,7 +260,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver assert(currentLiteralToReifyDisjunction eq disjunction) - formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !indexedLiteral.value, weight)) + formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !literal, weight)) } } @@ -265,10 +276,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } } - private fun handleUnknown(timeoutExceeded: Boolean): KMaxSATResult { - return KMaxSATResult(listOf(), KSolverStatus.SAT, maxSATSucceeded = false, timeoutExceeded) - } - override fun configure(configurator: KSolverConfiguration.() -> Unit) { solver.configure(configurator) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt index c3810169e..499943117 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt @@ -17,7 +17,9 @@ internal class MaxSATScopeManager { } /** - * Push a new scope. + * Push a new scope level. + * + * @see pop */ fun push() { if (currentScope != 0u) { @@ -29,7 +31,9 @@ internal class MaxSATScopeManager { } /** - * Pop n scopes removing soft constraints added in these scopes. + * Pop scope levels with removing soft constraints added in these scope levels. + * + * @see push */ fun pop(n: UInt = 1u, softConstraints: MutableList): MutableList { require(n <= currentScope) { From 04d582dfb00d5b705c97a03cd5a45c6e1770bb0a Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 5 Jul 2023 11:56:28 +0300 Subject: [PATCH 033/228] Remove useless file: Main.kt --- .../main/kotlin/io/ksmt/solver/maxsat/Main.kt | 35 ------------------- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 3 +- 2 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt deleted file mode 100644 index c0c7f809a..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Main.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.KContext -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.utils.getValue -import kotlin.time.Duration.Companion.milliseconds - -suspend fun main() { - with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) - - val x by intSort - val y by intSort - - val a1 = x gt 0.expr - val a2 = x lt y - val a3 = x + y le 0.expr - - maxSATSolver.assert(a3 eq a1) - maxSATSolver.assert(a3 or a2) - - maxSATSolver.assertSoft(a3, 3) - maxSATSolver.assertSoft(!a3, 5) - maxSATSolver.assertSoft(!a1, 10) - maxSATSolver.assertSoft(!a2, 3) - - val result = maxSATSolver.checkMaxSAT(50.milliseconds) - println("Max SAT succeeded: ${result.maxSATSucceeded}") - println("Hard constraints SAT status: ${result.hardConstraintsSATStatus}") - println("Size SAT soft constraints: ${result.satSoftConstraints.size}") - println("Soft constraints:\n${result.satSoftConstraints.forEach { println(it.expression) }}") - println("Timeout exceeded:\n${result.timeoutExceeded}") - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 0487531ea..d2d25a667 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -265,8 +265,7 @@ class KMaxSATSolverTest { for (constraint in constraintsToAssert) { assertTrue( satConstraints.any { - constraint.expression.internEquals(it.expression) && - constraint.weight == it.weight + constraint.expression.internEquals(it.expression) && constraint.weight == it.weight }, ) } From c77387040f234dc88c2ddcf6a33a6f787534e640 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 26 Jul 2023 17:03:04 +0300 Subject: [PATCH 034/228] Do weight unsigned, fix reification variables markers (bugfix) --- ksmt-maxsat/build.gradle.kts | 2 +- .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 16 ++-- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 78 +++++++++---------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/ksmt-maxsat/build.gradle.kts b/ksmt-maxsat/build.gradle.kts index e7beb90a0..99bc0b9ee 100644 --- a/ksmt-maxsat/build.gradle.kts +++ b/ksmt-maxsat/build.gradle.kts @@ -8,8 +8,8 @@ repositories { dependencies { implementation(project(":ksmt-core")) - implementation(project(":ksmt-z3")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation(project(":ksmt-z3")) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index 46b454610..d1a9556df 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -22,8 +22,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * * @see checkMaxSAT * */ - fun assertSoft(expr: KExpr, weight: Int) { - require(weight > 0) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } + fun assertSoft(expr: KExpr, weight: UInt) { + require(weight > 0u) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } val softConstraint = SoftConstraint(expr, weight) softConstraints.add(softConstraint) @@ -131,7 +131,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * @return a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ private fun splitUnsatCore(formula: MutableList, unsatCore: List>) - : Pair> { + : Pair> { // Filters soft constraints from the unsat core. val unsatCoreSoftConstraints = formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } @@ -203,7 +203,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver formula: MutableList, unsatCore: List, iter: Int, - weight: Int, + weight: UInt, ): Pair, List>> = with(ctx) { val literalsToReify = mutableListOf>() @@ -212,7 +212,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver formula.remove(coreElement) val coreElementExpr = coreElement.expression - val literalToReify = coreElementExpr.sort.mkConst("*$iter$index") + val literalToReify = coreElementExpr.sort.mkConst("*$iter:$index") val constraintToReify = coreElementExpr eq !literalToReify @@ -240,7 +240,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver formula: MutableList, literalsToReify: List>, iter: Int, - weight: Int, + weight: UInt, ) : MutableList = with(ctx) { literalsToReify.forEachIndexed { index, literal -> @@ -248,8 +248,8 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver if (index < indexLast) { val sort = literal.sort - val currentLiteralToReifyDisjunction = sort.mkConst("#$iter$index") - val nextLiteralToReifyDisjunction = sort.mkConst("#$iter${index + 1}") + val currentLiteralToReifyDisjunction = sort.mkConst("#$iter:$index") + val nextLiteralToReifyDisjunction = sort.mkConst("#$iter:${index + 1}") val disjunction = when (indexLast - index) { diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index d2d25a667..473b12a7b 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -19,8 +19,8 @@ class KMaxSATSolverTest { maxSATSolver.assert(a) maxSATSolver.assert(b) maxSATSolver.assert(c) - maxSATSolver.assertSoft(a and !c, 3) - maxSATSolver.assertSoft(!a, 5) + maxSATSolver.assertSoft(a and !c, 3u) + maxSATSolver.assertSoft(!a, 5u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() @@ -41,8 +41,8 @@ class KMaxSATSolverTest { maxSATSolver.assert(a or b) maxSATSolver.assert(!a or b) - maxSATSolver.assertSoft(a or !b, 2) - maxSATSolver.assertSoft(!a or !b, 3) + maxSATSolver.assertSoft(a or !b, 2u) + maxSATSolver.assertSoft(!a or !b, 3u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() @@ -50,7 +50,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSATResult.satSoftConstraints) } } @@ -63,9 +63,9 @@ class KMaxSATSolverTest { maxSATSolver.assert(a or b) - maxSATSolver.assertSoft(!a or b, 4) - maxSATSolver.assertSoft(a or !b, 6) - maxSATSolver.assertSoft(!a or !b, 2) + maxSATSolver.assertSoft(!a or b, 4u) + maxSATSolver.assertSoft(a or !b, 6u) + maxSATSolver.assertSoft(!a or !b, 2u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() @@ -74,7 +74,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) val softConstraintsToAssertSAT = - listOf(SoftConstraint(!a or b, 4), SoftConstraint(a or !b, 6)) + listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) } } @@ -89,10 +89,10 @@ class KMaxSATSolverTest { maxSATSolver.assert(x or y) - maxSATSolver.assertSoft(!x or y, 7) - maxSATSolver.assertSoft(x or !y, 6) - maxSATSolver.assertSoft(!x or !y, 3) - maxSATSolver.assertSoft(!x or !y, 4) + maxSATSolver.assertSoft(!x or y, 7u) + maxSATSolver.assertSoft(x or !y, 6u) + maxSATSolver.assertSoft(!x or !y, 3u) + maxSATSolver.assertSoft(!x or !y, 4u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() @@ -102,9 +102,9 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.satSoftConstraints.size == 3) assertSoftConstraintsSat( listOf( - SoftConstraint(!x or y, 7), - SoftConstraint(!x or !y, 3), - SoftConstraint(!x or !y, 4), + SoftConstraint(!x or y, 7u), + SoftConstraint(!x or !y, 3u), + SoftConstraint(!x or !y, 4u), ), maxSATResult.satSoftConstraints, ) @@ -121,10 +121,10 @@ class KMaxSATSolverTest { maxSATSolver.assert(x or y) - maxSATSolver.assertSoft(!x or y, 6) - maxSATSolver.assertSoft(x or !y, 6) - maxSATSolver.assertSoft(!x or !y, 3) - maxSATSolver.assertSoft(!x or !y, 2) + maxSATSolver.assertSoft(!x or y, 6u) + maxSATSolver.assertSoft(x or !y, 6u) + maxSATSolver.assertSoft(!x or !y, 3u) + maxSATSolver.assertSoft(!x or !y, 2u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() @@ -133,7 +133,7 @@ class KMaxSATSolverTest { assertTrue(maxSATResult.maxSATSucceeded) assertTrue(maxSATResult.satSoftConstraints.size == 2) assertSoftConstraintsSat( - listOf(SoftConstraint(!x or y, 6), SoftConstraint(x or !y, 6)), + listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), maxSATResult.satSoftConstraints, ) } @@ -148,15 +148,15 @@ class KMaxSATSolverTest { val b by boolSort maxSATSolver.assert(z) - maxSATSolver.assertSoft(a and b, 1) - maxSATSolver.assertSoft(!a and !b, 5) - maxSATSolver.assertSoft(a and b and z, 2) + maxSATSolver.assertSoft(a and b, 1u) + maxSATSolver.assertSoft(!a and !b, 5u) + maxSATSolver.assertSoft(a and b and z, 2u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSATResult.satSoftConstraints) } } @@ -175,17 +175,17 @@ class KMaxSATSolverTest { maxSATSolver.assert(a3 eq a1) maxSATSolver.assert(a3 or a2) - maxSATSolver.assertSoft(a3, 3) - maxSATSolver.assertSoft(!a3, 5) - maxSATSolver.assertSoft(!a1, 10) - maxSATSolver.assertSoft(!a2, 3) + maxSATSolver.assertSoft(a3, 3u) + maxSATSolver.assertSoft(!a3, 5u) + maxSATSolver.assertSoft(!a1, 10u) + maxSATSolver.assertSoft(!a2, 3u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.satSoftConstraints.size == 2) assertSoftConstraintsSat( - listOf(SoftConstraint(!a3, 5), SoftConstraint(!a1, 10)), + listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), maxSATResult.satSoftConstraints, ) } @@ -200,22 +200,22 @@ class KMaxSATSolverTest { maxSATSolver.assert(a or b) - maxSATSolver.assertSoft(!a or b, 1) + maxSATSolver.assertSoft(!a or b, 1u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) maxSATSolver.push() - maxSATSolver.assertSoft(a or !b, 1) - maxSATSolver.assertSoft(!a or !b, 1) + maxSATSolver.assertSoft(a or !b, 1u) + maxSATSolver.assertSoft(!a or !b, 1u) val maxSATResultScoped = maxSATSolver.checkMaxSAT() assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) maxSATSolver.pop() - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1)), maxSATResult.satSoftConstraints) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) } } @@ -231,20 +231,20 @@ class KMaxSATSolverTest { maxSATSolver.assert(a) maxSATSolver.push() - maxSATSolver.assertSoft(a or b, 1) - maxSATSolver.assertSoft(c or b, 1) + maxSATSolver.assertSoft(a or b, 1u) + maxSATSolver.assertSoft(c or b, 1u) runBlocking { val maxSATResult = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult.satSoftConstraints.size == 2) maxSATSolver.push() - maxSATSolver.assertSoft(!b and !c, 2) + maxSATSolver.assertSoft(!b and !c, 2u) val maxSATResult2 = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult2.satSoftConstraints.size == 2) maxSATSolver.push() - maxSATSolver.assertSoft(a or c, 1) + maxSATSolver.assertSoft(a or c, 1u) val maxSATResult3 = maxSATSolver.checkMaxSAT() assertTrue(maxSATResult3.satSoftConstraints.size == 3) From 6b84643de9970bf86a60825a2983cba7e2399bce Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 4 Aug 2023 12:27:40 +0300 Subject: [PATCH 035/228] Add timeout --- .../io/ksmt/solver/maxsat/Constraint.kt | 8 + .../io/ksmt/solver/maxsat/HardConstraint.kt | 6 + .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 4 - .../io/ksmt/solver/maxsat/KMaxSATSolver.kt | 152 ++++++++++-------- .../io/ksmt/solver/maxsat/SoftConstraint.kt | 2 +- 5 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt new file mode 100644 index 000000000..1f94cbd49 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt @@ -0,0 +1,8 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.expr.KExpr +import io.ksmt.sort.KBoolSort + +interface Constraint { + val expression: KExpr +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt new file mode 100644 index 000000000..8216ec74b --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.expr.KExpr +import io.ksmt.sort.KBoolSort + +class HardConstraint(override val expression: KExpr) : Constraint diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index bf377a8ad..5b9015e8f 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -15,13 +15,9 @@ import io.ksmt.solver.KSolverStatus * * It may end without success in case of exceeding the timeout or in case solver started returning UNKNOWN during * MaxSAT calculation. - * - * @property timeoutExceeded - * Shows whether timeout has been exceeded or not. */ class KMaxSATResult( val satSoftConstraints: List, val hardConstraintsSATStatus: KSolverStatus, val maxSATSucceeded: Boolean, - val timeoutExceeded: Boolean, ) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt index d1a9556df..a448d8a94 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt @@ -9,8 +9,9 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst -import kotlinx.coroutines.withTimeoutOrNull import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver where T : KSolverConfiguration { @@ -30,97 +31,104 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver scopeManager.incrementSoft() } + private var currentMaxSATResult: Triple>, KModel?> = + Triple(null, listOf(), null) + /** * Solve maximum satisfiability problem. * * @throws NotImplementedError */ - suspend fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { - var hardConstraintsSatStatus = KSolverStatus.UNKNOWN - var currentMaxSATResult: Triple>, KModel?> = - Triple(null, listOf(), null) + fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { + if (timeout.isNegative() || timeout == Duration.ZERO) { + error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") + } solver.push() - val maxSATResult = withTimeoutOrNull(timeout) { - if (softConstraints.isEmpty()) { - return@withTimeoutOrNull KMaxSATResult( - listOf(), - solver.check(), - maxSATSucceeded = true, - timeoutExceeded = false, - ) - } + val maxSATResult = runMaxSATLogic(timeout) - val status = solver.check() - hardConstraintsSatStatus = status - - if (status == KSolverStatus.UNSAT) { - return@withTimeoutOrNull KMaxSATResult( - listOf(), - status, - maxSATSucceeded = true, - timeoutExceeded = false, - ) - } else if (status == KSolverStatus.UNKNOWN) { - return@withTimeoutOrNull KMaxSATResult( - listOf(), - status, - maxSATSucceeded = false, - timeoutExceeded = false, - ) - } + solver.pop() + + currentMaxSATResult = Triple(KSolverStatus.UNKNOWN, listOf(), null) + + if (softConstraints.isEmpty()) { + return maxSATResult + } + + // TODO: get max SAT soft constraints subset + if (!maxSATResult.maxSATSucceeded) { + val (solverStatus, _, model) = currentMaxSATResult - var i = 0 - var formula = softConstraints.toMutableList() + return when (solverStatus) { + KSolverStatus.SAT -> handleSat(model!!) + KSolverStatus.UNSAT -> throw NotImplementedError() + KSolverStatus.UNKNOWN -> throw NotImplementedError() + else -> error("Unexpected status: $solverStatus") + } + } - unionSoftConstraintsWithSameExpressions(formula) + return maxSATResult + } - while (true) { - val (solverStatus, unsatCore, model) = checkSAT(formula) + private fun runMaxSATLogic(timeout: Duration): KMaxSATResult { + val clockStart = System.currentTimeMillis() - if (solverStatus == KSolverStatus.UNKNOWN) { - throw NotImplementedError() - } + if (softConstraints.isEmpty()) { + val hardConstraintsStatus = solver.check(timeout) - currentMaxSATResult = Triple(solverStatus, unsatCore, model) + return KMaxSATResult( + listOf(), + hardConstraintsStatus, + hardConstraintsStatus != KSolverStatus.UNKNOWN, + ) + } - if (solverStatus == KSolverStatus.SAT) { - return@withTimeoutOrNull handleSat(model!!) - } + val status = solver.check(timeout) - val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) + if (status == KSolverStatus.UNSAT) { + return KMaxSATResult(listOf(), status, true) + } else if (status == KSolverStatus.UNKNOWN) { + return KMaxSATResult(listOf(), status, false) + } - val (formulaReified, reificationLiterals) = - reifyUnsatCore(formula, splitUnsatCore, i, weight) + var i = 0 + var formula = softConstraints.toMutableList() - unionReificationLiterals(reificationLiterals) + unionSoftConstraintsWithSameExpressions(formula) - formula = applyMaxRes(formulaReified, reificationLiterals, i, weight) + while (true) { + val softConstraintsCheckRemainingTime = computeRemainingTime(timeout, clockStart) - i++ + if (softConstraintsCheckRemainingTime.isNegative() || softConstraintsCheckRemainingTime == Duration.ZERO) { + return KMaxSATResult(listOf(), status, false) } - } - solver.pop() + val (solverStatus, unsatCore, model) = + checkSAT(formula, softConstraintsCheckRemainingTime) - if (maxSATResult == null) { - val (solverStatus, _, model) = currentMaxSATResult + if (solverStatus == KSolverStatus.UNKNOWN) { + // TODO: get max SAT soft constraints subset + return KMaxSATResult(listOf(), status, false) + } - return when (solverStatus) { - null -> KMaxSATResult( - listOf(), - hardConstraintsSatStatus, - maxSATSucceeded = false, - timeoutExceeded = true, - ) - KSolverStatus.SAT -> handleSat(model!!) - KSolverStatus.UNSAT -> throw NotImplementedError() - KSolverStatus.UNKNOWN -> throw NotImplementedError() + currentMaxSATResult = Triple(solverStatus, unsatCore, model) + + if (solverStatus == KSolverStatus.SAT) { + return handleSat(model!!) } - } - return maxSATResult as KMaxSATResult + val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) + + val (formulaReified, reificationLiterals) = + reifyUnsatCore(formula, splitUnsatCore, i, weight) + + unionReificationLiterals(reificationLiterals) + + formula = applyMaxRes(formulaReified, reificationLiterals, i, weight) + + i++ + } } /** @@ -189,8 +197,9 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model * (if exists, null otherwise). */ - private fun checkSAT(assumptions: List): Triple>, KModel?> = - when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression })) { + private fun checkSAT(assumptions: List, timeout: Duration): + Triple>, KModel?> = + when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) @@ -269,13 +278,18 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver private fun handleSat(model: KModel): KMaxSATResult { val satSoftConstraints = getSatSoftConstraintsByModel(model) - return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = true, timeoutExceeded = false) + return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = true) } private fun getSatSoftConstraintsByModel(model: KModel): List { return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } } + private fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { + val msUnit = DurationUnit.MILLISECONDS + return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) + } + override fun configure(configurator: KSolverConfiguration.() -> Unit) { solver.configure(configurator) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt index 3833a54af..09e9b9e37 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt @@ -3,4 +3,4 @@ package io.ksmt.solver.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort -class SoftConstraint(val expression: KExpr, val weight: Int) +class SoftConstraint(override val expression: KExpr, val weight: UInt) : Constraint From 6feb1e6dc120603afecba8a1a15f2a4acbe1a6c7 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 7 Aug 2023 17:23:10 +0300 Subject: [PATCH 036/228] Add ksmt-maxsat-tests project --- buildSrc/src/main/kotlin/BenchmarkUtil.kt | 57 +++ .../src/main/kotlin/MaxSATBenchmarkUtil.kt | 33 ++ .../src/main/kotlin/SmtLibBenchmarkUtil.kt | 57 +-- ksmt-maxsat-test/build.gradle.kts | 49 ++ .../io/ksmt/maxsat/test/CurrentLineState.kt | 8 + .../kotlin/io/ksmt/maxsat/test/TestParser.kt | 77 +++ .../ksmt/maxsat/test/KMaxSATBenchmarkTest.kt | 457 ++++++++++++++++++ ksmt-test/build.gradle.kts | 2 +- settings.gradle.kts | 1 + 9 files changed, 696 insertions(+), 45 deletions(-) create mode 100644 buildSrc/src/main/kotlin/BenchmarkUtil.kt create mode 100644 buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt create mode 100644 ksmt-maxsat-test/build.gradle.kts create mode 100644 ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt create mode 100644 ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt diff --git a/buildSrc/src/main/kotlin/BenchmarkUtil.kt b/buildSrc/src/main/kotlin/BenchmarkUtil.kt new file mode 100644 index 000000000..70bcb4970 --- /dev/null +++ b/buildSrc/src/main/kotlin/BenchmarkUtil.kt @@ -0,0 +1,57 @@ +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.get +import java.io.File + +fun Project.downloadPreparedBenchmarkTestData( + downloadPath: File, + testDataPath: File, + benchmarkName: String, + benchmarkUrl: String, +): TaskProvider = + tasks.register("downloadPrepared${benchmarkName}BenchmarkTestData") { + doLast { + download(benchmarkUrl, downloadPath) + + copy { + from(zipTree(downloadPath)) + into(testDataPath) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + } + +fun Project.usePreparedBenchmarkTestData(path: File, benchmarkName: String): TaskProvider = + tasks.register("${benchmarkName}Benchmark-data-use") { + doLast { + check(path.exists()) { "No test data provided" } + val testResources = testResourceDir() ?: error("No resource directory found for benchmarks") + val testData = testResources.resolve("testData") + + testData.executeIfNotReady("test-data-ready") { + copy { + from(path) + into(testData) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + } + } + +fun Project.testResourceDir(): File? { + val sourceSets = (this as ExtensionAware).extensions.getByName("sourceSets") as SourceSetContainer + return sourceSets["test"]?.output?.resourcesDir +} + +inline fun File.executeIfNotReady(markerName: String, body: () -> Unit) { + val marker = this.resolve(markerName) + if (marker.exists()) return + + body() + + marker.createNewFile() +} diff --git a/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt b/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt new file mode 100644 index 000000000..6e6cb0130 --- /dev/null +++ b/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt @@ -0,0 +1,33 @@ +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.kotlin.dsl.support.unzipTo +import java.io.File + +fun Project.usePreparedMaxSATBenchmarkTestData(path: File): Task = + usePreparedBenchmarkTestData(path, MAXSAT_BENCHMARK_NAME_L) + .get() + .finalizedBy(unzipMaxSATBenchmarkTestFiles()) + +fun Project.downloadPreparedMaxSATBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) = + downloadPreparedBenchmarkTestData( + downloadPath, + testDataPath, + MAXSAT_BENCHMARK_NAME_U, + "$MAXSAT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/maxsat-benchmark.zip", + ) + +fun Project.unzipMaxSATBenchmarkTestFiles() = + tasks.register("unzip${MAXSAT_BENCHMARK_NAME_U}BenchmarkFiles") { + doLast { + val testResources = testResourceDir() ?: error("No resource directory found for benchmarks") + val testData = testResources.resolve("testData") + + testData.listFiles()?.forEach { if (it.isFile && it.extension == "zip") unzipTo(it.parentFile, it) } + } + } + +private const val MAXSAT_BENCHMARK_REPO_URL = "https://github.com/victoriafomina/ksmt" + +private const val MAXSAT_BENCHMARK_NAME_U = "MaxSat" + +private const val MAXSAT_BENCHMARK_NAME_L = "maxSat" diff --git a/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt b/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt index 42696d6a4..84677999d 100644 --- a/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt @@ -1,15 +1,12 @@ import org.gradle.api.Project import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.plugins.ExtensionAware -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.kotlin.dsl.get import java.io.File fun Project.mkSmtLibBenchmarkTestData(name: String) = tasks.register("smtLibBenchmark-$name") { doLast { val path = buildDir.resolve("smtLibBenchmark/$name") val downloadTarget = path.resolve("$name.zip") - val url = "$BENCHMARK_REPO_URL/zip/$name.zip" + val url = "$MK_SMTLIB_BENCHMARK_REPO_URL/zip/$name.zip" download(url, downloadTarget) @@ -36,49 +33,21 @@ fun Project.mkSmtLibBenchmarkTestData(name: String) = tasks.register("smtLibBenc } } -fun Project.usePreparedSmtLibBenchmarkTestData(path: File) = tasks.register("smtLibBenchmark-data-use") { - doLast { - check(path.exists()) { "No test data provided" } - val testResources = testResourceDir() ?: error("No resource directory found for benchmarks") - val testData = testResources.resolve("testData") - - testData.executeIfNotReady("test-data-ready") { - copy { - from(path) - into(testData) - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - } - } +fun Project.usePreparedSmtLibBenchmarkTestData(path: File) { + usePreparedBenchmarkTestData(path, SMTLIB_BENCHMARK_NAME_L) } -fun Project.downloadPreparedSmtLibBenchmarkTestData(downloadPath: File, testDataPath: File, version: String) = - tasks.register("downloadPreparedSmtLibBenchmarkTestData") { - doLast { - val benchmarksUrl = "https://github.com/UnitTestBot/ksmt/releases/download/$version/benchmarks.zip" - - download(benchmarksUrl, downloadPath) - - copy { - from(zipTree(downloadPath)) - into(testDataPath) - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - } - } - -private fun Project.testResourceDir(): File? { - val sourceSets = (this as ExtensionAware).extensions.getByName("sourceSets") as SourceSetContainer - return sourceSets["test"]?.output?.resourcesDir +fun Project.downloadPreparedSmtLibBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) { + downloadPreparedBenchmarkTestData( + downloadPath, + testDataPath, + SMTLIB_BENCHMARK_NAME_U, + "https://github.com/UnitTestBot/ksmt/releases/download/$testDataRevision/benchmarks.zip", + ) } -private const val BENCHMARK_REPO_URL = "http://smt-lib.loria.fr" +private const val MK_SMTLIB_BENCHMARK_REPO_URL = "http://smt-lib.loria.fr" -private inline fun File.executeIfNotReady(markerName: String, body: () -> Unit) { - val marker = this.resolve(markerName) - if (marker.exists()) return +private const val SMTLIB_BENCHMARK_NAME_U = "SmtLib" - body() - - marker.createNewFile() -} +private const val SMTLIB_BENCHMARK_NAME_L = "smtLib" diff --git a/ksmt-maxsat-test/build.gradle.kts b/ksmt-maxsat-test/build.gradle.kts new file mode 100644 index 000000000..35333a699 --- /dev/null +++ b/ksmt-maxsat-test/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("io.ksmt.ksmt-base") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":ksmt-core")) + implementation(project(":ksmt-maxsat")) + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.2") + testImplementation(project(":ksmt-z3")) +} + +val runBenchmarksBasedTests = project.booleanProperty("runBenchmarksBasedTests") ?: false + +// use benchmarks from testData directory instead of downloading +val usePreparedBenchmarks = project.booleanProperty("usePreparedBenchmarks") ?: true + +val testDataDir = projectDir.resolve("testData") +val unpackedTestDataDir = testDataDir.resolve("data") +val downloadedTestData = testDataDir.resolve("testData.zip") +val testDataRevision = project.stringProperty("testDataRevision") ?: "no-revision" + +val downloadPreparedBenchmarksTestData = downloadPreparedMaxSATBenchmarkTestData( + downloadPath = downloadedTestData, + testDataPath = unpackedTestDataDir, + testDataRevision = testDataRevision, +) + +val preparedMaxSATBenchmarkTestData = usePreparedMaxSATBenchmarkTestData(unpackedTestDataDir) + +val usePreparedTestData by tasks.registering { + tasks.withType().forEach { it.enabled = false } + if (!usePreparedBenchmarks) { + dependsOn.add(downloadPreparedBenchmarksTestData) + } + + finalizedBy(preparedMaxSATBenchmarkTestData) +} + +tasks.withType { + if (runBenchmarksBasedTests) { + dependsOn.add(usePreparedTestData) + } +} diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt new file mode 100644 index 000000000..9f98c0a1f --- /dev/null +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt @@ -0,0 +1,8 @@ +package io.ksmt.maxsat.test + +enum class CurrentLineState { + COMMENT, + HARD_CONSTRAINT, + SOFT_CONSTRAINT, + ERROR, +} diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt new file mode 100644 index 000000000..d67ddbfab --- /dev/null +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt @@ -0,0 +1,77 @@ +package io.ksmt.maxsat.test + +import io.ksmt.KContext +import io.ksmt.solver.maxsat.Constraint +import io.ksmt.solver.maxsat.HardConstraint +import io.ksmt.solver.maxsat.SoftConstraint +import io.ksmt.utils.mkConst +import java.io.File +import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.io.path.notExists + +fun parseTest(path: Path, ctx: KContext): List { + var currentState: CurrentLineState + + val constraints = mutableListOf() + + if (path.notExists()) { + error("Path [$path] does not exist") + } + + require(path.extension == "wcnf") { + "File extension cannot be '${path.extension}' as it must be 'wcnf'" + } + + File(path.toUri()).forEachLine { wcnfStr -> + currentState = when { + wcnfStr.startsWith("c") -> CurrentLineState.COMMENT + wcnfStr.startsWith("h ") -> CurrentLineState.HARD_CONSTRAINT + wcnfStr.substringBefore(" ").toUIntOrNull() != null -> CurrentLineState.SOFT_CONSTRAINT + else -> CurrentLineState.ERROR + } + + if (currentState == CurrentLineState.ERROR) { + error("Unexpected string:\n\"$wcnfStr\"") + } + + if (currentState == CurrentLineState.HARD_CONSTRAINT || currentState == CurrentLineState.SOFT_CONSTRAINT) { + val constraintBeginIndex = getConstraintBeginIndex(currentState, wcnfStr) + val constraintEndIndex = wcnfStr.lastIndex - 1 + + // We do not take the last element in the string as this is a zero indicating the constraint end. + val constraintStr = wcnfStr.substring(constraintBeginIndex, constraintEndIndex) + val constraintStrSplit = constraintStr.split(" ").filterNot { it.isEmpty() } + val constraintIntVars = constraintStrSplit.map { x -> x.toInt() } + + with(ctx) { + val constraintVars = constraintIntVars.map { + if (it < 0) { + !ctx.boolSort.mkConst("${-it}") + } else if (it > 0) { + ctx.boolSort.mkConst("$it") + } else { + error("Map element should not be 0!") + } + } + + val constraint = constraintVars.reduce { x, y -> x or y } + + if (currentState == CurrentLineState.HARD_CONSTRAINT) { + constraints.add(HardConstraint(constraint)) + } else { + val weight = wcnfStr.substringBefore(" ").toUInt() + constraints.add(SoftConstraint(constraint, weight)) + } + } + } + } + + return constraints +} + +private fun getConstraintBeginIndex(currentState: CurrentLineState, str: String): Int = when (currentState) { + CurrentLineState.HARD_CONSTRAINT -> 2 + CurrentLineState.SOFT_CONSTRAINT -> str.substringBefore(" ").length + 1 + else -> error("Unexpected value: [${currentState}]") +} diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt new file mode 100644 index 000000000..967ffec1b --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt @@ -0,0 +1,457 @@ +package io.ksmt.maxsat.test + +import io.ksmt.KContext +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.maxsat.HardConstraint +import io.ksmt.solver.maxsat.KMaxSATSolver +import io.ksmt.solver.maxsat.SoftConstraint +import io.ksmt.solver.z3.KZ3Solver +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.relativeTo +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.milliseconds + +class KMaxSATBenchmarkTest { + @ParameterizedTest(name = "{0}") + @MethodSource("maxSATTestData") + fun maxSATTest(name: String, samplePath: Path) = with(KContext()) { + val testData = maxSATTestNameToExpectedResult.find { it.first == name } + require(testData != null) { "Test [$name] expected result must be specified" } + + val constraints = parseTest(samplePath, this) + + val z3Solver = KZ3Solver(this) + val maxSATSolver = KMaxSATSolver(this, z3Solver) + + var sumOfSoftConstraintsWeights = 0u + + constraints.forEach { + if (it is HardConstraint) { + maxSATSolver.assert(it.expression) + } else { + maxSATSolver.assertSoft(it.expression, (it as SoftConstraint).weight) + sumOfSoftConstraintsWeights += it.weight + } + } + + val maxSATResult = maxSATSolver.checkMaxSAT(1000.milliseconds) + val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } + + assertEquals(KSolverStatus.SAT, maxSATResult.hardConstraintsSATStatus) + assertTrue(maxSATResult.maxSATSucceeded && maxSATResult.satSoftConstraints.isNotEmpty()) + assertEquals( + sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second, + satConstraintsScore.toULong(), + ) + } + + data class BenchmarkTestArguments( + val name: String, + val samplePath: Path, + ) : Arguments { + override fun get() = arrayOf(name, samplePath) + } + + companion object { + private fun testDataLocation(): Path = this::class.java.classLoader + .getResource("testData") + ?.toURI() + ?.let { Paths.get(it) } + ?: error("No test data") + + private fun prepareTestData(): List { + val testDataLocation = testDataLocation() + return testDataLocation + .listDirectoryEntries("*.wcnf") + .sorted() + .map { BenchmarkTestArguments(it.relativeTo(testDataLocation).toString(), it) } + } + + private val testData by lazy { + prepareTestData() + } + + @JvmStatic + fun maxSATTestData() = testData + } + + // Elements are pairs of test name and expected test result (cost equal to the excluded soft constraints sum). + private val maxSATTestNameToExpectedResult = hashSetOf>( + "ae_5_20_ibmq-casablanca_7.wcnf" to 15u, + "af-synthesis_stb_50_20_8.wcnf" to 120u, + "af-synthesis_stb_50_40_0.wcnf" to 111u, + "af-synthesis_stb_50_40_8.wcnf" to 117u, + "af-synthesis_stb_50_60_3.wcnf" to 115u, + "af-synthesis_stb_50_80_7.wcnf" to 115u, + "af-synthesis_stb_50_80_8.wcnf" to 116u, + "af-synthesis_stb_50_100_3.wcnf" to 104u, + "af-synthesis_stb_50_120_3.wcnf" to 100u, + "af-synthesis_stb_50_140_1.wcnf" to 127u, + "af-synthesis_stb_50_140_3.wcnf" to 96u, + "af-synthesis_stb_50_140_8.wcnf" to 113u, + "af-synthesis_stb_50_160_5.wcnf" to 113u, + "af-synthesis_stb_50_180_1.wcnf" to 130u, + "af-synthesis_stb_50_200_4.wcnf" to 105u, + "af-synthesis_stb_50_200_5.wcnf" to 102u, + "af-synthesis_stb_50_200_6.wcnf" to 111u, + "amazon.dimacs.wcnf" to 113575u, + "ar-3.wcnf" to 43814u, + "archlinux.dimacs.wcnf" to 11744u, + "auc.cat_paths_60_100_0006.txt.wcnf" to 70929u, + "auc.cat_paths_60_150_0007.txt.wcnf" to 100364u, + "auc.cat_paths_60_170_0007.txt.wcnf" to 94165u, + "auc.cat_paths_60_200_0009.txt.wcnf" to 157873u, + "auc.cat_reg_60_110_0004.txt.wcnf" to 75602u, + "auc.cat_reg_60_130_0001.txt.wcnf" to 104032u, + "auc.cat_reg_60_160_0002.txt.wcnf" to 164897u, + "auc.cat_sched_60_80_0005.txt.wcnf" to 34950u, + "auc.cat_sched_60_90_0001.txt.wcnf" to 82847u, + "auc.cat_sched_60_100_0003.txt.wcnf" to 34771u, + "auc.cat_sched_60_120_0004.txt.wcnf" to 82385u, + "auc.cat_sched_60_150_0001.txt.wcnf" to 155865u, + "auc.cat_sched_60_150_0005.txt.wcnf" to 72866u, + "auc.cat_sched_60_150_0009.txt.wcnf" to 58699u, + "auc.cat_sched_60_160_0003.txt.wcnf" to 118883u, + "auc.cat_sched_60_200_0005.txt.wcnf" to 172768u, + "cap71.wcsp.wcnf" to 9326144u, + "cap72.wcsp.wcnf" to 9777981u, + "cap91.wcsp.wcnf" to 7966472u, + "cap92.wcsp.wcnf" to 8547029u, + "cap131.wcsp.wcnf" to 7934385u, + "cap132.wcsp.wcnf" to 8514942u, + "car.formula_0.8_2021_atleast_15_max-3_reduced_incomplete_adaboost_2.wcnf" to 232u, + "causal_Meta_7_528.wcnf" to 55120u, + "causal_n5_i5_N1000_uai13_log_int.wcnf" to 46030289u, + "causal_n6_i1_N10000_uai14_log_int.wcnf" to 1510725680u, + "causal_n6_i6_N1000_uai14_log_int.wcnf" to 126257527700u, + "causal_n7_i8_N10000_uai14_log_int.wcnf" to 11486104693u, + "causal_n7_i10_N1000_uai14_log_int.wcnf" to 3246397504u, + "causal_Pigs_6_10000.wcnf" to 25539892u, + "causal_Statlog_7_752.wcnf" to 380356u, + "causal_Voting_7_435.wcnf" to 930263u, + "causal_Water_7_380.wcnf" to 414473u, + "comp04.wcnf" to 35u, + "comp06.wcnf" to 27u, + "CSG40-40-95.wcnf" to 8847u, + "CSG60-60-88.wcnf" to 7714u, + "CSGNaive60-60-53.wcnf" to 9829u, + "CSGNaive70-70-91.wcnf" to 11177u, + "dblp.dimacs.wcnf" to 25014u, + "dim.brock800_3.clq.wcnf" to 1079u, + "dim.c-fat200-1.clq.wcnf" to 14u, + "dim.c-fat500-1.clq.wcnf" to 10u, + "dim.san400_0.7_3.clq.wcnf" to 1201u, + "dir.5.wcsp.dir.wcnf" to 261u, + "dir.28.wcsp.dir.wcnf" to 270105u, + "dir.54.wcsp.dir.wcnf" to 37u, + "dir.404.wcsp.dir.wcnf" to 114u, + "dir.408.wcsp.dir.wcnf" to 6228u, + "dir.507.wcsp.dir.wcnf" to 27390u, + "dir.509.wcsp.dir.wcnf" to 27390u, + "dir.1403.wcsp.dir.wcnf" to 459246u, + "dir.1502.wcsp.dir.wcnf" to 28042u, + "dir.1506.wcsp.dir.wcnf" to 354517u, + "drmx-am12-outof-40-ecardn-w.wcnf" to 28u, + "drmx-am12-outof-40-esortn-w.wcnf" to 28u, + "drmx-am16-outof-45-emtot-w.wcnf" to 29u, + "drmx-am16-outof-45-eseqc-w.wcnf" to 29u, + "drmx-am20-outof-50-ekmtot-w.wcnf" to 30u, + "drmx-am20-outof-50-emtot-w.wcnf" to 30u, + "drmx-am20-outof-50-eseqc-w.wcnf" to 30u, + "drmx-am24-outof-55-emtot-w.wcnf" to 31u, + "drmx-am24-outof-55-etot-w.wcnf" to 31u, + "drmx-am28-outof-60-emtot-w.wcnf" to 32u, + "drmx-am28-outof-60-eseqc-w.wcnf" to 32u, + "drmx-am28-outof-60-etot-w.wcnf" to 32u, + "drmx-am32-outof-70-ecardn-w.wcnf" to 38u, + "drmx-am32-outof-70-ekmtot-w.wcnf" to 38u, + "drmx-am32-outof-70-emtot-w.wcnf" to 38u, + "drmx-am32-outof-70-etot-w.wcnf" to 38u, + "eas.310-15.wcnf" to 30501u, + "eas.310-28.wcnf" to 33151u, + "eas.310-29.wcnf" to 34431u, + "eas.310-33.wcnf" to 35463u, + "eas.310-43.wcnf" to 33138u, + "eas.310-44.wcnf" to 50871u, + "eas.310-55.wcnf" to 21952u, + "eas.310-74.wcnf" to 21867u, + "eas.310-91.wcnf" to 37183u, + "eas.310-93.wcnf" to 19146u, + "eas.310-94.wcnf" to 26160u, + "eas.310-95.wcnf" to 25854u, + "eas.310-97.wcnf" to 35249u, + "ebay.dimacs.wcnf" to 123941u, + "f1-DataDisplay_0_order4.seq-A-2-1-EDCBAir.wcnf" to 6223203u, + "f1-DataDisplay_0_order4.seq-A-2-2-abcdeir.wcnf" to 481429u, + "f1-DataDisplay_0_order4.seq-A-2-2-irabcde.wcnf" to 2220415u, + "f1-DataDisplay_0_order4.seq-A-3-1-EDCBAir.wcnf" to 6240245u, + "f1-DataDisplay_0_order4.seq-A-3-1-irabcde.wcnf" to 5960556u, + "f1-DataDisplay_0_order4.seq-A-3-2-irabcde.wcnf" to 5955300u, + "f1-DataDisplay_0_order4.seq-B-2-2-abcdeir.wcnf" to 53533u, + "f1-DataDisplay_0_order4.seq-B-2-2-irEDCBA.wcnf" to 2273346u, + "f49-DC_TotalLoss.seq-A-2-1-abcdeir.wcnf" to 27698412327u, + "f49-DC_TotalLoss.seq-A-2-1-irEDCBA.wcnf" to 14779649425u, + "f49-DC_TotalLoss.seq-A-2-combined-irabcde.wcnf" to 14735114187u, + "f49-DC_TotalLoss.seq-A-3-2-irEDCBA.wcnf" to 87222797189u, + "f49-DC_TotalLoss.seq-B-2-2-abcdeir.wcnf" to 44321234u, + "f49-DC_TotalLoss.seq-B-2-combined-EDCBAir.wcnf" to 83838199998u, + "f49-DC_TotalLoss.seq-B-3-combined-EDCBAir.wcnf" to 117355113043u, + "f49-DC_TotalLoss.seq-B-3-combined-irabcde.wcnf" to 87177360578u, + "facebook1.dimacs.wcnf" to 45581u, + "github.dimacs.wcnf" to 187405u, + "graphstate_6_6_rigetti-agave_8.wcnf" to 6u, + "grover-noancilla_4_52_rigetti-agave_8.wcnf" to 42u, + "grover-v-chain_4_52_ibmq-casablanca_7.wcnf" to 27u, + "guardian.dimacs.wcnf" to 160777u, + "inst2.lp.sm-extracted.wcnf" to 97u, + "inst10.lp.sm-extracted.wcnf" to 105u, + "inst22.lp.sm-extracted.wcnf" to 180u, + "instance1.wcnf" to 607u, + "instance2.wcnf" to 828u, + "ItalyInstance1.xml.wcnf" to 12u, + "k50-18-30.rna.pre.wcnf" to 462u, + "k50-21-38.rna.pre.wcnf" to 497u, + "k100-14-38.rna.pre.wcnf" to 1953u, + "k100-20-63.rna.pre.wcnf" to 2030u, + "k100-38-60.rna.pre.wcnf" to 1878u, + "k100-40-52.rna.pre.wcnf" to 1861u, + "k100-73-76.rna.pre.wcnf" to 2008u, + "k100-78-85.rna.pre.wcnf" to 1744u, + "lisbon-wedding-1-18.wcnf" to 961u, + "lisbon-wedding-2-18.wcnf" to 1137u, + "lisbon-wedding-3-17.wcnf" to 1035u, + "lisbon-wedding-4-18.wcnf" to 803u, + "lisbon-wedding-5-17.wcnf" to 802u, + "lisbon-wedding-9-17.wcnf" to 394u, + "lisbon-wedding-10-17.wcnf" to 377u, + "log.8.wcsp.log.wcnf" to 2u, + "log.28.wcsp.log.wcnf" to 270105u, + "log.408.wcsp.log.wcnf" to 6228u, + "log.505.wcsp.log.wcnf" to 21253u, + "log.1401.wcsp.log.wcnf" to 459106u, + "londonist.dimacs.wcnf" to 70703u, + "metro_8_8_5_20_10_6_500_1_0.lp.sm-extracted.wcnf" to 82u, + "metro_8_8_5_20_10_6_500_1_7.lp.sm-extracted.wcnf" to 89u, + "metro_8_8_5_20_10_6_500_1_9.lp.sm-extracted.wcnf" to 105u, + "metro_9_8_7_22_10_6_500_1_1.lp.sm-extracted.wcnf" to 52u, + "metro_9_8_7_22_10_6_500_1_2.lp.sm-extracted.wcnf" to 60u, + "metro_9_8_7_22_10_6_500_1_3.lp.sm-extracted.wcnf" to 44u, + "metro_9_8_7_30_10_6_500_1_5.lp.sm-extracted.wcnf" to 47u, + "metro_9_8_7_30_10_6_500_1_6.lp.sm-extracted.wcnf" to 31u, + "metro_9_8_7_30_10_6_500_1_7.lp.sm-extracted.wcnf" to 47u, + "metro_9_8_7_30_10_6_500_1_8.lp.sm-extracted.wcnf" to 55u, + "metro_9_9_10_35_13_7_500_2_7.lp.sm-extracted.wcnf" to 37u, + "MinWidthCB_milan_100_12_1k_1s_2t_3.wcnf" to 109520u, + "MinWidthCB_milan_200_12_1k_4s_1t_4.wcnf" to 108863u, + "MinWidthCB_mitdbsample_100_43_1k_2s_2t_2.wcnf" to 38570u, + "MinWidthCB_mitdbsample_100_64_1k_2s_1t_2.wcnf" to 66045u, + "MinWidthCB_mitdbsample_200_43_1k_2s_2t_2.wcnf" to 50615u, + "MinWidthCB_mitdbsample_200_64_1k_2s_1t_2.wcnf" to 78400u, + "MinWidthCB_mitdbsample_200_64_1k_2s_3t_2.wcnf" to 73730u, + "MinWidthCB_mitdbsample_300_26_1k_3s_2t_3.wcnf" to 32420u, + "MLI.ilpd_train_0_DNF_5_5.wcnf" to 700u, + "mul.role_smallcomp_multiple_0.3_6.wcnf" to 139251u, + "mul.role_smallcomp_multiple_1.0_6.wcnf" to 295598u, + "openstreetmap.dimacs.wcnf" to 65915u, + "pac.80cfe9a6-9b1b-11df-965e-00163e46d37a_l1.wcnf" to 1924238u, + "pac.fa3d0fb2-db9e-11df-a0ec-00163e3d3b7c_l1.wcnf" to 4569599u, + "pac.rand179_l1.wcnf" to 493118u, + "pac.rand892_l1.wcnf" to 224702u, + "pac.rand984_l1.wcnf" to 345082u, + "ped2.B.recomb1-0.01-2.wcnf" to 7u, + "ped2.B.recomb1-0.10-7.wcnf" to 588u, + "ped3.D.recomb10-0.20-12.wcnf" to 349u, + "ped3.D.recomb10-0.20-14.wcnf" to 7u, + "portfoliovqe_4_18_rigetti-agave_8.wcnf" to 33u, + "power-distribution_1_2.wcnf" to 3u, + "power-distribution_1_4.wcnf" to 3u, + "power-distribution_1_6.wcnf" to 3u, + "power-distribution_1_8.wcnf" to 3u, + "power-distribution_2_2.wcnf" to 10u, + "power-distribution_2_8.wcnf" to 10u, + "power-distribution_3_4.wcnf" to 1u, + "power-distribution_7_6.wcnf" to 18u, + "power-distribution_8_4.wcnf" to 40u, + "power-distribution_8_7.wcnf" to 40u, + "power-distribution_9_2.wcnf" to 18u, + "power-distribution_11_6.wcnf" to 126u, + "power-distribution_12_2.wcnf" to 216u, + "power-distribution_12_5.wcnf" to 216u, + "qaoa_4_16_ibmq-casablanca_7.wcnf" to 12u, + "qft_5_26_ibmq-casablanca_7.wcnf" to 15u, + "qftentangled_4_21_ibmq-casablanca_7.wcnf" to 15u, + "qftentangled_4_39_rigetti-agave_8.wcnf" to 18u, + "qftentangled_5_30_ibmq-london_5.wcnf" to 27u, + "qftentangled_5_48_rigetti-agave_8.wcnf" to 24u, + "qgan_6_15_ibmq-casablanca_7.wcnf" to 24u, + "qpeexact_5_26_ibmq-casablanca_7.wcnf" to 15u, + "qwalk-v-chain_3_30_ibmq-casablanca_7.wcnf" to 30u, + "qwalk-v-chain_5_102_ibmq-london_5.wcnf" to 81u, + "rail507.wcnf" to 174u, + "rail516.wcnf" to 182u, + "rail582.wcnf" to 211u, + "ran.max_cut_60_420_2.asc.wcnf" to 703u, + "ran.max_cut_60_420_5.asc.wcnf" to 715u, + "ran.max_cut_60_420_9.asc.wcnf" to 674u, + "ran.max_cut_60_500_2.asc.wcnf" to 900u, + "ran.max_cut_60_560_3.asc.wcnf" to 1054u, + "ran.max_cut_60_560_7.asc.wcnf" to 1053u, + "ran.max_cut_60_600_1.asc.wcnf" to 1156u, + "ran.max_cut_60_600_9.asc.wcnf" to 1149u, + "random-dif-2.rna.pre.wcnf" to 929u, + "random-dif-9.rna.pre.wcnf" to 456u, + "random-dif-16.rna.pre.wcnf" to 768u, + "random-dif-25.rna.pre.wcnf" to 512u, + "random-net-20-5_network-4.net.wcnf" to 19602u, + "random-net-30-3_network-2.net.wcnf" to 27606u, + "random-net-30-4_network-3.net.wcnf" to 24925u, + "random-net-40-2_network-8.net.wcnf" to 38289u, + "random-net-40-2_network-9.net.wcnf" to 35951u, + "random-net-40-3_network-5.net.wcnf" to 35488u, + "random-net-40-4_network-2.net.wcnf" to 36427u, + "random-net-50-3_network-5.net.wcnf" to 41356u, + "random-net-50-4_network-8.net.wcnf" to 43243u, + "random-net-60-3_network-3.net" to 50929u, + "random-net-100-1_network-3.net.wcnf" to 91570u, + "random-net-120-1_network-5.net.wcnf" to 117198u, + "random-net-220-1_network-7.net.wcnf" to 203783u, + "random-net-240-1_network-7.net.wcnf" to 219252u, + "random-net-260-1_network-4.net.wcnf" to 238131u, + "random-same-5.rna.pre.wcnf" to 456u, + "random-same-12.rna.pre.wcnf" to 597u, + "random-same-19.rna.pre.wcnf" to 337u, + "random-same-25.rna.pre.wcnf" to 224u, + "ran-scp.scp41_weighted.wcnf" to 429u, + "ran-scp.scp48_weighted.wcnf" to 492u, + "ran-scp.scp49_weighted.wcnf" to 641u, + "ran-scp.scp51_weighted.wcnf" to 253u, + "ran-scp.scp54_weighted.wcnf" to 242u, + "ran-scp.scp56_weighted.wcnf" to 213u, + "ran-scp.scp58_weighted.wcnf" to 288u, + "ran-scp.scp65_weighted.wcnf" to 161u, + "ran-scp.scp410_weighted.wcnf" to 514u, + "ran-scp.scpnre5_weighted.wcnf" to 28u, + "ran-scp.scpnrf1_weighted.wcnf" to 14u, + "ran-scp.scpnrf4_weighted.wcnf" to 14u, + "ran-scp.scpnrf5_weighted.wcnf" to 13u, + "realamprandom_4_72_rigetti-agave_8.wcnf" to 36u, + "role_smallcomp_0.7_11.wcnf" to 333834u, + "role_smallcomp_0.75_8.wcnf" to 348219u, + "role_smallcomp_0.85_4.wcnf" to 369639u, + "role_smallcomp_0.85_7.wcnf" to 369639u, + "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_2.wcnf" to 24564427u, + "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_3.wcnf" to 24564427u, + "Rounded_BTWBNSL_asia_10000_1_3.scores_TWBound_2.wcnf" to 2247208255u, + "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_2.wcnf" to 602126938u, + "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_3.wcnf" to 601946991u, + "Rounded_BTWBNSL_Heart.BIC_TWBound_2.wcnf" to 239742296u, + "Rounded_BTWBNSL_insurance_100_1_3.scores_TWBound_2.wcnf" to 170760179u, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_2.wcnf" to 1389279780u, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_3.wcnf" to 1388734978u, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_4.wcnf" to 1388734978u, + "Rounded_BTWBNSL_Water_1000_1_2.scores_TWBound_4.wcnf" to 1326306453u, + "Rounded_CorrelationClustering_Ionosphere_BINARY_N200_D0.200.wcnf" to 4604640u, + "Rounded_CorrelationClustering_Orl_BINARY_N320_D0.200.wcnf" to 4429109u, + "Rounded_CorrelationClustering_Protein1_BINARY_N360.wcnf" to 27536228u, + "Rounded_CorrelationClustering_Protein2_BINARY_N220.wcnf" to 13727551u, + "Rounded_CorrelationClustering_Protein2_UNARY_N100.wcnf" to 3913145u, + "simNo_1-s_15-m_50-n_50-fp_0.0001-fn_0.20.wcnf" to 11501657324586u, + "simNo_2-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 99635408482313u, + "simNo_3-s_5-m_50-n_50-fp_0.0001-fn_0.05.wcnf" to 18938961942919u, + "simNo_5-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 113321765415159u, + "simNo_6-s_5-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 90981027155327u, + "simNo_6-s_15-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 60142712649443u, + "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 74156301822200u, + "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 131749300472480u, + "simNo_9-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 131749300472480u, + "simNo_10-s_15-m_100-n_50-fp_0.01-fn_0.20.wcnf" to 84803002848794u, + "simNo_10-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 82981983123459u, + "su2random_4_18_ibmq-casablanca_7.wcnf" to 24u, + "su2random_5_30_ibmq-london_5.wcnf" to 51u, + "tcp_students_91_it_2.wcnf" to 3024u, + "tcp_students_91_it_3.wcnf" to 2430u, + "tcp_students_91_it_6.wcnf" to 2877u, + "tcp_students_91_it_7.wcnf" to 2505u, + "tcp_students_91_it_13.wcnf" to 2730u, + "tcp_students_98_it_8.wcnf" to 2727u, + "tcp_students_98_it_9.wcnf" to 2469u, + "tcp_students_98_it_12.wcnf" to 2994u, + "tcp_students_105_it_7.wcnf" to 3024u, + "tcp_students_105_it_13.wcnf" to 3360u, + "tcp_students_105_it_15.wcnf" to 3258u, + "tcp_students_112_it_1.wcnf" to 3513u, + "tcp_students_112_it_3.wcnf" to 2916u, + "tcp_students_112_it_5.wcnf" to 3366u, + "tcp_students_112_it_7.wcnf" to 3513u, + "tcp_students_112_it_15.wcnf" to 3585u, + "test1--n-5000.wcnf" to 20u, + "test2.wcnf" to 16u, + "test2--n-5000.wcnf" to 3u, + "test5--n-5000.wcnf" to 2u, + "test9--n-5000.wcnf" to 2u, + "test18--n-5000.wcnf" to 22u, + "test25--n-10000.wcnf" to 4u, + "test34--n-10000.wcnf" to 3u, + "test41--n-15000.wcnf" to 5u, + "test42--n-15000.wcnf" to 2u, + "test53--n-15000.wcnf" to 10u, + "test54--n-15000.wcnf" to 45u, + "test66--n-20000.wcnf" to 1u, + "test67--n-20000.wcnf" to 1u, + "test70--n-20000.wcnf" to 5u, + "test75--n-20000.wcnf" to 5u, + "up-.mancoosi-test-i10d0u98-11.wcnf" to 1780771u, + "up-.mancoosi-test-i10d0u98-16.wcnf" to 1780806u, + "up-.mancoosi-test-i20d0u98-9.wcnf" to 1780788u, + "up-.mancoosi-test-i30d0u98-3.wcnf" to 1780860u, + "up-.mancoosi-test-i40d0u98-7.wcnf" to 1780807u, + "up-.mancoosi-test-i40d0u98-17.wcnf" to 1780852u, + "vio.role_smallcomp_violations_0.3_3.wcnf" to 185080u, + "vio.role_smallcomp_violations_0.45_8.wcnf" to 244141u, + "vqe_4_12_ibmq-casablanca_7.wcnf" to 15u, + "vqe_5_20_ibmq-london_5.wcnf" to 33u, + "warehouse0.wcsp.wcnf" to 328u, + "warehouse1.wcsp.wcnf" to 730567u, + "wcn.adult_train_3_DNF_1_5.wcnf" to 24254u, + "wcn.ilpd_test_8_CNF_4_20.wcnf" to 287u, + "wcn.ionosphere_train_5_DNF_2_10.wcnf" to 47u, + "wcn.parkinsons_test_5_CNF_2_10.wcnf" to 58u, + "wcn.pima_test_3_CNF_1_5.wcnf" to 125u, + "wcn.tictactoe_test_8_CNF_2_20.wcnf" to 346u, + "wcn.titanic_test_7_CNF_5_20.wcnf" to 557u, + "wcn.titanic_test_8_DNF_1_20.wcnf" to 449u, + "wcn.titanic_train_7_CNF_5_15.wcnf" to 3262u, + "wcn.titanic_train_8_CNF_5_10.wcnf" to 2201u, + "wcn.transfusion_test_7_DNF_3_5.wcnf" to 96u, + "wcn.transfusion_train_2_CNF_5_10.wcnf" to 1600u, + "wcn.transfusion_train_3_CNF_3_15.wcnf" to 2400u, + "WCNF_pathways_p01.wcnf" to 2u, + "WCNF_pathways_p03.wcnf" to 30u, + "WCNF_pathways_p05.wcnf" to 60u, + "WCNF_pathways_p06.wcnf" to 64u, + "WCNF_pathways_p08.wcnf" to 182u, + "WCNF_pathways_p09.wcnf" to 157u, + "WCNF_pathways_p10.wcnf" to 129u, + "WCNF_pathways_p12.wcnf" to 188u, + "WCNF_pathways_p14.wcnf" to 207u, + "WCNF_pathways_p16.wcnf" to 257u, + "WCNF_storage_p02.wcnf" to 5u, + "WCNF_storage_p06.wcnf" to 173u, + "wei.SingleDay_3_weighted.wcnf" to 35439u, + "wei.Subnetwork_7_weighted.wcnf" to 43213u, + "wei.Subnetwork_9_weighted.wcnf" to 82813u, + "wikipedia.dimacs.wcnf" to 42676u, + "wpm.mancoosi-test-i1000d0u98-15.wcnf" to 92031744u, + "wpm.mancoosi-test-i2000d0u98-25.wcnf" to 332548069u, + "wpm.mancoosi-test-i3000d0u98-50.wcnf" to 422725765u, + "wpm.mancoosi-test-i3000d0u98-70.wcnf" to 512958012u, + "wpm.mancoosi-test-i4000d0u98-76.wcnf" to 738411504u, + "youtube.dimacs.wcnf" to 227167u, + ) +} diff --git a/ksmt-test/build.gradle.kts b/ksmt-test/build.gradle.kts index 6f2996fd9..744f0ff5a 100644 --- a/ksmt-test/build.gradle.kts +++ b/ksmt-test/build.gradle.kts @@ -101,7 +101,7 @@ val testDataRevision = project.stringProperty("testDataRevision") ?: "no-revisio val downloadPreparedBenchmarksTestData = downloadPreparedSmtLibBenchmarkTestData( downloadPath = downloadedTestData, testDataPath = unpackedTestDataDir, - version = testDataRevision + testDataRevision = testDataRevision ) tasks.withType { diff --git a/settings.gradle.kts b/settings.gradle.kts index 0bea54a97..179f3ca47 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,6 +8,7 @@ include("ksmt-runner:solver-generator") include("ksmt-test") include("ksmt-cvc5") include("ksmt-maxsat") +include("ksmt-maxsat-test") pluginManagement { resolutionStrategy { From 52ab4e1b131404b1cc4ed1070f36c940dfc8fda7 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 7 Aug 2023 17:28:48 +0300 Subject: [PATCH 037/228] Ignore maxsat test data --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7dc56f914..a75d245b9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ build # ignore ksmt test data ksmt-test/testData + +# ignore maxsat test data +ksmt-maxsat-test/testData From 05fb157c81321214b44ee0a284ac1ef93cfc2c44 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 7 Aug 2023 18:38:45 +0300 Subject: [PATCH 038/228] Update timeout value --- .../test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt index 967ffec1b..265f30924 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt @@ -16,6 +16,7 @@ import kotlin.io.path.relativeTo import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes class KMaxSATBenchmarkTest { @ParameterizedTest(name = "{0}") @@ -40,7 +41,7 @@ class KMaxSATBenchmarkTest { } } - val maxSATResult = maxSATSolver.checkMaxSAT(1000.milliseconds) + val maxSATResult = maxSATSolver.checkMaxSAT(1.minutes) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } assertEquals(KSolverStatus.SAT, maxSATResult.hardConstraintsSATStatus) From 472f36769b715074f389d8ed38908d00677b5fc3 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 8 Aug 2023 10:52:13 +0300 Subject: [PATCH 039/228] Run benchmark on single runner --- .github/workflows/run-long-maxsat-tests.yml | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/run-long-maxsat-tests.yml diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml new file mode 100644 index 000000000..ff8885f32 --- /dev/null +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -0,0 +1,68 @@ +name: Build and run long ksmt maxsat tests + +on: + workflow_dispatch: + +env: + TEST_DATA_REVISION: 0.0.0 + +jobs: + setup: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: Prepare test data (cache) + id: test-data-cache + uses: actions/cache@v3 + env: + cache-name: cache-maxsat-test-data + with: + key: maxsat-test-data-${{ env.TEST_DATA_REVISION }}-${{ matrix.os }} + path: ksmt-maxsat-test/testData/data/* + + - name: Set up JDK 1.8 + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'zulu' + cache: gradle + + - name: Prepare test data (download) + if: steps.test-data-cache.outputs.cache-hit != 'true' + uses: gradle/gradle-build-action@v2 + with: + arguments: | + :ksmt-maxsat-test:downloadPreparedMaxSatBenchmarkTestData + --no-daemon + -PtestDataRevision=${{ env.TEST_DATA_REVISION }} + + - name: Prepare test data (unpack) + uses: gradle/gradle-build-action@v2 + with: + arguments: | + :ksmt-maxsat-test:usePreparedTestData + --no-daemon + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + + - name: Run maxsat benchmark + uses: gradle/gradle-build-action@v2 + with: + arguments: | + :ksmt-maxsat-test:test + --no-daemon + + - name: Upload test report + if: always() + uses: actions/upload-artifact@v3 + with: + name: ksmt-maxsat-test-report + path: ksmt-maxsat-test/build/reports/tests/test/* From e3f1467b339ca2cc175a69157a7cb57ada07860c Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Tue, 8 Aug 2023 16:56:36 +0300 Subject: [PATCH 040/228] Fix bug with return type in SmtLibBenchmarkUtil --- buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt b/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt index 84677999d..534c2b2d6 100644 --- a/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/SmtLibBenchmarkUtil.kt @@ -33,18 +33,16 @@ fun Project.mkSmtLibBenchmarkTestData(name: String) = tasks.register("smtLibBenc } } -fun Project.usePreparedSmtLibBenchmarkTestData(path: File) { +fun Project.usePreparedSmtLibBenchmarkTestData(path: File) = usePreparedBenchmarkTestData(path, SMTLIB_BENCHMARK_NAME_L) -} -fun Project.downloadPreparedSmtLibBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) { +fun Project.downloadPreparedSmtLibBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) = downloadPreparedBenchmarkTestData( downloadPath, testDataPath, SMTLIB_BENCHMARK_NAME_U, "https://github.com/UnitTestBot/ksmt/releases/download/$testDataRevision/benchmarks.zip", ) -} private const val MK_SMTLIB_BENCHMARK_REPO_URL = "http://smt-lib.loria.fr" From 4642aff30ec28382879e7679b2a9515ec656d9e6 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 9 Aug 2023 12:04:50 +0300 Subject: [PATCH 041/228] Move MaxRes algorithm to a separate solver --- .../kotlin/io/ksmt/maxsat/test/TestParser.kt | 6 +- .../ksmt/maxsat/test/KMaxSATBenchmarkTest.kt | 9 +- .../io/ksmt/{solver => }/maxsat/Constraint.kt | 2 +- .../{solver => }/maxsat/HardConstraint.kt | 2 +- .../ksmt/{solver => }/maxsat/KMaxSATResult.kt | 2 +- .../ksmt/{solver => }/maxsat/MaxSATScope.kt | 2 +- .../{solver => }/maxsat/MaxSATScopeManager.kt | 2 +- .../{solver => }/maxsat/SoftConstraint.kt | 2 +- .../solvers/KMaxResSolver.kt} | 108 ++-------------- .../io/ksmt/maxsat/solvers/KMaxSATSolver.kt | 115 ++++++++++++++++++ .../{solver => }/maxsat/KMaxSATSolverTest.kt | 21 ++-- 11 files changed, 148 insertions(+), 123 deletions(-) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/Constraint.kt (80%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/HardConstraint.kt (81%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/KMaxSATResult.kt (96%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/MaxSATScope.kt (68%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/MaxSATScopeManager.kt (98%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver => }/maxsat/SoftConstraint.kt (83%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{solver/maxsat/KMaxSATSolver.kt => maxsat/solvers/KMaxResSolver.kt} (72%) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt rename ksmt-maxsat/src/test/kotlin/io/ksmt/{solver => }/maxsat/KMaxSATSolverTest.kt (93%) diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt index d67ddbfab..80f202b0f 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt @@ -1,9 +1,9 @@ package io.ksmt.maxsat.test import io.ksmt.KContext -import io.ksmt.solver.maxsat.Constraint -import io.ksmt.solver.maxsat.HardConstraint -import io.ksmt.solver.maxsat.SoftConstraint +import io.ksmt.maxsat.Constraint +import io.ksmt.maxsat.HardConstraint +import io.ksmt.maxsat.SoftConstraint import io.ksmt.utils.mkConst import java.io.File import java.nio.file.Path diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt index 265f30924..715d984cb 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt @@ -2,10 +2,10 @@ package io.ksmt.maxsat.test import io.ksmt.KContext import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.maxsat.HardConstraint -import io.ksmt.solver.maxsat.KMaxSATSolver -import io.ksmt.solver.maxsat.SoftConstraint +import io.ksmt.maxsat.HardConstraint +import io.ksmt.maxsat.SoftConstraint import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.maxsat.solvers.KMaxResSolver import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -15,7 +15,6 @@ import kotlin.io.path.listDirectoryEntries import kotlin.io.path.relativeTo import kotlin.test.assertEquals import kotlin.test.assertTrue -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes class KMaxSATBenchmarkTest { @@ -28,7 +27,7 @@ class KMaxSATBenchmarkTest { val constraints = parseTest(samplePath, this) val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) var sumOfSoftConstraintsWeights = 0u diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt similarity index 80% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt index 1f94cbd49..dcc2e7ee4 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/Constraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt similarity index 81% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt index 8216ec74b..2ffc36975 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/HardConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt similarity index 96% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt index 5b9015e8f..5905f28d9 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat import io.ksmt.solver.KSolverStatus diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt similarity index 68% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt index c54b6301d..d78b7a149 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScope.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt @@ -1,3 +1,3 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat internal class MaxSATScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt similarity index 98% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt index 499943117..5dfd05565 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat internal class MaxSATScopeManager { private var currentScope = 0u diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt similarity index 83% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt index 09e9b9e37..5feadac3b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt similarity index 72% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt index a448d8a94..0eec64bd6 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt @@ -1,8 +1,9 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr -import io.ksmt.expr.KTrue +import io.ksmt.maxsat.KMaxSATResult +import io.ksmt.maxsat.SoftConstraint import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration @@ -10,27 +11,9 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration - -class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver - where T : KSolverConfiguration { - private val scopeManager = MaxSATScopeManager() - private var softConstraints = mutableListOf() - - /** - * Assert softly an expression with weight (aka soft constraint) into solver. - * - * @see checkMaxSAT - * */ - fun assertSoft(expr: KExpr, weight: UInt) { - require(weight > 0u) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } - - val softConstraint = SoftConstraint(expr, weight) - softConstraints.add(softConstraint) - scopeManager.incrementSoft() - } +class KMaxResSolver(private val ctx: KContext, private val solver: KSolver) + : KMaxSATSolver(ctx, solver) { private var currentMaxSATResult: Triple>, KModel?> = Triple(null, listOf(), null) @@ -39,7 +22,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * * @throws NotImplementedError */ - fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult { + override fun checkMaxSAT(timeout: Duration): KMaxSATResult { if (timeout.isNegative() || timeout == Duration.ZERO) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") } @@ -139,7 +122,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver * @return a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ private fun splitUnsatCore(formula: MutableList, unsatCore: List>) - : Pair> { + : Pair> { // Filters soft constraints from the unsat core. val unsatCoreSoftConstraints = formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } @@ -191,20 +174,6 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver } } - /** - * Check on satisfiability hard constraints with assumed soft constraints. - * - * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model - * (if exists, null otherwise). - */ - private fun checkSAT(assumptions: List, timeout: Duration): - Triple>, KModel?> = - when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { - KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) - KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) - KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) - } - /** * Reify unsat core soft constraints with literals. */ @@ -251,7 +220,7 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver iter: Int, weight: UInt, ) - : MutableList = with(ctx) { + : MutableList = with(ctx) { literalsToReify.forEachIndexed { index, literal -> val indexLast = literalsToReify.lastIndex @@ -280,63 +249,4 @@ class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver val satSoftConstraints = getSatSoftConstraintsByModel(model) return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = true) } - - private fun getSatSoftConstraintsByModel(model: KModel): List { - return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } - } - - private fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { - val msUnit = DurationUnit.MILLISECONDS - return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) - } - - override fun configure(configurator: KSolverConfiguration.() -> Unit) { - solver.configure(configurator) - } - - override fun assert(expr: KExpr) { - solver.assert(expr) - } - - override fun assertAndTrack(expr: KExpr) { - solver.assertAndTrack(expr) - } - - override fun push() { - solver.push() - scopeManager.push() - } - - override fun pop(n: UInt) { - solver.pop(n) - softConstraints = scopeManager.pop(n, softConstraints) - } - - override fun check(timeout: Duration): KSolverStatus { - return solver.check(timeout) - } - - override fun checkWithAssumptions(assumptions: List>, timeout: Duration): KSolverStatus { - return solver.checkWithAssumptions(assumptions, timeout) - } - - override fun model(): KModel { - return solver.model() - } - - override fun unsatCore(): List> { - return solver.unsatCore() - } - - override fun reasonOfUnknown(): String { - return solver.reasonOfUnknown() - } - - override fun interrupt() { - solver.interrupt() - } - - override fun close() { - solver.close() - } -} +} \ No newline at end of file diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt new file mode 100644 index 000000000..2c1523578 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt @@ -0,0 +1,115 @@ +package io.ksmt.maxsat.solvers + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.expr.KTrue +import io.ksmt.maxsat.KMaxSATResult +import io.ksmt.maxsat.MaxSATScopeManager +import io.ksmt.maxsat.SoftConstraint +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus +import io.ksmt.sort.KBoolSort +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +abstract class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver + where T : KSolverConfiguration { + private val scopeManager = MaxSATScopeManager() + protected var softConstraints = mutableListOf() + + /** + * Assert softly an expression with weight (aka soft constraint) into solver. + * + * @see checkMaxSAT + * */ + fun assertSoft(expr: KExpr, weight: UInt) { + require(weight > 0u) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } + + val softConstraint = SoftConstraint(expr, weight) + softConstraints.add(softConstraint) + scopeManager.incrementSoft() + } + + /** + * Solve maximum satisfiability problem. + * + * @throws NotImplementedError + */ + abstract fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult + + /** + * Check on satisfiability hard constraints with assumed soft constraints. + * + * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model + * (if exists, null otherwise). + */ + protected fun checkSAT(assumptions: List, timeout: Duration): + Triple>, KModel?> = + when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { + KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) + KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) + KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) + } + + protected fun getSatSoftConstraintsByModel(model: KModel): List { + return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } + } + + protected fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { + val msUnit = DurationUnit.MILLISECONDS + return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) + } + + override fun configure(configurator: KSolverConfiguration.() -> Unit) { + solver.configure(configurator) + } + + override fun assert(expr: KExpr) { + solver.assert(expr) + } + + override fun assertAndTrack(expr: KExpr) { + solver.assertAndTrack(expr) + } + + override fun push() { + solver.push() + scopeManager.push() + } + + override fun pop(n: UInt) { + solver.pop(n) + softConstraints = scopeManager.pop(n, softConstraints) + } + + override fun check(timeout: Duration): KSolverStatus { + return solver.check(timeout) + } + + override fun checkWithAssumptions(assumptions: List>, timeout: Duration): KSolverStatus { + return solver.checkWithAssumptions(assumptions, timeout) + } + + override fun model(): KModel { + return solver.model() + } + + override fun unsatCore(): List> { + return solver.unsatCore() + } + + override fun reasonOfUnknown(): String { + return solver.reasonOfUnknown() + } + + override fun interrupt() { + solver.interrupt() + } + + override fun close() { + solver.close() + } +} \ No newline at end of file diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt similarity index 93% rename from ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt rename to ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt index 473b12a7b..c02c67618 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt @@ -1,8 +1,9 @@ -package io.ksmt.solver.maxsat +package io.ksmt.maxsat import io.ksmt.KContext import io.ksmt.solver.KSolverStatus import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.maxsat.solvers.KMaxResSolver import io.ksmt.utils.getValue import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertTrue @@ -12,7 +13,7 @@ class KMaxSATSolverTest { @Test fun noSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val a by boolSort val b by boolSort val c by boolSort @@ -34,7 +35,7 @@ class KMaxSATSolverTest { @Test fun oneOfTwoSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val a by boolSort val b by boolSort @@ -57,7 +58,7 @@ class KMaxSATSolverTest { @Test fun twoOfThreeSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val a by boolSort val b by boolSort @@ -82,7 +83,7 @@ class KMaxSATSolverTest { @Test fun sameExpressionSoftConstraintsSatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val x by boolSort val y by boolSort @@ -114,7 +115,7 @@ class KMaxSATSolverTest { @Test fun sameExpressionSoftConstraintsUnsatTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val x by boolSort val y by boolSort @@ -142,7 +143,7 @@ class KMaxSATSolverTest { @Test fun chooseOneConstraintByWeightTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val z by boolSort val a by boolSort val b by boolSort @@ -163,7 +164,7 @@ class KMaxSATSolverTest { @Test fun inequalitiesTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val x by intSort val y by intSort @@ -194,7 +195,7 @@ class KMaxSATSolverTest { @Test fun oneScopePushPopTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val a by boolSort val b by boolSort @@ -222,7 +223,7 @@ class KMaxSATSolverTest { @Test fun threeScopesPushPopTest() = with(KContext()) { val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxSATSolver(this, z3Solver) + val maxSATSolver = KMaxResSolver(this, z3Solver) val a by boolSort val b by boolSort From 7ed93607af393393795139b043ab46908ac5614f Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 10 Aug 2023 10:23:31 +0300 Subject: [PATCH 042/228] Use minOf instead of minBy --- .../src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt index 0eec64bd6..62166497e 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt @@ -127,7 +127,7 @@ class KMaxResSolver(private val ctx: KContext, private val unsatCoreSoftConstraints = formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } - val minWeight = unsatCoreSoftConstraints.minBy { it.weight }.weight + val minWeight = unsatCoreSoftConstraints.minOf { it.weight } val unsatCoreSoftConstraintsSplit = mutableListOf() From a01c1bbcd7ce917d831ba8d0414436a6f66f0ec1 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 30 Aug 2023 16:10:40 +0300 Subject: [PATCH 043/228] Implement primal dual MaxRes solver - Add KPrimalDualMaxResSolver - Rename: KMaxResSolver -> KPMResSolver - Add KMaxResSolver (abstraction for solvers using MaxRes) - Add objects: ModelUtils, CoreUtils and TimerUtils - Add new unit tests - Make tests run for both solvers --- .../maxsat/test/CurrentLineState.kt | 2 +- .../{ => solver}/maxsat/test/TestParser.kt | 8 +- .../maxsat/test/KMaxSATBenchmarkTest.kt | 20 +- .../solver/maxsat/test/KPMResBenchmarkTest.kt | 16 + .../test/KPrimalDualMaxResBenchmarkTest.kt | 16 + .../ksmt/{ => solver}/maxsat/KMaxSATResult.kt | 3 +- .../maxsat/constraints}/Constraint.kt | 2 +- .../maxsat/constraints}/HardConstraint.kt | 2 +- .../maxsat/constraints}/SoftConstraint.kt | 2 +- .../maxsat/scope}/MaxSATScope.kt | 2 +- .../maxsat/scope}/MaxSATScopeManager.kt | 4 +- .../solver/maxsat/solvers/KMaxResSolver.kt | 36 ++ .../maxsat/solvers/KMaxSATSolver.kt | 63 ++- .../maxsat/solvers/KPMResSolver.kt} | 100 ++--- .../maxsat/solvers/KPrimalDualMaxResSolver.kt | 370 ++++++++++++++++++ .../maxsat/solvers/utils/MinimalUnsatCore.kt | 115 ++++++ .../io/ksmt/solver/maxsat/utils/CoreUtils.kt | 30 ++ .../io/ksmt/solver/maxsat/utils/ModelUtils.kt | 36 ++ .../io/ksmt/solver/maxsat/utils/TimerUtils.kt | 12 + .../io/ksmt/maxsat/KMaxSATSolverTest.kt | 274 ------------- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 325 +++++++++++++++ .../io/ksmt/solver/maxsat/KPMResSolverTest.kt | 16 + .../maxsat/KPrimalDualMaxResSolverTest.kt | 16 + 23 files changed, 1086 insertions(+), 384 deletions(-) rename ksmt-maxsat-test/src/main/kotlin/io/ksmt/{ => solver}/maxsat/test/CurrentLineState.kt (73%) rename ksmt-maxsat-test/src/main/kotlin/io/ksmt/{ => solver}/maxsat/test/TestParser.kt (93%) rename ksmt-maxsat-test/src/test/kotlin/io/ksmt/{ => solver}/maxsat/test/KMaxSATBenchmarkTest.kt (97%) create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt rename ksmt-maxsat/src/main/kotlin/io/ksmt/{ => solver}/maxsat/KMaxSATResult.kt (90%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat => solver/maxsat/constraints}/Constraint.kt (74%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat => solver/maxsat/constraints}/HardConstraint.kt (76%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat => solver/maxsat/constraints}/SoftConstraint.kt (78%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat => solver/maxsat/scope}/MaxSATScope.kt (64%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat => solver/maxsat/scope}/MaxSATScopeManager.kt (94%) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt rename ksmt-maxsat/src/main/kotlin/io/ksmt/{ => solver}/maxsat/solvers/KMaxSATSolver.kt (60%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/{maxsat/solvers/KMaxResSolver.kt => solver/maxsat/solvers/KPMResSolver.kt} (64%) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt similarity index 73% rename from ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt rename to ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt index 9f98c0a1f..2dcb61020 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/CurrentLineState.kt +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt @@ -1,4 +1,4 @@ -package io.ksmt.maxsat.test +package io.ksmt.solver.maxsat.test enum class CurrentLineState { COMMENT, diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt similarity index 93% rename from ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt rename to ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt index 80f202b0f..8914fff4b 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/maxsat/test/TestParser.kt +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt @@ -1,9 +1,9 @@ -package io.ksmt.maxsat.test +package io.ksmt.solver.maxsat.test import io.ksmt.KContext -import io.ksmt.maxsat.Constraint -import io.ksmt.maxsat.HardConstraint -import io.ksmt.maxsat.SoftConstraint +import io.ksmt.solver.maxsat.constraints.Constraint +import io.ksmt.solver.maxsat.constraints.HardConstraint +import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.utils.mkConst import java.io.File import java.nio.file.Path diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt similarity index 97% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt rename to ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt index 715d984cb..6e8181653 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/maxsat/test/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt @@ -1,11 +1,11 @@ -package io.ksmt.maxsat.test +package io.ksmt.solver.maxsat.test import io.ksmt.KContext import io.ksmt.solver.KSolverStatus -import io.ksmt.maxsat.HardConstraint -import io.ksmt.maxsat.SoftConstraint -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.maxsat.solvers.KMaxResSolver +import io.ksmt.solver.maxsat.constraints.HardConstraint +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.z3.KZ3SolverConfiguration import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -17,17 +17,19 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.time.Duration.Companion.minutes -class KMaxSATBenchmarkTest { +abstract class KMaxSATBenchmarkTest { + abstract fun getSolver(): KMaxSATSolver + abstract val ctx: KContext + @ParameterizedTest(name = "{0}") @MethodSource("maxSATTestData") - fun maxSATTest(name: String, samplePath: Path) = with(KContext()) { + fun maxSATTest(name: String, samplePath: Path) = with(ctx) { val testData = maxSATTestNameToExpectedResult.find { it.first == name } require(testData != null) { "Test [$name] expected result must be specified" } val constraints = parseTest(samplePath, this) - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) + val maxSATSolver = getSolver() var sumOfSoftConstraintsWeights = 0u diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt new file mode 100644 index 000000000..e3fbace15 --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt @@ -0,0 +1,16 @@ +package io.ksmt.solver.maxsat.test + +import io.ksmt.KContext +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPMResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPMResBenchmarkTest : KMaxSATBenchmarkTest() { + override val ctx = KContext() + + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPMResSolver(this, z3Solver) + } +} diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt new file mode 100644 index 000000000..9c0df5a0c --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt @@ -0,0 +1,16 @@ +package io.ksmt.solver.maxsat.test + +import io.ksmt.KContext +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResBenchmarkTest : KMaxSATBenchmarkTest() { + override val ctx = KContext() + + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver) + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt similarity index 90% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index 5905f28d9..7b797359e 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -1,5 +1,6 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat +import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.KSolverStatus /** diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt similarity index 74% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt index dcc2e7ee4..fd08b8f5f 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/Constraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt similarity index 76% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt index 2ffc36975..e68be819d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/HardConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt similarity index 78% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt index 5feadac3b..ec753e0b5 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/SoftConstraint.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt similarity index 64% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt index d78b7a149..6a147a651 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScope.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt @@ -1,3 +1,3 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat.scope internal class MaxSATScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt similarity index 94% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt index 5dfd05565..21aa2eaf2 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/MaxSATScopeManager.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt @@ -1,4 +1,6 @@ -package io.ksmt.maxsat +package io.ksmt.solver.maxsat.scope + +import io.ksmt.solver.maxsat.constraints.SoftConstraint internal class MaxSATScopeManager { private var currentScope = 0u diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt new file mode 100644 index 000000000..12295a595 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt @@ -0,0 +1,36 @@ +package io.ksmt.solver.maxsat.solvers + +import io.ksmt.KContext +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.utils.CoreUtils + +abstract class KMaxResSolver( + private val ctx: KContext, + private val solver: KSolver, +) : KMaxSATSolver(ctx, solver) where T : KSolverConfiguration { + protected fun removeCoreAssumptions(core: List, assumptions: MutableList) { + assumptions.removeAll(core) + } + + protected fun splitCore( + core: List, + assumptions: MutableList, + ): Pair> { + val splitCore = mutableListOf() + + val minWeight = CoreUtils.getCoreWeight(core) + // Add fresh soft clauses for weights that are above w. + for (constraint in core) { + if (constraint.weight > minWeight) { + assumptions.add(SoftConstraint(constraint.expression, constraint.weight - minWeight)) + splitCore.add(SoftConstraint(constraint.expression, minWeight)) + } else { + splitCore.add(SoftConstraint(constraint.expression, constraint.weight)) + } + } + + return Pair(minWeight, splitCore) + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt similarity index 60% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt index 2c1523578..2f3639ed9 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxSATSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt @@ -1,27 +1,30 @@ -package io.ksmt.maxsat.solvers +package io.ksmt.solver.maxsat.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr -import io.ksmt.expr.KTrue -import io.ksmt.maxsat.KMaxSATResult -import io.ksmt.maxsat.MaxSATScopeManager -import io.ksmt.maxsat.SoftConstraint import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNKNOWN +import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsat.KMaxSATResult +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.scope.MaxSATScopeManager import io.ksmt.sort.KBoolSort import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration -abstract class KMaxSATSolver(private val ctx: KContext, private val solver: KSolver) : KSolver +abstract class KMaxSATSolver( + private val ctx: KContext, + private val solver: KSolver, +) : KSolver where T : KSolverConfiguration { private val scopeManager = MaxSATScopeManager() protected var softConstraints = mutableListOf() /** - * Assert softly an expression with weight (aka soft constraint) into solver. + * Softly assert an expression with weight (aka soft constraint) into solver. * * @see checkMaxSAT * */ @@ -40,6 +43,33 @@ abstract class KMaxSATSolver(private val ctx: KContext, private val solver: K */ abstract fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult + /** + * Union soft constraints with same expressions into a single soft constraint. + * + * The new soft constraint weight will be equal to the sum of old soft constraints weights. + */ + protected fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { + val exprToRepetitionsMap = mutableMapOf, Int>() + + formula.forEach { + if (exprToRepetitionsMap.containsKey(it.expression)) { + exprToRepetitionsMap[it.expression] = exprToRepetitionsMap[it.expression]!! + 1 + } else { + exprToRepetitionsMap[it.expression] = 1 + } + } + + exprToRepetitionsMap.forEach { (expr, repetitions) -> + if (repetitions > 1) { + val repeatedExpressions = formula.filter { it.expression == expr } + + formula.removeAll(repeatedExpressions) + val repeatedExpressionsWeightsSum = repeatedExpressions.sumOf { it.weight } + formula.add(SoftConstraint(expr, repeatedExpressionsWeightsSum)) + } + } + } + /** * Check on satisfiability hard constraints with assumed soft constraints. * @@ -49,18 +79,13 @@ abstract class KMaxSATSolver(private val ctx: KContext, private val solver: K protected fun checkSAT(assumptions: List, timeout: Duration): Triple>, KModel?> = when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { - KSolverStatus.SAT -> Triple(status, listOf(), solver.model()) - KSolverStatus.UNSAT -> Triple(status, solver.unsatCore(), null) - KSolverStatus.UNKNOWN -> Triple(status, listOf(), null) + SAT -> Triple(status, listOf(), solver.model()) + UNSAT -> Triple(status, solver.unsatCore(), null) + UNKNOWN -> Triple(status, listOf(), null) } protected fun getSatSoftConstraintsByModel(model: KModel): List { - return softConstraints.filter { model.eval(it.expression).internEquals(KTrue(ctx)) } - } - - protected fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { - val msUnit = DurationUnit.MILLISECONDS - return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) + return softConstraints.filter { model.eval(it.expression, true) == ctx.trueExpr } } override fun configure(configurator: KSolverConfiguration.() -> Unit) { @@ -112,4 +137,4 @@ abstract class KMaxSATSolver(private val ctx: KContext, private val solver: K override fun close() { solver.close() } -} \ No newline at end of file +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt similarity index 64% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt rename to ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt index 62166497e..40c752729 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/maxsat/solvers/KMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt @@ -1,19 +1,24 @@ -package io.ksmt.maxsat.solvers +package io.ksmt.solver.maxsat.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr -import io.ksmt.maxsat.KMaxSATResult -import io.ksmt.maxsat.SoftConstraint import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNKNOWN +import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsat.KMaxSATResult +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.utils.CoreUtils +import io.ksmt.solver.maxsat.utils.TimerUtils import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration -class KMaxResSolver(private val ctx: KContext, private val solver: KSolver) - : KMaxSATSolver(ctx, solver) { +class KPMResSolver(private val ctx: KContext, private val solver: KSolver) +: KMaxResSolver(ctx, solver) { private var currentMaxSATResult: Triple>, KModel?> = Triple(null, listOf(), null) @@ -33,7 +38,7 @@ class KMaxResSolver(private val ctx: KContext, private solver.pop() - currentMaxSATResult = Triple(KSolverStatus.UNKNOWN, listOf(), null) + currentMaxSATResult = Triple(UNKNOWN, listOf(), null) if (softConstraints.isEmpty()) { return maxSATResult @@ -44,9 +49,9 @@ class KMaxResSolver(private val ctx: KContext, private val (solverStatus, _, model) = currentMaxSATResult return when (solverStatus) { - KSolverStatus.SAT -> handleSat(model!!) - KSolverStatus.UNSAT -> throw NotImplementedError() - KSolverStatus.UNKNOWN -> throw NotImplementedError() + SAT -> processSat(model!!) + UNSAT -> throw NotImplementedError() + UNKNOWN -> throw NotImplementedError() else -> error("Unexpected status: $solverStatus") } } @@ -63,15 +68,15 @@ class KMaxResSolver(private val ctx: KContext, private return KMaxSATResult( listOf(), hardConstraintsStatus, - hardConstraintsStatus != KSolverStatus.UNKNOWN, + hardConstraintsStatus != UNKNOWN, ) } val status = solver.check(timeout) - if (status == KSolverStatus.UNSAT) { + if (status == UNSAT) { return KMaxSATResult(listOf(), status, true) - } else if (status == KSolverStatus.UNKNOWN) { + } else if (status == UNKNOWN) { return KMaxSATResult(listOf(), status, false) } @@ -81,7 +86,7 @@ class KMaxResSolver(private val ctx: KContext, private unionSoftConstraintsWithSameExpressions(formula) while (true) { - val softConstraintsCheckRemainingTime = computeRemainingTime(timeout, clockStart) + val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) if (softConstraintsCheckRemainingTime.isNegative() || softConstraintsCheckRemainingTime == Duration.ZERO) { return KMaxSATResult(listOf(), status, false) @@ -90,15 +95,15 @@ class KMaxResSolver(private val ctx: KContext, private val (solverStatus, unsatCore, model) = checkSAT(formula, softConstraintsCheckRemainingTime) - if (solverStatus == KSolverStatus.UNKNOWN) { + if (solverStatus == UNKNOWN) { // TODO: get max SAT soft constraints subset return KMaxSATResult(listOf(), status, false) } currentMaxSATResult = Triple(solverStatus, unsatCore, model) - if (solverStatus == KSolverStatus.SAT) { - return handleSat(model!!) + if (solverStatus == SAT) { + return processSat(model!!) } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) @@ -122,56 +127,10 @@ class KMaxResSolver(private val ctx: KContext, private * @return a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ private fun splitUnsatCore(formula: MutableList, unsatCore: List>) - : Pair> { - // Filters soft constraints from the unsat core. - val unsatCoreSoftConstraints = - formula.filter { x -> unsatCore.any { x.expression.internEquals(it) } } - - val minWeight = unsatCoreSoftConstraints.minOf { it.weight } - - val unsatCoreSoftConstraintsSplit = mutableListOf() - - unsatCoreSoftConstraints.forEach { x -> - if (x.weight > minWeight) { - val minWeightedSoftConstraint = SoftConstraint(x.expression, minWeight) - formula.add(minWeightedSoftConstraint) - formula.add(SoftConstraint(x.expression, x.weight - minWeight)) - formula.removeIf { it.weight == x.weight && it.expression == x.expression } - - unsatCoreSoftConstraintsSplit.add(minWeightedSoftConstraint) - } else { - unsatCoreSoftConstraintsSplit.add(x) - } - } - - return Pair(minWeight, unsatCoreSoftConstraintsSplit) - } - - /** - * Union soft constraints with same expressions into a single soft constraint. - * - * The new soft constraint weight will be equal to the sum of old soft constraints weights. - */ - private fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { - val exprToRepetitionsMap = mutableMapOf, Int>() - - formula.forEach { - if (exprToRepetitionsMap.containsKey(it.expression)) { - exprToRepetitionsMap[it.expression] = exprToRepetitionsMap[it.expression]!! + 1 - } else { - exprToRepetitionsMap[it.expression] = 1 - } - } - - exprToRepetitionsMap.forEach { (expr, repetitions) -> - if (repetitions > 1) { - val repeatedExpressions = formula.filter { it.expression == expr } - - formula.removeAll(repeatedExpressions) - val repeatedExpressionsWeightsSum = repeatedExpressions.sumOf { it.weight } - formula.add(SoftConstraint(expr, repeatedExpressionsWeightsSum)) - } - } + : Pair> { + val unsatCoreSoftConstraints = CoreUtils.coreToSoftConstraints(unsatCore, formula) + removeCoreAssumptions(unsatCoreSoftConstraints, formula) + return splitCore(unsatCoreSoftConstraints, formula) } /** @@ -191,7 +150,6 @@ class KMaxResSolver(private val ctx: KContext, private val coreElementExpr = coreElement.expression val literalToReify = coreElementExpr.sort.mkConst("*$iter:$index") - val constraintToReify = coreElementExpr eq !literalToReify assert(constraintToReify) @@ -220,7 +178,7 @@ class KMaxResSolver(private val ctx: KContext, private iter: Int, weight: UInt, ) - : MutableList = with(ctx) { + : MutableList = with(ctx) { literalsToReify.forEachIndexed { index, literal -> val indexLast = literalsToReify.lastIndex @@ -245,8 +203,8 @@ class KMaxResSolver(private val ctx: KContext, private return formula } - private fun handleSat(model: KModel): KMaxSATResult { + private fun processSat(model: KModel): KMaxSATResult { val satSoftConstraints = getSatSoftConstraintsByModel(model) - return KMaxSATResult(satSoftConstraints, KSolverStatus.SAT, maxSATSucceeded = true) + return KMaxSATResult(satSoftConstraints, SAT, true) } -} \ No newline at end of file +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt new file mode 100644 index 000000000..83af20035 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -0,0 +1,370 @@ +package io.ksmt.solver.maxsat.solvers + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNKNOWN +import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsat.KMaxSATResult +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.solvers.utils.MinimalUnsatCore +import io.ksmt.solver.maxsat.utils.CoreUtils +import io.ksmt.solver.maxsat.utils.ModelUtils +import io.ksmt.sort.KBoolSort +import kotlin.time.Duration + +class KPrimalDualMaxResSolver(private val ctx: KContext, private val solver: KSolver) : + KMaxResSolver(ctx, solver) { + private var _lower: UInt = 0u // Current lower frontier + private var _upper: UInt = 0u // Current upper frontier + private var _maxUpper = 0u // Max possible upper frontier + private var _correctionSetSize: Int = 0 // Current corrections set size + private val _maxCoreSize = 3 + private var _correctionSetModel: KModel? = null + private var _model: KModel? = null + private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) + + private data class WeightedCore(val expressions: List>, val weight: UInt) + + override fun checkMaxSAT(timeout: Duration): KMaxSATResult { + val hardConstraintsStatus = solver.check() + + if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { + return KMaxSATResult(listOf(), hardConstraintsStatus, true) + } else if (hardConstraintsStatus == UNKNOWN) { + return KMaxSATResult(listOf(), hardConstraintsStatus, false) + } + + solver.push() + initMaxSAT() + + val assumptions = softConstraints.toMutableList() + unionSoftConstraintsWithSameExpressions(assumptions) + + while (_lower < _upper) { + val status = checkSat(assumptions) + + when (status) { + SAT -> { + val correctionSet = getCorrectionSet(solver.model(), assumptions) + if (correctionSet.isEmpty()) { + if (_model != null) { + // Feasible optimum is found by the moment. + _lower = _upper + } + } else { + processSat(correctionSet, assumptions) + } + } + + UNSAT -> { + val processUnsatStatus = processUnsat(assumptions) + if (processUnsatStatus == UNSAT) { + _lower = _upper + } else if (processUnsatStatus == UNKNOWN) { + solver.pop() + return KMaxSATResult(listOf(), SAT, false) + } + } + + UNKNOWN -> { + solver.pop() + return KMaxSATResult(listOf(), SAT, false) + } + } + } + + _lower = _upper + + val result = KMaxSATResult(getSatSoftConstraintsByModel(_model!!), SAT, true) + + solver.pop() + + return result + } + + private fun processSat(correctionSet: List, assumptions: MutableList) { + removeCoreAssumptions(correctionSet, assumptions) + val (minWeight, _) = splitCore(correctionSet, assumptions) + correctionSetMaxResolve(correctionSet, assumptions, minWeight) + + _correctionSetModel = null + _correctionSetSize = 0 + } + + private fun processUnsat(assumptions: MutableList): KSolverStatus { + val (status, cores) = getCores(assumptions) + + if (status != SAT) { + return status + } + + if (cores.isEmpty()) { + return UNSAT + } + + for (core in cores) { + processUnsatCore(core, assumptions) + } + + return SAT + } + + private fun processUnsatCore(weightedCore: WeightedCore, assumptions: MutableList) = with(ctx) { + val core = weightedCore.expressions + + require(core.isNotEmpty()) { "Core should not be empty here" } + + maxResolve(weightedCore, assumptions) + + val fml = !(core.reduce { x, y -> x and y }) + assert(fml) + + _lower += weightedCore.weight + _lower = minOf(_lower, _upper) + + if (_correctionSetModel != null && _correctionSetSize > 0) { + // This estimate can overshoot for weighted soft constraints. + --_correctionSetSize + } + + // Here we also prefer a smaller correction set to core. + if (_correctionSetModel != null && _correctionSetSize < core.size) { + val correctionSet = getCorrectionSet(_correctionSetModel!!, assumptions) + if (correctionSet.size >= core.size) { + return + } + + var weight = 0u + for (asm in assumptions) { + val weight1 = asm.weight + if (weight != 0u && weight1 != weight) { + return + } + + weight = weight1 + } + + processSat(correctionSet, assumptions) + } + } + + private fun getCores(assumptions: MutableList): Pair> { + val cores = mutableListOf() + var status = UNSAT + + while (status == UNSAT) { + val minimalUnsatCore = minimizeCore(assumptions) + updateMinimalUnsatCoreModel(assumptions) + + if (minimalUnsatCore.isEmpty()) { + cores.clear() + _lower = _upper + return Pair(SAT, cores) + } + + // 1. remove all core literals from assumptions + // 2. re-add literals of higher weight than min-weight. + // 3. 'core' stores the core literals that are re-encoded as assumptions afterward + cores.add(WeightedCore(minimalUnsatCore.map { it.expression }, CoreUtils.getCoreWeight(minimalUnsatCore))) + + removeCoreAssumptions(minimalUnsatCore, assumptions) + splitCore(minimalUnsatCore, assumptions) + + if (minimalUnsatCore.size >= _maxCoreSize) { + return Pair(SAT, cores) + } + + status = checkSat(assumptions) + } + + return Pair(status, cores) + } + + private fun minimizeCore(assumptions: List): List = + _minimalUnsatCore.tryGetMinimalUnsatCore(assumptions) + + private fun updateMinimalUnsatCoreModel(assumptions: List) { + val (model, weight) = _minimalUnsatCore.getBestModel() + + if (model != null && _upper > weight) { + updateAssignment(model, assumptions) + } + } + + private fun updateAssignment(model: KModel, assumptions: List) { + var correctionSetSize = 0 + for (constr in assumptions) { + if (ModelUtils.expressionIsFalse(ctx, model, constr.expression)) { + ++correctionSetSize + } + } + + if (_correctionSetModel == null || correctionSetSize < _correctionSetSize) { + _correctionSetModel = model + _correctionSetSize = correctionSetSize + } + + val upper = ModelUtils.getModelCost(ctx, model, softConstraints) + + if (upper > _upper) { + return + } + + _model = model + + _upper = upper + } + + private fun maxResolve(weightedCore: WeightedCore, assumptions: MutableList) = with(ctx) { + val core = weightedCore.expressions + + require(core.isNotEmpty()) { "Core should not be empty here" } + + // d_0 := true + // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1 + // soft (b_i or !d_i) + // == (b_i or !(!b_{i-1} or d_{i-1})) + // == (b_i or b_0 & b_1 & ... & b_{i-1}) + // + // Soft constraint is satisfied if the previous soft constraint + // holds or if it is the first soft constraint to fail. + // + // The Soundness of this rule can be established using MaxRes. + + lateinit var d: KExpr + var dd: KExpr + var fml: KExpr + + for (index in 1..core.lastIndex) { + val b_i = core[index - 1] + val b_i1 = core[index] + + when (index) { + 1 -> { + d = b_i + } + + 2 -> { + d = b_i and d + } + + else -> { + dd = mkFreshConst("d", this.boolSort) + fml = mkImplies(dd, d) + assert(fml) + fml = mkImplies(dd, b_i) + assert(fml) + fml = d and b_i + // TODO: process!!! + // update_model(dd, fml); + // I use the string below instead!!! + assert(dd eq fml) + d = dd + } + } + + val asm = mkFreshConst("a", this.boolSort) + val cls = b_i1 or d + fml = mkImplies(asm, cls) + assumptions.add(SoftConstraint(asm, weightedCore.weight)) + // TODO: process!!! + // update_model(asm, cls); + // I use the string below instead!!! + assert(asm eq cls) + assert(fml) + } + } + + private fun correctionSetMaxResolve( + correctionSet: List, + assumptions: MutableList, + weight: UInt, + ) = with(ctx) { + if (correctionSet.isEmpty()) { + return + } + + var d: KExpr = falseExpr + var fml: KExpr + var asm: KExpr + // + // d_0 := false + // d_i := b_{i-1} or d_{i-1} for i = 1...sz-1 + // soft (b_i and d_i) + // == (b_i and (b_0 or b_1 or ... or b_{i-1})) + // + // asm => b_i + // asm => d_{i-1} or b_{i-1} + // d_i => d_{i-1} or b_{i-1} + // + for (i in 1..correctionSet.lastIndex) { + val b_i = correctionSet[i - 1].expression + val b_i1 = correctionSet[i].expression + val cls = b_i or d + + if (i > 2) { + d = mkFreshConst("d", this.boolSort) + fml = mkImplies(d, cls) + + // TODO: process!!! + // update_model(d, cls); + // I use the string below instead: + assert(d eq cls) + + assert(fml) + } else { + d = cls + } + + asm = mkFreshConst("a", this.boolSort) + fml = mkImplies(asm, b_i1) + assert(fml) + fml = mkImplies(asm, cls) + assert(fml) + assumptions.add(SoftConstraint(asm, weight)) + + fml = b_i1 and cls + // TODO: process!!! update_model(asm, fml); + // I use the string below instead: + assert(asm eq fml) + } + + fml = correctionSet + .map { it.expression } + .reduce { x, y -> x or y } + assert(fml) + } + + private fun initMaxSAT() { + _lower = 0u + _upper = softConstraints.sumOf { it.weight } + + _maxUpper = _upper + _correctionSetSize = 0 + + _model = null + _correctionSetModel = null + _minimalUnsatCore.reset() + } + + private fun getCorrectionSet(model: KModel, assumptions: List): List { + updateAssignment(model, assumptions) + + return ModelUtils.getCorrectionSet(ctx, model, assumptions) + } + + private fun checkSat(assumptions: List): KSolverStatus { + val status = solver.checkWithAssumptions(assumptions.map { it.expression }) + + if (status == SAT) { + updateAssignment(solver.model().detach(), assumptions) + } + + return status + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt new file mode 100644 index 000000000..e96b81b97 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt @@ -0,0 +1,115 @@ +package io.ksmt.solver.maxsat.solvers.utils + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNKNOWN +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.utils.CoreUtils +import io.ksmt.solver.maxsat.utils.ModelUtils +import io.ksmt.sort.KBoolSort + +internal class MinimalUnsatCore( + private val ctx: KContext, + private val solver: KSolver, +) { + private val _minimalUnsatCoreModel = MinimalUnsatCoreModel(ctx, solver) + + fun getBestModel(): Pair = _minimalUnsatCoreModel.getBestModel() + + // If solver starts returning unknown, we return non minimized unsat core. + fun tryGetMinimalUnsatCore(assumptions: List): List = with(ctx) { + val unsatCore = solver.unsatCore() + + if (unsatCore.isEmpty() || unsatCore.size == 1) { + return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + } + + val minimalUnsatCore = mutableListOf>() + + val unknown = unsatCore.toMutableList() + + while (unknown.isNotEmpty()) { + val expr = unknown.removeLast() + + val notExpr = !expr + val status = solver.checkWithAssumptions(minimalUnsatCore + unknown + notExpr) + + when (status) { + UNKNOWN -> return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + + SAT -> { + minimalUnsatCore.add(expr) + _minimalUnsatCoreModel.updateModel(assumptions) + } + + else -> processUnsat(notExpr, unknown, minimalUnsatCore) + } + } + + return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + } + + fun reset() = _minimalUnsatCoreModel.reset() + + private fun processUnsat( + notExpr: KExpr, + unknown: MutableList>, + minimalUnsatCore: List>, + ) { + val core = solver.unsatCore() + + if (!core.contains(notExpr)) { + // unknown := core \ mus + unknown.clear() + + for (e in core) { + if (!minimalUnsatCore.contains(e)) { + unknown.add(e) + } + } + } + } + + private class MinimalUnsatCoreModel( + private val ctx: KContext, + private val solver: KSolver, + ) { + private var _model: KModel? = null + private var _weight = 0u + + fun getBestModel(): Pair { + return Pair(_model, _weight) + } + + fun updateModel(assumptions: List) { + reset() + + if (assumptions.isEmpty()) { + return + } + + val model = solver.model().detach() + var weight = 0u + + for (asm in assumptions) { + if (ModelUtils.expressionIsFalse(ctx, model, asm.expression)) { + weight += asm.weight + } + } + + if (_model == null || weight < _weight) { + _model = model + _weight = weight + } + } + + fun reset() { + _model = null + _weight = 0u + } + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt new file mode 100644 index 000000000..104a74f8e --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt @@ -0,0 +1,30 @@ +package io.ksmt.solver.maxsat.utils + +import io.ksmt.expr.KExpr +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.sort.KBoolSort + +internal object CoreUtils { + fun coreToSoftConstraints(core: List>, assumptions: List): List { + val softs = mutableListOf() + for (soft in assumptions) { + if (core.any { it.internEquals(soft.expression) }) { + softs.add(soft) + } + } + + require(core.size == softs.size) { + "Unsat core size [${core.size}] was not equal to corresponding soft constraints size [${softs.size}]" + } + + return softs + } + + fun getCoreWeight(core: List): UInt { + if (core.isEmpty()) { + return 0u + } + + return core.minOf { it.weight } + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt new file mode 100644 index 000000000..d0787b223 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt @@ -0,0 +1,36 @@ +package io.ksmt.solver.maxsat.utils + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.solver.KModel +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.sort.KBoolSort + +internal object ModelUtils { + fun expressionIsFalse(ctx: KContext, model: KModel, expression: KExpr) = + model.eval(expression, true) == ctx.falseExpr + + fun getModelCost(ctx: KContext, model: KModel, softConstraints: List): UInt { + var upper = 0u + + for (soft in softConstraints) { + if (expressionIsFalse(ctx, model, soft.expression)) { + upper += soft.weight + } + } + + return upper + } + + fun getCorrectionSet(ctx: KContext, model: KModel, softConstraints: List): List { + val correctionSet = mutableListOf() + + for (constr in softConstraints) { + if (expressionIsFalse(ctx, model, constr.expression)) { + correctionSet.add(constr) + } + } + + return correctionSet + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt new file mode 100644 index 000000000..f53878c39 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt @@ -0,0 +1,12 @@ +package io.ksmt.solver.maxsat.utils + +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + +internal object TimerUtils { + fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { + val msUnit = DurationUnit.MILLISECONDS + return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt deleted file mode 100644 index c02c67618..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/maxsat/KMaxSATSolverTest.kt +++ /dev/null @@ -1,274 +0,0 @@ -package io.ksmt.maxsat - -import io.ksmt.KContext -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.maxsat.solvers.KMaxResSolver -import io.ksmt.utils.getValue -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test - -class KMaxSATSolverTest { - @Test - fun noSoftConstraintsSatTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - val a by boolSort - val b by boolSort - val c by boolSort - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(c) - maxSATSolver.assertSoft(a and !c, 3u) - maxSATSolver.assertSoft(!a, 5u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) - } - } - - @Test - fun oneOfTwoSoftConstraintsSatTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - maxSATSolver.assert(!a or b) - - maxSATSolver.assertSoft(a or !b, 2u) - maxSATSolver.assertSoft(!a or !b, 3u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSATResult.satSoftConstraints) - } - } - - @Test - fun twoOfThreeSoftConstraintsSatTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - - maxSATSolver.assertSoft(!a or b, 4u) - maxSATSolver.assertSoft(a or !b, 6u) - maxSATSolver.assertSoft(!a or !b, 2u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - val softConstraintsToAssertSAT = - listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) - assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) - } - } - - @Test - fun sameExpressionSoftConstraintsSatTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - - val x by boolSort - val y by boolSort - - maxSATSolver.assert(x or y) - - maxSATSolver.assertSoft(!x or y, 7u) - maxSATSolver.assertSoft(x or !y, 6u) - maxSATSolver.assertSoft(!x or !y, 3u) - maxSATSolver.assertSoft(!x or !y, 4u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSoftConstraintsSat( - listOf( - SoftConstraint(!x or y, 7u), - SoftConstraint(!x or !y, 3u), - SoftConstraint(!x or !y, 4u), - ), - maxSATResult.satSoftConstraints, - ) - } - } - - @Test - fun sameExpressionSoftConstraintsUnsatTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - - val x by boolSort - val y by boolSort - - maxSATSolver.assert(x or y) - - maxSATSolver.assertSoft(!x or y, 6u) - maxSATSolver.assertSoft(x or !y, 6u) - maxSATSolver.assertSoft(!x or !y, 3u) - maxSATSolver.assertSoft(!x or !y, 2u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == KSolverStatus.SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat( - listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), - maxSATResult.satSoftConstraints, - ) - } - } - - @Test - fun chooseOneConstraintByWeightTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - val z by boolSort - val a by boolSort - val b by boolSort - - maxSATSolver.assert(z) - maxSATSolver.assertSoft(a and b, 1u) - maxSATSolver.assertSoft(!a and !b, 5u) - maxSATSolver.assertSoft(a and b and z, 2u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSATResult.satSoftConstraints) - } - } - - @Test - fun inequalitiesTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - - val x by intSort - val y by intSort - - val a1 = x gt 0.expr - val a2 = x lt y - val a3 = x + y le 0.expr - - maxSATSolver.assert(a3 eq a1) - maxSATSolver.assert(a3 or a2) - - maxSATSolver.assertSoft(a3, 3u) - maxSATSolver.assertSoft(!a3, 5u) - maxSATSolver.assertSoft(!a1, 10u) - maxSATSolver.assertSoft(!a2, 3u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat( - listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), - maxSATResult.satSoftConstraints, - ) - } - } - - @Test - fun oneScopePushPopTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - - maxSATSolver.assertSoft(!a or b, 1u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) - - maxSATSolver.push() - - maxSATSolver.assertSoft(a or !b, 1u) - maxSATSolver.assertSoft(!a or !b, 1u) - val maxSATResultScoped = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) - - maxSATSolver.pop() - - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) - } - } - - @Test - fun threeScopesPushPopTest() = with(KContext()) { - val z3Solver = KZ3Solver(this) - val maxSATSolver = KMaxResSolver(this, z3Solver) - - val a by boolSort - val b by boolSort - val c by boolSort - - maxSATSolver.assert(a) - - maxSATSolver.push() - maxSATSolver.assertSoft(a or b, 1u) - maxSATSolver.assertSoft(c or b, 1u) - - runBlocking { - val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 2) - - maxSATSolver.push() - maxSATSolver.assertSoft(!b and !c, 2u) - val maxSATResult2 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult2.satSoftConstraints.size == 2) - - maxSATSolver.push() - maxSATSolver.assertSoft(a or c, 1u) - val maxSATResult3 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult3.satSoftConstraints.size == 3) - - maxSATSolver.pop(2u) - val maxSATResult4 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult4.satSoftConstraints.size == 2) - - maxSATSolver.pop() - val maxSATResult5 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) - } - } - - private fun assertSoftConstraintsSat( - constraintsToAssert: List, - satConstraints: List, - ) { - for (constraint in constraintsToAssert) { - assertTrue( - satConstraints.any { - constraint.expression.internEquals(it.expression) && constraint.weight == it.weight - }, - ) - } - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt new file mode 100644 index 000000000..55c66fb6c --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -0,0 +1,325 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.KContext +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.utils.getValue +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +interface KMaxSATSolverTest { + fun getSolver(): KMaxSATSolver + val ctx: KContext + + @Test + fun hardConstraintsUnsatTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(!a) + } + + @Test + fun hardConstraintsUnsatNoSoftTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(!a) + + maxSATSolver.assertSoft(a and !b, 3u) + maxSATSolver.assertSoft(!a, 5u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == UNSAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) + } + + @Test + fun noSoftConstraintsTest() = with(ctx) { + val maxSATSolver = getSolver() + + val a by boolSort + val b by boolSort + val c by boolSort + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(c) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) + } + + @Test + fun noSoftConstraintsSatTest() = with(ctx) { + val maxSATSolver = getSolver() + + val a by boolSort + val b by boolSort + val c by boolSort + maxSATSolver.assert(a) + maxSATSolver.assert(b) + maxSATSolver.assert(c) + maxSATSolver.assertSoft(a and !c, 3u) + maxSATSolver.assertSoft(!a, 5u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) + } + + @Test + fun oneOfTwoSoftConstraintsSatTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + + maxSATSolver.assert(a or b) + maxSATSolver.assert(!a or b) + + maxSATSolver.assertSoft(a or !b, 2u) + maxSATSolver.assertSoft(!a or !b, 3u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSATResult.satSoftConstraints) + } + + @Test + fun twoOfThreeSoftConstraintsSatTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + + maxSATSolver.assert(a or b) + + maxSATSolver.assertSoft(!a or b, 4u) + maxSATSolver.assertSoft(a or !b, 6u) + maxSATSolver.assertSoft(!a or !b, 2u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + val softConstraintsToAssertSAT = + listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) + assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) + } + + @Test + fun sameExpressionSoftConstraintsSatTest() = with(ctx) { + val maxSATSolver = getSolver() + + val x by boolSort + val y by boolSort + + maxSATSolver.assert(x or y) + + maxSATSolver.assertSoft(!x or y, 7u) + maxSATSolver.assertSoft(x or !y, 6u) + maxSATSolver.assertSoft(!x or !y, 3u) + maxSATSolver.assertSoft(!x or !y, 4u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 3) + assertSoftConstraintsSat( + listOf( + SoftConstraint(!x or y, 7u), + SoftConstraint(!x or !y, 3u), + SoftConstraint(!x or !y, 4u), + ), + maxSATResult.satSoftConstraints, + ) + } + + @Test + fun sameExpressionSoftConstraintsUnsatTest() = with(ctx) { + val maxSATSolver = getSolver() + + val x by boolSort + val y by boolSort + + maxSATSolver.assert(x or y) + + maxSATSolver.assertSoft(!x or y, 6u) + maxSATSolver.assertSoft(x or !y, 6u) + maxSATSolver.assertSoft(!x or !y, 3u) + maxSATSolver.assertSoft(!x or !y, 2u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), + maxSATResult.satSoftConstraints, + ) + } + + @Test + fun chooseOneConstraintByWeightTest() = with(ctx) { + val maxSATSolver = getSolver() + val z by boolSort + val a by boolSort + val b by boolSort + + maxSATSolver.assert(z) + maxSATSolver.assertSoft(a and b, 1u) + maxSATSolver.assertSoft(!a and !b, 5u) + maxSATSolver.assertSoft(a and b and z, 2u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSATResult.satSoftConstraints) + } + + @Test + fun equalWeightsTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + + maxSATSolver.assert(a or b) + maxSATSolver.assert(!a or b) + + maxSATSolver.assertSoft(a or !b, 2u) + maxSATSolver.assertSoft(!a or !b, 2u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.satSoftConstraints.size == 1) + } + + @Test + fun inequalitiesTest() = with(ctx) { + val maxSATSolver = getSolver() + + val x by intSort + val y by intSort + + val a1 = x gt 0.expr + val a2 = x lt y + val a3 = x + y le 0.expr + + maxSATSolver.assert(a3 eq a1) + maxSATSolver.assert(a3 or a2) + + maxSATSolver.assertSoft(a3, 3u) + maxSATSolver.assertSoft(!a3, 5u) + maxSATSolver.assertSoft(!a1, 10u) + maxSATSolver.assertSoft(!a2, 3u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), + maxSATResult.satSoftConstraints, + ) + } + + @Test + fun oneScopePushPopTest() = with(ctx) { + val maxSATSolver = getSolver() + val a by boolSort + val b by boolSort + + maxSATSolver.assert(a or b) + + maxSATSolver.assertSoft(!a or b, 1u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) + + maxSATSolver.push() + + maxSATSolver.assertSoft(a or !b, 1u) + maxSATSolver.assertSoft(!a or !b, 1u) + + val maxSATResultScoped = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) + + maxSATSolver.pop() + + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) + } + + @Test + fun threeScopesPushPopTest() = with(ctx) { + val maxSATSolver = getSolver() + + val a by boolSort + val b by boolSort + val c by boolSort + + maxSATSolver.assert(a) + + maxSATSolver.push() + maxSATSolver.assertSoft(a or b, 1u) + maxSATSolver.assertSoft(c or b, 1u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.satSoftConstraints.size == 2) + + maxSATSolver.push() + maxSATSolver.assertSoft(!b and !c, 2u) + val maxSATResult2 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult2.satSoftConstraints.size == 2) + + maxSATSolver.push() + maxSATSolver.assertSoft(a or c, 1u) + val maxSATResult3 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult3.satSoftConstraints.size == 3) + + maxSATSolver.pop(2u) + val maxSATResult4 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult4.satSoftConstraints.size == 2) + + maxSATSolver.pop() + val maxSATResult5 = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) + } + + private fun assertSoftConstraintsSat( + constraintsToAssert: List, + satConstraints: List, + ) { + for (constraint in constraintsToAssert) { + assertTrue( + satConstraints.any { + constraint.expression.internEquals(it.expression) && constraint.weight == it.weight + }, + ) + } + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt new file mode 100644 index 000000000..b0b3e6605 --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt @@ -0,0 +1,16 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.KContext +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPMResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPMResSolverTest : KMaxSATSolverTest { + override val ctx = KContext() + + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPMResSolver(this, z3Solver) + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt new file mode 100644 index 000000000..9741d1bc0 --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt @@ -0,0 +1,16 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.KContext +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolverTest : KMaxSATSolverTest { + override val ctx = KContext() + + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver) + } +} From d4b5c3911352205c10d462f7bc448d840b16c082 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 30 Aug 2023 18:02:59 +0300 Subject: [PATCH 044/228] Add timeout support for primal dual MaxRes solver --- .../solver/maxsat/solvers/KPMResSolver.kt | 4 +- .../maxsat/solvers/KPrimalDualMaxResSolver.kt | 64 +++++++++++++++---- .../maxsat/solvers/utils/MinimalUnsatCore.kt | 16 ++++- .../io/ksmt/solver/maxsat/utils/TimerUtils.kt | 3 + 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt index 40c752729..7fe44f038 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt @@ -28,7 +28,7 @@ class KPMResSolver(private val ctx: KContext, private * @throws NotImplementedError */ override fun checkMaxSAT(timeout: Duration): KMaxSATResult { - if (timeout.isNegative() || timeout == Duration.ZERO) { + if (TimerUtils.timeoutExceeded(timeout)) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") } @@ -88,7 +88,7 @@ class KPMResSolver(private val ctx: KContext, private while (true) { val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) - if (softConstraintsCheckRemainingTime.isNegative() || softConstraintsCheckRemainingTime == Duration.ZERO) { + if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { return KMaxSATResult(listOf(), status, false) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt index 83af20035..32b5936a8 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -14,6 +14,7 @@ import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.maxsat.solvers.utils.MinimalUnsatCore import io.ksmt.solver.maxsat.utils.CoreUtils import io.ksmt.solver.maxsat.utils.ModelUtils +import io.ksmt.solver.maxsat.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration @@ -31,7 +32,13 @@ class KPrimalDualMaxResSolver(private val ctx: KContex private data class WeightedCore(val expressions: List>, val weight: UInt) override fun checkMaxSAT(timeout: Duration): KMaxSATResult { - val hardConstraintsStatus = solver.check() + val clockStart = System.currentTimeMillis() + + if (TimerUtils.timeoutExceeded(timeout)) { + error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") + } + + val hardConstraintsStatus = solver.check(timeout) if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { return KMaxSATResult(listOf(), hardConstraintsStatus, true) @@ -46,7 +53,12 @@ class KPrimalDualMaxResSolver(private val ctx: KContex unionSoftConstraintsWithSameExpressions(assumptions) while (_lower < _upper) { - val status = checkSat(assumptions) + val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { + throw NotImplementedError() + } + + val status = checkSat(assumptions, timeout) when (status) { SAT -> { @@ -62,18 +74,27 @@ class KPrimalDualMaxResSolver(private val ctx: KContex } UNSAT -> { - val processUnsatStatus = processUnsat(assumptions) + val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(remainingTime)) { + solver.pop() + throw NotImplementedError() + } + + val processUnsatStatus = processUnsat(assumptions, remainingTime) if (processUnsatStatus == UNSAT) { _lower = _upper + // TODO: process this case as it can happen when timeout exceeded + solver.pop() + throw NotImplementedError() } else if (processUnsatStatus == UNKNOWN) { solver.pop() - return KMaxSATResult(listOf(), SAT, false) + throw NotImplementedError() } } UNKNOWN -> { solver.pop() - return KMaxSATResult(listOf(), SAT, false) + throw NotImplementedError() } } } @@ -96,8 +117,8 @@ class KPrimalDualMaxResSolver(private val ctx: KContex _correctionSetSize = 0 } - private fun processUnsat(assumptions: MutableList): KSolverStatus { - val (status, cores) = getCores(assumptions) + private fun processUnsat(assumptions: MutableList, timeout: Duration): KSolverStatus { + val (status, cores) = getCores(assumptions, timeout) if (status != SAT) { return status @@ -153,12 +174,22 @@ class KPrimalDualMaxResSolver(private val ctx: KContex } } - private fun getCores(assumptions: MutableList): Pair> { + private fun getCores( + assumptions: MutableList, + timeout: Duration, + ): Pair> { + val clockStart = System.currentTimeMillis() + val cores = mutableListOf() var status = UNSAT while (status == UNSAT) { - val minimalUnsatCore = minimizeCore(assumptions) + val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime)) { + return Pair(SAT, cores) // TODO: is this status Ok? + } + + val minimalUnsatCore = minimizeCore(assumptions, minimizeCoreRemainingTime) updateMinimalUnsatCoreModel(assumptions) if (minimalUnsatCore.isEmpty()) { @@ -179,14 +210,19 @@ class KPrimalDualMaxResSolver(private val ctx: KContex return Pair(SAT, cores) } - status = checkSat(assumptions) + val checkSatRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(checkSatRemainingTime)) { + return Pair(SAT, cores) // TODO: is this status Ok? + } + + status = checkSat(assumptions, checkSatRemainingTime) } return Pair(status, cores) } - private fun minimizeCore(assumptions: List): List = - _minimalUnsatCore.tryGetMinimalUnsatCore(assumptions) + private fun minimizeCore(assumptions: List, timeout: Duration): List = + _minimalUnsatCore.tryGetMinimalUnsatCore(assumptions, timeout) private fun updateMinimalUnsatCoreModel(assumptions: List) { val (model, weight) = _minimalUnsatCore.getBestModel() @@ -358,8 +394,8 @@ class KPrimalDualMaxResSolver(private val ctx: KContex return ModelUtils.getCorrectionSet(ctx, model, assumptions) } - private fun checkSat(assumptions: List): KSolverStatus { - val status = solver.checkWithAssumptions(assumptions.map { it.expression }) + private fun checkSat(assumptions: List, timeout: Duration): KSolverStatus { + val status = solver.checkWithAssumptions(assumptions.map { it.expression }, timeout) if (status == SAT) { updateAssignment(solver.model().detach(), assumptions) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt index e96b81b97..dce048813 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt @@ -10,7 +10,9 @@ import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.maxsat.utils.CoreUtils import io.ksmt.solver.maxsat.utils.ModelUtils +import io.ksmt.solver.maxsat.utils.TimerUtils import io.ksmt.sort.KBoolSort +import kotlin.time.Duration internal class MinimalUnsatCore( private val ctx: KContext, @@ -21,7 +23,12 @@ internal class MinimalUnsatCore( fun getBestModel(): Pair = _minimalUnsatCoreModel.getBestModel() // If solver starts returning unknown, we return non minimized unsat core. - fun tryGetMinimalUnsatCore(assumptions: List): List = with(ctx) { + fun tryGetMinimalUnsatCore( + assumptions: List, + timeout: Duration = Duration.INFINITE, + ): List = with(ctx) { + val clockStart = System.currentTimeMillis() + val unsatCore = solver.unsatCore() if (unsatCore.isEmpty() || unsatCore.size == 1) { @@ -33,10 +40,15 @@ internal class MinimalUnsatCore( val unknown = unsatCore.toMutableList() while (unknown.isNotEmpty()) { + val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(remainingTime)) { + return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + } + val expr = unknown.removeLast() val notExpr = !expr - val status = solver.checkWithAssumptions(minimalUnsatCore + unknown + notExpr) + val status = solver.checkWithAssumptions(minimalUnsatCore + unknown + notExpr, remainingTime) when (status) { UNKNOWN -> return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt index f53878c39..aebe282ae 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt @@ -9,4 +9,7 @@ internal object TimerUtils { val msUnit = DurationUnit.MILLISECONDS return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) } + + fun timeoutExceeded(timeout: Duration): Boolean = + timeout.isNegative() || timeout == Duration.ZERO } From d4422bd5ff9c25a53271ac475fe1719e3da94c68 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 31 Aug 2023 12:09:57 +0300 Subject: [PATCH 045/228] Add KMaxSatContext - Support in KPrimalDualSolver two strategies: PrimalMaxRes and PrimalDualMaxRes --- .../io/ksmt/solver/maxsat/KMaxSATContext.kt | 16 +++++++++ .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 2 +- .../maxsat/solvers/KPrimalDualMaxResSolver.kt | 33 ++++++++++++++----- 3 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt new file mode 100644 index 000000000..c8eff97c2 --- /dev/null +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt @@ -0,0 +1,16 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.maxsat.KMaxSATContext.Strategy.PrimalDualMaxRes + +class KMaxSATContext( + val strategy: Strategy = PrimalDualMaxRes, + val preferLargeWeightConstraintsForCores: Boolean = true, + val minimizeCores: Boolean = true, + val getMultipleCores: Boolean = true, +) { + + enum class Strategy { + PrimalMaxRes, + PrimalDualMaxRes, + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt index 7b797359e..867c494ef 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt @@ -1,7 +1,7 @@ package io.ksmt.solver.maxsat -import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.maxsat.constraints.SoftConstraint /** * @property satSoftConstraints diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt index 32b5936a8..38151e066 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -9,6 +9,8 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsat.KMaxSATContext +import io.ksmt.solver.maxsat.KMaxSATContext.Strategy.PrimalDualMaxRes import io.ksmt.solver.maxsat.KMaxSATResult import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.maxsat.solvers.utils.MinimalUnsatCore @@ -18,7 +20,11 @@ import io.ksmt.solver.maxsat.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration -class KPrimalDualMaxResSolver(private val ctx: KContext, private val solver: KSolver) : +class KPrimalDualMaxResSolver( + private val ctx: KContext, + private val solver: KSolver, + private val maxSatCtx: KMaxSATContext, +) : KMaxResSolver(ctx, solver) { private var _lower: UInt = 0u // Current lower frontier private var _upper: UInt = 0u // Current upper frontier @@ -62,14 +68,20 @@ class KPrimalDualMaxResSolver(private val ctx: KContex when (status) { SAT -> { - val correctionSet = getCorrectionSet(solver.model(), assumptions) - if (correctionSet.isEmpty()) { - if (_model != null) { - // Feasible optimum is found by the moment. - _lower = _upper + when (maxSatCtx.strategy) { + KMaxSATContext.Strategy.PrimalMaxRes -> _upper = _lower + + KMaxSATContext.Strategy.PrimalDualMaxRes -> { + val correctionSet = getCorrectionSet(solver.model(), assumptions) + if (correctionSet.isEmpty()) { + if (_model != null) { + // Feasible optimum is found by the moment. + _lower = _upper + } + } else { + processSat(correctionSet, assumptions) + } } - } else { - processSat(correctionSet, assumptions) } } @@ -146,7 +158,10 @@ class KPrimalDualMaxResSolver(private val ctx: KContex assert(fml) _lower += weightedCore.weight - _lower = minOf(_lower, _upper) + + if (maxSatCtx.strategy == PrimalDualMaxRes) { + _lower = minOf(_lower, _upper) + } if (_correctionSetModel != null && _correctionSetSize > 0) { // This estimate can overshoot for weighted soft constraints. From 73af599e4db06fcb9c0bf6cf7d289ab8d2f2b273 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 31 Aug 2023 13:00:11 +0300 Subject: [PATCH 046/228] Fix tests: pass MaxSAT context to KPrimalDualMaxResSolver constructor --- .../ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt | 3 ++- .../io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt index 9c0df5a0c..cdbcb0d4d 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt @@ -1,6 +1,7 @@ package io.ksmt.solver.maxsat.test import io.ksmt.KContext +import io.ksmt.solver.maxsat.KMaxSATContext import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver @@ -11,6 +12,6 @@ class KPrimalDualMaxResBenchmarkTest : KMaxSATBenchmarkTest() { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) } } diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt index 9741d1bc0..32e2b0b93 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt @@ -11,6 +11,6 @@ class KPrimalDualMaxResSolverTest : KMaxSATSolverTest { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) } } From f584b3169c51637323d85021cca1a6eed2a55d9d Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 31 Aug 2023 14:37:20 +0300 Subject: [PATCH 047/228] Add hill-climb approach when checking satisfiability --- .../maxsat/solvers/KPrimalDualMaxResSolver.kt | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt index 38151e066..9e3d4384b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -64,7 +64,7 @@ class KPrimalDualMaxResSolver( throw NotImplementedError() } - val status = checkSat(assumptions, timeout) + val status = checkSATHillClimb(assumptions, timeout) when (status) { SAT -> { @@ -230,7 +230,7 @@ class KPrimalDualMaxResSolver( return Pair(SAT, cores) // TODO: is this status Ok? } - status = checkSat(assumptions, checkSatRemainingTime) + status = checkSATHillClimb(assumptions, checkSatRemainingTime) } return Pair(status, cores) @@ -409,6 +409,53 @@ class KPrimalDualMaxResSolver( return ModelUtils.getCorrectionSet(ctx, model, assumptions) } + private fun checkSATHillClimb(assumptions: MutableList, timeout: Duration): KSolverStatus { + var status = SAT + + if (maxSatCtx.preferLargeWeightConstraintsForCores && assumptions.isNotEmpty()) { + val clockStart = System.currentTimeMillis() + + // Give preference to cores that have large minimal values. + assumptions.sortByDescending { it.weight } + + var lastIndex = 0 + var index = 0 + + while (index < assumptions.size && status == SAT) { + while (assumptions.size > 20 * (index - lastIndex) && index < assumptions.size) { + index = getNextIndex(assumptions, index) + } + lastIndex = index + + val assumptionsToCheck = assumptions.subList(0, index) + + val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(remainingTime)) { + return UNKNOWN + } + + status = checkSat(assumptionsToCheck, remainingTime) + } + } else { + status = checkSat(assumptions, timeout) + } + + return status + } + + private fun getNextIndex(assumptions: List, index: Int): Int { + var currentIndex = index + + if (currentIndex < assumptions.size) { + val weight = assumptions[currentIndex].weight + ++currentIndex + while (currentIndex < assumptions.size && weight == assumptions[currentIndex].weight) { + ++currentIndex + } + } + return currentIndex + } + private fun checkSat(assumptions: List, timeout: Duration): KSolverStatus { val status = solver.checkWithAssumptions(assumptions.map { it.expression }, timeout) From 7eb8b402804a6c28e02db1e7b40048db7448e7b3 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 31 Aug 2023 14:39:59 +0300 Subject: [PATCH 048/228] Let extract a single core or multiple cores --- .../ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt index 9e3d4384b..e9f064ec0 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -130,7 +130,13 @@ class KPrimalDualMaxResSolver( } private fun processUnsat(assumptions: MutableList, timeout: Duration): KSolverStatus { - val (status, cores) = getCores(assumptions, timeout) + val (status, cores) = if (maxSatCtx.getMultipleCores) { + getCores(assumptions, timeout) + } else { + val core = solver.unsatCore() + val coreSoftConstraints = CoreUtils.coreToSoftConstraints(core, assumptions) + Pair(SAT, listOf(WeightedCore(core, CoreUtils.getCoreWeight(coreSoftConstraints)))) + } if (status != SAT) { return status From 59e652ae796b373810f3fade026608e10da0c4a2 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 8 Sep 2023 17:14:49 +0300 Subject: [PATCH 049/228] Fix a bug when getting a single core in KPrimalDualMaxResSolver --- .../solver/maxsat/solvers/KPrimalDualMaxResSolver.kt | 10 ++-------- .../solver/maxsat/solvers/utils/MinimalUnsatCore.kt | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt index e9f064ec0..6b6812515 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt @@ -30,7 +30,7 @@ class KPrimalDualMaxResSolver( private var _upper: UInt = 0u // Current upper frontier private var _maxUpper = 0u // Max possible upper frontier private var _correctionSetSize: Int = 0 // Current corrections set size - private val _maxCoreSize = 3 + private val _maxCoreSize = if (maxSatCtx.getMultipleCores) 3 else 1 private var _correctionSetModel: KModel? = null private var _model: KModel? = null private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) @@ -130,13 +130,7 @@ class KPrimalDualMaxResSolver( } private fun processUnsat(assumptions: MutableList, timeout: Duration): KSolverStatus { - val (status, cores) = if (maxSatCtx.getMultipleCores) { - getCores(assumptions, timeout) - } else { - val core = solver.unsatCore() - val coreSoftConstraints = CoreUtils.coreToSoftConstraints(core, assumptions) - Pair(SAT, listOf(WeightedCore(core, CoreUtils.getCoreWeight(coreSoftConstraints)))) - } + val (status, cores) = getCores(assumptions, timeout) if (status != SAT) { return status diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt index dce048813..12633eef4 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt @@ -31,7 +31,7 @@ internal class MinimalUnsatCore( val unsatCore = solver.unsatCore() - if (unsatCore.isEmpty() || unsatCore.size == 1) { + if (unsatCore.isEmpty()) { return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) } From 43b6b69c1e539185cb54cbd23297777af6118a32 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Fri, 8 Sep 2023 17:21:29 +0300 Subject: [PATCH 050/228] Add unit tests for all configurations * KPMResSolver * KPrimalDualMaxResSolver (with configurations of large weight constraints for cores, cores minimization, getting multiple cores) --- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 37 ++++++++----------- .../io/ksmt/solver/maxsat/KPMResSolverTest.kt | 5 +-- .../maxsat/KPrimalDualMaxResSolver2Test.kt | 13 +++++++ .../maxsat/KPrimalDualMaxResSolver3Test.kt | 13 +++++++ .../maxsat/KPrimalDualMaxResSolver4Test.kt | 13 +++++++ .../maxsat/KPrimalDualMaxResSolverTest.kt | 5 +-- 6 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt create mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 55c66fb6c..43b065c9e 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -7,16 +7,27 @@ import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.z3.KZ3SolverConfiguration import io.ksmt.utils.getValue +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -interface KMaxSATSolverTest { - fun getSolver(): KMaxSATSolver - val ctx: KContext +abstract class KMaxSATSolverTest { + abstract fun getSolver(): KMaxSATSolver + + protected val ctx: KContext = KContext() + private lateinit var maxSATSolver: KMaxSATSolver + + @BeforeEach + fun initSolver() { + maxSATSolver = getSolver() + } + + @AfterEach + fun closeSolver() = maxSATSolver.close() @Test fun hardConstraintsUnsatTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort maxSATSolver.assert(a) @@ -26,7 +37,6 @@ interface KMaxSATSolverTest { @Test fun hardConstraintsUnsatNoSoftTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort maxSATSolver.assert(a) @@ -45,8 +55,6 @@ interface KMaxSATSolverTest { @Test fun noSoftConstraintsTest() = with(ctx) { - val maxSATSolver = getSolver() - val a by boolSort val b by boolSort val c by boolSort @@ -63,8 +71,6 @@ interface KMaxSATSolverTest { @Test fun noSoftConstraintsSatTest() = with(ctx) { - val maxSATSolver = getSolver() - val a by boolSort val b by boolSort val c by boolSort @@ -83,7 +89,6 @@ interface KMaxSATSolverTest { @Test fun oneOfTwoSoftConstraintsSatTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort @@ -103,7 +108,6 @@ interface KMaxSATSolverTest { @Test fun twoOfThreeSoftConstraintsSatTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort @@ -125,8 +129,6 @@ interface KMaxSATSolverTest { @Test fun sameExpressionSoftConstraintsSatTest() = with(ctx) { - val maxSATSolver = getSolver() - val x by boolSort val y by boolSort @@ -154,8 +156,6 @@ interface KMaxSATSolverTest { @Test fun sameExpressionSoftConstraintsUnsatTest() = with(ctx) { - val maxSATSolver = getSolver() - val x by boolSort val y by boolSort @@ -179,7 +179,6 @@ interface KMaxSATSolverTest { @Test fun chooseOneConstraintByWeightTest() = with(ctx) { - val maxSATSolver = getSolver() val z by boolSort val a by boolSort val b by boolSort @@ -199,7 +198,6 @@ interface KMaxSATSolverTest { @Test fun equalWeightsTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort @@ -217,8 +215,6 @@ interface KMaxSATSolverTest { @Test fun inequalitiesTest() = with(ctx) { - val maxSATSolver = getSolver() - val x by intSort val y by intSort @@ -247,7 +243,6 @@ interface KMaxSATSolverTest { @Test fun oneScopePushPopTest() = with(ctx) { - val maxSATSolver = getSolver() val a by boolSort val b by boolSort @@ -276,8 +271,6 @@ interface KMaxSATSolverTest { @Test fun threeScopesPushPopTest() = with(ctx) { - val maxSATSolver = getSolver() - val a by boolSort val b by boolSort val c by boolSort diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt index b0b3e6605..455662384 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt @@ -1,14 +1,11 @@ package io.ksmt.solver.maxsat -import io.ksmt.KContext import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.maxsat.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration -class KPMResSolverTest : KMaxSATSolverTest { - override val ctx = KContext() - +class KPMResSolverTest : KMaxSATSolverTest() { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt new file mode 100644 index 000000000..3e4cc80cf --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver2Test : KMaxSATSolverTest() { + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(preferLargeWeightConstraintsForCores = false)) + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt new file mode 100644 index 000000000..4befdbedf --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver3Test : KMaxSATSolverTest() { + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(minimizeCores = false)) + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt new file mode 100644 index 000000000..f3f0d6f03 --- /dev/null +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsat + +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver4Test : KMaxSATSolverTest() { + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(getMultipleCores = false)) + } +} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt index 32e2b0b93..2f21b6ebd 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt @@ -1,14 +1,11 @@ package io.ksmt.solver.maxsat -import io.ksmt.KContext import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration -class KPrimalDualMaxResSolverTest : KMaxSATSolverTest { - override val ctx = KContext() - +class KPrimalDualMaxResSolverTest : KMaxSATSolverTest() { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) From 18fe5a221215327518a707041f3ef9c30fcb982a Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 5 Oct 2023 19:48:58 +0300 Subject: [PATCH 051/228] Support running MaxSMT benchmark tests --- .../src/main/kotlin/MaxSATBenchmarkUtil.kt | 37 ++++- ksmt-maxsat-test/build.gradle.kts | 52 ++++-- .../ksmt/solver/maxsat/test/MaxSMTTestInfo.kt | 7 + .../io/ksmt/solver/maxsat/test/TestParser.kt | 43 ++++- .../maxsat/test/KMaxSMTBenchmarkBasedTest.kt | 40 +++++ .../test/{ => sat}/KMaxSATBenchmarkTest.kt | 68 +++----- .../KPMResSATBenchmarkTest.kt} | 7 +- .../KPrimalDualMaxResSATBenchmarkTest.kt} | 7 +- .../maxsat/test/smt/KMaxSMTBenchmarkTest.kt | 155 ++++++++++++++++++ .../maxsat/test/smt/KPMResSMTBenchmarkTest.kt | 24 +++ .../solver/maxsat/solvers/KPMResSolver.kt | 4 +- .../io/ksmt/solver/maxsat/utils/CoreUtils.kt | 14 +- 12 files changed, 383 insertions(+), 75 deletions(-) create mode 100644 ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt rename ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/{ => sat}/KMaxSATBenchmarkTest.kt (93%) rename ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/{KPMResBenchmarkTest.kt => sat/KPMResSATBenchmarkTest.kt} (70%) rename ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/{KPrimalDualMaxResBenchmarkTest.kt => sat/KPrimalDualMaxResSATBenchmarkTest.kt} (73%) create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt create mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt diff --git a/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt b/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt index 6e6cb0130..6e7a8e582 100644 --- a/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt @@ -1,14 +1,49 @@ import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.DuplicatesStrategy import org.gradle.kotlin.dsl.support.unzipTo import java.io.File +fun Project.usePreparedMaxSmtBenchmarkTestData(path: File) = + usePreparedBenchmarkTestData(path, "maxSmt") + +fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = tasks.register("maxSmtBenchmark-$name") { + doLast { + val path = buildDir.resolve("maxSmtBenchmark/$name") + val downloadTarget = path.resolve("$name.zip") + val url = "https://github.com/victoriafomina/gen-benchmark/releases/download/$testDataRevision/$name.zip" + + download(url, downloadTarget) + + path.executeIfNotReady("unpack-complete") { + copy { + from(zipTree(downloadTarget)) + into(path) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + + val testResources = testResourceDir() ?: error("No resource directory found for $name benchmark") + val testData = testResources.resolve("testData") + + testData.executeIfNotReady("$name-copy-complete") { + val maxSmtFiles = path.walkTopDown().filter { it.extension == "smt2" || it.extension == "maxsmt" }.toList() + copy { + from(maxSmtFiles.toTypedArray()) + into(testData) + rename { "${name}_$it" } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + } +} + fun Project.usePreparedMaxSATBenchmarkTestData(path: File): Task = usePreparedBenchmarkTestData(path, MAXSAT_BENCHMARK_NAME_L) .get() .finalizedBy(unzipMaxSATBenchmarkTestFiles()) -fun Project.downloadPreparedMaxSATBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) = +fun Project.downloadMaxSATBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) = downloadPreparedBenchmarkTestData( downloadPath, testDataPath, diff --git a/ksmt-maxsat-test/build.gradle.kts b/ksmt-maxsat-test/build.gradle.kts index 35333a699..a41ed694c 100644 --- a/ksmt-maxsat-test/build.gradle.kts +++ b/ksmt-maxsat-test/build.gradle.kts @@ -13,37 +13,69 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.2") testImplementation(project(":ksmt-z3")) + testImplementation(project(":ksmt-test")) + testImplementation(project(":ksmt-runner")) + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") } -val runBenchmarksBasedTests = project.booleanProperty("runBenchmarksBasedTests") ?: false +val maxSmtBenchmarks = listOfNotNull( + "QF_ABVFP", // 8.84M + "QF_BV-1", // 17.2M + "QF_BV-2", // 20.8М +) + +val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false +val runMaxSATBenchmarkBasedTests = project.booleanProperty("runMaxSATBenchmarkBasedTests") ?: false // use benchmarks from testData directory instead of downloading -val usePreparedBenchmarks = project.booleanProperty("usePreparedBenchmarks") ?: true +val usePreparedMaxSMTBenchmarks = project.booleanProperty("usePreparedMaxSMTBenchmarks") ?: true +val usePreparedMaxSATBenchmark = project.booleanProperty("usePreparedMaxSATBenchmark") ?: true val testDataDir = projectDir.resolve("testData") val unpackedTestDataDir = testDataDir.resolve("data") val downloadedTestData = testDataDir.resolve("testData.zip") -val testDataRevision = project.stringProperty("testDataRevision") ?: "no-revision" -val downloadPreparedBenchmarksTestData = downloadPreparedMaxSATBenchmarkTestData( +val maxSmtTestDataRevision = project.stringProperty("maxSmtTestDataRevision") ?: "no-revision" +val maxSatTestDataRevision = project.stringProperty("maxSatTestDataRevision") ?: "no-revision" + +val usePreparedMaxSmtTestData = usePreparedMaxSmtBenchmarkTestData(unpackedTestDataDir) +val downloadPreparedMaxSmtBenchmarkTestData = + maxSmtBenchmarks.map { maxSmtBenchmarkTestData(it, maxSmtTestDataRevision) } + +val usePreparedMaxSMTTestData by tasks.registering { + tasks.withType().forEach { it.enabled = false } + if (usePreparedMaxSMTBenchmarks) { + dependsOn.add(usePreparedMaxSmtTestData) + } else { + dependsOn.addAll(downloadPreparedMaxSmtBenchmarkTestData) + } +} + +tasks.withType { + if (runMaxSMTBenchmarkBasedTests) { + dependsOn.add(usePreparedMaxSMTTestData) + } +} + +val downloadPreparedMaxSATBenchmarkTestData = downloadMaxSATBenchmarkTestData( downloadPath = downloadedTestData, testDataPath = unpackedTestDataDir, - testDataRevision = testDataRevision, + testDataRevision = maxSatTestDataRevision, ) val preparedMaxSATBenchmarkTestData = usePreparedMaxSATBenchmarkTestData(unpackedTestDataDir) -val usePreparedTestData by tasks.registering { +val usePreparedMaxSATTestData by tasks.registering { tasks.withType().forEach { it.enabled = false } - if (!usePreparedBenchmarks) { - dependsOn.add(downloadPreparedBenchmarksTestData) + if (!usePreparedMaxSATBenchmark) { + dependsOn.add(downloadPreparedMaxSATBenchmarkTestData) } finalizedBy(preparedMaxSATBenchmarkTestData) } tasks.withType { - if (runBenchmarksBasedTests) { - dependsOn.add(usePreparedTestData) + if (runMaxSATBenchmarkBasedTests) { + dependsOn.add(usePreparedMaxSATTestData) } } diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt new file mode 100644 index 000000000..eef8c0310 --- /dev/null +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt @@ -0,0 +1,7 @@ +package io.ksmt.solver.maxsat.test + +data class MaxSMTTestInfo( + val softConstraintsWeights: List, + val softConstraintsWeightsSum: ULong, + val satSoftConstraintsWeightsSum: ULong +) diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt index 8914fff4b..8740d6409 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt +++ b/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt @@ -10,11 +10,42 @@ import java.nio.file.Path import kotlin.io.path.extension import kotlin.io.path.notExists -fun parseTest(path: Path, ctx: KContext): List { - var currentState: CurrentLineState +fun parseMaxSMTTestInfo(path: Path): MaxSMTTestInfo { + if (path.notExists()) { + error("Path [$path] does not exist") + } - val constraints = mutableListOf() + require(path.extension == "maxsmt") { + "File extension cannot be '${path.extension}' as it must be 'maxsmt'" + } + + var softConstraintsWeights = listOf() + var softConstraintsWeightsSum = 0uL + var satSoftConstraintsWeightsSum = 0uL + + File(path.toUri()).forEachLine { str -> + when { + str.startsWith("soft_constraints_weights: ") -> { + softConstraintsWeights = str + .removePrefix("soft_constraints_weights: ") + .split(" ") + .map { it.toUInt() } + } + + str.startsWith("soft_constraints_weights_sum: ") -> { + softConstraintsWeightsSum = str.removePrefix("soft_constraints_weights_sum: ").toULong() + } + str.startsWith("sat_soft_constraints_weights_sum: ") -> { + satSoftConstraintsWeightsSum = str.removePrefix("sat_soft_constraints_weights_sum: ").toULong() + } + } + } + + return MaxSMTTestInfo(softConstraintsWeights, softConstraintsWeightsSum, satSoftConstraintsWeightsSum) +} + +fun parseMaxSATTest(path: Path, ctx: KContext): List { if (path.notExists()) { error("Path [$path] does not exist") } @@ -23,6 +54,10 @@ fun parseTest(path: Path, ctx: KContext): List { "File extension cannot be '${path.extension}' as it must be 'wcnf'" } + var currentState: CurrentLineState + + val constraints = mutableListOf() + File(path.toUri()).forEachLine { wcnfStr -> currentState = when { wcnfStr.startsWith("c") -> CurrentLineState.COMMENT @@ -73,5 +108,5 @@ fun parseTest(path: Path, ctx: KContext): List { private fun getConstraintBeginIndex(currentState: CurrentLineState, str: String): Int = when (currentState) { CurrentLineState.HARD_CONSTRAINT -> 2 CurrentLineState.SOFT_CONSTRAINT -> str.substringBefore(" ").length + 1 - else -> error("Unexpected value: [${currentState}]") + else -> error("Unexpected value: [$currentState]") } diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt new file mode 100644 index 000000000..b3501f470 --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt @@ -0,0 +1,40 @@ +package io.ksmt.solver.maxsat.test + +import org.junit.jupiter.params.provider.Arguments +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.relativeTo + +interface KMaxSMTBenchmarkBasedTest { + data class BenchmarkTestArguments( + val name: String, + val samplePath: Path, + ) : Arguments { + override fun get() = arrayOf(name, samplePath) + } + + companion object { + private fun testDataLocation(): Path = + this::class.java.classLoader.getResource("testData")?.toURI()?.let { Paths.get(it) } + ?: error("No test data") + + private fun prepareTestData(): List { + val testDataLocation = testDataLocation() + return testDataLocation.listDirectoryEntries("*.smt2").sorted() + .map { + BenchmarkTestArguments( + it.relativeTo(testDataLocation).toString(), + it, + ) + } + } + + private val testData by lazy { + prepareTestData() + } + + @JvmStatic + fun maxSMTTestData() = testData + } +} diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt similarity index 93% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt rename to ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt index 6e8181653..a18c984df 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt @@ -1,35 +1,43 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsat.test.sat import io.ksmt.KContext -import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.maxsat.constraints.HardConstraint import io.ksmt.solver.maxsat.constraints.SoftConstraint import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsat.test.parseMaxSATTest import io.ksmt.solver.z3.KZ3SolverConfiguration +import org.junit.jupiter.api.AfterEach +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 import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.relativeTo import kotlin.test.assertEquals import kotlin.test.assertTrue -import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds -abstract class KMaxSATBenchmarkTest { +abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { abstract fun getSolver(): KMaxSATSolver - abstract val ctx: KContext + + protected val ctx: KContext = KContext() + private lateinit var maxSATSolver: KMaxSATSolver + + @BeforeEach + fun initSolver() { + maxSATSolver = getSolver() + } + + @AfterEach + fun closeSolver() = maxSATSolver.close() @ParameterizedTest(name = "{0}") - @MethodSource("maxSATTestData") + @MethodSource("maxSMTTestData") fun maxSATTest(name: String, samplePath: Path) = with(ctx) { val testData = maxSATTestNameToExpectedResult.find { it.first == name } require(testData != null) { "Test [$name] expected result must be specified" } - val constraints = parseTest(samplePath, this) - - val maxSATSolver = getSolver() + val constraints = parseMaxSATTest(samplePath, this) var sumOfSoftConstraintsWeights = 0u @@ -42,10 +50,10 @@ abstract class KMaxSATBenchmarkTest { } } - val maxSATResult = maxSATSolver.checkMaxSAT(1.minutes) + val maxSATResult = maxSATSolver.checkMaxSAT(20.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } - assertEquals(KSolverStatus.SAT, maxSATResult.hardConstraintsSATStatus) + assertEquals(SAT, maxSATResult.hardConstraintsSATStatus) assertTrue(maxSATResult.maxSATSucceeded && maxSATResult.satSoftConstraints.isNotEmpty()) assertEquals( sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second, @@ -53,36 +61,6 @@ abstract class KMaxSATBenchmarkTest { ) } - data class BenchmarkTestArguments( - val name: String, - val samplePath: Path, - ) : Arguments { - override fun get() = arrayOf(name, samplePath) - } - - companion object { - private fun testDataLocation(): Path = this::class.java.classLoader - .getResource("testData") - ?.toURI() - ?.let { Paths.get(it) } - ?: error("No test data") - - private fun prepareTestData(): List { - val testDataLocation = testDataLocation() - return testDataLocation - .listDirectoryEntries("*.wcnf") - .sorted() - .map { BenchmarkTestArguments(it.relativeTo(testDataLocation).toString(), it) } - } - - private val testData by lazy { - prepareTestData() - } - - @JvmStatic - fun maxSATTestData() = testData - } - // Elements are pairs of test name and expected test result (cost equal to the excluded soft constraints sum). private val maxSATTestNameToExpectedResult = hashSetOf>( "ae_5_20_ibmq-casablanca_7.wcnf" to 15u, diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt similarity index 70% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt rename to ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt index e3fbace15..f6404e525 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPMResBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt @@ -1,14 +1,11 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsat.test.sat -import io.ksmt.KContext import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.maxsat.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration -class KPMResBenchmarkTest : KMaxSATBenchmarkTest() { - override val ctx = KContext() - +class KPMResSATBenchmarkTest : KMaxSATBenchmarkTest() { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt similarity index 73% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt rename to ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt index cdbcb0d4d..6c5e4c381 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KPrimalDualMaxResBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -1,15 +1,12 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsat.test.sat -import io.ksmt.KContext import io.ksmt.solver.maxsat.KMaxSATContext import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration -class KPrimalDualMaxResBenchmarkTest : KMaxSATBenchmarkTest() { - override val ctx = KContext() - +class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { override fun getSolver(): KMaxSATSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt new file mode 100644 index 000000000..e9ed171b7 --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt @@ -0,0 +1,155 @@ +package io.ksmt.solver.maxsat.test.smt + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.core.KsmtWorkerArgs +import io.ksmt.runner.core.KsmtWorkerFactory +import io.ksmt.runner.core.KsmtWorkerPool +import io.ksmt.runner.core.RdServer +import io.ksmt.runner.core.WorkerInitializationFailedException +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsat.test.parseMaxSMTTestInfo +import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.sort.KBoolSort +import io.ksmt.test.TestRunner +import io.ksmt.test.TestWorker +import io.ksmt.test.TestWorkerProcess +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import java.io.File +import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { + abstract fun getSolver(): KMaxSATSolver + + protected val ctx: KContext = KContext() + private lateinit var maxSATSolver: KMaxSATSolver + + @BeforeEach + fun initSolver() { + maxSATSolver = getSolver() + } + + @AfterEach + fun closeSolver() = maxSATSolver.close() + + fun maxSMTTest( + name: String, + samplePath: Path, + mkKsmtAssertions: suspend TestRunner.(List>) -> List>, + ) { + val extension = "smt2" + require(samplePath.extension == extension) { + "File extension cannot be '${samplePath.extension}' as it must be $extension" + } + + lateinit var ksmtAssertions: List> + + testWorkers.withWorker(ctx) { worker -> + val assertions = worker.parseFile(samplePath) + val convertedAssertions = worker.convertAssertions(assertions) + ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + } + + val maxSmtTestPath = File(samplePath.toString().removeSuffix(extension) + "maxsmt").toPath() + val maxSmtTestInfo = parseMaxSMTTestInfo(maxSmtTestPath) + + val softConstraintsSize = maxSmtTestInfo.softConstraintsWeights.size + + val softExpressions = + ksmtAssertions.subList(ksmtAssertions.size - softConstraintsSize, ksmtAssertions.size) + val hardExpressions = ksmtAssertions.subList(0, ksmtAssertions.size - softConstraintsSize) + + hardExpressions.forEach { + maxSATSolver.assert(it) + } + + maxSmtTestInfo.softConstraintsWeights + .zip(softExpressions) + .forEach { (weight, expr) -> + maxSATSolver.assertSoft(expr, weight) + } + + val maxSATResult = maxSATSolver.checkMaxSAT(60.seconds) + val satSoftConstraintsWeightsSum = maxSATResult.satSoftConstraints.sumOf { it.weight } + + assertEquals(SAT, maxSATResult.hardConstraintsSATStatus) + assertTrue(maxSATResult.maxSATSucceeded) + assertEquals( + maxSmtTestInfo.satSoftConstraintsWeightsSum, + satSoftConstraintsWeightsSum.toULong(), + ) + } + + private fun KsmtWorkerPool.withWorker( + ctx: KContext, + body: suspend (TestRunner) -> Unit, + ) = runBlocking { + val worker = try { + getOrCreateFreeWorker() + } catch (ex: WorkerInitializationFailedException) { + ignoreTest { "worker initialization failed -- ${ex.message}" } + } + worker.astSerializationCtx.initCtx(ctx) + worker.lifetime.onTermination { + worker.astSerializationCtx.resetCtx() + } + try { + TestRunner(ctx, TEST_WORKER_SINGLE_OPERATION_TIMEOUT, worker).let { + try { + it.init() + body(it) + } finally { + it.delete() + } + } + } catch (ex: TimeoutCancellationException) { + ignoreTest { "worker timeout -- ${ex.message}" } + } finally { + worker.release() + } + } + + // See [handleIgnoredTests] + private inline fun ignoreTest(message: () -> String?): Nothing { + throw IgnoreTestException(message()) + } + + class IgnoreTestException(message: String?) : Exception(message) + + companion object { + val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 25.seconds + + internal lateinit var testWorkers: KsmtWorkerPool + + @BeforeAll + @JvmStatic + fun initWorkerPools() { + testWorkers = KsmtWorkerPool( + maxWorkerPoolSize = 4, + workerProcessIdleTimeout = 10.minutes, + workerFactory = object : KsmtWorkerFactory { + override val childProcessEntrypoint = TestWorkerProcess::class + override fun updateArgs(args: KsmtWorkerArgs): KsmtWorkerArgs = args + override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) + }, + ) + } + + @AfterAll + @JvmStatic + fun closeWorkerPools() = testWorkers.terminate() + } +} diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt new file mode 100644 index 000000000..d5f1a61a1 --- /dev/null +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt @@ -0,0 +1,24 @@ +package io.ksmt.solver.maxsat.test.smt + +import io.ksmt.solver.maxsat.solvers.KMaxSATSolver +import io.ksmt.solver.maxsat.solvers.KPMResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import java.nio.file.Path + +class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(): KMaxSATSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPMResSolver(this, z3Solver) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath) { assertions -> + assertions + } + } +} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt index 7fe44f038..b47e63b6b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt @@ -50,8 +50,8 @@ class KPMResSolver(private val ctx: KContext, private return when (solverStatus) { SAT -> processSat(model!!) - UNSAT -> throw NotImplementedError() - UNKNOWN -> throw NotImplementedError() + UNSAT -> KMaxSATResult(listOf(), SAT, false) // TODO: support anytime solving + UNKNOWN -> KMaxSATResult(listOf(), SAT, false) // TODO: support anytime solving else -> error("Unexpected status: $solverStatus") } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt index 104a74f8e..fb563763a 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt +++ b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt @@ -6,15 +6,23 @@ import io.ksmt.sort.KBoolSort internal object CoreUtils { fun coreToSoftConstraints(core: List>, assumptions: List): List { + val uniqueCoreElements = mutableListOf>() + core.forEach { + if (!uniqueCoreElements.any { u -> u.internEquals(it) }) { + uniqueCoreElements.add(it) + } + } + val softs = mutableListOf() for (soft in assumptions) { - if (core.any { it.internEquals(soft.expression) }) { + if (uniqueCoreElements.any { it.internEquals(soft.expression) }) { softs.add(soft) } } - require(core.size == softs.size) { - "Unsat core size [${core.size}] was not equal to corresponding soft constraints size [${softs.size}]" + require(uniqueCoreElements.size == softs.size) { + "Unsat core size [${uniqueCoreElements.size}] was not equal to corresponding " + + "soft constraints size [${softs.size}]" } return softs From ce4a5faa0d0dfb0828d2d174063549a18b85aba1 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 5 Oct 2023 19:51:29 +0300 Subject: [PATCH 052/228] Add similar expressions test --- .../io/ksmt/solver/maxsat/KMaxSATSolverTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index 43b065c9e..cc9d100a9 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -303,6 +303,20 @@ abstract class KMaxSATSolverTest { assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) } + @Test + fun similarExpressionsTest(): Unit = with(ctx) { + val a by boolSort + val b by boolSort + + maxSATSolver.assertSoft(a or b, 1u) + maxSATSolver.assertSoft(!a or b, 1u) + maxSATSolver.assertSoft(a or !b, 1u) + maxSATSolver.assertSoft(!a or !b, 1u) + maxSATSolver.assertSoft(!a or !b or !b, 1u) + + maxSATSolver.checkMaxSAT() + } + private fun assertSoftConstraintsSat( constraintsToAssert: List, satConstraints: List, From 72d21fe04bcede31702aede877f381a9dcec27f9 Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:52:40 +0300 Subject: [PATCH 053/228] Set up running long MaxSMT tests --- .github/workflows/maxsmt-tests-matrix.json | 16 ++++ .github/workflows/run-long-maxsmt-tests.yml | 88 +++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 .github/workflows/maxsmt-tests-matrix.json create mode 100644 .github/workflows/run-long-maxsmt-tests.yml diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json new file mode 100644 index 000000000..387ee1eaf --- /dev/null +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -0,0 +1,16 @@ +{ + "logics": [ + { + "NAME": "QF_ABV", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_BV-1", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_BV-2", + "TEST_DATA_REVISION": "0.0.0" + } + ] +} diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml new file mode 100644 index 000000000..6842c9b30 --- /dev/null +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -0,0 +1,88 @@ +name: Build and run long MaxSMT tests + +on: + workflow_dispatch: + inputs: + commitSHA: + description: Commit SHA + required: false + type: string + default: "" + +env: + TEST_DATA_REVISION: 0.0.0 + +jobs: + prepare-matrix: + runs-on: ubuntu-latest + outputs: + logics-matrix: ${{ steps.set-matrix.outputs.maxsmt-tests-matrix }} + steps: + - name: Print environment variables + run: printenv + + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Check out ${{ github.event.inputs.commitSHA }} commit + if: github.event.inputs.commitSHA != '' + run: | + git config --global --add safe.directory ${GITHUB_WORKSPACE} + git fetch + git checkout ${{ github.event.inputs.commitSHA }} + + - id: set-matrix + name: Read and print config from maxsmt-tests-matrix.json + run: | + MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json)) + echo "maxsmt-tests-matrix=$MAXSMT_TESTS" >> $GITHUB_OUTPUT + echo $MAXSMT_TESTS + + run-maxsmt-tests: + needs: prepare-matrix + continue-on-error: true + runs-on: ubuntu-latest + strategy: + matrix: ${{ fromJSON(needs.prepare-matrix.outputs.logics-matrix) }} + + name: Run ${{ matrix.NAME }} tests + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Check out ${{ github.event.inputs.commitSHA }} commit + if: github.event.inputs.commitSHA != '' + run: | + git config --global --add safe.directory ${GITHUB_WORKSPACE} + git fetch + git checkout ${{ github.event.inputs.commitSHA }} + + - name: Set up JDK 1.8 + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'zulu' + + - name: Download ${{ matrix.logics.NAME }} tests + uses: gradle/gradle-build-action@v2 + with: + arguments: | + :ksmt-maxsat-test:maxSmtBenchmark-${{ matrix.logics.NAME }} + --no-daemon + -PmaxSmtTestDataRevision=${{ env.TEST_DATA_REVISION }} + + - name: Run ${{ matrix.logics.NAME }} tests + uses: gradle/gradle-build-action@v2 + with: + arguments: | + :ksmt-maxsat-test:test + --no-daemon + --tests "io.ksmt.solver.maxsat.test.smt.KPMResSMTBenchmarkTest.maxSMTTest" + + - name: Archive ${{ matrix.logics.NAME }} test report + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.logics.NAME }}_test_report + path: ksmt-maxsat-test/build/reports/tests/test/* From 8df46222d2a18922a3e98639eb381fe7cb3d962c Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 19 Oct 2023 19:53:20 +0300 Subject: [PATCH 054/228] Rename MaxSATBenchmarkUtil to MaxSMTBenchmarkUtil --- ...ATBenchmarkUtil.kt => MaxSMTBenchmarkUtil.kt} | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) rename buildSrc/src/main/kotlin/{MaxSATBenchmarkUtil.kt => MaxSMTBenchmarkUtil.kt} (80%) diff --git a/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt similarity index 80% rename from buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt rename to buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt index 6e7a8e582..2cf340dd2 100644 --- a/buildSrc/src/main/kotlin/MaxSATBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt @@ -11,7 +11,7 @@ fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = ta doLast { val path = buildDir.resolve("maxSmtBenchmark/$name") val downloadTarget = path.resolve("$name.zip") - val url = "https://github.com/victoriafomina/gen-benchmark/releases/download/$testDataRevision/$name.zip" + val url = "$MAXSMT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/$name.zip" download(url, downloadTarget) @@ -39,7 +39,7 @@ fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = ta } fun Project.usePreparedMaxSATBenchmarkTestData(path: File): Task = - usePreparedBenchmarkTestData(path, MAXSAT_BENCHMARK_NAME_L) + usePreparedBenchmarkTestData(path, "maxSat") .get() .finalizedBy(unzipMaxSATBenchmarkTestFiles()) @@ -47,12 +47,12 @@ fun Project.downloadMaxSATBenchmarkTestData(downloadPath: File, testDataPath: Fi downloadPreparedBenchmarkTestData( downloadPath, testDataPath, - MAXSAT_BENCHMARK_NAME_U, - "$MAXSAT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/maxsat-benchmark.zip", + "MaxSat", + "$MAXSMT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/maxsat-benchmark.zip", ) fun Project.unzipMaxSATBenchmarkTestFiles() = - tasks.register("unzip${MAXSAT_BENCHMARK_NAME_U}BenchmarkFiles") { + tasks.register("unzipMaxSatBenchmarkFiles") { doLast { val testResources = testResourceDir() ?: error("No resource directory found for benchmarks") val testData = testResources.resolve("testData") @@ -61,8 +61,4 @@ fun Project.unzipMaxSATBenchmarkTestFiles() = } } -private const val MAXSAT_BENCHMARK_REPO_URL = "https://github.com/victoriafomina/ksmt" - -private const val MAXSAT_BENCHMARK_NAME_U = "MaxSat" - -private const val MAXSAT_BENCHMARK_NAME_L = "maxSat" +private const val MAXSMT_BENCHMARK_REPO_URL = "https://github.com/victoriafomina/ksmt" From 0c722cafdee84ca990e55561fa264d8a933d6ef0 Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:44:28 +0300 Subject: [PATCH 055/228] Update test data directory --- .gitignore | 3 +++ .../src/main/kotlin/MaxSMTBenchmarkUtil.kt | 21 ++++--------------- ksmt-maxsat-test/build.gradle.kts | 2 +- .../maxsat/test/KMaxSMTBenchmarkBasedTest.kt | 12 +++++------ 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index a75d245b9..5e6d4e8a8 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ ksmt-test/testData # ignore maxsat test data ksmt-maxsat-test/testData + +# ignore maxsmt test data] +ksmt-maxsat-test/src/test/resources/maxSmtBenchmark diff --git a/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt index 2cf340dd2..e291a869f 100644 --- a/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt @@ -9,7 +9,7 @@ fun Project.usePreparedMaxSmtBenchmarkTestData(path: File) = fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = tasks.register("maxSmtBenchmark-$name") { doLast { - val path = buildDir.resolve("maxSmtBenchmark/$name") + val path = testResourcesDir().resolve("maxSmtBenchmark/$name") val downloadTarget = path.resolve("$name.zip") val url = "$MAXSMT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/$name.zip" @@ -22,19 +22,6 @@ fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = ta duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } - - val testResources = testResourceDir() ?: error("No resource directory found for $name benchmark") - val testData = testResources.resolve("testData") - - testData.executeIfNotReady("$name-copy-complete") { - val maxSmtFiles = path.walkTopDown().filter { it.extension == "smt2" || it.extension == "maxsmt" }.toList() - copy { - from(maxSmtFiles.toTypedArray()) - into(testData) - rename { "${name}_$it" } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - } } } @@ -54,11 +41,11 @@ fun Project.downloadMaxSATBenchmarkTestData(downloadPath: File, testDataPath: Fi fun Project.unzipMaxSATBenchmarkTestFiles() = tasks.register("unzipMaxSatBenchmarkFiles") { doLast { - val testResources = testResourceDir() ?: error("No resource directory found for benchmarks") - val testData = testResources.resolve("testData") - + val testData = testResourcesDir() testData.listFiles()?.forEach { if (it.isFile && it.extension == "zip") unzipTo(it.parentFile, it) } } } +private fun Project.testResourcesDir() = projectDir.resolve("src/test/resources") + private const val MAXSMT_BENCHMARK_REPO_URL = "https://github.com/victoriafomina/ksmt" diff --git a/ksmt-maxsat-test/build.gradle.kts b/ksmt-maxsat-test/build.gradle.kts index a41ed694c..f5d2a178c 100644 --- a/ksmt-maxsat-test/build.gradle.kts +++ b/ksmt-maxsat-test/build.gradle.kts @@ -31,7 +31,7 @@ val runMaxSATBenchmarkBasedTests = project.booleanProperty("runMaxSATBenchmarkBa val usePreparedMaxSMTBenchmarks = project.booleanProperty("usePreparedMaxSMTBenchmarks") ?: true val usePreparedMaxSATBenchmark = project.booleanProperty("usePreparedMaxSATBenchmark") ?: true -val testDataDir = projectDir.resolve("testData") +val testDataDir = projectDir.resolve("src/resources/testData") val unpackedTestDataDir = testDataDir.resolve("data") val downloadedTestData = testDataDir.resolve("testData.zip") diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt index b3501f470..db84940e6 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt @@ -3,8 +3,6 @@ package io.ksmt.solver.maxsat.test import org.junit.jupiter.params.provider.Arguments import java.nio.file.Path import java.nio.file.Paths -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.relativeTo interface KMaxSMTBenchmarkBasedTest { data class BenchmarkTestArguments( @@ -16,16 +14,18 @@ interface KMaxSMTBenchmarkBasedTest { companion object { private fun testDataLocation(): Path = - this::class.java.classLoader.getResource("testData")?.toURI()?.let { Paths.get(it) } + this::class.java.classLoader.getResource("maxSmtBenchmark")?.toURI() + ?.let { Paths.get(it) } ?: error("No test data") private fun prepareTestData(): List { val testDataLocation = testDataLocation() - return testDataLocation.listDirectoryEntries("*.smt2").sorted() + return testDataLocation.toFile().walkTopDown().filter { f -> f.isFile && f.extension == "smt2" }.toList() + .sorted() .map { BenchmarkTestArguments( - it.relativeTo(testDataLocation).toString(), - it, + it.relativeTo(testDataLocation.toFile()).toString(), + it.toPath(), ) } } From 8244dd838a44854f46ef588336214e8c2f59c77d Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:38:20 +0300 Subject: [PATCH 056/228] Create monitoring.sh --- scripts/monitoring.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 scripts/monitoring.sh diff --git a/scripts/monitoring.sh b/scripts/monitoring.sh new file mode 100644 index 000000000..bf9508c63 --- /dev/null +++ b/scripts/monitoring.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +PUSHGATEWAY_HOSTNAME=${1} +PUSHGATEWAY_USER=${2} +PUSHGATEWAY_PASSWORD=${3} +PUSHGATEWAY_ADDITIONAL_PATH=/pushgateway + +PROM_ADDITIONAL_LABELS=/service/ksmt +SLEEP_TIME_SECONDS=15 +VERSION_CADVISOR=v0.36.0 +VERSION_CURL=7.84.0 +VERSION_NODE_EXPORTER=v1.3.1 +PORT_CADVISOR=9280 +PORT_NODE_EXPORTER=9100 + +# base linux system metrics +if ! netstat -tulpn | grep -q ${PORT_NODE_EXPORTER} ; then + docker run -d --name node_exporter \ + --net="host" \ + --pid="host" \ + --volume="/:/host:ro,rslave" \ + quay.io/prometheus/node-exporter:${VERSION_NODE_EXPORTER} \ + --path.rootfs=/host + docker run -d --name curl-node \ + --net="host" \ + --entrypoint=/bin/sh \ + curlimages/curl:${VERSION_CURL} \ + "-c" "while true; do curl localhost:9100/metrics | curl -u ${PUSHGATEWAY_USER}:${PUSHGATEWAY_PASSWORD} --data-binary @- https://${PUSHGATEWAY_HOSTNAME}${PUSHGATEWAY_ADDITIONAL_PATH}/metrics/job/pushgateway/instance/${GITHUB_RUN_ID}-${HOSTNAME}${PROM_ADDITIONAL_LABELS} ; sleep ${SLEEP_TIME_SECONDS}; done" +fi From 8ce6098737f149fd9c135d1389c3f4c5daa5f204 Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:45:26 +0300 Subject: [PATCH 057/228] Add monitoring to long tests launch --- .github/workflows/run-long-maxsmt-tests.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 6842c9b30..1c402e8a7 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -11,6 +11,7 @@ on: env: TEST_DATA_REVISION: 0.0.0 + PUSHGATEWAY_HOSTNAME: monitoring.utbot.org jobs: prepare-matrix: @@ -64,6 +65,15 @@ jobs: java-version: 8 distribution: 'zulu' + - name: Run monitoring + # secret uploaded using base64 encoding to have one-line output: + # cat file | base64 -w 0 + continue-on-error: true + run: | + chmod +x ./scripts/monitoring.sh + ./scripts/monitoring.sh "${PUSHGATEWAY_HOSTNAME}" "${{ secrets.PUSHGATEWAY_USER }}" "${{ secrets.PUSHGATEWAY_PASSWORD }}" + echo "Please visit Grafana to check metrics: https://${PUSHGATEWAY_HOSTNAME}/d/rYdddlPWk/node-exporter-full?orgId=1&from=now-1h&to=now&var-service=github&var-instance=${GITHUB_RUN_ID}-${HOSTNAME}&refresh=1m" + - name: Download ${{ matrix.logics.NAME }} tests uses: gradle/gradle-build-action@v2 with: From 29992433a4906d9ee331012c6122056faaa8b42c Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 23 Oct 2023 12:36:23 +0300 Subject: [PATCH 058/228] Add more MaxSMT unit tests --- .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt index cc9d100a9..ac1bbccb5 100644 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt @@ -8,6 +8,7 @@ import io.ksmt.solver.maxsat.solvers.KMaxSATSolver import io.ksmt.solver.z3.KZ3SolverConfiguration import io.ksmt.utils.getValue import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -33,6 +34,11 @@ abstract class KMaxSATSolverTest { maxSATSolver.assert(a) maxSATSolver.assert(b) maxSATSolver.assert(!a) + + val maxSATResult = maxSATSolver.checkMaxSAT() + assertTrue(maxSATResult.hardConstraintsSATStatus == UNSAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.isEmpty()) } @Test @@ -106,6 +112,58 @@ abstract class KMaxSATSolverTest { assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSATResult.satSoftConstraints) } + @Test + fun twoOfFourSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + val d by boolSort + + maxSATSolver.assert(a or b) + maxSATSolver.assert(c or d) + maxSATSolver.assert(!a or b) + maxSATSolver.assert(!c or d) + + maxSATSolver.assertSoft(a or !b, 2u) + maxSATSolver.assertSoft(c or !d, 2u) + maxSATSolver.assertSoft(!a or !b, 3u) + maxSATSolver.assertSoft(!c or !d, 3u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!a or !b, 3u), SoftConstraint(!c or !d, 3u)), + maxSATResult.satSoftConstraints, + ) + } + + @Test + fun sixOfEightSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + val d by boolSort + + maxSATSolver.assertSoft(a or b, 9u) + maxSATSolver.assertSoft(c or d, 9u) + maxSATSolver.assertSoft(!a or b, 9u) + maxSATSolver.assertSoft(!c or d, 9u) + maxSATSolver.assertSoft(a or !b, 2u) + maxSATSolver.assertSoft(c or !d, 2u) + maxSATSolver.assertSoft(!a or !b, 3u) + maxSATSolver.assertSoft(!c or !d, 3u) + + val maxSATResult = maxSATSolver.checkMaxSAT() + + assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) + assertTrue(maxSATResult.maxSATSucceeded) + assertTrue(maxSATResult.satSoftConstraints.size == 6) + assertEquals(42u, maxSATResult.satSoftConstraints.sumOf { it.weight }) + } + @Test fun twoOfThreeSoftConstraintsSatTest() = with(ctx) { val a by boolSort From d39fb0cdf42bc23e1ffaa27b18942010b4126535 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 23 Oct 2023 12:37:46 +0300 Subject: [PATCH 059/228] Add messages to MaxSMT benchmark tests assertions --- .../solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt | 11 ++++++++--- .../solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt index a18c984df..be53fb2ea 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt @@ -52,12 +52,17 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { val maxSATResult = maxSATSolver.checkMaxSAT(20.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } + val expectedSatConstraintsScore = + sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second - assertEquals(SAT, maxSATResult.hardConstraintsSATStatus) - assertTrue(maxSATResult.maxSATSucceeded && maxSATResult.satSoftConstraints.isNotEmpty()) + assertEquals(SAT, maxSATResult.hardConstraintsSATStatus, "Hard constraints must be SAT") + assertTrue(maxSATResult.maxSATSucceeded, "MaxSAT was not successful [$name]") + assertTrue(maxSATResult.satSoftConstraints.isNotEmpty(), "Soft constraints size should not be 0") assertEquals( - sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second, + expectedSatConstraintsScore, satConstraintsScore.toULong(), + "Soft constraints score was [$satConstraintsScore], " + + "but must be [$expectedSatConstraintsScore]", ) } diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt index e9ed171b7..b24083f19 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt @@ -85,11 +85,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val maxSATResult = maxSATSolver.checkMaxSAT(60.seconds) val satSoftConstraintsWeightsSum = maxSATResult.satSoftConstraints.sumOf { it.weight } - assertEquals(SAT, maxSATResult.hardConstraintsSATStatus) - assertTrue(maxSATResult.maxSATSucceeded) + assertEquals(SAT, maxSATResult.hardConstraintsSATStatus, "Hard constraints must be SAT") + assertTrue(maxSATResult.maxSATSucceeded, "MaxSMT was not successful [$name]") assertEquals( maxSmtTestInfo.satSoftConstraintsWeightsSum, satSoftConstraintsWeightsSum.toULong(), + "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + + "but must be [${maxSmtTestInfo.softConstraintsWeightsSum}]", ) } From 26048aad081ad830cee8290d027f54351bc66e05 Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:31:47 +0300 Subject: [PATCH 060/228] Update maxsmt-tests-matrix.json --- .github/workflows/maxsmt-tests-matrix.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 387ee1eaf..29938c9bf 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,7 +1,11 @@ { "logics": [ { - "NAME": "QF_ABV", + "NAME": "QF_ABVFP", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_ALIA", "TEST_DATA_REVISION": "0.0.0" }, { From 234ee20cdcfa0b0c15556722d9068972e8aec193 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 23 Oct 2023 17:32:47 +0300 Subject: [PATCH 061/228] Update logics for MaxSMT tests --- ksmt-maxsat-test/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsat-test/build.gradle.kts b/ksmt-maxsat-test/build.gradle.kts index f5d2a178c..7a0e641c2 100644 --- a/ksmt-maxsat-test/build.gradle.kts +++ b/ksmt-maxsat-test/build.gradle.kts @@ -19,7 +19,8 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABVFP", // 8.84M + "QF_ABVFP", // 7.37M + "QF_ALIA", // 700K "QF_BV-1", // 17.2M "QF_BV-2", // 20.8М ) From fa05d474680de547160afcbee5e5b2f9814955ff Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 25 Oct 2023 15:47:52 +0300 Subject: [PATCH 062/228] Upgrade Kotlin version to 1.9.0 --- buildSrc/src/main/kotlin/Publications.kt | 4 ++-- version.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/Publications.kt b/buildSrc/src/main/kotlin/Publications.kt index 246a852fa..26c625b3d 100644 --- a/buildSrc/src/main/kotlin/Publications.kt +++ b/buildSrc/src/main/kotlin/Publications.kt @@ -1,5 +1,5 @@ -import gradle.kotlin.dsl.accessors._87b80c14bf1c4d505c7a71d7741e0994.publishing -import gradle.kotlin.dsl.accessors._87b80c14bf1c4d505c7a71d7741e0994.signing +import gradle.kotlin.dsl.accessors._0942c15ed799f01c8e4bd02858158e54.publishing +import gradle.kotlin.dsl.accessors._0942c15ed799f01c8e4bd02858158e54.signing import groovy.util.Node import org.gradle.api.Project import org.gradle.api.artifacts.Dependency diff --git a/version.properties b/version.properties index 5b9aeb949..2517fc6da 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -kotlin=1.7.20 -detekt=1.20.0 +kotlin=1.9.0 +detekt=1.20.0 \ No newline at end of file From 895193d73cba11e89acbf7c88c6a8614add173ba Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Wed, 25 Oct 2023 16:24:08 +0300 Subject: [PATCH 063/228] Rename maxsat subproject to maxsmt --- .gitignore | 4 +- .../sat/KPrimalDualMaxResSATBenchmarkTest.kt | 14 - .../io/ksmt/solver/maxsat/KMaxSATResult.kt | 24 -- .../ksmt/solver/maxsat/scope/MaxSATScope.kt | 3 - .../ksmt/solver/maxsat/KMaxSATSolverTest.kt | 390 ------------------ .../io/ksmt/solver/maxsat/KPMResSolverTest.kt | 13 - .../maxsat/KPrimalDualMaxResSolver2Test.kt | 13 - .../maxsat/KPrimalDualMaxResSolver3Test.kt | 13 - .../maxsat/KPrimalDualMaxResSolver4Test.kt | 13 - .../maxsat/KPrimalDualMaxResSolverTest.kt | 13 - .../build.gradle.kts | 4 +- .../solver/maxsmt}/test/CurrentLineState.kt | 2 +- .../solver/maxsmt}/test/MaxSMTTestInfo.kt | 2 +- .../io/ksmt/solver/maxsmt}/test/TestParser.kt | 8 +- .../maxsmt}/test/KMaxSMTBenchmarkBasedTest.kt | 2 +- .../maxsmt}/test/sat/KMaxSATBenchmarkTest.kt | 22 +- .../test/sat/KPMResSATBenchmarkTest.kt | 8 +- .../sat/KPrimalDualMaxResSATBenchmarkTest.kt | 14 + .../maxsmt}/test/smt/KMaxSMTBenchmarkTest.kt | 44 +- .../test/smt/KPMResSMTBenchmarkTest.kt | 8 +- {ksmt-maxsat => ksmt-maxsmt}/build.gradle.kts | 0 .../io/ksmt/solver/maxsmt/KMaxSMTContext.kt | 6 +- .../io/ksmt/solver/maxsmt/KMaxSMTResult.kt | 24 ++ .../solver/maxsmt}/constraints/Constraint.kt | 2 +- .../maxsmt}/constraints/HardConstraint.kt | 2 +- .../maxsmt}/constraints/SoftConstraint.kt | 2 +- .../ksmt/solver/maxsmt/scope/MaxSMTScope.kt | 3 + .../solver/maxsmt/scope/MaxSMTScopeManager.kt | 10 +- .../solver/maxsmt}/solvers/KMaxResSolver.kt | 8 +- .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 20 +- .../solver/maxsmt}/solvers/KPMResSolver.kt | 109 +++-- .../solvers/KPrimalDualMaxResSolver.kt | 50 +-- .../maxsmt}/solvers/utils/MinimalUnsatCore.kt | 10 +- .../io/ksmt/solver/maxsmt}/utils/CoreUtils.kt | 6 +- .../ksmt/solver/maxsmt}/utils/ModelUtils.kt | 4 +- .../ksmt/solver/maxsmt}/utils/TimerUtils.kt | 2 +- .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 390 ++++++++++++++++++ .../io/ksmt/solver/maxsmt/KPMResSolverTest.kt | 13 + .../maxsmt/KPrimalDualMaxResSolver2Test.kt | 13 + .../maxsmt/KPrimalDualMaxResSolver3Test.kt | 13 + .../maxsmt/KPrimalDualMaxResSolver4Test.kt | 13 + .../maxsmt/KPrimalDualMaxResSolverTest.kt | 13 + settings.gradle.kts | 4 +- 43 files changed, 674 insertions(+), 657 deletions(-) delete mode 100644 ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt delete mode 100644 ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt delete mode 100644 ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt rename {ksmt-maxsat-test => ksmt-maxsmt-test}/build.gradle.kts (94%) rename {ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt}/test/CurrentLineState.kt (73%) rename {ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt}/test/MaxSMTTestInfo.kt (82%) rename {ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt}/test/TestParser.kt (95%) rename {ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt}/test/KMaxSMTBenchmarkBasedTest.kt (97%) rename {ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt}/test/sat/KMaxSATBenchmarkTest.kt (97%) rename {ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt}/test/sat/KPMResSATBenchmarkTest.kt (55%) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt rename {ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt}/test/smt/KMaxSMTBenchmarkTest.kt (78%) rename {ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt}/test/smt/KPMResSMTBenchmarkTest.kt (74%) rename {ksmt-maxsat => ksmt-maxsmt}/build.gradle.kts (100%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt (71%) create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/constraints/Constraint.kt (74%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/constraints/HardConstraint.kt (76%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/constraints/SoftConstraint.kt (78%) create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScope.kt rename ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScopeManager.kt (84%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/solvers/KMaxResSolver.kt (84%) rename ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt (87%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/solvers/KPMResSolver.kt (64%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/solvers/KPrimalDualMaxResSolver.kt (91%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/solvers/utils/MinimalUnsatCore.kt (93%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/utils/CoreUtils.kt (86%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/utils/ModelUtils.kt (91%) rename {ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat => ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt}/utils/TimerUtils.kt (92%) create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt create mode 100644 ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt diff --git a/.gitignore b/.gitignore index 5e6d4e8a8..14e3283e2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ build ksmt-test/testData # ignore maxsat test data -ksmt-maxsat-test/testData +ksmt-maxsmt-test/testData # ignore maxsmt test data] -ksmt-maxsat-test/src/test/resources/maxSmtBenchmark +ksmt-maxsmt-test/src/test/resources/maxSmtBenchmark diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt deleted file mode 100644 index 6c5e4c381..000000000 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.ksmt.solver.maxsat.test.sat - -import io.ksmt.solver.maxsat.KMaxSATContext -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) - } -} diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt deleted file mode 100644 index 867c494ef..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATResult.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.maxsat.constraints.SoftConstraint - -/** - * @property satSoftConstraints - * - MaxSAT has succeeded -> contains soft constraints from MaxSAT solution. - * - MaxSAT has not succeeded -> contains soft constraints algorithm considered as satisfiable (incomplete solution). - * - * @property hardConstraintsSATStatus - * Shows satisfiability status of hardly asserted constraints' conjunction. - * - * @property maxSATSucceeded - * Shows whether MaxSAT calculation has succeeded or not. - * - * It may end without success in case of exceeding the timeout or in case solver started returning UNKNOWN during - * MaxSAT calculation. - */ -class KMaxSATResult( - val satSoftConstraints: List, - val hardConstraintsSATStatus: KSolverStatus, - val maxSATSucceeded: Boolean, -) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt b/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt deleted file mode 100644 index 6a147a651..000000000 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScope.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.ksmt.solver.maxsat.scope - -internal class MaxSATScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt deleted file mode 100644 index ac1bbccb5..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KMaxSATSolverTest.kt +++ /dev/null @@ -1,390 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.KContext -import io.ksmt.solver.KSolverStatus.SAT -import io.ksmt.solver.KSolverStatus.UNSAT -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.z3.KZ3SolverConfiguration -import io.ksmt.utils.getValue -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -abstract class KMaxSATSolverTest { - abstract fun getSolver(): KMaxSATSolver - - protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSATSolver - - @BeforeEach - fun initSolver() { - maxSATSolver = getSolver() - } - - @AfterEach - fun closeSolver() = maxSATSolver.close() - - @Test - fun hardConstraintsUnsatTest() = with(ctx) { - val a by boolSort - val b by boolSort - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(!a) - - val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.hardConstraintsSATStatus == UNSAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) - } - - @Test - fun hardConstraintsUnsatNoSoftTest() = with(ctx) { - val a by boolSort - val b by boolSort - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(!a) - - maxSATSolver.assertSoft(a and !b, 3u) - maxSATSolver.assertSoft(!a, 5u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == UNSAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) - } - - @Test - fun noSoftConstraintsTest() = with(ctx) { - val a by boolSort - val b by boolSort - val c by boolSort - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(c) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) - } - - @Test - fun noSoftConstraintsSatTest() = with(ctx) { - val a by boolSort - val b by boolSort - val c by boolSort - maxSATSolver.assert(a) - maxSATSolver.assert(b) - maxSATSolver.assert(c) - maxSATSolver.assertSoft(a and !c, 3u) - maxSATSolver.assertSoft(!a, 5u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.isEmpty()) - } - - @Test - fun oneOfTwoSoftConstraintsSatTest() = with(ctx) { - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - maxSATSolver.assert(!a or b) - - maxSATSolver.assertSoft(a or !b, 2u) - maxSATSolver.assertSoft(!a or !b, 3u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSATResult.satSoftConstraints) - } - - @Test - fun twoOfFourSoftConstraintsSatTest() = with(ctx) { - val a by boolSort - val b by boolSort - val c by boolSort - val d by boolSort - - maxSATSolver.assert(a or b) - maxSATSolver.assert(c or d) - maxSATSolver.assert(!a or b) - maxSATSolver.assert(!c or d) - - maxSATSolver.assertSoft(a or !b, 2u) - maxSATSolver.assertSoft(c or !d, 2u) - maxSATSolver.assertSoft(!a or !b, 3u) - maxSATSolver.assertSoft(!c or !d, 3u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat( - listOf(SoftConstraint(!a or !b, 3u), SoftConstraint(!c or !d, 3u)), - maxSATResult.satSoftConstraints, - ) - } - - @Test - fun sixOfEightSoftConstraintsSatTest() = with(ctx) { - val a by boolSort - val b by boolSort - val c by boolSort - val d by boolSort - - maxSATSolver.assertSoft(a or b, 9u) - maxSATSolver.assertSoft(c or d, 9u) - maxSATSolver.assertSoft(!a or b, 9u) - maxSATSolver.assertSoft(!c or d, 9u) - maxSATSolver.assertSoft(a or !b, 2u) - maxSATSolver.assertSoft(c or !d, 2u) - maxSATSolver.assertSoft(!a or !b, 3u) - maxSATSolver.assertSoft(!c or !d, 3u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 6) - assertEquals(42u, maxSATResult.satSoftConstraints.sumOf { it.weight }) - } - - @Test - fun twoOfThreeSoftConstraintsSatTest() = with(ctx) { - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - - maxSATSolver.assertSoft(!a or b, 4u) - maxSATSolver.assertSoft(a or !b, 6u) - maxSATSolver.assertSoft(!a or !b, 2u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - val softConstraintsToAssertSAT = - listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) - assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSATResult.satSoftConstraints) - } - - @Test - fun sameExpressionSoftConstraintsSatTest() = with(ctx) { - val x by boolSort - val y by boolSort - - maxSATSolver.assert(x or y) - - maxSATSolver.assertSoft(!x or y, 7u) - maxSATSolver.assertSoft(x or !y, 6u) - maxSATSolver.assertSoft(!x or !y, 3u) - maxSATSolver.assertSoft(!x or !y, 4u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 3) - assertSoftConstraintsSat( - listOf( - SoftConstraint(!x or y, 7u), - SoftConstraint(!x or !y, 3u), - SoftConstraint(!x or !y, 4u), - ), - maxSATResult.satSoftConstraints, - ) - } - - @Test - fun sameExpressionSoftConstraintsUnsatTest() = with(ctx) { - val x by boolSort - val y by boolSort - - maxSATSolver.assert(x or y) - - maxSATSolver.assertSoft(!x or y, 6u) - maxSATSolver.assertSoft(x or !y, 6u) - maxSATSolver.assertSoft(!x or !y, 3u) - maxSATSolver.assertSoft(!x or !y, 2u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat( - listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), - maxSATResult.satSoftConstraints, - ) - } - - @Test - fun chooseOneConstraintByWeightTest() = with(ctx) { - val z by boolSort - val a by boolSort - val b by boolSort - - maxSATSolver.assert(z) - maxSATSolver.assertSoft(a and b, 1u) - maxSATSolver.assertSoft(!a and !b, 5u) - maxSATSolver.assertSoft(a and b and z, 2u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 1) - assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSATResult.satSoftConstraints) - } - - @Test - fun equalWeightsTest() = with(ctx) { - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - maxSATSolver.assert(!a or b) - - maxSATSolver.assertSoft(a or !b, 2u) - maxSATSolver.assertSoft(!a or !b, 2u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.satSoftConstraints.size == 1) - } - - @Test - fun inequalitiesTest() = with(ctx) { - val x by intSort - val y by intSort - - val a1 = x gt 0.expr - val a2 = x lt y - val a3 = x + y le 0.expr - - maxSATSolver.assert(a3 eq a1) - maxSATSolver.assert(a3 or a2) - - maxSATSolver.assertSoft(a3, 3u) - maxSATSolver.assertSoft(!a3, 5u) - maxSATSolver.assertSoft(!a1, 10u) - maxSATSolver.assertSoft(!a2, 3u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResult.satSoftConstraints.size == 2) - assertSoftConstraintsSat( - listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), - maxSATResult.satSoftConstraints, - ) - } - - @Test - fun oneScopePushPopTest() = with(ctx) { - val a by boolSort - val b by boolSort - - maxSATSolver.assert(a or b) - - maxSATSolver.assertSoft(!a or b, 1u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) - - maxSATSolver.push() - - maxSATSolver.assertSoft(a or !b, 1u) - maxSATSolver.assertSoft(!a or !b, 1u) - - val maxSATResultScoped = maxSATSolver.checkMaxSAT() - - assertTrue(maxSATResult.hardConstraintsSATStatus == SAT) - assertTrue(maxSATResult.maxSATSucceeded) - assertTrue(maxSATResultScoped.satSoftConstraints.size == 2) - - maxSATSolver.pop() - - assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSATResult.satSoftConstraints) - } - - @Test - fun threeScopesPushPopTest() = with(ctx) { - val a by boolSort - val b by boolSort - val c by boolSort - - maxSATSolver.assert(a) - - maxSATSolver.push() - maxSATSolver.assertSoft(a or b, 1u) - maxSATSolver.assertSoft(c or b, 1u) - - val maxSATResult = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult.satSoftConstraints.size == 2) - - maxSATSolver.push() - maxSATSolver.assertSoft(!b and !c, 2u) - val maxSATResult2 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult2.satSoftConstraints.size == 2) - - maxSATSolver.push() - maxSATSolver.assertSoft(a or c, 1u) - val maxSATResult3 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult3.satSoftConstraints.size == 3) - - maxSATSolver.pop(2u) - val maxSATResult4 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult4.satSoftConstraints.size == 2) - - maxSATSolver.pop() - val maxSATResult5 = maxSATSolver.checkMaxSAT() - assertTrue(maxSATResult5.satSoftConstraints.isEmpty()) - } - - @Test - fun similarExpressionsTest(): Unit = with(ctx) { - val a by boolSort - val b by boolSort - - maxSATSolver.assertSoft(a or b, 1u) - maxSATSolver.assertSoft(!a or b, 1u) - maxSATSolver.assertSoft(a or !b, 1u) - maxSATSolver.assertSoft(!a or !b, 1u) - maxSATSolver.assertSoft(!a or !b or !b, 1u) - - maxSATSolver.checkMaxSAT() - } - - private fun assertSoftConstraintsSat( - constraintsToAssert: List, - satConstraints: List, - ) { - for (constraint in constraintsToAssert) { - assertTrue( - satConstraints.any { - constraint.expression.internEquals(it.expression) && constraint.weight == it.weight - }, - ) - } - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt deleted file mode 100644 index 455662384..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPMResSolverTest.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPMResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPMResSolverTest : KMaxSATSolverTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPMResSolver(this, z3Solver) - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt deleted file mode 100644 index 3e4cc80cf..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver2Test.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPrimalDualMaxResSolver2Test : KMaxSATSolverTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(preferLargeWeightConstraintsForCores = false)) - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt deleted file mode 100644 index 4befdbedf..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver3Test.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPrimalDualMaxResSolver3Test : KMaxSATSolverTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(minimizeCores = false)) - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt deleted file mode 100644 index f3f0d6f03..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolver4Test.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPrimalDualMaxResSolver4Test : KMaxSATSolverTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext(getMultipleCores = false)) - } -} diff --git a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt deleted file mode 100644 index 2f21b6ebd..000000000 --- a/ksmt-maxsat/src/test/kotlin/io/ksmt/solver/maxsat/KPrimalDualMaxResSolverTest.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.ksmt.solver.maxsat - -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration - -class KPrimalDualMaxResSolverTest : KMaxSATSolverTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSATContext()) - } -} diff --git a/ksmt-maxsat-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts similarity index 94% rename from ksmt-maxsat-test/build.gradle.kts rename to ksmt-maxsmt-test/build.gradle.kts index 7a0e641c2..c675a6af0 100644 --- a/ksmt-maxsat-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -8,7 +8,7 @@ repositories { dependencies { implementation(project(":ksmt-core")) - implementation(project(":ksmt-maxsat")) + implementation(project(":ksmt-maxsmt")) testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.2") @@ -16,6 +16,8 @@ dependencies { testImplementation(project(":ksmt-test")) testImplementation(project(":ksmt-runner")) testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + testImplementation("io.github.oshai:kotlin-logging-jvm:5.1.0") + testImplementation("org.slf4j:slf4j-simple:2.0.9") } val maxSmtBenchmarks = listOfNotNull( diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/CurrentLineState.kt similarity index 73% rename from ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt rename to ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/CurrentLineState.kt index 2dcb61020..3dea1d4ab 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/CurrentLineState.kt +++ b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/CurrentLineState.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsmt.test enum class CurrentLineState { COMMENT, diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/MaxSMTTestInfo.kt similarity index 82% rename from ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt rename to ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/MaxSMTTestInfo.kt index eef8c0310..ad6bfee68 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/MaxSMTTestInfo.kt +++ b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/MaxSMTTestInfo.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsmt.test data class MaxSMTTestInfo( val softConstraintsWeights: List, diff --git a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/TestParser.kt similarity index 95% rename from ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt rename to ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/TestParser.kt index 8740d6409..e803284f0 100644 --- a/ksmt-maxsat-test/src/main/kotlin/io/ksmt/solver/maxsat/test/TestParser.kt +++ b/ksmt-maxsmt-test/src/main/kotlin/io/ksmt/solver/maxsmt/test/TestParser.kt @@ -1,9 +1,9 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsmt.test import io.ksmt.KContext -import io.ksmt.solver.maxsat.constraints.Constraint -import io.ksmt.solver.maxsat.constraints.HardConstraint -import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.constraints.Constraint +import io.ksmt.solver.maxsmt.constraints.HardConstraint +import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.utils.mkConst import java.io.File import java.nio.file.Path diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt similarity index 97% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt index db84940e6..42205e5bf 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/KMaxSMTBenchmarkBasedTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.test +package io.ksmt.solver.maxsmt.test import org.junit.jupiter.params.provider.Arguments import java.nio.file.Path diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt similarity index 97% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index be53fb2ea..a84ab741f 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -1,12 +1,12 @@ -package io.ksmt.solver.maxsat.test.sat +package io.ksmt.solver.maxsmt.test.sat import io.ksmt.KContext import io.ksmt.solver.KSolverStatus.SAT -import io.ksmt.solver.maxsat.constraints.HardConstraint -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.test.KMaxSMTBenchmarkBasedTest -import io.ksmt.solver.maxsat.test.parseMaxSATTest +import io.ksmt.solver.maxsmt.constraints.HardConstraint +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsmt.test.parseMaxSATTest import io.ksmt.solver.z3.KZ3SolverConfiguration import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -18,10 +18,10 @@ import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { - abstract fun getSolver(): KMaxSATSolver + abstract fun getSolver(): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSATSolver + private lateinit var maxSATSolver: KMaxSMTSolver @BeforeEach fun initSolver() { @@ -50,13 +50,13 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - val maxSATResult = maxSATSolver.checkMaxSAT(20.seconds) + val maxSATResult = maxSATSolver.checkMaxSMT(20.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second - assertEquals(SAT, maxSATResult.hardConstraintsSATStatus, "Hard constraints must be SAT") - assertTrue(maxSATResult.maxSATSucceeded, "MaxSAT was not successful [$name]") + assertEquals(SAT, maxSATResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + assertTrue(maxSATResult.maxSMTSucceeded, "MaxSAT was not successful [$name]") assertTrue(maxSATResult.satSoftConstraints.isNotEmpty(), "Soft constraints size should not be 0") assertEquals( expectedSatConstraintsScore, diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt similarity index 55% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt index f6404e525..d7e57e5d0 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/sat/KPMResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt @@ -1,12 +1,12 @@ -package io.ksmt.solver.maxsat.test.sat +package io.ksmt.solver.maxsmt.test.sat -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPMResSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration class KPMResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt new file mode 100644 index 000000000..014e99eea --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -0,0 +1,14 @@ +package io.ksmt.solver.maxsmt.test.sat + +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) + } +} diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt similarity index 78% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index b24083f19..720be3e9e 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -1,5 +1,7 @@ -package io.ksmt.solver.maxsat.test.smt +package io.ksmt.solver.maxsmt.test.smt +import io.github.oshai.kotlinlogging.KotlinLogging +import io.github.oshai.kotlinlogging.withLoggingContext import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.runner.core.KsmtWorkerArgs @@ -9,9 +11,10 @@ import io.ksmt.runner.core.RdServer import io.ksmt.runner.core.WorkerInitializationFailedException import io.ksmt.runner.generated.models.TestProtocolModel import io.ksmt.solver.KSolverStatus.SAT -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.test.KMaxSMTBenchmarkBasedTest -import io.ksmt.solver.maxsat.test.parseMaxSMTTestInfo +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.z3.KZ3SolverConfiguration import io.ksmt.sort.KBoolSort import io.ksmt.test.TestRunner @@ -26,24 +29,28 @@ import org.junit.jupiter.api.BeforeEach import java.io.File import java.nio.file.Path import kotlin.io.path.extension +import kotlin.system.measureTimeMillis import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds +import kotlin.time.DurationUnit +import kotlin.time.toDuration abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { - abstract fun getSolver(): KMaxSATSolver + abstract fun getSolver(): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSATSolver + private lateinit var maxSMTSolver: KMaxSMTSolver + private val logger = KotlinLogging.logger {} @BeforeEach fun initSolver() { - maxSATSolver = getSolver() + maxSMTSolver = getSolver() } @AfterEach - fun closeSolver() = maxSATSolver.close() + fun closeSolver() = maxSMTSolver.close() fun maxSMTTest( name: String, @@ -73,20 +80,29 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val hardExpressions = ksmtAssertions.subList(0, ksmtAssertions.size - softConstraintsSize) hardExpressions.forEach { - maxSATSolver.assert(it) + maxSMTSolver.assert(it) } maxSmtTestInfo.softConstraintsWeights .zip(softExpressions) .forEach { (weight, expr) -> - maxSATSolver.assertSoft(expr, weight) + maxSMTSolver.assertSoft(expr, weight) } - val maxSATResult = maxSATSolver.checkMaxSAT(60.seconds) - val satSoftConstraintsWeightsSum = maxSATResult.satSoftConstraints.sumOf { it.weight } + lateinit var maxSMTResult: KMaxSMTResult - assertEquals(SAT, maxSATResult.hardConstraintsSATStatus, "Hard constraints must be SAT") - assertTrue(maxSATResult.maxSATSucceeded, "MaxSMT was not successful [$name]") + val elapsedTime = measureTimeMillis { + maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds) + } + + withLoggingContext("test" to name) { + logger.info { "Elapsed time: [${elapsedTime.toDuration(DurationUnit.MILLISECONDS).inWholeSeconds} s]" } + } + + val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } + + assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") assertEquals( maxSmtTestInfo.satSoftConstraintsWeightsSum, satSoftConstraintsWeightsSum.toULong(), diff --git a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt similarity index 74% rename from ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt index d5f1a61a1..5984058fc 100644 --- a/ksmt-maxsat-test/src/test/kotlin/io/ksmt/solver/maxsat/test/smt/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt @@ -1,7 +1,7 @@ -package io.ksmt.solver.maxsat.test.smt +package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.maxsat.solvers.KMaxSATSolver -import io.ksmt.solver.maxsat.solvers.KPMResSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.solver.z3.KZ3SolverConfiguration import org.junit.jupiter.params.ParameterizedTest @@ -9,7 +9,7 @@ import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Path class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { - override fun getSolver(): KMaxSATSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) } diff --git a/ksmt-maxsat/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts similarity index 100% rename from ksmt-maxsat/build.gradle.kts rename to ksmt-maxsmt/build.gradle.kts diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt similarity index 71% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt index c8eff97c2..95ffa24ec 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/KMaxSATContext.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt @@ -1,8 +1,8 @@ -package io.ksmt.solver.maxsat +package io.ksmt.solver.maxsmt -import io.ksmt.solver.maxsat.KMaxSATContext.Strategy.PrimalDualMaxRes +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes -class KMaxSATContext( +class KMaxSMTContext( val strategy: Strategy = PrimalDualMaxRes, val preferLargeWeightConstraintsForCores: Boolean = true, val minimizeCores: Boolean = true, diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt new file mode 100644 index 000000000..f9aa8fd3d --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt @@ -0,0 +1,24 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.maxsmt.constraints.SoftConstraint + +/** + * @property satSoftConstraints + * - MaxSMT has succeeded -> contains soft constraints from MaxSMT solution. + * - MaxSMT has not succeeded -> contains soft constraints algorithm considered as satisfiable (suboptimal solution). + * + * @property hardConstraintsSatStatus + * Shows satisfiability status of hardly asserted constraints' conjunction. + * + * @property maxSMTSucceeded + * Shows whether MaxSMT calculation has succeeded or not. + * + * It may end without success in case of exceeding the timeout or in case solver started returning UNKNOWN during + * MaxSAT calculation. + */ +class KMaxSMTResult( + val satSoftConstraints: List, + val hardConstraintsSatStatus: KSolverStatus, + val maxSMTSucceeded: Boolean, +) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/Constraint.kt similarity index 74% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/Constraint.kt index fd08b8f5f..b5f959557 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/Constraint.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/Constraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.constraints +package io.ksmt.solver.maxsmt.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/HardConstraint.kt similarity index 76% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/HardConstraint.kt index e68be819d..a73fb709d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/HardConstraint.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/HardConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.constraints +package io.ksmt.solver.maxsmt.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/SoftConstraint.kt similarity index 78% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/SoftConstraint.kt index ec753e0b5..c19168773 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/constraints/SoftConstraint.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/constraints/SoftConstraint.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.constraints +package io.ksmt.solver.maxsmt.constraints import io.ksmt.expr.KExpr import io.ksmt.sort.KBoolSort diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScope.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScope.kt new file mode 100644 index 000000000..b0c82189d --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScope.kt @@ -0,0 +1,3 @@ +package io.ksmt.solver.maxsmt.scope + +internal class MaxSMTScope(val scopeAddedSoftConstraints: Int) diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScopeManager.kt similarity index 84% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScopeManager.kt index 21aa2eaf2..19df85201 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/scope/MaxSATScopeManager.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/scope/MaxSMTScopeManager.kt @@ -1,11 +1,11 @@ -package io.ksmt.solver.maxsat.scope +package io.ksmt.solver.maxsmt.scope -import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.constraints.SoftConstraint -internal class MaxSATScopeManager { +internal class MaxSMTScopeManager { private var currentScope = 0u - private val prevScopes = mutableListOf() + private val prevScopes = mutableListOf() private var scopeAddedSoftConstraints = 0 @@ -25,7 +25,7 @@ internal class MaxSATScopeManager { */ fun push() { if (currentScope != 0u) { - prevScopes.add(MaxSATScope(scopeAddedSoftConstraints)) + prevScopes.add(MaxSMTScope(scopeAddedSoftConstraints)) scopeAddedSoftConstraints = 0 } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt similarity index 84% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt index 12295a595..8b372497d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt @@ -1,15 +1,15 @@ -package io.ksmt.solver.maxsat.solvers +package io.ksmt.solver.maxsmt.solvers import io.ksmt.KContext import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.utils.CoreUtils +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.utils.CoreUtils abstract class KMaxResSolver( private val ctx: KContext, private val solver: KSolver, -) : KMaxSATSolver(ctx, solver) where T : KSolverConfiguration { +) : KMaxSMTSolver(ctx, solver) where T : KSolverConfiguration { protected fun removeCoreAssumptions(core: List, assumptions: MutableList) { assumptions.removeAll(core) } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt similarity index 87% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index 2f3639ed9..f148f215f 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KMaxSATSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.solvers +package io.ksmt.solver.maxsmt.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr @@ -9,24 +9,24 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT -import io.ksmt.solver.maxsat.KMaxSATResult -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.scope.MaxSATScopeManager +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.scope.MaxSMTScopeManager import io.ksmt.sort.KBoolSort import kotlin.time.Duration -abstract class KMaxSATSolver( +abstract class KMaxSMTSolver( private val ctx: KContext, private val solver: KSolver, ) : KSolver where T : KSolverConfiguration { - private val scopeManager = MaxSATScopeManager() + private val scopeManager = MaxSMTScopeManager() protected var softConstraints = mutableListOf() /** * Softly assert an expression with weight (aka soft constraint) into solver. * - * @see checkMaxSAT + * @see checkMaxSMT * */ fun assertSoft(expr: KExpr, weight: UInt) { require(weight > 0u) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } @@ -37,11 +37,13 @@ abstract class KMaxSATSolver( } /** - * Solve maximum satisfiability problem. + * Solve maximum satisfiability modulo theories problem. + * + * @param collectStatistics specifies whether statistics (elapsed time to execute method etc.) should be collected or not. * * @throws NotImplementedError */ - abstract fun checkMaxSAT(timeout: Duration = Duration.INFINITE): KMaxSATResult + abstract fun checkMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult /** * Union soft constraints with same expressions into a single soft constraint. diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt similarity index 64% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index b47e63b6b..8509ae0bd 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.solvers +package io.ksmt.solver.maxsmt.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr @@ -9,63 +9,58 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT -import io.ksmt.solver.maxsat.KMaxSATResult -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.utils.CoreUtils -import io.ksmt.solver.maxsat.utils.TimerUtils +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.utils.CoreUtils +import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration -class KPMResSolver(private val ctx: KContext, private val solver: KSolver) -: KMaxResSolver(ctx, solver) { - private var currentMaxSATResult: Triple>, KModel?> = +class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : + KMaxResSolver(ctx, solver) { + private var currentMaxSMTResult: Triple>, KModel?> = Triple(null, listOf(), null) - /** - * Solve maximum satisfiability problem. - * - * @throws NotImplementedError - */ - override fun checkMaxSAT(timeout: Duration): KMaxSATResult { + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { if (TimerUtils.timeoutExceeded(timeout)) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") } solver.push() - val maxSATResult = runMaxSATLogic(timeout) + val maxSMTResult = runMaxSMTLogic(timeout) solver.pop() - currentMaxSATResult = Triple(UNKNOWN, listOf(), null) + currentMaxSMTResult = Triple(UNKNOWN, listOf(), null) if (softConstraints.isEmpty()) { - return maxSATResult + return maxSMTResult } // TODO: get max SAT soft constraints subset - if (!maxSATResult.maxSATSucceeded) { - val (solverStatus, _, model) = currentMaxSATResult + if (!maxSMTResult.maxSMTSucceeded) { + val (solverStatus, _, model) = currentMaxSMTResult return when (solverStatus) { SAT -> processSat(model!!) - UNSAT -> KMaxSATResult(listOf(), SAT, false) // TODO: support anytime solving - UNKNOWN -> KMaxSATResult(listOf(), SAT, false) // TODO: support anytime solving + UNSAT -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving + UNKNOWN -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving else -> error("Unexpected status: $solverStatus") } } - return maxSATResult + return maxSMTResult } - private fun runMaxSATLogic(timeout: Duration): KMaxSATResult { + private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { val clockStart = System.currentTimeMillis() if (softConstraints.isEmpty()) { val hardConstraintsStatus = solver.check(timeout) - return KMaxSATResult( + return KMaxSMTResult( listOf(), hardConstraintsStatus, hardConstraintsStatus != UNKNOWN, @@ -75,9 +70,9 @@ class KPMResSolver(private val ctx: KContext, private val status = solver.check(timeout) if (status == UNSAT) { - return KMaxSATResult(listOf(), status, true) + return KMaxSMTResult(listOf(), status, true) } else if (status == UNKNOWN) { - return KMaxSATResult(listOf(), status, false) + return KMaxSMTResult(listOf(), status, false) } var i = 0 @@ -89,7 +84,7 @@ class KPMResSolver(private val ctx: KContext, private val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { - return KMaxSATResult(listOf(), status, false) + return KMaxSMTResult(listOf(), status, false) } val (solverStatus, unsatCore, model) = @@ -97,10 +92,10 @@ class KPMResSolver(private val ctx: KContext, private if (solverStatus == UNKNOWN) { // TODO: get max SAT soft constraints subset - return KMaxSATResult(listOf(), status, false) + return KMaxSMTResult(listOf(), status, false) } - currentMaxSATResult = Triple(solverStatus, unsatCore, model) + currentMaxSMTResult = Triple(solverStatus, unsatCore, model) if (solverStatus == SAT) { return processSat(model!!) @@ -126,8 +121,10 @@ class KPMResSolver(private val ctx: KContext, private * * @return a pair of minimum weight and a list of unsat core soft constraints with minimum weight. */ - private fun splitUnsatCore(formula: MutableList, unsatCore: List>) - : Pair> { + private fun splitUnsatCore( + formula: MutableList, + unsatCore: List>, + ): Pair> { val unsatCoreSoftConstraints = CoreUtils.coreToSoftConstraints(unsatCore, formula) removeCoreAssumptions(unsatCoreSoftConstraints, formula) return splitCore(unsatCoreSoftConstraints, formula) @@ -177,34 +174,34 @@ class KPMResSolver(private val ctx: KContext, private literalsToReify: List>, iter: Int, weight: UInt, - ) - : MutableList = with(ctx) { - literalsToReify.forEachIndexed { index, literal -> - val indexLast = literalsToReify.lastIndex - - if (index < indexLast) { - val sort = literal.sort - val currentLiteralToReifyDisjunction = sort.mkConst("#$iter:$index") - val nextLiteralToReifyDisjunction = sort.mkConst("#$iter:${index + 1}") - - val disjunction = - when (indexLast - index) { - // The second element is omitted as it is an empty disjunction. - 1 -> literalsToReify[index + 1] - else -> literalsToReify[index + 1] or nextLiteralToReifyDisjunction - } - - assert(currentLiteralToReifyDisjunction eq disjunction) - - formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !literal, weight)) + ): MutableList = + with(ctx) { + literalsToReify.forEachIndexed { index, literal -> + val indexLast = literalsToReify.lastIndex + + if (index < indexLast) { + val sort = literal.sort + val currentLiteralToReifyDisjunction = sort.mkConst("#$iter:$index") + val nextLiteralToReifyDisjunction = sort.mkConst("#$iter:${index + 1}") + + val disjunction = + when (indexLast - index) { + // The second element is omitted as it is an empty disjunction. + 1 -> literalsToReify[index + 1] + else -> literalsToReify[index + 1] or nextLiteralToReifyDisjunction + } + + assert(currentLiteralToReifyDisjunction eq disjunction) + + formula.add(SoftConstraint(!currentLiteralToReifyDisjunction or !literal, weight)) + } } - } - return formula - } + return formula + } - private fun processSat(model: KModel): KMaxSATResult { + private fun processSat(model: KModel): KMaxSMTResult { val satSoftConstraints = getSatSoftConstraintsByModel(model) - return KMaxSATResult(satSoftConstraints, SAT, true) + return KMaxSMTResult(satSoftConstraints, SAT, true) } } diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt similarity index 91% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 6b6812515..d85afd59d 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.solvers +package io.ksmt.solver.maxsmt.solvers import io.ksmt.KContext import io.ksmt.expr.KExpr @@ -9,35 +9,35 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT -import io.ksmt.solver.maxsat.KMaxSATContext -import io.ksmt.solver.maxsat.KMaxSATContext.Strategy.PrimalDualMaxRes -import io.ksmt.solver.maxsat.KMaxSATResult -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.solvers.utils.MinimalUnsatCore -import io.ksmt.solver.maxsat.utils.CoreUtils -import io.ksmt.solver.maxsat.utils.ModelUtils -import io.ksmt.solver.maxsat.utils.TimerUtils +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.solvers.utils.MinimalUnsatCore +import io.ksmt.solver.maxsmt.utils.CoreUtils +import io.ksmt.solver.maxsmt.utils.ModelUtils +import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration class KPrimalDualMaxResSolver( private val ctx: KContext, private val solver: KSolver, - private val maxSatCtx: KMaxSATContext, + private val maxSmtCtx: KMaxSMTContext, ) : KMaxResSolver(ctx, solver) { private var _lower: UInt = 0u // Current lower frontier private var _upper: UInt = 0u // Current upper frontier private var _maxUpper = 0u // Max possible upper frontier private var _correctionSetSize: Int = 0 // Current corrections set size - private val _maxCoreSize = if (maxSatCtx.getMultipleCores) 3 else 1 + private val _maxCoreSize = if (maxSmtCtx.getMultipleCores) 3 else 1 private var _correctionSetModel: KModel? = null private var _model: KModel? = null private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) private data class WeightedCore(val expressions: List>, val weight: UInt) - override fun checkMaxSAT(timeout: Duration): KMaxSATResult { + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val clockStart = System.currentTimeMillis() if (TimerUtils.timeoutExceeded(timeout)) { @@ -47,13 +47,13 @@ class KPrimalDualMaxResSolver( val hardConstraintsStatus = solver.check(timeout) if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { - return KMaxSATResult(listOf(), hardConstraintsStatus, true) + return KMaxSMTResult(listOf(), hardConstraintsStatus, true) } else if (hardConstraintsStatus == UNKNOWN) { - return KMaxSATResult(listOf(), hardConstraintsStatus, false) + return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } solver.push() - initMaxSAT() + initMaxSMT() val assumptions = softConstraints.toMutableList() unionSoftConstraintsWithSameExpressions(assumptions) @@ -64,14 +64,14 @@ class KPrimalDualMaxResSolver( throw NotImplementedError() } - val status = checkSATHillClimb(assumptions, timeout) + val status = checkSatHillClimb(assumptions, timeout) when (status) { SAT -> { - when (maxSatCtx.strategy) { - KMaxSATContext.Strategy.PrimalMaxRes -> _upper = _lower + when (maxSmtCtx.strategy) { + KMaxSMTContext.Strategy.PrimalMaxRes -> _upper = _lower - KMaxSATContext.Strategy.PrimalDualMaxRes -> { + KMaxSMTContext.Strategy.PrimalDualMaxRes -> { val correctionSet = getCorrectionSet(solver.model(), assumptions) if (correctionSet.isEmpty()) { if (_model != null) { @@ -113,7 +113,7 @@ class KPrimalDualMaxResSolver( _lower = _upper - val result = KMaxSATResult(getSatSoftConstraintsByModel(_model!!), SAT, true) + val result = KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, true) solver.pop() @@ -159,7 +159,7 @@ class KPrimalDualMaxResSolver( _lower += weightedCore.weight - if (maxSatCtx.strategy == PrimalDualMaxRes) { + if (maxSmtCtx.strategy == PrimalDualMaxRes) { _lower = minOf(_lower, _upper) } @@ -230,7 +230,7 @@ class KPrimalDualMaxResSolver( return Pair(SAT, cores) // TODO: is this status Ok? } - status = checkSATHillClimb(assumptions, checkSatRemainingTime) + status = checkSatHillClimb(assumptions, checkSatRemainingTime) } return Pair(status, cores) @@ -391,7 +391,7 @@ class KPrimalDualMaxResSolver( assert(fml) } - private fun initMaxSAT() { + private fun initMaxSMT() { _lower = 0u _upper = softConstraints.sumOf { it.weight } @@ -409,10 +409,10 @@ class KPrimalDualMaxResSolver( return ModelUtils.getCorrectionSet(ctx, model, assumptions) } - private fun checkSATHillClimb(assumptions: MutableList, timeout: Duration): KSolverStatus { + private fun checkSatHillClimb(assumptions: MutableList, timeout: Duration): KSolverStatus { var status = SAT - if (maxSatCtx.preferLargeWeightConstraintsForCores && assumptions.isNotEmpty()) { + if (maxSmtCtx.preferLargeWeightConstraintsForCores && assumptions.isNotEmpty()) { val clockStart = System.currentTimeMillis() // Give preference to cores that have large minimal values. diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt similarity index 93% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index 12633eef4..caeeed978 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.solvers.utils +package io.ksmt.solver.maxsmt.solvers.utils import io.ksmt.KContext import io.ksmt.expr.KExpr @@ -7,10 +7,10 @@ import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN -import io.ksmt.solver.maxsat.constraints.SoftConstraint -import io.ksmt.solver.maxsat.utils.CoreUtils -import io.ksmt.solver.maxsat.utils.ModelUtils -import io.ksmt.solver.maxsat.utils.TimerUtils +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.utils.CoreUtils +import io.ksmt.solver.maxsmt.utils.ModelUtils +import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt similarity index 86% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt index fb563763a..b0046cc43 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/CoreUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt @@ -1,7 +1,7 @@ -package io.ksmt.solver.maxsat.utils +package io.ksmt.solver.maxsmt.utils import io.ksmt.expr.KExpr -import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.sort.KBoolSort internal object CoreUtils { @@ -22,7 +22,7 @@ internal object CoreUtils { require(uniqueCoreElements.size == softs.size) { "Unsat core size [${uniqueCoreElements.size}] was not equal to corresponding " + - "soft constraints size [${softs.size}]" + "soft constraints size [${softs.size}]" } return softs diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt similarity index 91% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt index d0787b223..d59f2b024 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/ModelUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt @@ -1,9 +1,9 @@ -package io.ksmt.solver.maxsat.utils +package io.ksmt.solver.maxsmt.utils import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.solver.KModel -import io.ksmt.solver.maxsat.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.sort.KBoolSort internal object ModelUtils { diff --git a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt similarity index 92% rename from ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt index aebe282ae..e27021c8b 100644 --- a/ksmt-maxsat/src/main/kotlin/io/ksmt/solver/maxsat/utils/TimerUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsat.utils +package io.ksmt.solver.maxsmt.utils import kotlin.time.Duration import kotlin.time.DurationUnit diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt new file mode 100644 index 000000000..de1d3a474 --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -0,0 +1,390 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.KContext +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.utils.getValue +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +abstract class KMaxSMTSolverTest { + abstract fun getSolver(): KMaxSMTSolver + + protected val ctx: KContext = KContext() + private lateinit var maxSMTSolver: KMaxSMTSolver + + @BeforeEach + fun initSolver() { + maxSMTSolver = getSolver() + } + + @AfterEach + fun closeSolver() = maxSMTSolver.close() + + @Test + fun hardConstraintsUnsatTest() = with(ctx) { + val a by boolSort + val b by boolSort + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(!a) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult.hardConstraintsSatStatus == UNSAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) + } + + @Test + fun hardConstraintsUnsatNoSoftTest() = with(ctx) { + val a by boolSort + val b by boolSort + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(!a) + + maxSMTSolver.assertSoft(a and !b, 3u) + maxSMTSolver.assertSoft(!a, 5u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == UNSAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) + } + + @Test + fun noSoftConstraintsTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(c) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) + } + + @Test + fun noSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + maxSMTSolver.assert(a) + maxSMTSolver.assert(b) + maxSMTSolver.assert(c) + maxSMTSolver.assertSoft(a and !c, 3u) + maxSMTSolver.assertSoft(!a, 5u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) + } + + @Test + fun oneOfTwoSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assert(a or b) + maxSMTSolver.assert(!a or b) + + maxSMTSolver.assertSoft(a or !b, 2u) + maxSMTSolver.assertSoft(!a or !b, 3u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSMTResult.satSoftConstraints) + } + + @Test + fun twoOfFourSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + val d by boolSort + + maxSMTSolver.assert(a or b) + maxSMTSolver.assert(c or d) + maxSMTSolver.assert(!a or b) + maxSMTSolver.assert(!c or d) + + maxSMTSolver.assertSoft(a or !b, 2u) + maxSMTSolver.assertSoft(c or !d, 2u) + maxSMTSolver.assertSoft(!a or !b, 3u) + maxSMTSolver.assertSoft(!c or !d, 3u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!a or !b, 3u), SoftConstraint(!c or !d, 3u)), + maxSMTResult.satSoftConstraints, + ) + } + + @Test + fun sixOfEightSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + val d by boolSort + + maxSMTSolver.assertSoft(a or b, 9u) + maxSMTSolver.assertSoft(c or d, 9u) + maxSMTSolver.assertSoft(!a or b, 9u) + maxSMTSolver.assertSoft(!c or d, 9u) + maxSMTSolver.assertSoft(a or !b, 2u) + maxSMTSolver.assertSoft(c or !d, 2u) + maxSMTSolver.assertSoft(!a or !b, 3u) + maxSMTSolver.assertSoft(!c or !d, 3u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 6) + assertEquals(42u, maxSMTResult.satSoftConstraints.sumOf { it.weight }) + } + + @Test + fun twoOfThreeSoftConstraintsSatTest() = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assert(a or b) + + maxSMTSolver.assertSoft(!a or b, 4u) + maxSMTSolver.assertSoft(a or !b, 6u) + maxSMTSolver.assertSoft(!a or !b, 2u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 2) + val softConstraintsToAssertSAT = + listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) + assertSoftConstraintsSat(softConstraintsToAssertSAT, maxSMTResult.satSoftConstraints) + } + + @Test + fun sameExpressionSoftConstraintsSatTest() = with(ctx) { + val x by boolSort + val y by boolSort + + maxSMTSolver.assert(x or y) + + maxSMTSolver.assertSoft(!x or y, 7u) + maxSMTSolver.assertSoft(x or !y, 6u) + maxSMTSolver.assertSoft(!x or !y, 3u) + maxSMTSolver.assertSoft(!x or !y, 4u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 3) + assertSoftConstraintsSat( + listOf( + SoftConstraint(!x or y, 7u), + SoftConstraint(!x or !y, 3u), + SoftConstraint(!x or !y, 4u), + ), + maxSMTResult.satSoftConstraints, + ) + } + + @Test + fun sameExpressionSoftConstraintsUnsatTest() = with(ctx) { + val x by boolSort + val y by boolSort + + maxSMTSolver.assert(x or y) + + maxSMTSolver.assertSoft(!x or y, 6u) + maxSMTSolver.assertSoft(x or !y, 6u) + maxSMTSolver.assertSoft(!x or !y, 3u) + maxSMTSolver.assertSoft(!x or !y, 2u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), + maxSMTResult.satSoftConstraints, + ) + } + + @Test + fun chooseOneConstraintByWeightTest() = with(ctx) { + val z by boolSort + val a by boolSort + val b by boolSort + + maxSMTSolver.assert(z) + maxSMTSolver.assertSoft(a and b, 1u) + maxSMTSolver.assertSoft(!a and !b, 5u) + maxSMTSolver.assertSoft(a and b and z, 2u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 1) + assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSMTResult.satSoftConstraints) + } + + @Test + fun equalWeightsTest() = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assert(a or b) + maxSMTSolver.assert(!a or b) + + maxSMTSolver.assertSoft(a or !b, 2u) + maxSMTSolver.assertSoft(!a or !b, 2u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.satSoftConstraints.size == 1) + } + + @Test + fun inequalitiesTest() = with(ctx) { + val x by intSort + val y by intSort + + val a1 = x gt 0.expr + val a2 = x lt y + val a3 = x + y le 0.expr + + maxSMTSolver.assert(a3 eq a1) + maxSMTSolver.assert(a3 or a2) + + maxSMTSolver.assertSoft(a3, 3u) + maxSMTSolver.assertSoft(!a3, 5u) + maxSMTSolver.assertSoft(!a1, 10u) + maxSMTSolver.assertSoft(!a2, 3u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResult.satSoftConstraints.size == 2) + assertSoftConstraintsSat( + listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), + maxSMTResult.satSoftConstraints, + ) + } + + @Test + fun oneScopePushPopTest() = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assert(a or b) + + maxSMTSolver.assertSoft(!a or b, 1u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSMTResult.satSoftConstraints) + + maxSMTSolver.push() + + maxSMTSolver.assertSoft(a or !b, 1u) + maxSMTSolver.assertSoft(!a or !b, 1u) + + val maxSMTResultScoped = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(maxSMTResultScoped.satSoftConstraints.size == 2) + + maxSMTSolver.pop() + + assertSoftConstraintsSat(listOf(SoftConstraint(!a or b, 1u)), maxSMTResult.satSoftConstraints) + } + + @Test + fun threeScopesPushPopTest() = with(ctx) { + val a by boolSort + val b by boolSort + val c by boolSort + + maxSMTSolver.assert(a) + + maxSMTSolver.push() + maxSMTSolver.assertSoft(a or b, 1u) + maxSMTSolver.assertSoft(c or b, 1u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult.satSoftConstraints.size == 2) + + maxSMTSolver.push() + maxSMTSolver.assertSoft(!b and !c, 2u) + val maxSMTResult2 = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult2.satSoftConstraints.size == 2) + + maxSMTSolver.push() + maxSMTSolver.assertSoft(a or c, 1u) + val maxSMTResult3 = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult3.satSoftConstraints.size == 3) + + maxSMTSolver.pop(2u) + val maxSMTResult4 = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult4.satSoftConstraints.size == 2) + + maxSMTSolver.pop() + val maxSMTResult5 = maxSMTSolver.checkMaxSMT() + assertTrue(maxSMTResult5.satSoftConstraints.isEmpty()) + } + + @Test + fun similarExpressionsTest(): Unit = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assertSoft(a or b, 1u) + maxSMTSolver.assertSoft(!a or b, 1u) + maxSMTSolver.assertSoft(a or !b, 1u) + maxSMTSolver.assertSoft(!a or !b, 1u) + maxSMTSolver.assertSoft(!a or !b or !b, 1u) + + maxSMTSolver.checkMaxSMT() + } + + private fun assertSoftConstraintsSat( + constraintsToAssert: List, + satConstraints: List, + ) { + for (constraint in constraintsToAssert) { + assertTrue( + satConstraints.any { + constraint.expression.internEquals(it.expression) && constraint.weight == it.weight + }, + ) + } + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt new file mode 100644 index 000000000..0c228a264 --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPMResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPMResSolverTest : KMaxSMTSolverTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPMResSolver(this, z3Solver) + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt new file mode 100644 index 000000000..a6dbc80ee --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver2Test : KMaxSMTSolverTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt new file mode 100644 index 000000000..daa908bc8 --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver3Test : KMaxSMTSolverTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(minimizeCores = false)) + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt new file mode 100644 index 000000000..7c70cb50e --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolver4Test : KMaxSMTSolverTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(getMultipleCores = false)) + } +} diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt new file mode 100644 index 000000000..1162e2d90 --- /dev/null +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt @@ -0,0 +1,13 @@ +package io.ksmt.solver.maxsmt + +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration + +class KPrimalDualMaxResSolverTest : KMaxSMTSolverTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 2dad1d99f..48ed5ac48 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,8 +22,8 @@ include("ksmt-runner:solver-generator") include("ksmt-test") include("ksmt-symfpu") -include("ksmt-maxsat") -include("ksmt-maxsat-test") +include("ksmt-maxsmt") +include("ksmt-maxsmt-test") pluginManagement { resolutionStrategy { From 4e116fb16e3bd35ba1be3d46bb9e4af8fc16679d Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:54:40 +0300 Subject: [PATCH 064/228] Update maxsmt-tests-matrix.json --- .github/workflows/maxsmt-tests-matrix.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 29938c9bf..7e6f713a9 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,20 +1,12 @@ { "logics": [ { - "NAME": "QF_ABVFP", + "NAME": "QF_ABVFP_light", "TEST_DATA_REVISION": "0.0.0" }, { "NAME": "QF_ALIA", "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_BV-1", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_BV-2", - "TEST_DATA_REVISION": "0.0.0" } ] } From 51f20069793be9108f4d1303f7f0c1c8f3c7d7b6 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 26 Oct 2023 15:56:49 +0300 Subject: [PATCH 065/228] Update tests list in build.gradle.kts --- ksmt-maxsmt-test/build.gradle.kts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index c675a6af0..64e16a928 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -21,10 +21,9 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABVFP", // 7.37M + "QF_ABVFP_light", // 338K + // "QF_ABVFP", // 7.37M "QF_ALIA", // 700K - "QF_BV-1", // 17.2M - "QF_BV-2", // 20.8М ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From efb04b81d7999335cfc992795ccd5cc8ad6efcb2 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 26 Oct 2023 16:32:20 +0300 Subject: [PATCH 066/228] Rename maxsat project to maxsmt in workflows --- .github/workflows/run-long-maxsat-tests.yml | 10 +++++----- .github/workflows/run-long-maxsmt-tests.yml | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index ff8885f32..f0e50826f 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -25,7 +25,7 @@ jobs: cache-name: cache-maxsat-test-data with: key: maxsat-test-data-${{ env.TEST_DATA_REVISION }}-${{ matrix.os }} - path: ksmt-maxsat-test/testData/data/* + path: ksmt-maxsmt-test/testData/data/* - name: Set up JDK 1.8 uses: actions/setup-java@v3 @@ -39,7 +39,7 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsat-test:downloadPreparedMaxSatBenchmarkTestData + :ksmt-maxsmt-test:downloadPreparedMaxSatBenchmarkTestData --no-daemon -PtestDataRevision=${{ env.TEST_DATA_REVISION }} @@ -47,7 +47,7 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsat-test:usePreparedTestData + :ksmt-maxsmt-test:usePreparedTestData --no-daemon - name: Setup tmate session @@ -57,7 +57,7 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsat-test:test + :ksmt-maxsmt-test:test --no-daemon - name: Upload test report @@ -65,4 +65,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: ksmt-maxsat-test-report - path: ksmt-maxsat-test/build/reports/tests/test/* + path: ksmt-maxsmt-test/build/reports/tests/test/* diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 1c402e8a7..7b83e5c24 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -78,7 +78,7 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsat-test:maxSmtBenchmark-${{ matrix.logics.NAME }} + :ksmt-maxsmt-test:maxSmtBenchmark-${{ matrix.logics.NAME }} --no-daemon -PmaxSmtTestDataRevision=${{ env.TEST_DATA_REVISION }} @@ -86,13 +86,13 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsat-test:test + :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsat.test.smt.KPMResSMTBenchmarkTest.maxSMTTest" + --tests "io.ksmt.solver.maxsmt.test.smt.KPMResSMTBenchmarkTest.maxSMTTest" - name: Archive ${{ matrix.logics.NAME }} test report if: ${{ always() }} uses: actions/upload-artifact@v3 with: name: ${{ matrix.logics.NAME }}_test_report - path: ksmt-maxsat-test/build/reports/tests/test/* + path: ksmt-maxsmt-test/build/reports/tests/test/* From 83a03e7e731ceb721b1d2a758d370a113e31b384 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 26 Oct 2023 17:14:18 +0300 Subject: [PATCH 067/228] Add QF_AUFLIA to MaxSMT tests --- .github/workflows/maxsmt-tests-matrix.json | 6 +++++- ksmt-maxsmt-test/build.gradle.kts | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 7e6f713a9..8f6e671fd 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,11 +1,15 @@ { "logics": [ + { + "NAME": "QF_ALIA", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_ABVFP_light", "TEST_DATA_REVISION": "0.0.0" }, { - "NAME": "QF_ALIA", + "NAME": "QF_AUFLIA", "TEST_DATA_REVISION": "0.0.0" } ] diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 64e16a928..de2bb6935 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -21,9 +21,10 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABVFP_light", // 338K - // "QF_ABVFP", // 7.37M "QF_ALIA", // 700K + // "QF_ABVFP", // 7.37M + "QF_ABVFP_light", // 338K + "QF_AUFLIA", // 24.1M ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From abd32e25c1c06477649f6334936c1d04c477be7d Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Thu, 26 Oct 2023 17:34:38 +0300 Subject: [PATCH 068/228] Fix MaxSMT test assert message --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 720be3e9e..888404365 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -107,7 +107,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { maxSmtTestInfo.satSoftConstraintsWeightsSum, satSoftConstraintsWeightsSum.toULong(), "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + - "but must be [${maxSmtTestInfo.softConstraintsWeightsSum}]", + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", ) } From 221516115d330ed0e0aa1326af7b61220ad136a1 Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 30 Oct 2023 16:58:18 +0300 Subject: [PATCH 069/228] Update build.gradle.kts --- ksmt-maxsmt-test/build.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index de2bb6935..805963ad5 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -21,10 +21,7 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ALIA", // 700K - // "QF_ABVFP", // 7.37M - "QF_ABVFP_light", // 338K - "QF_AUFLIA", // 24.1M + "QF_ABVFP-light", // 444K ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From 16abc140bed15b86dd454fa6016fff45d54067b1 Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:59:05 +0300 Subject: [PATCH 070/228] Update maxsmt-tests-matrix.json --- .github/workflows/maxsmt-tests-matrix.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 8f6e671fd..a9bf10e6d 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,15 +1,7 @@ { "logics": [ { - "NAME": "QF_ALIA", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_ABVFP_light", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_AUFLIA", + "NAME": "QF_ABVFP-light", "TEST_DATA_REVISION": "0.0.0" } ] From 9a35343d3a1a869b55ccea26b0e8e2d23cfdb02b Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:51:46 +0300 Subject: [PATCH 071/228] Update run-long-maxsmt-tests.yml --- .github/workflows/run-long-maxsmt-tests.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 7b83e5c24..9f00faf44 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -3,6 +3,12 @@ name: Build and run long MaxSMT tests on: workflow_dispatch: inputs: + solver: + type: choice + description: What solver configuration to choose + options: + - KPMRes + - KPrimalDualMaxRes commitSHA: description: Commit SHA required: false @@ -88,7 +94,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.smt.KPMResSMTBenchmarkTest.maxSMTTest" + --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMTTest" - name: Archive ${{ matrix.logics.NAME }} test report if: ${{ always() }} From cbf8f6bf57a4f0448f1ad27a942a98abe415ff2e Mon Sep 17 00:00:00 2001 From: victoriafomina Date: Mon, 30 Oct 2023 17:57:25 +0300 Subject: [PATCH 072/228] Create KPrimalDualMaxResSMTBenchmarkTest.kt --- .../smt/KPrimalDualMaxResSMTBenchmarkTest.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt new file mode 100644 index 000000000..1050e714d --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt @@ -0,0 +1,25 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.z3.KZ3SolverConfiguration +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import java.nio.file.Path + +class KPrimalDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(): KMaxSMTSolver = with(ctx) { + val z3Solver = KZ3Solver(this) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath) { assertions -> + assertions + } + } +} From b8eb1172cf4fa658517bc02b1e92b49cb96bb103 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 9 Nov 2023 12:16:39 +0300 Subject: [PATCH 073/228] Fix a bug in core minimization --- .../io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index caeeed978..573d462a5 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -48,7 +48,9 @@ internal class MinimalUnsatCore( val expr = unknown.removeLast() val notExpr = !expr - val status = solver.checkWithAssumptions(minimalUnsatCore + unknown + notExpr, remainingTime) + minimalUnsatCore.add(notExpr) + + val status = solver.checkWithAssumptions(minimalUnsatCore + unknown, remainingTime) when (status) { UNKNOWN -> return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) From e6d2549f9ff29e1abee9e9e687f3e5b9653bfdd9 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 9 Nov 2023 12:17:28 +0300 Subject: [PATCH 074/228] Add asserts to similar expressions test --- .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index de1d3a474..80c106f87 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -271,6 +271,25 @@ abstract class KMaxSMTSolverTest { assertTrue(maxSMTResult.satSoftConstraints.size == 1) } + @Test + fun similarExpressionsTest(): Unit = with(ctx) { + val a by boolSort + val b by boolSort + + maxSMTSolver.assertSoft(a or b, 1u) + maxSMTSolver.assertSoft(!a or b, 1u) + maxSMTSolver.assertSoft(a or !b, 1u) + maxSMTSolver.assertSoft(!a or !b, 1u) + maxSMTSolver.assertSoft(!a or !b or !b, 1u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) + assertTrue(maxSMTResult.satSoftConstraints.size == 4) + assertTrue(maxSMTResult.satSoftConstraints.any { it.expression.internEquals(!a or !b) }) + assertTrue(maxSMTResult.satSoftConstraints.any { it.expression.internEquals(!a or !b or !b) }) + } + @Test fun inequalitiesTest() = with(ctx) { val x by intSort @@ -361,20 +380,6 @@ abstract class KMaxSMTSolverTest { assertTrue(maxSMTResult5.satSoftConstraints.isEmpty()) } - @Test - fun similarExpressionsTest(): Unit = with(ctx) { - val a by boolSort - val b by boolSort - - maxSMTSolver.assertSoft(a or b, 1u) - maxSMTSolver.assertSoft(!a or b, 1u) - maxSMTSolver.assertSoft(a or !b, 1u) - maxSMTSolver.assertSoft(!a or !b, 1u) - maxSMTSolver.assertSoft(!a or !b or !b, 1u) - - maxSMTSolver.checkMaxSMT() - } - private fun assertSoftConstraintsSat( constraintsToAssert: List, satConstraints: List, From 684b371c27050e12174b4f8b6917ed19d2358254 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 10 Nov 2023 13:20:44 +0300 Subject: [PATCH 075/228] Add test when three expressions are inconsistent --- .../io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index 80c106f87..ac743822c 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -318,6 +318,24 @@ abstract class KMaxSMTSolverTest { ) } + @Test + fun threeExpressionsAreInconsistentTest() = with(ctx) { + val x by boolSort + val y by boolSort + + maxSMTSolver.assertSoft(x, 671u) + maxSMTSolver.assertSoft(y, 783u) + maxSMTSolver.assertSoft(!x and !y or !x or !y, 859u) + + val maxSMTResult = maxSMTSolver.checkMaxSMT() + + assertTrue(maxSMTResult.maxSMTSucceeded) + assertSoftConstraintsSat( + listOf(SoftConstraint(y, 783u), SoftConstraint(!x and !y or !x or !y, 859u)), + maxSMTResult.satSoftConstraints + ) + } + @Test fun oneScopePushPopTest() = with(ctx) { val a by boolSort From 350a5c4175f181b5b358e9a774e5acc477809560 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 10 Nov 2023 13:21:03 +0300 Subject: [PATCH 076/228] Fix typo in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 14e3283e2..dfbda80f6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ ksmt-test/testData ksmt-maxsmt-test/testData # ignore maxsmt test data] +# ignore maxsmt test data ksmt-maxsmt-test/src/test/resources/maxSmtBenchmark From e2a8f2aabee252e2db12111cf365f262e92460b5 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 10 Nov 2023 15:06:13 +0300 Subject: [PATCH 077/228] Add possibility to choose solver for MaxSMT benchmark tests --- .gitignore | 1 - ksmt-maxsmt-test/build.gradle.kts | 5 +++ .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 34 ++++++++++++---- .../maxsmt/test/smt/KPMResSMTBenchmarkTest.kt | 39 ++++++++++++++----- .../smt/KPrimalDualMaxResSMTBenchmarkTest.kt | 39 ++++++++++++++----- .../io/ksmt/solver/maxsmt/test/util/Solver.kt | 5 +++ 6 files changed, 97 insertions(+), 26 deletions(-) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt diff --git a/.gitignore b/.gitignore index dfbda80f6..43937d99f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,5 @@ ksmt-test/testData # ignore maxsat test data ksmt-maxsmt-test/testData -# ignore maxsmt test data] # ignore maxsmt test data ksmt-maxsmt-test/src/test/resources/maxSmtBenchmark diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 805963ad5..39be03c43 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -12,7 +12,12 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.2") + testImplementation(project(":ksmt-z3")) + testImplementation(project(":ksmt-bitwuzla")) + testImplementation(project(":ksmt-cvc5")) + testImplementation(project(":ksmt-yices")) + testImplementation(project(":ksmt-test")) testImplementation(project(":ksmt-runner")) testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 888404365..95d48e0b0 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -10,12 +10,22 @@ import io.ksmt.runner.core.KsmtWorkerPool import io.ksmt.runner.core.RdServer import io.ksmt.runner.core.WorkerInitializationFailedException import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo -import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.solver.maxsmt.test.util.Solver +import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA +import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.util.Solver.YICES +import io.ksmt.solver.maxsmt.test.util.Solver.Z3 +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort import io.ksmt.test.TestRunner import io.ksmt.test.TestWorker @@ -25,7 +35,6 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.BeforeEach import java.io.File import java.nio.file.Path import kotlin.io.path.extension @@ -38,15 +47,23 @@ import kotlin.time.DurationUnit import kotlin.time.toDuration abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { - abstract fun getSolver(): KMaxSMTSolver + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { + return when (solver) { + Z3 -> KZ3Solver(this) as KSolver + BITWUZLA -> KBitwuzlaSolver(this) as KSolver + CVC5 -> KCvc5Solver(this) as KSolver + YICES -> KYicesSolver(this) as KSolver + } + } + + abstract fun getSolver(solver: Solver): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSMTSolver: KMaxSMTSolver + private lateinit var maxSMTSolver: KMaxSMTSolver private val logger = KotlinLogging.logger {} - @BeforeEach - fun initSolver() { - maxSMTSolver = getSolver() + private fun initSolver(solver: Solver) { + maxSMTSolver = getSolver(solver) } @AfterEach @@ -56,7 +73,10 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { name: String, samplePath: Path, mkKsmtAssertions: suspend TestRunner.(List>) -> List>, + solver: Solver = Z3, ) { + initSolver(solver) + val extension = "smt2" require(samplePath.extension == extension) { "File extension cannot be '${samplePath.extension}' as it must be $extension" diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt index 5984058fc..905048c1a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt @@ -1,24 +1,45 @@ package io.ksmt.solver.maxsmt.test.smt +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.solver.maxsmt.test.util.Solver +import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA +import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.util.Solver.YICES +import io.ksmt.solver.maxsmt.test.util.Solver.Z3 import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Path class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPMResSolver(this, z3Solver) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver: KSolver = getSmtSolver(solver) + return KPMResSolver(this, smtSolver) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") - fun maxSMTTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath) { assertions -> - assertions - } + fun maxSMTZ3Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTCvc5Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTYicesTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt index 1050e714d..7d7107d5c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt @@ -1,25 +1,46 @@ package io.ksmt.solver.maxsmt.test.smt +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration +import io.ksmt.solver.maxsmt.test.util.Solver +import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA +import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.util.Solver.YICES +import io.ksmt.solver.maxsmt.test.util.Solver.Z3 import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Path class KPrimalDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver: KSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") - fun maxSMTTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath) { assertions -> - assertions - } + fun maxSMTZ3Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTCvc5Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTTestYices(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt new file mode 100644 index 000000000..dded32579 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt @@ -0,0 +1,5 @@ +package io.ksmt.solver.maxsmt.test.util + +enum class Solver { + Z3, BITWUZLA, CVC5, YICES +} \ No newline at end of file From 7643f6ae4257e115690c06960b45d7a7d3932cdb Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:07:01 +0300 Subject: [PATCH 078/228] Update run-long-maxsmt-tests.yml: support choosing solver --- .github/workflows/run-long-maxsmt-tests.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 9f00faf44..e050f9d7d 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -5,10 +5,18 @@ on: inputs: solver: type: choice - description: What solver configuration to choose + description: Chosen MaxSMT solver configuration options: - KPMRes - KPrimalDualMaxRes + smtSolver: + type: choice + description: Chosen SMT solver + options: + - Z3 + - Bitwuzla + - Cvc5 + - Yices commitSHA: description: Commit SHA required: false @@ -94,7 +102,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMTTest" + --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMT${{ github.event.inputs.smtSolver }}Test" - name: Archive ${{ matrix.logics.NAME }} test report if: ${{ always() }} From 73451b45431a26449f183ee661629f8303e6eb1e Mon Sep 17 00:00:00 2001 From: Victoria <32179813+victoriafomina@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:42:48 +0300 Subject: [PATCH 079/228] Update run-long-maxsmt-tests.yml: update test report name --- .github/workflows/run-long-maxsmt-tests.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index e050f9d7d..3bef24beb 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -39,13 +39,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Check out ${{ github.event.inputs.commitSHA }} commit - if: github.event.inputs.commitSHA != '' - run: | - git config --global --add safe.directory ${GITHUB_WORKSPACE} - git fetch - git checkout ${{ github.event.inputs.commitSHA }} - - id: set-matrix name: Read and print config from maxsmt-tests-matrix.json run: | @@ -108,5 +101,5 @@ jobs: if: ${{ always() }} uses: actions/upload-artifact@v3 with: - name: ${{ matrix.logics.NAME }}_test_report + name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* From 03ce90fbb7f6d5e841813086e16f9190bffef4d5 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 10 Nov 2023 16:05:39 +0300 Subject: [PATCH 080/228] Run PDMRes solver benchmark tests in different configurations --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 36 ++++++++++++++++--- .../maxsmt/test/smt/KPMResSMTBenchmarkTest.kt | 33 +---------------- .../smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt | 15 ++++++++ .../smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt | 15 ++++++++ .../smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt | 15 ++++++++ .../smt/KPrimalDualMaxResSMTBenchmarkTest.kt | 35 ++---------------- .../maxsmt/test/{util => utils}/Solver.kt | 2 +- 7 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{util => utils}/Solver.kt (55%) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 95d48e0b0..7e9f266e1 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -19,11 +19,11 @@ import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo -import io.ksmt.solver.maxsmt.test.util.Solver -import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA -import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 -import io.ksmt.solver.maxsmt.test.util.Solver.YICES -import io.ksmt.solver.maxsmt.test.util.Solver.Z3 +import io.ksmt.solver.maxsmt.test.utils.Solver +import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA +import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.utils.Solver.YICES +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -35,6 +35,8 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import java.io.File import java.nio.file.Path import kotlin.io.path.extension @@ -69,6 +71,30 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @AfterEach fun closeSolver() = maxSMTSolver.close() + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTZ3Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTCvc5Test(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTYicesTest(name: String, samplePath: Path) { + maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + } + fun maxSMTTest( name: String, samplePath: Path, diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt index 905048c1a..b5439fe87 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt @@ -4,42 +4,11 @@ import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver -import io.ksmt.solver.maxsmt.test.util.Solver -import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA -import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 -import io.ksmt.solver.maxsmt.test.util.Solver.YICES -import io.ksmt.solver.maxsmt.test.util.Solver.Z3 -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import java.nio.file.Path +import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver: KSolver = getSmtSolver(solver) return KPMResSolver(this, smtSolver) } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTZ3Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTCvc5Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTYicesTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) - } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt new file mode 100644 index 000000000..96d77d5bb --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt @@ -0,0 +1,15 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver: KSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt new file mode 100644 index 000000000..020ab2ad2 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt @@ -0,0 +1,15 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver: KSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = false)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt new file mode 100644 index 000000000..2df715435 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt @@ -0,0 +1,15 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver: KSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = false)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt index 7d7107d5c..c89a38671 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt @@ -5,42 +5,11 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.maxsmt.test.util.Solver -import io.ksmt.solver.maxsmt.test.util.Solver.BITWUZLA -import io.ksmt.solver.maxsmt.test.util.Solver.CVC5 -import io.ksmt.solver.maxsmt.test.util.Solver.YICES -import io.ksmt.solver.maxsmt.test.util.Solver.Z3 -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource -import java.nio.file.Path +import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver: KSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTZ3Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTCvc5Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) - } - - @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") - fun maxSMTTestYices(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext()) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt similarity index 55% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt index dded32579..d31484acd 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/util/Solver.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt @@ -1,4 +1,4 @@ -package io.ksmt.solver.maxsmt.test.util +package io.ksmt.solver.maxsmt.test.utils enum class Solver { Z3, BITWUZLA, CVC5, YICES From 6a33a56b865154e62df26c854d90ba123415762a Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 10 Nov 2023 18:13:12 +0300 Subject: [PATCH 081/228] Format KMaxSMTSolverTest.kt --- .../test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index ac743822c..0364938d7 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -1,6 +1,6 @@ package io.ksmt.solver.maxsmt -import io.ksmt.KContext +import io.ksmt.KContext import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNSAT import io.ksmt.solver.maxsmt.constraints.SoftConstraint @@ -332,7 +332,7 @@ abstract class KMaxSMTSolverTest { assertTrue(maxSMTResult.maxSMTSucceeded) assertSoftConstraintsSat( listOf(SoftConstraint(y, 783u), SoftConstraint(!x and !y or !x or !y, 859u)), - maxSMTResult.satSoftConstraints + maxSMTResult.satSoftConstraints, ) } From b12e8f46afdddec6ecb627ed6ec142121e40c0ce Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 16 Nov 2023 18:29:25 +0300 Subject: [PATCH 082/228] Add statistics collection --- ksmt-maxsmt-test/build.gradle.kts | 13 +++- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 65 ++++++++++++------ .../test/statistics/JsonStatisticsHelper.kt | 33 +++++++++ .../test/statistics/MaxSMTTestStatistics.kt | 10 +++ .../ksmt/solver/maxsmt/test/utils/Solver.kt | 2 +- ksmt-maxsmt/build.gradle.kts | 8 ++- .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 43 +++++++----- .../solver/maxsmt/solvers/KPMResSolver.kt | 65 +++++++++++++++++- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 67 +++++++++++++++++-- .../solvers/utils/MinimalCoreStatistics.kt | 6 ++ .../maxsmt/solvers/utils/MinimalUnsatCore.kt | 27 +++++++- .../maxsmt/statistics/KMaxSMTStatistics.kt | 10 +++ version.properties | 5 +- 13 files changed, 300 insertions(+), 54 deletions(-) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalCoreStatistics.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/statistics/KMaxSMTStatistics.kt diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 39be03c43..77d7c02e0 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.konan.properties.loadProperties + plugins { id("io.ksmt.ksmt-base") } @@ -6,12 +8,14 @@ repositories { mavenCentral() } +val versions = loadProperties(projectDir.parentFile.resolve("version.properties").absolutePath) + dependencies { implementation(project(":ksmt-core")) implementation(project(":ksmt-maxsmt")) - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") - testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:${versions["junit-jupiter"]}") + testImplementation("org.junit.jupiter:junit-jupiter-params:${versions["junit-jupiter"]}") testImplementation(project(":ksmt-z3")) testImplementation(project(":ksmt-bitwuzla")) @@ -20,9 +24,12 @@ dependencies { testImplementation(project(":ksmt-test")) testImplementation(project(":ksmt-runner")) - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") testImplementation("io.github.oshai:kotlin-logging-jvm:5.1.0") testImplementation("org.slf4j:slf4j-simple:2.0.9") + + testImplementation("com.google.code.gson:gson:2.10.1") } val maxSmtBenchmarks = listOfNotNull( diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 7e9f266e1..c3ada703d 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -1,7 +1,6 @@ package io.ksmt.solver.maxsmt.test.smt import io.github.oshai.kotlinlogging.KotlinLogging -import io.github.oshai.kotlinlogging.withLoggingContext import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.runner.core.KsmtWorkerArgs @@ -19,6 +18,8 @@ import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo +import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper +import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 @@ -39,14 +40,14 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.io.File import java.nio.file.Path +import java.nio.file.Paths import kotlin.io.path.extension +import kotlin.random.Random import kotlin.system.measureTimeMillis import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds -import kotlin.time.DurationUnit -import kotlin.time.toDuration abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { @@ -74,28 +75,28 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTZ3Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> assertions }, Z3) + testMaxSMTSolver(name, samplePath, { assertions -> assertions }, Z3) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTCvc5Test(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTYicesTest(name: String, samplePath: Path) { - maxSMTTest(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) } - fun maxSMTTest( + private fun testMaxSMTSolver( name: String, samplePath: Path, mkKsmtAssertions: suspend TestRunner.(List>) -> List>, @@ -136,25 +137,32 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } lateinit var maxSMTResult: KMaxSMTResult - val elapsedTime = measureTimeMillis { - maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds) + maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds, true) } + val testStatistics = MaxSMTTestStatistics(maxSMTSolver.collectMaxSMTStatistics()) + testStatistics.name = name + testStatistics.maxSMTCallElapsedTimeMs = elapsedTime - withLoggingContext("test" to name) { - logger.info { "Elapsed time: [${elapsedTime.toDuration(DurationUnit.MILLISECONDS).inWholeSeconds} s]" } - } + logger.info { "Test name: [$name]" } + logger.info { "Elapsed time (MaxSMT call): [${elapsedTime}ms]" } val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } - assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") - assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") - assertEquals( - maxSmtTestInfo.satSoftConstraintsWeightsSum, - satSoftConstraintsWeightsSum.toULong(), - "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + - "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", - ) + try { + assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + assertEquals( + maxSmtTestInfo.satSoftConstraintsWeightsSum, + satSoftConstraintsWeightsSum.toULong(), + "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", + ) + testStatistics.correctnessError = false + assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") + testStatistics.passed = true + } finally { + jsonHelper.appendTestStatisticsToFile(testStatistics) + } } private fun KsmtWorkerPool.withWorker( @@ -197,6 +205,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 25.seconds internal lateinit var testWorkers: KsmtWorkerPool + private lateinit var jsonHelper: JsonStatisticsHelper @BeforeAll @JvmStatic @@ -210,10 +219,22 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) }, ) + + jsonHelper = + JsonStatisticsHelper( + File( + "${ + Paths.get("").toAbsolutePath() + }/src/test/resources/maxsmt-statistics-${Random.nextInt(0, 10000)}.json", + ), + ) } @AfterAll @JvmStatic - fun closeWorkerPools() = testWorkers.terminate() + fun closeWorkerPools() { + testWorkers.terminate() + jsonHelper.markLastTestStatisticsAsProcessed() + } } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt new file mode 100644 index 000000000..5705c7d4f --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt @@ -0,0 +1,33 @@ +package io.ksmt.solver.maxsmt.test.statistics + +import com.google.gson.Gson +import java.io.File + +internal class JsonStatisticsHelper(private val jsonFile: File) { + private var firstMet = false + private var lastMet = false + private val gson = Gson().newBuilder().setPrettyPrinting().create() + + init { + if (!jsonFile.exists()) { + jsonFile.createNewFile() + } + } + + fun appendTestStatisticsToFile(statistics: MaxSMTTestStatistics) { + require(!lastMet) { "It's not allowed to append statistics when the last test is processed" } + + if (firstMet) { + jsonFile.appendText(",") + } else { + jsonFile.appendText("{\n\"TESTS\": [\n") + firstMet = true + } + jsonFile.appendText(gson.toJson(statistics)) + } + + fun markLastTestStatisticsAsProcessed() { + lastMet = true + jsonFile.appendText("\n]\n}") + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt new file mode 100644 index 000000000..e056d9fee --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -0,0 +1,10 @@ +package io.ksmt.solver.maxsmt.test.statistics + +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics + +internal data class MaxSMTTestStatistics(val maxSMTCallStatistics: KMaxSMTStatistics) { + var name = "" + var maxSMTCallElapsedTimeMs: Long = 0 + var passed = false + var correctnessError = true +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt index d31484acd..dcfbc03b9 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt @@ -2,4 +2,4 @@ package io.ksmt.solver.maxsmt.test.utils enum class Solver { Z3, BITWUZLA, CVC5, YICES -} \ No newline at end of file +} diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index 99bc0b9ee..ace4ce8da 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.konan.properties.loadProperties + plugins { id("io.ksmt.ksmt-base") } @@ -6,10 +8,12 @@ repositories { mavenCentral() } +val versions = loadProperties(projectDir.parentFile.resolve("version.properties").absolutePath) + dependencies { implementation(project(":ksmt-core")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:${versions["junit-jupiter"]}") testImplementation(project(":ksmt-z3")) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index f148f215f..e3aca0ef1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -6,12 +6,10 @@ import io.ksmt.solver.KModel import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.KSolverStatus.SAT -import io.ksmt.solver.KSolverStatus.UNKNOWN -import io.ksmt.solver.KSolverStatus.UNSAT import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.scope.MaxSMTScopeManager +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.sort.KBoolSort import kotlin.time.Duration @@ -22,6 +20,7 @@ abstract class KMaxSMTSolver( where T : KSolverConfiguration { private val scopeManager = MaxSMTScopeManager() protected var softConstraints = mutableListOf() + protected lateinit var maxSMTStatistics: KMaxSMTStatistics /** * Softly assert an expression with weight (aka soft constraint) into solver. @@ -36,6 +35,14 @@ abstract class KMaxSMTSolver( scopeManager.incrementSoft() } + /** + * Solve maximum satisfiability modulo theories problem. + * + * @throws NotImplementedError + */ + fun checkMaxSMT(timeout: Duration = Duration.INFINITE): KMaxSMTResult = + checkMaxSMT(timeout, collectStatistics = false) + /** * Solve maximum satisfiability modulo theories problem. * @@ -43,7 +50,21 @@ abstract class KMaxSMTSolver( * * @throws NotImplementedError */ - abstract fun checkMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult + abstract fun checkMaxSMT( + timeout: Duration = Duration.INFINITE, + collectStatistics: Boolean, + ): KMaxSMTResult + + /** + * Get last MaxSMT launch statistics (number of queries to solver, MaxSMT timeout etc.). + */ + fun collectMaxSMTStatistics(): KMaxSMTStatistics { + require(this::maxSMTStatistics.isInitialized) { + "MaxSMT statistics is only available after MaxSMT launches with statistics collection enabled" + } + + return maxSMTStatistics + } /** * Union soft constraints with same expressions into a single soft constraint. @@ -72,20 +93,6 @@ abstract class KMaxSMTSolver( } } - /** - * Check on satisfiability hard constraints with assumed soft constraints. - * - * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model - * (if exists, null otherwise). - */ - protected fun checkSAT(assumptions: List, timeout: Duration): - Triple>, KModel?> = - when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { - SAT -> Triple(status, listOf(), solver.model()) - UNSAT -> Triple(status, solver.unsatCore(), null) - UNKNOWN -> Triple(status, listOf(), null) - } - protected fun getSatSoftConstraintsByModel(model: KModel): List { return softConstraints.filter { model.eval(it.expression, true) == ctx.trueExpr } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 8509ae0bd..66f9aea4d 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -9,24 +9,44 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import io.ksmt.utils.mkConst import kotlin.time.Duration +import kotlin.time.TimeSource.Monotonic.markNow class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : KMaxResSolver(ctx, solver) { private var currentMaxSMTResult: Triple>, KModel?> = Triple(null, listOf(), null) + private var collectStatistics = false + private val maxSmtCtx = KMaxSMTContext( + strategy = PrimalMaxRes, + preferLargeWeightConstraintsForCores = false, + minimizeCores = false, + getMultipleCores = false, + ) override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { + val markCheckMaxSMTStart = markNow() + if (TimerUtils.timeoutExceeded(timeout)) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") } + this.collectStatistics = collectStatistics + + if (this.collectStatistics) { + maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx) + maxSMTStatistics.timeoutMs = timeout.inWholeMilliseconds + } + solver.push() val maxSMTResult = runMaxSMTLogic(timeout) @@ -36,12 +56,19 @@ class KPMResSolver(private val ctx: KContext, private currentMaxSMTResult = Triple(UNKNOWN, listOf(), null) if (softConstraints.isEmpty()) { + if (this.collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } + return maxSMTResult } // TODO: get max SAT soft constraints subset if (!maxSMTResult.maxSMTSucceeded) { val (solverStatus, _, model) = currentMaxSMTResult + if (this.collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } return when (solverStatus) { SAT -> processSat(model!!) @@ -51,6 +78,10 @@ class KPMResSolver(private val ctx: KContext, private } } + if (this.collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } + return maxSMTResult } @@ -58,8 +89,14 @@ class KPMResSolver(private val ctx: KContext, private val clockStart = System.currentTimeMillis() if (softConstraints.isEmpty()) { + val markHardConstraintsCheckStart = markNow() val hardConstraintsStatus = solver.check(timeout) + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds + } + return KMaxSMTResult( listOf(), hardConstraintsStatus, @@ -67,8 +104,14 @@ class KPMResSolver(private val ctx: KContext, private ) } + val markSoftConstraintsCheckStart = markNow() val status = solver.check(timeout) + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markSoftConstraintsCheckStart).inWholeMilliseconds + } + if (status == UNSAT) { return KMaxSMTResult(listOf(), status, true) } else if (status == UNKNOWN) { @@ -87,8 +130,14 @@ class KPMResSolver(private val ctx: KContext, private return KMaxSMTResult(listOf(), status, false) } + val markCheckSatStart = markNow() val (solverStatus, unsatCore, model) = - checkSAT(formula, softConstraintsCheckRemainingTime) + checkSat(formula, softConstraintsCheckRemainingTime) + + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckSatStart).inWholeMilliseconds + } if (solverStatus == UNKNOWN) { // TODO: get max SAT soft constraints subset @@ -204,4 +253,18 @@ class KPMResSolver(private val ctx: KContext, private val satSoftConstraints = getSatSoftConstraintsByModel(model) return KMaxSMTResult(satSoftConstraints, SAT, true) } + + /** + * Check on satisfiability hard constraints with assumed soft constraints. + * + * @return a triple of solver status, unsat core (if exists, empty list otherwise) and model + * (if exists, null otherwise). + */ + private fun checkSat(assumptions: List, timeout: Duration): + Triple>, KModel?> = + when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { + SAT -> Triple(status, listOf(), solver.model()) + UNSAT -> Triple(status, solver.unsatCore(), null) + UNKNOWN -> Triple(status, listOf(), null) + } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index d85afd59d..784816042 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -11,14 +11,17 @@ import io.ksmt.solver.KSolverStatus.UNKNOWN import io.ksmt.solver.KSolverStatus.UNSAT import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.solvers.utils.MinimalUnsatCore +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.ModelUtils import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration +import kotlin.time.TimeSource.Monotonic.markNow class KPrimalDualMaxResSolver( private val ctx: KContext, @@ -34,21 +37,41 @@ class KPrimalDualMaxResSolver( private var _correctionSetModel: KModel? = null private var _model: KModel? = null private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) + private var collectStatistics = false private data class WeightedCore(val expressions: List>, val weight: UInt) override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val clockStart = System.currentTimeMillis() + val markCheckMaxSMTStart = markNow() if (TimerUtils.timeoutExceeded(timeout)) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") } + this.collectStatistics = collectStatistics + + if (this.collectStatistics) { + maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx) + maxSMTStatistics.timeoutMs = timeout.inWholeMilliseconds + } + + val markHardConstraintsCheckStart = markNow() val hardConstraintsStatus = solver.check(timeout) + if (this.collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds + } if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } return KMaxSMTResult(listOf(), hardConstraintsStatus, true) } else if (hardConstraintsStatus == UNKNOWN) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } @@ -61,6 +84,9 @@ class KPrimalDualMaxResSolver( while (_lower < _upper) { val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } throw NotImplementedError() } @@ -69,9 +95,9 @@ class KPrimalDualMaxResSolver( when (status) { SAT -> { when (maxSmtCtx.strategy) { - KMaxSMTContext.Strategy.PrimalMaxRes -> _upper = _lower + PrimalMaxRes -> _upper = _lower - KMaxSMTContext.Strategy.PrimalDualMaxRes -> { + PrimalDualMaxRes -> { val correctionSet = getCorrectionSet(solver.model(), assumptions) if (correctionSet.isEmpty()) { if (_model != null) { @@ -89,6 +115,9 @@ class KPrimalDualMaxResSolver( val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) if (TimerUtils.timeoutExceeded(remainingTime)) { solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } throw NotImplementedError() } @@ -97,15 +126,24 @@ class KPrimalDualMaxResSolver( _lower = _upper // TODO: process this case as it can happen when timeout exceeded solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } throw NotImplementedError() } else if (processUnsatStatus == UNKNOWN) { solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } throw NotImplementedError() } } UNKNOWN -> { solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } throw NotImplementedError() } } @@ -117,6 +155,9 @@ class KPrimalDualMaxResSolver( solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + } return result } @@ -236,8 +277,16 @@ class KPrimalDualMaxResSolver( return Pair(status, cores) } - private fun minimizeCore(assumptions: List, timeout: Duration): List = - _minimalUnsatCore.tryGetMinimalUnsatCore(assumptions, timeout) + private fun minimizeCore(assumptions: List, timeout: Duration): List { + val minimalUnsatCore = _minimalUnsatCore.tryGetMinimalUnsatCore(assumptions, timeout, collectStatistics) + if (collectStatistics) { + val minimalCoreStatistics = _minimalUnsatCore.collectStatistics() + maxSMTStatistics.queriesToSolverNumber += minimalCoreStatistics.queriesToSolverNumber + maxSMTStatistics.timeInSolverQueriesMs += minimalCoreStatistics.timeInSolverQueriesMs + } + + return minimalUnsatCore + } private fun updateMinimalUnsatCoreModel(assumptions: List) { val (model, weight) = _minimalUnsatCore.getBestModel() @@ -434,10 +483,20 @@ class KPrimalDualMaxResSolver( return UNKNOWN } + val markCheckAssumptionsStart = markNow() status = checkSat(assumptionsToCheck, remainingTime) + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckAssumptionsStart).inWholeMilliseconds + } } } else { + val markCheckStart = markNow() status = checkSat(assumptions, timeout) + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckStart).inWholeMilliseconds + } } return status diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalCoreStatistics.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalCoreStatistics.kt new file mode 100644 index 000000000..98bc53c56 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalCoreStatistics.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsmt.solvers.utils + +internal class MinimalCoreStatistics { + var queriesToSolverNumber = 0 + var timeInSolverQueriesMs: Long = 0 +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index 573d462a5..85b3869ff 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -13,26 +13,33 @@ import io.ksmt.solver.maxsmt.utils.ModelUtils import io.ksmt.solver.maxsmt.utils.TimerUtils import io.ksmt.sort.KBoolSort import kotlin.time.Duration +import kotlin.time.TimeSource.Monotonic.markNow internal class MinimalUnsatCore( private val ctx: KContext, private val solver: KSolver, ) { private val _minimalUnsatCoreModel = MinimalUnsatCoreModel(ctx, solver) + private lateinit var coreStatistics: MinimalCoreStatistics fun getBestModel(): Pair = _minimalUnsatCoreModel.getBestModel() - // If solver starts returning unknown, we return non minimized unsat core. + // If solver starts returning unknown or exceeds the timeout, we return non minimized unsat core. fun tryGetMinimalUnsatCore( assumptions: List, timeout: Duration = Duration.INFINITE, + collectStatistics: Boolean = false, ): List = with(ctx) { val clockStart = System.currentTimeMillis() + if (collectStatistics) { + coreStatistics = MinimalCoreStatistics() + } + val unsatCore = solver.unsatCore() if (unsatCore.isEmpty()) { - return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + return emptyList() } val minimalUnsatCore = mutableListOf>() @@ -50,7 +57,12 @@ internal class MinimalUnsatCore( val notExpr = !expr minimalUnsatCore.add(notExpr) + val markCheckStart = markNow() val status = solver.checkWithAssumptions(minimalUnsatCore + unknown, remainingTime) + if (collectStatistics) { + coreStatistics.queriesToSolverNumber++ + coreStatistics.timeInSolverQueriesMs += (markNow() - markCheckStart).inWholeMilliseconds + } when (status) { UNKNOWN -> return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) @@ -67,6 +79,17 @@ internal class MinimalUnsatCore( return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) } + /** + * Get last core minimization statistics (number of queries to solver, time in solver queries (ms) etc.). + */ + fun collectStatistics(): MinimalCoreStatistics { + require(this::coreStatistics.isInitialized) { + "Minimal core construction statistics is only available after core minimization launches with statistics collection enabled" + } + + return coreStatistics + } + fun reset() = _minimalUnsatCoreModel.reset() private fun processUnsat( diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/statistics/KMaxSMTStatistics.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/statistics/KMaxSMTStatistics.kt new file mode 100644 index 000000000..a6252bd3c --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/statistics/KMaxSMTStatistics.kt @@ -0,0 +1,10 @@ +package io.ksmt.solver.maxsmt.statistics + +import io.ksmt.solver.maxsmt.KMaxSMTContext + +data class KMaxSMTStatistics(val maxSmtCtx: KMaxSMTContext) { + var timeoutMs: Long = 0 + var elapsedTimeMs: Long = 0 + var timeInSolverQueriesMs: Long = 0 + var queriesToSolverNumber = 0 +} diff --git a/version.properties b/version.properties index 2517fc6da..074ae9108 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,5 @@ kotlin=1.9.0 -detekt=1.20.0 \ No newline at end of file +detekt=1.20.0 + +junit-jupiter=5.8.2 +kotlinx-coroutines=1.7.2 \ No newline at end of file From ad0c2c350fb7573b78c7b916af38e2d36abac86d Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 17 Nov 2023 12:12:23 +0300 Subject: [PATCH 083/228] Add more test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index a9bf10e6d..52caa07fb 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -3,6 +3,10 @@ { "NAME": "QF_ABVFP-light", "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_ABVFP-light2", + "TEST_DATA_REVISION": "0.0.0" } ] } diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 77d7c02e0..db8726cd2 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -33,7 +33,8 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABVFP-light", // 444K + "QF_ABVFP-light", // 354K + "QF_ABVFP-light2", // 651K ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From b3d52ff12c689aad62433fac83bce5e753961536 Mon Sep 17 00:00:00 2001 From: Victoria Date: Mon, 20 Nov 2023 12:01:52 +0300 Subject: [PATCH 084/228] Refactor checking hard constraints in KPMResSolver --- .../solver/maxsmt/solvers/KPMResSolver.kt | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 66f9aea4d..468e65640 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -88,15 +88,15 @@ class KPMResSolver(private val ctx: KContext, private private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { val clockStart = System.currentTimeMillis() - if (softConstraints.isEmpty()) { - val markHardConstraintsCheckStart = markNow() - val hardConstraintsStatus = solver.check(timeout) + val markHardConstraintsCheckStart = markNow() + val hardConstraintsStatus = solver.check(timeout) - if (collectStatistics) { - maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds - } + if (collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds + } + if (softConstraints.isEmpty()) { return KMaxSMTResult( listOf(), hardConstraintsStatus, @@ -104,18 +104,10 @@ class KPMResSolver(private val ctx: KContext, private ) } - val markSoftConstraintsCheckStart = markNow() - val status = solver.check(timeout) - - if (collectStatistics) { - maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markSoftConstraintsCheckStart).inWholeMilliseconds - } - - if (status == UNSAT) { - return KMaxSMTResult(listOf(), status, true) - } else if (status == UNKNOWN) { - return KMaxSMTResult(listOf(), status, false) + if (hardConstraintsStatus == UNSAT) { + return KMaxSMTResult(listOf(), hardConstraintsStatus, true) + } else if (hardConstraintsStatus == UNKNOWN) { + return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } var i = 0 @@ -124,15 +116,15 @@ class KPMResSolver(private val ctx: KContext, private unionSoftConstraintsWithSameExpressions(formula) while (true) { - val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) - if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { - return KMaxSMTResult(listOf(), status, false) + if (TimerUtils.timeoutExceeded(checkRemainingTime)) { + return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } val markCheckSatStart = markNow() val (solverStatus, unsatCore, model) = - checkSat(formula, softConstraintsCheckRemainingTime) + checkSat(formula, checkRemainingTime) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ @@ -141,7 +133,7 @@ class KPMResSolver(private val ctx: KContext, private if (solverStatus == UNKNOWN) { // TODO: get max SAT soft constraints subset - return KMaxSMTResult(listOf(), status, false) + return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } currentMaxSMTResult = Triple(solverStatus, unsatCore, model) From 3a575410e1cd7599534fa1289b55947fec37d4e8 Mon Sep 17 00:00:00 2001 From: Victoria Date: Mon, 20 Nov 2023 12:27:46 +0300 Subject: [PATCH 085/228] Update assertions in MaxSMT tests --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 6 ++++-- .../solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index c3ada703d..60e8ad8a1 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -150,15 +150,17 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } try { + assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + if (maxSmtTestInfo.satSoftConstraintsWeightsSum != satSoftConstraintsWeightsSum.toULong()) { + testStatistics.checkedSoftConstraintsSumIsWrong = true + } assertEquals( maxSmtTestInfo.satSoftConstraintsWeightsSum, satSoftConstraintsWeightsSum.toULong(), "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", ) - testStatistics.correctnessError = false - assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") testStatistics.passed = true } finally { jsonHelper.appendTestStatisticsToFile(testStatistics) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index e056d9fee..557251918 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -6,5 +6,5 @@ internal data class MaxSMTTestStatistics(val maxSMTCallStatistics: KMaxSMTStatis var name = "" var maxSMTCallElapsedTimeMs: Long = 0 var passed = false - var correctnessError = true + var checkedSoftConstraintsSumIsWrong = false } From b1530da07344a26276890031234366b8f2732b41 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 23 Nov 2023 13:00:14 +0300 Subject: [PATCH 086/228] Fix KPrimalDualMaxRes solver bugs - Update model only when checking all assumptions - Use expressionIsNotTrue instead of expressionIsFalse --- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 14 +++++++++----- .../maxsmt/solvers/utils/MinimalUnsatCore.kt | 2 +- .../io/ksmt/solver/maxsmt/utils/ModelUtils.kt | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 784816042..df3238ccf 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -299,7 +299,7 @@ class KPrimalDualMaxResSolver( private fun updateAssignment(model: KModel, assumptions: List) { var correctionSetSize = 0 for (constr in assumptions) { - if (ModelUtils.expressionIsFalse(ctx, model, constr.expression)) { + if (ModelUtils.expressionIsNotTrue(ctx, model, constr.expression)) { ++correctionSetSize } } @@ -484,7 +484,7 @@ class KPrimalDualMaxResSolver( } val markCheckAssumptionsStart = markNow() - status = checkSat(assumptionsToCheck, remainingTime) + status = checkSat(assumptionsToCheck, assumptionsToCheck.size == assumptions.size, remainingTime) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckAssumptionsStart).inWholeMilliseconds @@ -492,7 +492,7 @@ class KPrimalDualMaxResSolver( } } else { val markCheckStart = markNow() - status = checkSat(assumptions, timeout) + status = checkSat(assumptions, true, timeout) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckStart).inWholeMilliseconds @@ -515,10 +515,14 @@ class KPrimalDualMaxResSolver( return currentIndex } - private fun checkSat(assumptions: List, timeout: Duration): KSolverStatus { + private fun checkSat( + assumptions: List, + passedAllAssumptions: Boolean, + timeout: Duration, + ): KSolverStatus { val status = solver.checkWithAssumptions(assumptions.map { it.expression }, timeout) - if (status == SAT) { + if (passedAllAssumptions && status == SAT) { updateAssignment(solver.model().detach(), assumptions) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index 85b3869ff..29a658611 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -133,7 +133,7 @@ internal class MinimalUnsatCore( var weight = 0u for (asm in assumptions) { - if (ModelUtils.expressionIsFalse(ctx, model, asm.expression)) { + if (ModelUtils.expressionIsNotTrue(ctx, model, asm.expression)) { weight += asm.weight } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt index d59f2b024..6159944c3 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt @@ -7,14 +7,14 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.sort.KBoolSort internal object ModelUtils { - fun expressionIsFalse(ctx: KContext, model: KModel, expression: KExpr) = - model.eval(expression, true) == ctx.falseExpr + fun expressionIsNotTrue(ctx: KContext, model: KModel, expression: KExpr) = + model.eval(expression, true) != ctx.trueExpr fun getModelCost(ctx: KContext, model: KModel, softConstraints: List): UInt { var upper = 0u for (soft in softConstraints) { - if (expressionIsFalse(ctx, model, soft.expression)) { + if (expressionIsNotTrue(ctx, model, soft.expression)) { upper += soft.weight } } @@ -26,7 +26,7 @@ internal object ModelUtils { val correctionSet = mutableListOf() for (constr in softConstraints) { - if (expressionIsFalse(ctx, model, constr.expression)) { + if (expressionIsNotTrue(ctx, model, constr.expression)) { correctionSet.add(constr) } } From 21d104d181e50cc2a994b79f72d1b73ebb0e035e Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 24 Nov 2023 14:42:03 +0300 Subject: [PATCH 087/228] Add more logics for MaxSMT tests --- .github/workflows/maxsmt-tests-matrix.json | 12 ++++++++++++ ksmt-maxsmt-test/build.gradle.kts | 3 +++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 52caa07fb..89c460e88 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -7,6 +7,18 @@ { "NAME": "QF_ABVFP-light2", "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_AUFBVLIA-light", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_BV-light", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_UFLRA-light", + "TEST_DATA_REVISION": "0.0.0" } ] } diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index db8726cd2..8639cb5bc 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -35,6 +35,9 @@ dependencies { val maxSmtBenchmarks = listOfNotNull( "QF_ABVFP-light", // 354K "QF_ABVFP-light2", // 651K + "QF_AUFBVLIA-light", // 3.8M + "QF_BV-light", // 8.96M + "QF_UFLRA-light", // 2.27M ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From 265fe5bd99dd569a966aa70fa12459f2b6ea4cf9 Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 28 Nov 2023 12:57:13 +0300 Subject: [PATCH 088/228] Run solvers only on supported theories for MaxSMT tests --- .github/workflows/maxsmt-tests-matrix.json | 46 ++++++++++----------- .github/workflows/run-long-maxsmt-tests.yml | 22 +++++++--- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 89c460e88..e64135a0b 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,24 +1,22 @@ -{ - "logics": [ - { - "NAME": "QF_ABVFP-light", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_ABVFP-light2", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_AUFBVLIA-light", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_BV-light", - "TEST_DATA_REVISION": "0.0.0" - }, - { - "NAME": "QF_UFLRA-light", - "TEST_DATA_REVISION": "0.0.0" - } - ] -} +[ + { + "NAME": "QF_ABVFP-light", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_ABVFP-light2", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_AUFBVLIA-light", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_BV-light", + "TEST_DATA_REVISION": "0.0.0" + }, + { + "NAME": "QF_UFLRA-light", + "TEST_DATA_REVISION": "0.0.0" + } +] diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 3bef24beb..3a01fdc3a 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -6,7 +6,7 @@ on: solver: type: choice description: Chosen MaxSMT solver configuration - options: + options: - KPMRes - KPrimalDualMaxRes smtSolver: @@ -16,7 +16,7 @@ on: - Z3 - Bitwuzla - Cvc5 - - Yices + - Yices2 commitSHA: description: Commit SHA required: false @@ -40,9 +40,21 @@ jobs: uses: actions/checkout@v3 - id: set-matrix - name: Read and print config from maxsmt-tests-matrix.json + name: Create and print MaxSMT logics matrix + + # Exclude LIA, LRA tests for Bitwuzla solver + # Exclude FP tests for Yices2 solver run: | - MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json)) + if ${{ github.event.inputs.smtSolver == 'Bitwuzla' }} + then + MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("LIA") or contains("LRA") | not) ] }')) + elif ${{ github.event.inputs.smtSolver == 'Yices2' }} + then + MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("FP") | not) ] }')) + else + MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] ] }')) + fi + echo "maxsmt-tests-matrix=$MAXSMT_TESTS" >> $GITHUB_OUTPUT echo $MAXSMT_TESTS @@ -53,7 +65,7 @@ jobs: strategy: matrix: ${{ fromJSON(needs.prepare-matrix.outputs.logics-matrix) }} - name: Run ${{ matrix.NAME }} tests + name: Run ${{ matrix.logics.NAME }} tests steps: - name: Checkout repository From 670fa11965d7ff3e1e8555137168f99f95b0be27 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 29 Nov 2023 17:16:36 +0300 Subject: [PATCH 089/228] Add QF_UF MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index e64135a0b..38746db89 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -15,6 +15,10 @@ "NAME": "QF_BV-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_UF-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UFLRA-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 8639cb5bc..9bb2d1531 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -37,6 +37,7 @@ val maxSmtBenchmarks = listOfNotNull( "QF_ABVFP-light2", // 651K "QF_AUFBVLIA-light", // 3.8M "QF_BV-light", // 8.96M + "QF_UF-light", // 3.79M "QF_UFLRA-light", // 2.27M ) From 0232cac0b5eaa11359685bcda7cf25d94ae80aaf Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 29 Nov 2023 17:19:05 +0300 Subject: [PATCH 090/228] Generalize solvers configurations in tests using out --- .../solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 12 ++++++------ .../solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt | 3 +-- .../test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt | 3 +-- .../test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt | 3 +-- .../test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt | 3 +-- .../test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt | 3 +-- .../io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt | 2 +- .../io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt | 2 +- .../io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt | 2 +- .../solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 2 +- .../io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 6 +++--- .../kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt | 4 ++-- .../solver/maxsmt/KPrimalDualMaxResSolver2Test.kt | 4 ++-- .../solver/maxsmt/KPrimalDualMaxResSolver3Test.kt | 4 ++-- .../solver/maxsmt/KPrimalDualMaxResSolver4Test.kt | 4 ++-- .../solver/maxsmt/KPrimalDualMaxResSolverTest.kt | 4 ++-- 16 files changed, 28 insertions(+), 33 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 60e8ad8a1..38757df5c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -50,19 +50,19 @@ import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { - protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { return when (solver) { - Z3 -> KZ3Solver(this) as KSolver - BITWUZLA -> KBitwuzlaSolver(this) as KSolver - CVC5 -> KCvc5Solver(this) as KSolver - YICES -> KYicesSolver(this) as KSolver + Z3 -> KZ3Solver(this) + BITWUZLA -> KBitwuzlaSolver(this) + CVC5 -> KCvc5Solver(this) + YICES -> KYicesSolver(this) } } abstract fun getSolver(solver: Solver): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSMTSolver: KMaxSMTSolver + private lateinit var maxSMTSolver: KMaxSMTSolver private val logger = KotlinLogging.logger {} private fun initSolver(solver: Solver) { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt index b5439fe87..e281ce941 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver @@ -8,7 +7,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { - val smtSolver: KSolver = getSmtSolver(solver) + val smtSolver = getSmtSolver(solver) return KPMResSolver(this, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt index 96d77d5bb..7257c827c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver @@ -9,7 +8,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { - val smtSolver: KSolver = getSmtSolver(solver) + val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt index 020ab2ad2..8bc2c837a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver @@ -9,7 +8,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { - val smtSolver: KSolver = getSmtSolver(solver) + val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = false)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt index 2df715435..581b34f04 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver @@ -9,7 +8,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { - val smtSolver: KSolver = getSmtSolver(solver) + val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = false)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt index c89a38671..e4026efd3 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.smt -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver @@ -9,7 +8,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { - val smtSolver: KSolver = getSmtSolver(solver) + val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext()) } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt index 8b372497d..c955a8fc8 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt @@ -8,7 +8,7 @@ import io.ksmt.solver.maxsmt.utils.CoreUtils abstract class KMaxResSolver( private val ctx: KContext, - private val solver: KSolver, + private val solver: KSolver, ) : KMaxSMTSolver(ctx, solver) where T : KSolverConfiguration { protected fun removeCoreAssumptions(core: List, assumptions: MutableList) { assumptions.removeAll(core) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index e3aca0ef1..689a46547 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -15,7 +15,7 @@ import kotlin.time.Duration abstract class KMaxSMTSolver( private val ctx: KContext, - private val solver: KSolver, + private val solver: KSolver, ) : KSolver where T : KSolverConfiguration { private val scopeManager = MaxSMTScopeManager() diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 468e65640..5e16ec1d4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -21,7 +21,7 @@ import io.ksmt.utils.mkConst import kotlin.time.Duration import kotlin.time.TimeSource.Monotonic.markNow -class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : +class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : KMaxResSolver(ctx, solver) { private var currentMaxSMTResult: Triple>, KModel?> = Triple(null, listOf(), null) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index df3238ccf..d281c2f23 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -25,7 +25,7 @@ import kotlin.time.TimeSource.Monotonic.markNow class KPrimalDualMaxResSolver( private val ctx: KContext, - private val solver: KSolver, + private val solver: KSolver, private val maxSmtCtx: KMaxSMTContext, ) : KMaxResSolver(ctx, solver) { diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index 0364938d7..cdd516eca 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -1,11 +1,11 @@ package io.ksmt.solver.maxsmt import io.ksmt.KContext +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNSAT import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver -import io.ksmt.solver.z3.KZ3SolverConfiguration import io.ksmt.utils.getValue import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals @@ -14,10 +14,10 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test abstract class KMaxSMTSolverTest { - abstract fun getSolver(): KMaxSMTSolver + abstract fun getSolver(): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSMTSolver: KMaxSMTSolver + private lateinit var maxSMTSolver: KMaxSMTSolver @BeforeEach fun initSolver() { diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt index 0c228a264..b4b4b0244 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPMResSolverTest : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt index a6dbc80ee..99fc3f1e5 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPrimalDualMaxResSolver2Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt index daa908bc8..7a89d9202 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPrimalDualMaxResSolver3Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(minimizeCores = false)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt index 7c70cb50e..f1b0405c5 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPrimalDualMaxResSolver4Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(getMultipleCores = false)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt index 1162e2d90..0e9674d7d 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPrimalDualMaxResSolverTest : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) } From 40b00f0e22c015e8bca530b5e8fa4e6af81f17ee Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 29 Nov 2023 17:41:00 +0300 Subject: [PATCH 091/228] Add QF_AUFBV MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 38746db89..276aa49b1 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -7,6 +7,10 @@ "NAME": "QF_ABVFP-light2", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_AUFBV", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_AUFBVLIA-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 9bb2d1531..72667df70 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { val maxSmtBenchmarks = listOfNotNull( "QF_ABVFP-light", // 354K "QF_ABVFP-light2", // 651K + "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M "QF_BV-light", // 8.96M "QF_UF-light", // 3.79M From a34677dc137de1d13e3f393dc142c35a23fa9951 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 29 Nov 2023 18:01:51 +0300 Subject: [PATCH 092/228] Generalize solver in MaxSAT tests --- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 6 +++--- .../ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt | 4 ++-- .../maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index a84ab741f..dbed0be14 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -1,13 +1,13 @@ package io.ksmt.solver.maxsmt.test.sat import io.ksmt.KContext +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.maxsmt.constraints.HardConstraint import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSATTest -import io.ksmt.solver.z3.KZ3SolverConfiguration import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest @@ -18,10 +18,10 @@ import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { - abstract fun getSolver(): KMaxSMTSolver + abstract fun getSolver(): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSMTSolver + private lateinit var maxSATSolver: KMaxSMTSolver @BeforeEach fun initSolver() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt index d7e57e5d0..626b7ce09 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt.test.sat +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPMResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt index 014e99eea..d3bd8373a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -1,13 +1,13 @@ package io.ksmt.solver.maxsmt.test.sat +import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver -import io.ksmt.solver.z3.KZ3SolverConfiguration class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) } From 27f106d31e932c9a4847edaac2331c8020f51b56 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 29 Nov 2023 19:29:49 +0300 Subject: [PATCH 093/228] Rename solver to Yices in workflow --- .github/workflows/run-long-maxsmt-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 3a01fdc3a..e564d6172 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -16,7 +16,7 @@ on: - Z3 - Bitwuzla - Cvc5 - - Yices2 + - Yices commitSHA: description: Commit SHA required: false @@ -43,12 +43,12 @@ jobs: name: Create and print MaxSMT logics matrix # Exclude LIA, LRA tests for Bitwuzla solver - # Exclude FP tests for Yices2 solver + # Exclude FP tests for Yices solver run: | if ${{ github.event.inputs.smtSolver == 'Bitwuzla' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("LIA") or contains("LRA") | not) ] }')) - elif ${{ github.event.inputs.smtSolver == 'Yices2' }} + elif ${{ github.event.inputs.smtSolver == 'Yices' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("FP") | not) ] }')) else From 78d907320d9c2bcc4ec15bd6a67357651b30dc11 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 30 Nov 2023 11:00:13 +0300 Subject: [PATCH 094/228] Update QF_ABVFP MaxSMT benchmark information --- .github/workflows/maxsmt-tests-matrix.json | 4 ---- ksmt-maxsmt-test/build.gradle.kts | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 276aa49b1..f33134ef5 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -3,10 +3,6 @@ "NAME": "QF_ABVFP-light", "TEST_DATA_REVISION": "0.0.0" }, - { - "NAME": "QF_ABVFP-light2", - "TEST_DATA_REVISION": "0.0.0" - }, { "NAME": "QF_AUFBV", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 72667df70..d10b441ef 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -33,8 +33,7 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABVFP-light", // 354K - "QF_ABVFP-light2", // 651K + "QF_ABVFP-light", // 650K "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M "QF_BV-light", // 8.96M From 69461f76dce8456c42b97aef8e0bbd97aadfed8d Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 10:56:35 +0300 Subject: [PATCH 095/228] Collect and merge MaxSMT test run statistics --- .github/workflows/run-long-maxsmt-tests.yml | 42 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index e564d6172..54a3e900e 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -109,9 +109,49 @@ jobs: --no-daemon --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMT${{ github.event.inputs.smtSolver }}Test" - - name: Archive ${{ matrix.logics.NAME }} test report + - name: Add logic name to test run statistics + # Add logic name to JSON with test statistics + run: | + cat <<< $(jq -S '. += { "NAME": "${{ matrix.logics.NAME }}" }' ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json) > ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json + + - name: Upload ${{ matrix.logics.NAME }} test statistics + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics + path: ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json + + - name: Upload ${{ matrix.logics.NAME }} test report if: ${{ always() }} uses: actions/upload-artifact@v3 with: name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* + + merge-maxsmt-statistics: + needs: run-maxsmt-tests + runs-on: ubuntu-latest + steps: + - name: Download test statistics + uses: actions/download-artifact@v3 + id: download + with: + path: downloads + + - name: Add merged test run statistics path to environment variables + run: | + export MERGED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics.json + echo "MERGED_STATISTICS_PATH=$MERGED_STATISTICS_PATH" >> $GITHUB_ENV + + - name: Merge test run statistics + # Merge all test run statistic files into single + run: | + jq -s '.' ${{steps.download.outputs.download-path}}/*_test_statistics/*.json > ${{ env.MERGED_STATISTICS_PATH }}_temp + cat ${{ env.MERGED_STATISTICS_PATH }}_temp | jq '{ "logics": . }' > ${{ env.MERGED_STATISTICS_PATH }} + + - name: Upload test run statistics + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics + path: ${{ env.MERGED_STATISTICS_PATH }} From ec7103772000d0fd772900282bab9b0a246eeaec Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 11:02:08 +0300 Subject: [PATCH 096/228] Add SMT solver name to statistics --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 1 + .../ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 38757df5c..0773caba4 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -141,6 +141,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds, true) } val testStatistics = MaxSMTTestStatistics(maxSMTSolver.collectMaxSMTStatistics()) + testStatistics.smtSolver = solver testStatistics.name = name testStatistics.maxSMTCallElapsedTimeMs = elapsedTime diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index 557251918..6576423a3 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -1,8 +1,11 @@ package io.ksmt.solver.maxsmt.test.statistics import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics +import io.ksmt.solver.maxsmt.test.utils.Solver +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 internal data class MaxSMTTestStatistics(val maxSMTCallStatistics: KMaxSMTStatistics) { + var smtSolver: Solver = Z3 var name = "" var maxSMTCallElapsedTimeMs: Long = 0 var passed = false From 5d042a438d1e7aeca32e828a603780acbc98566d Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 11:09:13 +0300 Subject: [PATCH 097/228] Add QF_FP MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index f33134ef5..12f5ce3a8 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -15,6 +15,10 @@ "NAME": "QF_BV-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_FP", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UF-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index d10b441ef..1b4336363 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -37,6 +37,7 @@ val maxSmtBenchmarks = listOfNotNull( "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M "QF_BV-light", // 8.96M + "QF_FP", // 250K "QF_UF-light", // 3.79M "QF_UFLRA-light", // 2.27M ) From 3bab4905e71f803d1e2acb6c6f74c8029173a745 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 11:19:45 +0300 Subject: [PATCH 098/228] Add comments to MaxSMT benchmark workflow --- .github/workflows/run-long-maxsmt-tests.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 54a3e900e..6a8d4a9ae 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -41,7 +41,6 @@ jobs: - id: set-matrix name: Create and print MaxSMT logics matrix - # Exclude LIA, LRA tests for Bitwuzla solver # Exclude FP tests for Yices solver run: | @@ -110,7 +109,7 @@ jobs: --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMT${{ github.event.inputs.smtSolver }}Test" - name: Add logic name to test run statistics - # Add logic name to JSON with test statistics + # Create an object with the field 'logics' from the JSON list run: | cat <<< $(jq -S '. += { "NAME": "${{ matrix.logics.NAME }}" }' ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json) > ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json @@ -144,7 +143,11 @@ jobs: echo "MERGED_STATISTICS_PATH=$MERGED_STATISTICS_PATH" >> $GITHUB_ENV - name: Merge test run statistics - # Merge all test run statistic files into single + # 1. Merge all test run statistics files into a single file: + # - merge all test run statistics files (${{steps.download.outputs.download-path}}/*_test_statistics/*.json) for different theories + # - save merged files into a single JSON file (${{ env.MERGED_STATISTICS_PATH }}_temp) + # + # 2. Create an object with the field 'logics' from the JSON list run: | jq -s '.' ${{steps.download.outputs.download-path}}/*_test_statistics/*.json > ${{ env.MERGED_STATISTICS_PATH }}_temp cat ${{ env.MERGED_STATISTICS_PATH }}_temp | jq '{ "logics": . }' > ${{ env.MERGED_STATISTICS_PATH }} From 4dbe40fa7059d2c2e7b286d15214836f345a1f04 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 11:52:21 +0300 Subject: [PATCH 099/228] Update to always add logic name to statistics --- .github/workflows/run-long-maxsmt-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 6a8d4a9ae..975583079 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -109,6 +109,7 @@ jobs: --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMT${{ github.event.inputs.smtSolver }}Test" - name: Add logic name to test run statistics + if: ${{ always() }} # Create an object with the field 'logics' from the JSON list run: | cat <<< $(jq -S '. += { "NAME": "${{ matrix.logics.NAME }}" }' ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json) > ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json From 52123c25c9daefd11ea754e8c1d8abc53c4651d8 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 12:29:19 +0300 Subject: [PATCH 100/228] Move SAT soft constraints sum calculation in tests --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 0773caba4..952c9a96c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -148,11 +148,11 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Test name: [$name]" } logger.info { "Elapsed time (MaxSMT call): [${elapsedTime}ms]" } - val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } - try { assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + + val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } if (maxSmtTestInfo.satSoftConstraintsWeightsSum != satSoftConstraintsWeightsSum.toULong()) { testStatistics.checkedSoftConstraintsSumIsWrong = true } From c4c0f75d7464cc346211a64a81b4ffd6015efb2b Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 15:18:11 +0300 Subject: [PATCH 101/228] Collect statistics from ignored, failed when parsing file/convert expressions test --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 26 ++++++++++++++----- .../test/statistics/MaxSMTTestStatistics.kt | 9 ++++--- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 952c9a96c..d1e482c74 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -110,11 +110,24 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } lateinit var ksmtAssertions: List> + val testStatistics = MaxSMTTestStatistics(name, solver) - testWorkers.withWorker(ctx) { worker -> - val assertions = worker.parseFile(samplePath) - val convertedAssertions = worker.convertAssertions(assertions) - ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + try { + testWorkers.withWorker(ctx) { worker -> + val assertions = worker.parseFile(samplePath) + val convertedAssertions = worker.convertAssertions(assertions) + ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + } + } catch (ex: IgnoreTestException) { + testStatistics.ignoredTest = true + testStatistics.exceptionMessage = ex.message.toString() + throw ex + } catch (ex: Exception) { + testStatistics.failedOnParsingOrConvertingExpressions = true + testStatistics.exceptionMessage = ex.message.toString() + throw ex + } finally { + jsonHelper.appendTestStatisticsToFile(testStatistics) } val maxSmtTestPath = File(samplePath.toString().removeSuffix(extension) + "maxsmt").toPath() @@ -140,9 +153,8 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val elapsedTime = measureTimeMillis { maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds, true) } - val testStatistics = MaxSMTTestStatistics(maxSMTSolver.collectMaxSMTStatistics()) - testStatistics.smtSolver = solver - testStatistics.name = name + + testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.maxSMTCallElapsedTimeMs = elapsedTime logger.info { "Test name: [$name]" } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index 6576423a3..cd60762b7 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -2,12 +2,13 @@ package io.ksmt.solver.maxsmt.test.statistics import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.test.utils.Solver -import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 -internal data class MaxSMTTestStatistics(val maxSMTCallStatistics: KMaxSMTStatistics) { - var smtSolver: Solver = Z3 - var name = "" +internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver) { + var maxSMTCallStatistics: KMaxSMTStatistics? = null var maxSMTCallElapsedTimeMs: Long = 0 var passed = false + var ignoredTest = false + var failedOnParsingOrConvertingExpressions = false + var exceptionMessage: String? = null var checkedSoftConstraintsSumIsWrong = false } From 1c3081c46c6fdfbc3d62c3fa686535c815e6802a Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 15:48:11 +0300 Subject: [PATCH 102/228] Add QF_UFBV MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 12f5ce3a8..5cfb69627 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -23,6 +23,10 @@ "NAME": "QF_UF-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_UFBV-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UFLRA-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 1b4336363..861eb97fe 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -39,6 +39,7 @@ val maxSmtBenchmarks = listOfNotNull( "QF_BV-light", // 8.96M "QF_FP", // 250K "QF_UF-light", // 3.79M + "QF_UFBV-light", // 6.26M "QF_UFLRA-light", // 2.27M ) From 21f58ec48beead5f12b497b1e0d29104ab07b64f Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 15:55:00 +0300 Subject: [PATCH 103/228] Remove elapsed time from MaxSMTTestStatistics * elapsed time is stored in KMaxSMTCallStatistics --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 1 - .../ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index d1e482c74..8ec094b45 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -155,7 +155,6 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() - testStatistics.maxSMTCallElapsedTimeMs = elapsedTime logger.info { "Test name: [$name]" } logger.info { "Elapsed time (MaxSMT call): [${elapsedTime}ms]" } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index cd60762b7..af63c2091 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -5,7 +5,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver) { var maxSMTCallStatistics: KMaxSMTStatistics? = null - var maxSMTCallElapsedTimeMs: Long = 0 var passed = false var ignoredTest = false var failedOnParsingOrConvertingExpressions = false From 9c856dbafcdee20e4e7d0a7b1315cc95d34bfab8 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 16:23:34 +0300 Subject: [PATCH 104/228] Run MaxSMT tests on Ubuntu-20.04 --- .github/workflows/run-long-maxsat-tests.yml | 2 +- .github/workflows/run-long-maxsmt-tests.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index f0e50826f..de3f73674 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-20.04, windows-latest] fail-fast: false steps: diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 975583079..fa051dcbb 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -29,7 +29,7 @@ env: jobs: prepare-matrix: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 outputs: logics-matrix: ${{ steps.set-matrix.outputs.maxsmt-tests-matrix }} steps: @@ -60,7 +60,7 @@ jobs: run-maxsmt-tests: needs: prepare-matrix continue-on-error: true - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: matrix: ${{ fromJSON(needs.prepare-matrix.outputs.logics-matrix) }} @@ -130,7 +130,7 @@ jobs: merge-maxsmt-statistics: needs: run-maxsmt-tests - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - name: Download test statistics uses: actions/download-artifact@v3 From b07a245dc09fe9d62278a60e60e15298564957d9 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 19:08:46 +0300 Subject: [PATCH 105/228] Add script analyzing statistics --- scripts/analyze_maxsmt_statistics.py | 204 +++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 scripts/analyze_maxsmt_statistics.py diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py new file mode 100644 index 000000000..181143016 --- /dev/null +++ b/scripts/analyze_maxsmt_statistics.py @@ -0,0 +1,204 @@ +import json +import operator +from functools import reduce +from pathlib import Path +from sys import argv + + +def main(args): + analyze_maxsmt_statistics(Path(args[1]), Path(args[2])) + + +def analyze_maxsmt_statistics(stat_file, analyzed_stat_file_to_save): + if not stat_file.exists() or not stat_file.is_file(): + raise FileExistsError(f"File with statistics [{str(stat_file)}] does not exist") + + with open(stat_file, "r") as f: + data = f.read() + + stat = json.loads(data) + logics_size = len(stat["logics"]) + logics_statistics = [] + + def obj_dict(obj): + return obj.__dict__ + + for i in range(0, logics_size): + logic_stat = create_logic_statistics((stat["logics"])[i]) + logics_statistics.append(logic_stat) + logic_stat_str = json.dumps(logic_stat, default=obj_dict, indent=2, separators=(',', ': ')) + print(logic_stat_str + "\n") + + with open(analyzed_stat_file_to_save, "a") as f: + logics_stat_str = json.dumps(logics_statistics, default=obj_dict, indent=2, separators=(',', ': ')) + f.write(logics_stat_str) + + +def create_tests_size_statistics(tests): + tests_size = len(tests) + passed_tests_percent = len(list(filter(lambda x: x["passed"], tests))) / tests_size * 100 + tests_executed_maxsmt_size = len(list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests))) + failed_or_ignored_tests_size = len(list(filter(lambda x: not x["passed"], tests))) + failed_tests_wrong_soft_constr_sum_size = len(list(filter(lambda x: x["checkedSoftConstraintsSumIsWrong"], tests))) + ignored_tests_size = len(list(filter(lambda x: x["ignoredTest"], tests))) + failed_on_parsing_or_converting_expressions_size = len( + list(filter(lambda x: x["failedOnParsingOrConvertingExpressions"], tests))) + + def get_unique_exception_messages(collection): + # Set is converted to list in order to dump statistics to JSON (otherwise, the script fails + # with such error: 'AttributeError: 'set' object has no attribute '__dict__'.'). + return list( + set(map(lambda x: None if x.get("exceptionMessage") is None else x["exceptionMessage"], collection))) + + failed_on_parsing_or_converting_expressions_exception_messages = get_unique_exception_messages( + list(filter(lambda x: x["failedOnParsingOrConvertingExpressions"], tests))) + + return TestsSizeStatistics(tests_size, passed_tests_percent, tests_executed_maxsmt_size, + failed_or_ignored_tests_size, ignored_tests_size, + failed_on_parsing_or_converting_expressions_size, + failed_on_parsing_or_converting_expressions_exception_messages, + failed_tests_wrong_soft_constr_sum_size) + + +def create_tests_queries_to_solver_statistics(tests): + def max_smt_stat(test): + return test["maxSMTCallStatistics"] + + def queries_to_solver_number(test): + if isinstance(test, int): + return test + return max_smt_stat(test)["queriesToSolverNumber"] + + def time_in_solver_queries_ms(test): + if isinstance(test, int): + return test + return max_smt_stat(test)["timeInSolverQueriesMs"] + + def elapsed_time_ms(test): + return max_smt_stat(test)["elapsedTimeMs"] + + tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) + tests_executed_maxsmt_size = len(tests_executed_maxsmt) + + avg_queries_to_solver_number = 0 if tests_executed_maxsmt_size == 0 else reduce( + lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), + tests_executed_maxsmt, 0) / tests_executed_maxsmt_size + + def is_zero(value): + return abs(value) < 0.00000001 + + avg_time_per_solver_queries_percent_list = map( + lambda x: time_in_solver_queries_ms(x) / elapsed_time_ms(x) * 100 if not is_zero( + elapsed_time_ms(x)) else elapsed_time_ms(x), + tests_executed_maxsmt) + avg_time_per_solver_queries_percent = \ + 0 if tests_executed_maxsmt_size == 0 else reduce(operator.add, avg_time_per_solver_queries_percent_list, + 0) / tests_executed_maxsmt_size + + failed_tests = list(filter(lambda x: not x["passed"], tests_executed_maxsmt)) + avg_failed_test_queries_to_solver_number = 0 if tests_executed_maxsmt_size == 0 else reduce( + lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), failed_tests, + 0) / tests_executed_maxsmt_size + + return TestsQueriesToSolverStatistics(avg_queries_to_solver_number, avg_time_per_solver_queries_percent, + avg_failed_test_queries_to_solver_number) + + +def create_tests_elapsed_time_statistics(tests): + def max_smt_stat(test): + return test["maxSMTCallStatistics"] + + def elapsed_time_ms(test): + if isinstance(test, int): + return test + return max_smt_stat(test)["elapsedTimeMs"] + + tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) + tests_executed_maxsmt_size = len(tests_executed_maxsmt) + + avg_elapsed_time_ms = 0 if tests_executed_maxsmt_size == 0 else reduce( + lambda x, y: elapsed_time_ms(x) + elapsed_time_ms(y), tests_executed_maxsmt, + 0) / tests_executed_maxsmt_size + + passed_tests = list(filter(lambda x: x["passed"], tests_executed_maxsmt)) + avg_elapsed_passed_tests_time_ms = 0 if tests_executed_maxsmt_size == 0 else reduce( + lambda x, y: elapsed_time_ms(x) + elapsed_time_ms(y), passed_tests, + 0) / tests_executed_maxsmt_size + + failed_tests = list(filter(lambda x: not x["passed"], tests_executed_maxsmt)) + avg_elapsed_failed_tests_time_ms = 0 if tests_executed_maxsmt_size == 0 else reduce( + lambda x, y: elapsed_time_ms(x) + elapsed_time_ms(y), failed_tests, + 0) / tests_executed_maxsmt_size + + return TestsElapsedTimeStatistics(avg_elapsed_time_ms, avg_elapsed_passed_tests_time_ms, + avg_elapsed_failed_tests_time_ms) + + +class MaxSMTContext: + def __int__(self, strategy, prefer_large_weight_constraints_for_cores, minimize_cores, get_multiple_cores): + self.strategy = strategy + self.prefer_large_weight_constraints_for_cores = prefer_large_weight_constraints_for_cores + self.minimize_cores = minimize_cores + self.get_multiple_cores = get_multiple_cores + + +class TestsSizeStatistics: + def __init__(self, tests_size, passed_tests_percent, tests_executed_maxsmt_size, failed_tests_size, + ignored_tests_size, + failed_on_parsing_or_converting_expressions_size, + failed_on_parsing_or_converting_expressions_exception_messages, + failed_tests_wrong_soft_constr_sum_size): + self.tests_size = tests_size + self.passed_tests_percent = passed_tests_percent + self.tests_executed_maxsmt_size = tests_executed_maxsmt_size + self.failed_tests_size = failed_tests_size + self.ignored_tests_size = ignored_tests_size + self.failed_on_parsing_or_converting_expressions_size = failed_on_parsing_or_converting_expressions_size + self.failed_on_parsing_or_converting_expressions_exception_messages = ( + failed_on_parsing_or_converting_expressions_exception_messages) + self.failed_tests_wrong_soft_constr_sum_size = failed_tests_wrong_soft_constr_sum_size + + +class TestsQueriesToSolverStatistics: + def __init__(self, avg_queries_to_solver_number, avg_time_per_solver_queries_percent, + avg_failed_test_queries_to_solver_number): + self.avg_queries_to_solver_number = avg_queries_to_solver_number + self.avg_time_per_solver_queries_percent = avg_time_per_solver_queries_percent + self.avg_failed_test_queries_to_solver_number = avg_failed_test_queries_to_solver_number + + +class TestsElapsedTimeStatistics: + def __init__(self, avg_elapsed_time_ms, avg_elapsed_passed_tests_time_ms, + avg_elapsed_failed_tests_time_ms): + self.avg_elapsed_time_ms = avg_elapsed_time_ms + self.avg_elapsed_passed_tests_time_ms = avg_elapsed_passed_tests_time_ms + self.avg_elapsed_failed_tests_time_ms = avg_elapsed_failed_tests_time_ms + + +class LogicTestsStatistics: + def __init__(self, smt_solver, name, timeout_ms, max_smt_ctx, tests_size_stat: TestsSizeStatistics, + tests_queries_to_solver_stat: TestsQueriesToSolverStatistics, + tests_elapsed_time_stat: TestsElapsedTimeStatistics): + self.smt_solver = smt_solver + self.name = name + self.timeout_ms = timeout_ms + self.max_smt_ctx = max_smt_ctx + self.tests_size_stat = tests_size_stat + self.tests_queries_to_solver_stat = tests_queries_to_solver_stat + self.tests_elapsed_time_stat = tests_elapsed_time_stat + + +def create_logic_statistics(logic): + tests = logic["TESTS"] + first_test = tests[0] + test_executed_maxsmt = filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests) + first_max_smt_call_stat = None if test_executed_maxsmt is None else (list(test_executed_maxsmt)[0])[ + "maxSMTCallStatistics"] + return LogicTestsStatistics(first_test["smtSolver"], logic["NAME"], first_max_smt_call_stat["timeoutMs"], + first_max_smt_call_stat["maxSmtCtx"], create_tests_size_statistics(tests), + create_tests_queries_to_solver_statistics(tests), + create_tests_elapsed_time_statistics(tests)) + + +if __name__ == "__main__": + main(argv) From da3d8541fb6e5c39c29e8da5a838c77ddd64a4ed Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 19:26:26 +0300 Subject: [PATCH 106/228] Add statistics analysis to MaxSMT benchmark workflow --- .github/workflows/run-long-maxsmt-tests.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index fa051dcbb..4403e85db 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -128,7 +128,7 @@ jobs: name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* - merge-maxsmt-statistics: + merge-and-analyze-maxsmt-statistics: needs: run-maxsmt-tests runs-on: ubuntu-20.04 steps: @@ -138,11 +138,14 @@ jobs: with: path: downloads - - name: Add merged test run statistics path to environment variables + - name: Add merged test run statistics and analyzed statistics paths to environment variables run: | export MERGED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics.json echo "MERGED_STATISTICS_PATH=$MERGED_STATISTICS_PATH" >> $GITHUB_ENV + export ANALYZED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_analyzed_test_statistics.json + echo "ANALYZED_STATISTICS_PATH=$ANALYZED_STATISTICS_PATH" >> $GITHUB_ENV + - name: Merge test run statistics # 1. Merge all test run statistics files into a single file: # - merge all test run statistics files (${{steps.download.outputs.download-path}}/*_test_statistics/*.json) for different theories @@ -153,9 +156,17 @@ jobs: jq -s '.' ${{steps.download.outputs.download-path}}/*_test_statistics/*.json > ${{ env.MERGED_STATISTICS_PATH }}_temp cat ${{ env.MERGED_STATISTICS_PATH }}_temp | jq '{ "logics": . }' > ${{ env.MERGED_STATISTICS_PATH }} + - name: Analyze test run statistics + run: python ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} + - name: Upload test run statistics - if: ${{ always() }} uses: actions/upload-artifact@v3 with: name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics path: ${{ env.MERGED_STATISTICS_PATH }} + + - name: Upload analyzed test run statistics + uses: actions/upload-artifact@v3 + with: + name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_analyzed_test_statistics + path: ${{ env.ANALYZED_STATISTICS_PATH }} From 429eaa12b108760e7117ba98dcba8203779ddd1b Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 20:06:07 +0300 Subject: [PATCH 107/228] Move analyze MaxSMT test run statistics step --- .github/workflows/run-long-maxsmt-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 4403e85db..48e908856 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -156,15 +156,15 @@ jobs: jq -s '.' ${{steps.download.outputs.download-path}}/*_test_statistics/*.json > ${{ env.MERGED_STATISTICS_PATH }}_temp cat ${{ env.MERGED_STATISTICS_PATH }}_temp | jq '{ "logics": . }' > ${{ env.MERGED_STATISTICS_PATH }} - - name: Analyze test run statistics - run: python ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} - - name: Upload test run statistics uses: actions/upload-artifact@v3 with: name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics path: ${{ env.MERGED_STATISTICS_PATH }} + - name: Analyze test run statistics + run: python ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} + - name: Upload analyzed test run statistics uses: actions/upload-artifact@v3 with: From 094006561e7bed5023c41c55707df73759a13f53 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 22:56:39 +0300 Subject: [PATCH 108/228] Fix work with JSON in statistics analyzer --- scripts/analyze_maxsmt_statistics.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index 181143016..d9587cbe0 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -14,9 +14,8 @@ def analyze_maxsmt_statistics(stat_file, analyzed_stat_file_to_save): raise FileExistsError(f"File with statistics [{str(stat_file)}] does not exist") with open(stat_file, "r") as f: - data = f.read() + stat = json.load(f) - stat = json.loads(data) logics_size = len(stat["logics"]) logics_statistics = [] @@ -29,9 +28,8 @@ def obj_dict(obj): logic_stat_str = json.dumps(logic_stat, default=obj_dict, indent=2, separators=(',', ': ')) print(logic_stat_str + "\n") - with open(analyzed_stat_file_to_save, "a") as f: - logics_stat_str = json.dumps(logics_statistics, default=obj_dict, indent=2, separators=(',', ': ')) - f.write(logics_stat_str) + with open(analyzed_stat_file_to_save, "w") as f: + json.dump(logics_statistics, f, default=obj_dict, indent=2, separators=(',', ': ')) def create_tests_size_statistics(tests): From 2a694bf5eddf4c96d55475703c0a1cea43d0566b Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 1 Dec 2023 23:40:34 +0300 Subject: [PATCH 109/228] Set up encoding in JSON analyzer --- scripts/analyze_maxsmt_statistics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index d9587cbe0..f51de27a4 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -13,7 +13,7 @@ def analyze_maxsmt_statistics(stat_file, analyzed_stat_file_to_save): if not stat_file.exists() or not stat_file.is_file(): raise FileExistsError(f"File with statistics [{str(stat_file)}] does not exist") - with open(stat_file, "r") as f: + with open(stat_file, "r", encoding="utf-8") as f: stat = json.load(f) logics_size = len(stat["logics"]) @@ -28,7 +28,7 @@ def obj_dict(obj): logic_stat_str = json.dumps(logic_stat, default=obj_dict, indent=2, separators=(',', ': ')) print(logic_stat_str + "\n") - with open(analyzed_stat_file_to_save, "w") as f: + with open(analyzed_stat_file_to_save, "w", encoding="utf-8") as f: json.dump(logics_statistics, f, default=obj_dict, indent=2, separators=(',', ': ')) From 9744336bd9b7d0ce5584015593e8f5913c2b687c Mon Sep 17 00:00:00 2001 From: Victoria Date: Sat, 2 Dec 2023 19:59:18 +0300 Subject: [PATCH 110/228] Add repository checkout in MaxSMT benchmark workflow --- .github/workflows/run-long-maxsmt-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 48e908856..e24f3671d 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -132,6 +132,9 @@ jobs: needs: run-maxsmt-tests runs-on: ubuntu-20.04 steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Download test statistics uses: actions/download-artifact@v3 id: download From 0ddf3a10a8506e119286ffa2d78dea31d5909c2d Mon Sep 17 00:00:00 2001 From: Victoria Date: Sun, 3 Dec 2023 21:54:54 +0300 Subject: [PATCH 111/228] Generate statistics with random string suffix --- .../ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 4 ++-- .../kotlin/io/ksmt/solver/maxsmt/test/utils/StringUtil.kt | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/StringUtil.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 8ec094b45..74c63824f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -25,6 +25,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 +import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -42,7 +43,6 @@ import java.io.File import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.extension -import kotlin.random.Random import kotlin.system.measureTimeMillis import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -239,7 +239,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { File( "${ Paths.get("").toAbsolutePath() - }/src/test/resources/maxsmt-statistics-${Random.nextInt(0, 10000)}.json", + }/src/test/resources/maxsmt-statistics-${getRandomString(16)}.json", ), ) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/StringUtil.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/StringUtil.kt new file mode 100644 index 000000000..879645f5e --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/StringUtil.kt @@ -0,0 +1,8 @@ +package io.ksmt.solver.maxsmt.test.utils + +fun getRandomString(length: Int): String { + val charset = ('a'..'z') + ('0'..'9') + + return List(length) { charset.random() } + .joinToString("") +} From 1ae263965a871a5f0f56ff9dc1f155db176f88d0 Mon Sep 17 00:00:00 2001 From: Victoria Date: Sun, 3 Dec 2023 22:46:02 +0300 Subject: [PATCH 112/228] Fix a bug with core minimization --- .../io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index 29a658611..b2369b52c 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -55,10 +55,9 @@ internal class MinimalUnsatCore( val expr = unknown.removeLast() val notExpr = !expr - minimalUnsatCore.add(notExpr) val markCheckStart = markNow() - val status = solver.checkWithAssumptions(minimalUnsatCore + unknown, remainingTime) + val status = solver.checkWithAssumptions(minimalUnsatCore + notExpr + unknown, remainingTime) if (collectStatistics) { coreStatistics.queriesToSolverNumber++ coreStatistics.timeInSolverQueriesMs += (markNow() - markCheckStart).inWholeMilliseconds @@ -76,7 +75,7 @@ internal class MinimalUnsatCore( } } - return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + return CoreUtils.coreToSoftConstraints(minimalUnsatCore, assumptions) } /** From 764a8c505963c65b50d41cdb4bcf7b6b57b1da89 Mon Sep 17 00:00:00 2001 From: Victoria Date: Mon, 4 Dec 2023 12:12:19 +0300 Subject: [PATCH 113/228] Add lost path to MaxSMT statistics analyzer script --- .github/workflows/run-long-maxsmt-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index e24f3671d..365bd341c 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -166,7 +166,7 @@ jobs: path: ${{ env.MERGED_STATISTICS_PATH }} - name: Analyze test run statistics - run: python ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} + run: python ./scripts/analyze_maxsmt_statistics.py ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} - name: Upload analyzed test run statistics uses: actions/upload-artifact@v3 From bb3aa1948a4bb99f01bfd702302e7c99fcd08a2e Mon Sep 17 00:00:00 2001 From: Victoria Date: Mon, 4 Dec 2023 13:06:34 +0300 Subject: [PATCH 114/228] Do core minimization optional for KPDMRes --- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index d281c2f23..4c856089d 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -240,15 +240,21 @@ class KPrimalDualMaxResSolver( var status = UNSAT while (status == UNSAT) { - val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) - if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime)) { - return Pair(SAT, cores) // TODO: is this status Ok? - } + var unsatCore: List + + if (maxSmtCtx.minimizeCores) { + val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime)) { + return Pair(SAT, cores) // TODO: is this status Ok? + } - val minimalUnsatCore = minimizeCore(assumptions, minimizeCoreRemainingTime) - updateMinimalUnsatCoreModel(assumptions) + unsatCore = minimizeCore(assumptions, minimizeCoreRemainingTime) + updateMinimalUnsatCoreModel(assumptions) + } else { + unsatCore = CoreUtils.coreToSoftConstraints(solver.unsatCore(), assumptions) + } - if (minimalUnsatCore.isEmpty()) { + if (unsatCore.isEmpty()) { cores.clear() _lower = _upper return Pair(SAT, cores) @@ -257,12 +263,12 @@ class KPrimalDualMaxResSolver( // 1. remove all core literals from assumptions // 2. re-add literals of higher weight than min-weight. // 3. 'core' stores the core literals that are re-encoded as assumptions afterward - cores.add(WeightedCore(minimalUnsatCore.map { it.expression }, CoreUtils.getCoreWeight(minimalUnsatCore))) + cores.add(WeightedCore(unsatCore.map { it.expression }, CoreUtils.getCoreWeight(unsatCore))) - removeCoreAssumptions(minimalUnsatCore, assumptions) - splitCore(minimalUnsatCore, assumptions) + removeCoreAssumptions(unsatCore, assumptions) + splitCore(unsatCore, assumptions) - if (minimalUnsatCore.size >= _maxCoreSize) { + if (unsatCore.size >= _maxCoreSize) { return Pair(SAT, cores) } From c5f6ab451018e0000e2d228d7f7fa58f8146154e Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 6 Dec 2023 14:56:15 +0300 Subject: [PATCH 115/228] Use `==` instead of internEquals --- .../main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt | 4 ++-- .../test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt index b0046cc43..c9e355513 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt @@ -8,14 +8,14 @@ internal object CoreUtils { fun coreToSoftConstraints(core: List>, assumptions: List): List { val uniqueCoreElements = mutableListOf>() core.forEach { - if (!uniqueCoreElements.any { u -> u.internEquals(it) }) { + if (!uniqueCoreElements.any { u -> u == it }) { uniqueCoreElements.add(it) } } val softs = mutableListOf() for (soft in assumptions) { - if (uniqueCoreElements.any { it.internEquals(soft.expression) }) { + if (uniqueCoreElements.any { it == soft.expression }) { softs.add(soft) } } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index cdd516eca..81109f0bc 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -286,8 +286,8 @@ abstract class KMaxSMTSolverTest { assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) assertTrue(maxSMTResult.satSoftConstraints.size == 4) - assertTrue(maxSMTResult.satSoftConstraints.any { it.expression.internEquals(!a or !b) }) - assertTrue(maxSMTResult.satSoftConstraints.any { it.expression.internEquals(!a or !b or !b) }) + assertTrue(maxSMTResult.satSoftConstraints.any { it.expression == !a or !b }) + assertTrue(maxSMTResult.satSoftConstraints.any { it.expression == !a or !b or !b }) } @Test @@ -405,7 +405,7 @@ abstract class KMaxSMTSolverTest { for (constraint in constraintsToAssert) { assertTrue( satConstraints.any { - constraint.expression.internEquals(it.expression) && constraint.weight == it.weight + constraint.expression == it.expression && constraint.weight == it.weight }, ) } From e223e752ffc9bc8cf9827200e5ea54dd9491fca0 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 6 Dec 2023 15:00:05 +0300 Subject: [PATCH 116/228] Divide MaxSMT tests by primal and dual strategies --- ...est.kt => KDualMaxRes2SMTBenchmarkTest.kt} | 2 +- ...est.kt => KDualMaxRes3SMTBenchmarkTest.kt} | 2 +- ...est.kt => KDualMaxRes4SMTBenchmarkTest.kt} | 2 +- ...Test.kt => KDualMaxResSMTBenchmarkTest.kt} | 2 +- .../smt/KPrimalMaxRes2SMTBenchmarkTest.kt | 22 +++++++++++++++++++ .../smt/KPrimalMaxRes3SMTBenchmarkTest.kt | 15 +++++++++++++ .../smt/KPrimalMaxRes4SMTBenchmarkTest.kt | 19 ++++++++++++++++ .../test/smt/KPrimalMaxResSMTBenchmarkTest.kt | 15 +++++++++++++ 8 files changed, 75 insertions(+), 4 deletions(-) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/{KPrimalDualMaxRes2SMTBenchmarkTest.kt => KDualMaxRes2SMTBenchmarkTest.kt} (89%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/{KPrimalDualMaxRes3SMTBenchmarkTest.kt => KDualMaxRes3SMTBenchmarkTest.kt} (88%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/{KPrimalDualMaxRes4SMTBenchmarkTest.kt => KDualMaxRes4SMTBenchmarkTest.kt} (88%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/{KPrimalDualMaxResSMTBenchmarkTest.kt => KDualMaxResSMTBenchmarkTest.kt} (88%) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt similarity index 89% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt index 7257c827c..dafad3562 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt @@ -6,7 +6,7 @@ import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.utils.Solver -class KPrimalDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { +class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt similarity index 88% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt index 8bc2c837a..517264856 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt @@ -6,7 +6,7 @@ import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.utils.Solver -class KPrimalDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { +class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = false)) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt similarity index 88% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt index 581b34f04..6c02d060b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt @@ -6,7 +6,7 @@ import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.utils.Solver -class KPrimalDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { +class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = false)) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt similarity index 88% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt index e4026efd3..0f261efd2 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt @@ -6,7 +6,7 @@ import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.utils.Solver -class KPrimalDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { +class KDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext()) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt new file mode 100644 index 000000000..a13a70c63 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt @@ -0,0 +1,22 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver( + this, + smtSolver, + KMaxSMTContext( + strategy = PrimalMaxRes, + preferLargeWeightConstraintsForCores = false, + ), + ) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt new file mode 100644 index 000000000..48b91062a --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt @@ -0,0 +1,15 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = false)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt new file mode 100644 index 000000000..7e9674856 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt @@ -0,0 +1,19 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver( + this, + smtSolver, + KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = false), + ) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt new file mode 100644 index 000000000..89db2bcfc --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt @@ -0,0 +1,15 @@ +package io.ksmt.solver.maxsmt.test.smt + +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.utils.Solver + +class KPrimalMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes)) + } +} From d4d660f83f1868cfdcc9fe490685a6fddb82aeff Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 6 Dec 2023 15:08:30 +0300 Subject: [PATCH 117/228] Ignore MaxSMT statistics files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 43937d99f..77c2c0aed 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ ksmt-maxsmt-test/testData # ignore maxsmt test data ksmt-maxsmt-test/src/test/resources/maxSmtBenchmark + +# ignore maxsmt statistics +ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json + From ce4dd738f26cb9b77365883114d10c353c9cd4a3 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 6 Dec 2023 18:31:57 +0300 Subject: [PATCH 118/228] Divide MaxSMT workflows into PMRes and PrimalDualMaxRes --- .../run-long-maxsmt-tests-pdmres.yml | 98 +++++++++++++++++++ .../workflows/run-long-maxsmt-tests-pmres.yml | 31 ++++++ .github/workflows/run-long-maxsmt-tests.yml | 42 ++++---- 3 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/run-long-maxsmt-tests-pdmres.yml create mode 100644 .github/workflows/run-long-maxsmt-tests-pmres.yml diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml new file mode 100644 index 000000000..94424392d --- /dev/null +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -0,0 +1,98 @@ +name: (PrimalDualMaxRes solver) Build and run long MaxSMT tests + +on: + workflow_dispatch: + inputs: + solver: + type: choice + description: Chosen MaxSMT solver + options: + - KPrimalDualMaxRes + smtSolver: + type: choice + description: Chosen SMT solver + options: + - Z3 + - Bitwuzla + - Cvc5 + - Yices + strategy: + type: choice + description: Chosen MaxSMT solving strategy + options: + - Dual + - Primal + minimizeCores: + type: boolean + description: Core minimization option (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + required: true + default: true + preferLargeWeightCores: + type: boolean + description: Prefer large weight constraints for cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + required: true + default: true + getMultipleCores: + type: boolean + description: Get multiple cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + required: true + default: true + commitSHA: + description: Commit SHA (latest commit is default) + required: false + type: string + default: "" + +jobs: + verify-inputs: + runs-on: ubuntu-20.04 + steps: + - name: Verify inputs + # Check only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] is false + # (supported tests configurations) + run: | + if ([[ "${{ inputs.minimizeCores }}" == "false" ]] && [[ "${{ inputs.preferLargeWeightCores }}" == "false" ]]) || \ + ([[ "${{ inputs.minimizeCores }}" == "false" ]] && [[ "${{ inputs.getMultipleCores }}" == "false" ]]) || \ + ([[ "${{ inputs.preferLargeWeightCores }}" == "false" ]] && [[ "${{ inputs.getMultipleCores }}" == "false" ]]) + then + echo "Only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false" + exit 1 + fi + + construct-solver-name: + needs: verify-inputs + runs-on: ubuntu-20.04 + outputs: + solver: ${{ steps.set-output.outputs.solver }} + steps: + - name: Construct solver name + run: | + if [[ "${{ inputs.minimizeCores }}" == true ]] && \ + [[ "${{ inputs.preferLargeWeightCores }}" == true ]] && \ + [[ "${{ inputs.getMultipleCores }}" == true ]] + then + export OPTION_TO_VALUE="" + elif [[ "${{ inputs.preferLargeWeightCores }}" == false ]] + then + export OPTION_TO_VALUE="2" + elif [[ "${{ inputs.minimizeCores }}" == false ]] + then + export OPTION_TO_VALUE="3" + elif [[ "${{ inputs.getMultipleCores }}" == false ]] + then + export OPTION_TO_VALUE="4" + fi + + echo "OPTION_TO_VALUE=$OPTION_TO_VALUE" >> $GITHUB_ENV + + - name: Set solver name to output + id: set-output + run: echo "solver=K${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT + + run-long-maxsmt-tests: + needs: construct-solver-name + uses: ./.github/workflows/run-long-maxsmt-tests.yml + with: + solver: ${{ needs.construct-solver-name.outputs.solver }} + smtSolver: ${{ inputs.smtSolver }} + commitSHA: ${{ inputs.commitSHA }} diff --git a/.github/workflows/run-long-maxsmt-tests-pmres.yml b/.github/workflows/run-long-maxsmt-tests-pmres.yml new file mode 100644 index 000000000..202570ee8 --- /dev/null +++ b/.github/workflows/run-long-maxsmt-tests-pmres.yml @@ -0,0 +1,31 @@ +name: (PMRes solver) Build and run long MaxSMT tests + +on: + workflow_dispatch: + inputs: + solver: + type: choice + description: Chosen MaxSMT solver + options: + - KPMRes + smtSolver: + type: choice + description: Chosen SMT solver + options: + - Z3 + - Bitwuzla + - Cvc5 + - Yices + commitSHA: + description: Commit SHA (latest commit is default) + required: false + type: string + default: "" + +jobs: + run-long-maxsmt-tests: + uses: ./.github/workflows/run-long-maxsmt-tests.yml + with: + solver: ${{ inputs.solver }} + smtSolver: ${{ inputs.smtSolver }} + commitSHA: ${{ inputs.commitSHA }} diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 365bd341c..81ec9dd6a 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -1,24 +1,18 @@ name: Build and run long MaxSMT tests on: - workflow_dispatch: + workflow_call: inputs: solver: - type: choice + type: string description: Chosen MaxSMT solver configuration - options: - - KPMRes - - KPrimalDualMaxRes + required: true smtSolver: - type: choice + type: string description: Chosen SMT solver - options: - - Z3 - - Bitwuzla - - Cvc5 - - Yices + required: true commitSHA: - description: Commit SHA + description: Commit SHA (latest commit is default) required: false type: string default: "" @@ -44,10 +38,10 @@ jobs: # Exclude LIA, LRA tests for Bitwuzla solver # Exclude FP tests for Yices solver run: | - if ${{ github.event.inputs.smtSolver == 'Bitwuzla' }} + if ${{ inputs.smtSolver == 'Bitwuzla' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("LIA") or contains("LRA") | not) ] }')) - elif ${{ github.event.inputs.smtSolver == 'Yices' }} + elif ${{ inputs.smtSolver == 'Yices' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("FP") | not) ] }')) else @@ -70,12 +64,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Check out ${{ github.event.inputs.commitSHA }} commit - if: github.event.inputs.commitSHA != '' + - name: Check out ${{ inputs.commitSHA }} commit + if: inputs.commitSHA != '' run: | git config --global --add safe.directory ${GITHUB_WORKSPACE} git fetch - git checkout ${{ github.event.inputs.commitSHA }} + git checkout ${{ inputs.commitSHA }} - name: Set up JDK 1.8 uses: actions/setup-java@v3 @@ -106,7 +100,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.smt.${{ github.event.inputs.solver }}SMTBenchmarkTest.maxSMT${{ github.event.inputs.smtSolver }}Test" + --tests "io.ksmt.solver.maxsmt.test.smt.${{ inputs.solver }}SMTBenchmarkTest.maxSMT${{ inputs.smtSolver }}Test" - name: Add logic name to test run statistics if: ${{ always() }} @@ -118,14 +112,14 @@ jobs: if: ${{ always() }} uses: actions/upload-artifact@v3 with: - name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics + name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics path: ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json - name: Upload ${{ matrix.logics.NAME }} test report if: ${{ always() }} uses: actions/upload-artifact@v3 with: - name: ${{ matrix.logics.NAME }}_${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_report + name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* merge-and-analyze-maxsmt-statistics: @@ -143,10 +137,10 @@ jobs: - name: Add merged test run statistics and analyzed statistics paths to environment variables run: | - export MERGED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics.json + export MERGED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics.json echo "MERGED_STATISTICS_PATH=$MERGED_STATISTICS_PATH" >> $GITHUB_ENV - export ANALYZED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_analyzed_test_statistics.json + export ANALYZED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ inputs.solver }}_${{ inputs.smtSolver }}_analyzed_test_statistics.json echo "ANALYZED_STATISTICS_PATH=$ANALYZED_STATISTICS_PATH" >> $GITHUB_ENV - name: Merge test run statistics @@ -162,7 +156,7 @@ jobs: - name: Upload test run statistics uses: actions/upload-artifact@v3 with: - name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_test_statistics + name: ${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics path: ${{ env.MERGED_STATISTICS_PATH }} - name: Analyze test run statistics @@ -171,5 +165,5 @@ jobs: - name: Upload analyzed test run statistics uses: actions/upload-artifact@v3 with: - name: ${{ github.event.inputs.solver }}_${{ github.event.inputs.smtSolver }}_analyzed_test_statistics + name: ${{ inputs.solver }}_${{ inputs.smtSolver }}_analyzed_test_statistics path: ${{ env.ANALYZED_STATISTICS_PATH }} From 655335aef9ed72b25c714a83a4478ee713369f7f Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 6 Dec 2023 19:53:47 +0300 Subject: [PATCH 119/228] Do not union soft constraints with same expressions --- .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 27 ------------------- .../solver/maxsmt/solvers/KPMResSolver.kt | 2 -- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 1 - .../io/ksmt/solver/maxsmt/utils/CoreUtils.kt | 21 +++++---------- 4 files changed, 6 insertions(+), 45 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index 689a46547..c4b918fc2 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -66,33 +66,6 @@ abstract class KMaxSMTSolver( return maxSMTStatistics } - /** - * Union soft constraints with same expressions into a single soft constraint. - * - * The new soft constraint weight will be equal to the sum of old soft constraints weights. - */ - protected fun unionSoftConstraintsWithSameExpressions(formula: MutableList) { - val exprToRepetitionsMap = mutableMapOf, Int>() - - formula.forEach { - if (exprToRepetitionsMap.containsKey(it.expression)) { - exprToRepetitionsMap[it.expression] = exprToRepetitionsMap[it.expression]!! + 1 - } else { - exprToRepetitionsMap[it.expression] = 1 - } - } - - exprToRepetitionsMap.forEach { (expr, repetitions) -> - if (repetitions > 1) { - val repeatedExpressions = formula.filter { it.expression == expr } - - formula.removeAll(repeatedExpressions) - val repeatedExpressionsWeightsSum = repeatedExpressions.sumOf { it.weight } - formula.add(SoftConstraint(expr, repeatedExpressionsWeightsSum)) - } - } - } - protected fun getSatSoftConstraintsByModel(model: KModel): List { return softConstraints.filter { model.eval(it.expression, true) == ctx.trueExpr } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 5e16ec1d4..2a9de047d 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -113,8 +113,6 @@ class KPMResSolver(private val ctx: KContext, private var i = 0 var formula = softConstraints.toMutableList() - unionSoftConstraintsWithSameExpressions(formula) - while (true) { val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 4c856089d..99b8cf9c0 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -79,7 +79,6 @@ class KPrimalDualMaxResSolver( initMaxSMT() val assumptions = softConstraints.toMutableList() - unionSoftConstraintsWithSameExpressions(assumptions) while (_lower < _upper) { val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt index c9e355513..e54a6f6c0 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt @@ -6,23 +6,14 @@ import io.ksmt.sort.KBoolSort internal object CoreUtils { fun coreToSoftConstraints(core: List>, assumptions: List): List { - val uniqueCoreElements = mutableListOf>() - core.forEach { - if (!uniqueCoreElements.any { u -> u == it }) { - uniqueCoreElements.add(it) - } - } - val softs = mutableListOf() - for (soft in assumptions) { - if (uniqueCoreElements.any { it == soft.expression }) { - softs.add(soft) - } - } - require(uniqueCoreElements.size == softs.size) { - "Unsat core size [${uniqueCoreElements.size}] was not equal to corresponding " + - "soft constraints size [${softs.size}]" + for (element in core) { + val softElement = assumptions.find { it.expression == element } + + require(softElement != null) { "Assumptions do not contain an element from the core" } + + softs.add(softElement) } return softs From 6b43caca3d090c4bbc1f0b5e322148711d0487ca Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 12:05:13 +0300 Subject: [PATCH 120/228] Fix checking out commit in the MaxSMT workflow --- .github/workflows/run-long-maxsat-tests.yml | 2 +- .github/workflows/run-long-maxsmt-tests.yml | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index de3f73674..f75836980 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Prepare test data (cache) id: test-data-cache diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 81ec9dd6a..6a9597576 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -30,8 +30,10 @@ jobs: - name: Print environment variables run: printenv - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository (or SHA) + uses: actions/checkout@v4 + with: + ref: ${{ inputs.commitSHA }} - id: set-matrix name: Create and print MaxSMT logics matrix @@ -61,15 +63,10 @@ jobs: name: Run ${{ matrix.logics.NAME }} tests steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Check out ${{ inputs.commitSHA }} commit - if: inputs.commitSHA != '' - run: | - git config --global --add safe.directory ${GITHUB_WORKSPACE} - git fetch - git checkout ${{ inputs.commitSHA }} + - name: Checkout repository (or SHA) + uses: actions/checkout@v4 + with: + ref: ${{ inputs.commitSHA }} - name: Set up JDK 1.8 uses: actions/setup-java@v3 @@ -127,7 +124,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download test statistics uses: actions/download-artifact@v3 From 5f293d3af1cb773c948fe05dd542d3299c7e879e Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 12:18:13 +0300 Subject: [PATCH 121/228] Add QF_ABV MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 5cfb69627..ec24ce857 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -1,4 +1,8 @@ [ + { + "NAME": "QF_ABV-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_ABVFP-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 861eb97fe..0ebaa498c 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( + "QF_ABV-light", // 2.84M "QF_ABVFP-light", // 650K "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M From d911a7b32743e1b981d9c2f375c9118d65a1bdd1 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 13:03:05 +0300 Subject: [PATCH 122/228] Fix the bug with duplicated test statistics collection --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 74c63824f..e40671a4a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -121,13 +121,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } catch (ex: IgnoreTestException) { testStatistics.ignoredTest = true testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) throw ex } catch (ex: Exception) { testStatistics.failedOnParsingOrConvertingExpressions = true testStatistics.exceptionMessage = ex.message.toString() - throw ex - } finally { jsonHelper.appendTestStatisticsToFile(testStatistics) + throw ex } val maxSmtTestPath = File(samplePath.toString().removeSuffix(extension) + "maxsmt").toPath() From 9c12bd4276ef67b10ed00e73c0e1f09fdf84b818 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 14:59:19 +0300 Subject: [PATCH 123/228] Update MaxSMT tests archives sizes --- ksmt-maxsmt-test/build.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 0ebaa498c..8a2267c65 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -33,15 +33,15 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABV-light", // 2.84M + "QF_ABV-light", // 3.03M "QF_ABVFP-light", // 650K "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M - "QF_BV-light", // 8.96M + "QF_BV-light", // 8.55M "QF_FP", // 250K "QF_UF-light", // 3.79M - "QF_UFBV-light", // 6.26M - "QF_UFLRA-light", // 2.27M + "QF_UFBV-light", // 6.27M + "QF_UFLRA-light", // 2.23M ) val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false From 4c41c300c6fdd84e117d374d20b2c8c248a5b9c1 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 16:22:45 +0300 Subject: [PATCH 124/228] Expand analyzed MaxSMT statistics content --- scripts/analyze_maxsmt_statistics.py | 66 ++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index f51de27a4..228601758 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -34,8 +34,9 @@ def obj_dict(obj): def create_tests_size_statistics(tests): tests_size = len(tests) - passed_tests_percent = len(list(filter(lambda x: x["passed"], tests))) / tests_size * 100 tests_executed_maxsmt_size = len(list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests))) + tests_executed_maxsmt_passed_tests_percent = 0 if tests_executed_maxsmt_size == 0 \ + else len(list(filter(lambda x: x["passed"], tests))) / tests_executed_maxsmt_size * 100 failed_or_ignored_tests_size = len(list(filter(lambda x: not x["passed"], tests))) failed_tests_wrong_soft_constr_sum_size = len(list(filter(lambda x: x["checkedSoftConstraintsSumIsWrong"], tests))) ignored_tests_size = len(list(filter(lambda x: x["ignoredTest"], tests))) @@ -51,7 +52,8 @@ def get_unique_exception_messages(collection): failed_on_parsing_or_converting_expressions_exception_messages = get_unique_exception_messages( list(filter(lambda x: x["failedOnParsingOrConvertingExpressions"], tests))) - return TestsSizeStatistics(tests_size, passed_tests_percent, tests_executed_maxsmt_size, + return TestsSizeStatistics(tests_size, tests_executed_maxsmt_size, + tests_executed_maxsmt_passed_tests_percent, failed_or_ignored_tests_size, ignored_tests_size, failed_on_parsing_or_converting_expressions_size, failed_on_parsing_or_converting_expressions_exception_messages, @@ -78,28 +80,50 @@ def elapsed_time_ms(test): tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) tests_executed_maxsmt_size = len(tests_executed_maxsmt) + tests_executed_maxsmt_passed = list(filter(lambda x: x["passed"], tests_executed_maxsmt)) + tests_executed_maxsmt_passed_size = len(tests_executed_maxsmt_passed) + + tests_executed_maxsmt_failed = list(filter(lambda x: not x["passed"], tests_executed_maxsmt)) + tests_executed_maxsmt_failed_size = len(tests_executed_maxsmt_failed) + avg_queries_to_solver_number = 0 if tests_executed_maxsmt_size == 0 else reduce( lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), tests_executed_maxsmt, 0) / tests_executed_maxsmt_size + avg_queries_to_solver_passed_tests_number = 0 if tests_executed_maxsmt_passed_size == 0 else reduce( + lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), + tests_executed_maxsmt_passed, 0) / tests_executed_maxsmt_passed_size + + avg_queries_to_solver_failed_tests_number = 0 if tests_executed_maxsmt_failed_size == 0 else reduce( + lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), + tests_executed_maxsmt_failed, 0) / tests_executed_maxsmt_failed_size + def is_zero(value): return abs(value) < 0.00000001 - avg_time_per_solver_queries_percent_list = map( - lambda x: time_in_solver_queries_ms(x) / elapsed_time_ms(x) * 100 if not is_zero( - elapsed_time_ms(x)) else elapsed_time_ms(x), - tests_executed_maxsmt) + def avg_time_per_solver_queries_percent_list(bunch_of_tests): + return map(lambda x: time_in_solver_queries_ms(x) / elapsed_time_ms(x) * 100 if not is_zero( + elapsed_time_ms(x)) else elapsed_time_ms(x), bunch_of_tests) + avg_time_per_solver_queries_percent = \ - 0 if tests_executed_maxsmt_size == 0 else reduce(operator.add, avg_time_per_solver_queries_percent_list, + 0 if tests_executed_maxsmt_size == 0 else reduce(operator.add, avg_time_per_solver_queries_percent_list( + tests_executed_maxsmt), 0) / tests_executed_maxsmt_size + avg_time_per_solver_queries_passed_tests_percent = \ + 0 if tests_executed_maxsmt_passed_size == 0 else reduce(operator.add, avg_time_per_solver_queries_percent_list( + tests_executed_maxsmt_passed), + 0) / tests_executed_maxsmt_passed_size - failed_tests = list(filter(lambda x: not x["passed"], tests_executed_maxsmt)) - avg_failed_test_queries_to_solver_number = 0 if tests_executed_maxsmt_size == 0 else reduce( - lambda x, y: queries_to_solver_number(x) + queries_to_solver_number(y), failed_tests, - 0) / tests_executed_maxsmt_size + avg_time_per_solver_queries_failed_tests_percent = \ + 0 if tests_executed_maxsmt_failed_size == 0 else reduce(operator.add, avg_time_per_solver_queries_percent_list( + tests_executed_maxsmt_failed), + 0) / tests_executed_maxsmt_failed_size - return TestsQueriesToSolverStatistics(avg_queries_to_solver_number, avg_time_per_solver_queries_percent, - avg_failed_test_queries_to_solver_number) + return TestsQueriesToSolverStatistics(avg_queries_to_solver_number, avg_queries_to_solver_passed_tests_number, + avg_queries_to_solver_failed_tests_number, + avg_time_per_solver_queries_percent, + avg_time_per_solver_queries_passed_tests_percent, + avg_time_per_solver_queries_failed_tests_percent) def create_tests_elapsed_time_statistics(tests): @@ -141,14 +165,16 @@ def __int__(self, strategy, prefer_large_weight_constraints_for_cores, minimize_ class TestsSizeStatistics: - def __init__(self, tests_size, passed_tests_percent, tests_executed_maxsmt_size, failed_tests_size, + def __init__(self, tests_size, tests_executed_maxsmt_size, + tests_executed_maxsmt_passed_tests_percent, + failed_tests_size, ignored_tests_size, failed_on_parsing_or_converting_expressions_size, failed_on_parsing_or_converting_expressions_exception_messages, failed_tests_wrong_soft_constr_sum_size): self.tests_size = tests_size - self.passed_tests_percent = passed_tests_percent self.tests_executed_maxsmt_size = tests_executed_maxsmt_size + self.tests_executed_maxsmt_passed_tests_percent = tests_executed_maxsmt_passed_tests_percent, self.failed_tests_size = failed_tests_size self.ignored_tests_size = ignored_tests_size self.failed_on_parsing_or_converting_expressions_size = failed_on_parsing_or_converting_expressions_size @@ -158,11 +184,15 @@ def __init__(self, tests_size, passed_tests_percent, tests_executed_maxsmt_size, class TestsQueriesToSolverStatistics: - def __init__(self, avg_queries_to_solver_number, avg_time_per_solver_queries_percent, - avg_failed_test_queries_to_solver_number): + def __init__(self, avg_queries_to_solver_number, avg_queries_to_solver_passed_tests_number, + avg_queries_to_solver_failed_tests_number, avg_time_per_solver_queries_percent, + avg_time_per_solver_queries_passed_tests_percent, avg_time_per_solver_queries_failed_tests_percent): self.avg_queries_to_solver_number = avg_queries_to_solver_number + self.avg_queries_to_solver_passed_tests_number = avg_queries_to_solver_passed_tests_number + self.avg_queries_to_solver_failed_tests_number = avg_queries_to_solver_failed_tests_number self.avg_time_per_solver_queries_percent = avg_time_per_solver_queries_percent - self.avg_failed_test_queries_to_solver_number = avg_failed_test_queries_to_solver_number + self.avg_time_per_solver_queries_passed_tests_percent = avg_time_per_solver_queries_passed_tests_percent + self.avg_time_per_solver_queries_failed_tests_percent = avg_time_per_solver_queries_failed_tests_percent class TestsElapsedTimeStatistics: From dcddae4a6469a0f0601d5cbf71a8e99667513e64 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 16:51:23 +0300 Subject: [PATCH 125/228] Add QF_AUFLIA MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index ec24ce857..a651bc9ce 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -15,6 +15,10 @@ "NAME": "QF_AUFBVLIA-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_AUFLIA", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_BV-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 8a2267c65..147017c19 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -33,10 +33,11 @@ dependencies { } val maxSmtBenchmarks = listOfNotNull( - "QF_ABV-light", // 3.03M + "QF_ABV-light", // 2.56M "QF_ABVFP-light", // 650K "QF_AUFBV", // 233K "QF_AUFBVLIA-light", // 3.8M + "QF_AUFLIA", // 244K "QF_BV-light", // 8.55M "QF_FP", // 250K "QF_UF-light", // 3.79M From ef39f8cfdf118df9f7d9e004f88d434ac41de756 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 7 Dec 2023 17:28:15 +0300 Subject: [PATCH 126/228] Add QF_UFLIA MaxSMT test data --- .github/workflows/maxsmt-tests-matrix.json | 4 ++++ ksmt-maxsmt-test/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index a651bc9ce..8e5812a27 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -35,6 +35,10 @@ "NAME": "QF_UFBV-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_UFLIA-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UFLRA-light", "TEST_DATA_REVISION": "0.0.0" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 147017c19..f8d97e153 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -42,6 +42,7 @@ val maxSmtBenchmarks = listOfNotNull( "QF_FP", // 250K "QF_UF-light", // 3.79M "QF_UFBV-light", // 6.27M + "QF_UFLIA-light", // 488K "QF_UFLRA-light", // 2.23M ) From c6bb303c94f35fe9478da62fa7da5a7183a311ed Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 8 Dec 2023 19:07:56 +0300 Subject: [PATCH 127/228] Update MaxSAT workflow in order to run tests on multiple runners --- .github/workflows/run-long-maxsat-tests.yml | 25 +++----- .github/workflows/run-long-maxsmt-tests.yml | 2 +- .../src/main/kotlin/MaxSMTBenchmarkUtil.kt | 64 ++++++++----------- ksmt-maxsmt-test/build.gradle.kts | 57 ++++++++--------- .../maxsmt/test/KMaxSMTBenchmarkBasedTest.kt | 18 ++++-- .../maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 7 +- 6 files changed, 78 insertions(+), 95 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index f75836980..f8e67163e 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -1,4 +1,4 @@ -name: Build and run long ksmt maxsat tests +name: Build and run long ksmt MaxSAT tests on: workflow_dispatch: @@ -7,12 +7,12 @@ env: TEST_DATA_REVISION: 0.0.0 jobs: - setup: - runs-on: ${{ matrix.os }} + run-maxsat-tests: + runs-on: ubuntu-20.04 strategy: matrix: - os: [ubuntu-20.04, windows-latest] + BENCHMARK_NUMBER: [ 1, 2, 3, 4, 5, 6 ] fail-fast: false steps: @@ -39,30 +39,21 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: | - :ksmt-maxsmt-test:downloadPreparedMaxSatBenchmarkTestData + :ksmt-maxsmt-test:maxsat-benchmark-${{ matrix.BENCHMARK_NUMBER }} --no-daemon -PtestDataRevision=${{ env.TEST_DATA_REVISION }} - - name: Prepare test data (unpack) - uses: gradle/gradle-build-action@v2 - with: - arguments: | - :ksmt-maxsmt-test:usePreparedTestData - --no-daemon - - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - - - name: Run maxsat benchmark + - name: Run MaxSAT benchmark uses: gradle/gradle-build-action@v2 with: arguments: | :ksmt-maxsmt-test:test --no-daemon + --tests "io.ksmt.solver.maxsmt.test.sat.KPMResSATBenchmarkTest" - name: Upload test report if: always() uses: actions/upload-artifact@v3 with: - name: ksmt-maxsat-test-report + name: ksmt-maxsat-test-report-${{ matrix.BENCHMARK_NUMBER }} path: ksmt-maxsmt-test/build/reports/tests/test/* diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 6a9597576..952bc572a 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -89,7 +89,7 @@ jobs: arguments: | :ksmt-maxsmt-test:maxSmtBenchmark-${{ matrix.logics.NAME }} --no-daemon - -PmaxSmtTestDataRevision=${{ env.TEST_DATA_REVISION }} + -PtestDataRevision=${{ env.TEST_DATA_REVISION }} - name: Run ${{ matrix.logics.NAME }} tests uses: gradle/gradle-build-action@v2 diff --git a/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt index e291a869f..319519ecc 100644 --- a/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt +++ b/buildSrc/src/main/kotlin/MaxSMTBenchmarkUtil.kt @@ -1,51 +1,41 @@ import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.file.DuplicatesStrategy import org.gradle.kotlin.dsl.support.unzipTo -import java.io.File - -fun Project.usePreparedMaxSmtBenchmarkTestData(path: File) = - usePreparedBenchmarkTestData(path, "maxSmt") fun Project.maxSmtBenchmarkTestData(name: String, testDataRevision: String) = tasks.register("maxSmtBenchmark-$name") { doLast { - val path = testResourcesDir().resolve("maxSmtBenchmark/$name") - val downloadTarget = path.resolve("$name.zip") - val url = "$MAXSMT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/$name.zip" - - download(url, downloadTarget) - - path.executeIfNotReady("unpack-complete") { - copy { - from(zipTree(downloadTarget)) - into(path) - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - } + downloadBenchmarkTestData(name, testDataRevision) + } +} + +fun Project.maxSatBenchmarkTestData(name: String, testDataRevision: String) = tasks.register(name) { + doLast { + downloadBenchmarkTestData(name, testDataRevision) + unzipMaxSATBenchmarkTestFiles() } } -fun Project.usePreparedMaxSATBenchmarkTestData(path: File): Task = - usePreparedBenchmarkTestData(path, "maxSat") - .get() - .finalizedBy(unzipMaxSATBenchmarkTestFiles()) - -fun Project.downloadMaxSATBenchmarkTestData(downloadPath: File, testDataPath: File, testDataRevision: String) = - downloadPreparedBenchmarkTestData( - downloadPath, - testDataPath, - "MaxSat", - "$MAXSMT_BENCHMARK_REPO_URL/releases/download/$testDataRevision/maxsat-benchmark.zip", - ) - -fun Project.unzipMaxSATBenchmarkTestFiles() = - tasks.register("unzipMaxSatBenchmarkFiles") { - doLast { - val testData = testResourcesDir() - testData.listFiles()?.forEach { if (it.isFile && it.extension == "zip") unzipTo(it.parentFile, it) } +fun Project.downloadBenchmarkTestData(name: String, testDataRevision: String) { + val path = testResourcesDir().resolve("maxSmtBenchmark/$name") + val downloadTarget = path.resolve("$name.zip") + val url = "$BENCHMARKS_REPO_URL/releases/download/$testDataRevision/$name.zip" + + download(url, downloadTarget) + + path.executeIfNotReady("unpack-complete") { + copy { + from(zipTree(downloadTarget)) + into(path) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } +} + +fun Project.unzipMaxSATBenchmarkTestFiles() { + val testData = testResourcesDir().resolve("maxSmtBenchmark") + testData.walk().forEach { if (it.isFile && it.extension == "zip") unzipTo(it.parentFile, it) } +} private fun Project.testResourcesDir() = projectDir.resolve("src/test/resources") -private const val MAXSMT_BENCHMARK_REPO_URL = "https://github.com/victoriafomina/ksmt" +private const val BENCHMARKS_REPO_URL = "https://github.com/victoriafomina/ksmt" diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index f8d97e153..43df06c50 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -46,58 +46,51 @@ val maxSmtBenchmarks = listOfNotNull( "QF_UFLRA-light", // 2.23M ) -val runMaxSMTBenchmarkBasedTests = project.booleanProperty("runMaxSMTBenchmarkBasedTests") ?: false -val runMaxSATBenchmarkBasedTests = project.booleanProperty("runMaxSATBenchmarkBasedTests") ?: false +val maxSatBenchmarks = listOfNotNull( + "maxsat-benchmark-1", // 48.6M + "maxsat-benchmark-2", // 34.1M + "maxsat-benchmark-3", // 56.8M + "maxsat-benchmark-4", // 17.2M + "maxsat-benchmark-5", // 90.4M + "maxsat-benchmark-6", // 37.9M +) -// use benchmarks from testData directory instead of downloading -val usePreparedMaxSMTBenchmarks = project.booleanProperty("usePreparedMaxSMTBenchmarks") ?: true -val usePreparedMaxSATBenchmark = project.booleanProperty("usePreparedMaxSATBenchmark") ?: true +val runMaxSmtBenchmarkTests = project.booleanProperty("runMaxSmtBenchmarkTests") ?: false +val runMaxSatBenchmarkTests = project.booleanProperty("runMaxSatBenchmarkTests") ?: false -val testDataDir = projectDir.resolve("src/resources/testData") -val unpackedTestDataDir = testDataDir.resolve("data") -val downloadedTestData = testDataDir.resolve("testData.zip") +// Use benchmarks from maxSmtBenchmark directory (test resources) instead of downloading +val usePreparedBenchmarks = project.booleanProperty("usePreparedBenchmarks") ?: true -val maxSmtTestDataRevision = project.stringProperty("maxSmtTestDataRevision") ?: "no-revision" -val maxSatTestDataRevision = project.stringProperty("maxSatTestDataRevision") ?: "no-revision" +val testDataRevision = project.stringProperty("testDataRevision") ?: "no-revision" -val usePreparedMaxSmtTestData = usePreparedMaxSmtBenchmarkTestData(unpackedTestDataDir) val downloadPreparedMaxSmtBenchmarkTestData = - maxSmtBenchmarks.map { maxSmtBenchmarkTestData(it, maxSmtTestDataRevision) } + maxSmtBenchmarks.map { maxSmtBenchmarkTestData(it, testDataRevision) } -val usePreparedMaxSMTTestData by tasks.registering { +val prepareMaxSmtTestData by tasks.registering { tasks.withType().forEach { it.enabled = false } - if (usePreparedMaxSMTBenchmarks) { - dependsOn.add(usePreparedMaxSmtTestData) - } else { + if (!usePreparedBenchmarks) { dependsOn.addAll(downloadPreparedMaxSmtBenchmarkTestData) } } tasks.withType { - if (runMaxSMTBenchmarkBasedTests) { - dependsOn.add(usePreparedMaxSMTTestData) + if (runMaxSmtBenchmarkTests) { + dependsOn.add(prepareMaxSmtTestData) } } -val downloadPreparedMaxSATBenchmarkTestData = downloadMaxSATBenchmarkTestData( - downloadPath = downloadedTestData, - testDataPath = unpackedTestDataDir, - testDataRevision = maxSatTestDataRevision, -) - -val preparedMaxSATBenchmarkTestData = usePreparedMaxSATBenchmarkTestData(unpackedTestDataDir) +val downloadPreparedMaxSatBenchmarkTestData = + maxSatBenchmarks.map { maxSatBenchmarkTestData(it, testDataRevision) } -val usePreparedMaxSATTestData by tasks.registering { +val prepareMaxSatTestData by tasks.registering { tasks.withType().forEach { it.enabled = false } - if (!usePreparedMaxSATBenchmark) { - dependsOn.add(downloadPreparedMaxSATBenchmarkTestData) + if (!usePreparedBenchmarks) { + dependsOn.addAll(downloadPreparedMaxSatBenchmarkTestData) } - - finalizedBy(preparedMaxSATBenchmarkTestData) } tasks.withType { - if (runMaxSATBenchmarkBasedTests) { - dependsOn.add(usePreparedMaxSATTestData) + if (runMaxSatBenchmarkTests) { + dependsOn.add(prepareMaxSatTestData) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt index 42205e5bf..ffe519734 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/KMaxSMTBenchmarkBasedTest.kt @@ -18,9 +18,10 @@ interface KMaxSMTBenchmarkBasedTest { ?.let { Paths.get(it) } ?: error("No test data") - private fun prepareTestData(): List { + private fun prepareTestData(extension: String): List { val testDataLocation = testDataLocation() - return testDataLocation.toFile().walkTopDown().filter { f -> f.isFile && f.extension == "smt2" }.toList() + return testDataLocation.toFile().walkTopDown() + .filter { f -> f.isFile && (f.extension == extension) }.toList() .sorted() .map { BenchmarkTestArguments( @@ -30,11 +31,18 @@ interface KMaxSMTBenchmarkBasedTest { } } - private val testData by lazy { - prepareTestData() + private val maxSmtTestData by lazy { + prepareTestData("smt2") } @JvmStatic - fun maxSMTTestData() = testData + fun maxSMTTestData() = maxSmtTestData + + private val maxSatTestData by lazy { + prepareTestData("wcnf") + } + + @JvmStatic + fun maxSATTestData() = maxSatTestData } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index dbed0be14..690bb60c5 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -13,6 +13,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Path +import kotlin.io.path.name import kotlin.test.assertEquals import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds @@ -32,9 +33,9 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { fun closeSolver() = maxSATSolver.close() @ParameterizedTest(name = "{0}") - @MethodSource("maxSMTTestData") + @MethodSource("maxSATTestData") fun maxSATTest(name: String, samplePath: Path) = with(ctx) { - val testData = maxSATTestNameToExpectedResult.find { it.first == name } + val testData = maxSATTestNameToExpectedResult.find { it.first == samplePath.name } require(testData != null) { "Test [$name] expected result must be specified" } val constraints = parseMaxSATTest(samplePath, this) @@ -53,7 +54,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { val maxSATResult = maxSATSolver.checkMaxSMT(20.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } val expectedSatConstraintsScore = - sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.find { it.first == name }!!.second + sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second assertEquals(SAT, maxSATResult.hardConstraintsSatStatus, "Hard constraints must be SAT") assertTrue(maxSATResult.maxSMTSucceeded, "MaxSAT was not successful [$name]") From 52f0335b703256a88290909e61632eadd977bb71 Mon Sep 17 00:00:00 2001 From: Victoria Date: Mon, 11 Dec 2023 19:44:07 +0300 Subject: [PATCH 128/228] Process cases when exception is thrown during MaxSMT solving --- .../ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index e40671a4a..363ebca0d 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -151,7 +151,14 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { - maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds, true) + try { + maxSMTResult = maxSMTSolver.checkMaxSMT(INFINITE, true) + } catch (ex: Exception) { + testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() + testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + throw ex + } } testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() From 7f31dd4c8b84a527a2572ec4cab17f740f7f3ee5 Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 12 Dec 2023 16:43:36 +0300 Subject: [PATCH 129/228] Update MaxSMT tests timeout --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 363ebca0d..c72282bf9 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -152,7 +152,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkMaxSMT(INFINITE, true) + maxSMTResult = maxSMTSolver.checkMaxSMT(60.seconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() From 1c84b456c6beab2e93f3fd82d91e11c6ef22b5ce Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 16 Jan 2024 16:55:03 +0300 Subject: [PATCH 130/228] Log events in KPrimalDualMaxResSolver --- .gitignore | 2 ++ ksmt-maxsmt-test/build.gradle.kts | 5 ++-- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 10 +++++-- ksmt-maxsmt/build.gradle.kts | 3 ++ .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 27 +++++++++++++++++ ksmt-maxsmt/src/main/resources/log4j.xml | 29 +++++++++++++++++++ version.properties | 7 ++++- 7 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ksmt-maxsmt/src/main/resources/log4j.xml diff --git a/.gitignore b/.gitignore index 77c2c0aed..4dddda5e0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ ksmt-maxsmt-test/src/test/resources/maxSmtBenchmark # ignore maxsmt statistics ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json +**/logs/ + diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 43df06c50..bf23fafa2 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -26,8 +26,9 @@ dependencies { testImplementation(project(":ksmt-runner")) testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") - testImplementation("io.github.oshai:kotlin-logging-jvm:5.1.0") - testImplementation("org.slf4j:slf4j-simple:2.0.9") + + testImplementation("io.github.oshai:kotlin-logging-jvm:${versions["kotlin-logging-jvm"]}") + testImplementation("org.slf4j:slf4j-log4j12:${versions["slf4j-log4j12"]}") testImplementation("com.google.code.gson:gson:2.10.1") } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index c72282bf9..af6442266 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -109,6 +109,8 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { "File extension cannot be '${samplePath.extension}' as it must be $extension" } + logger.info { "Test name: [$name]" } + lateinit var ksmtAssertions: List> val testStatistics = MaxSMTTestStatistics(name, solver) @@ -122,11 +124,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { testStatistics.ignoredTest = true testStatistics.exceptionMessage = ex.message.toString() jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } throw ex } catch (ex: Exception) { testStatistics.failedOnParsingOrConvertingExpressions = true testStatistics.exceptionMessage = ex.message.toString() jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } throw ex } @@ -157,14 +161,14 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } throw ex } } testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() - logger.info { "Test name: [$name]" } - logger.info { "Elapsed time (MaxSMT call): [${elapsedTime}ms]" } + logger.info { "Elapsed time: $elapsedTime ms --- MaxSMT call${System.lineSeparator()}" } try { assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") @@ -193,6 +197,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val worker = try { getOrCreateFreeWorker() } catch (ex: WorkerInitializationFailedException) { + logger.error { ex.message + System.lineSeparator() } ignoreTest { "worker initialization failed -- ${ex.message}" } } worker.astSerializationCtx.initCtx(ctx) @@ -209,6 +214,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } } catch (ex: TimeoutCancellationException) { + logger.error { ex.message + System.lineSeparator() } ignoreTest { "worker timeout -- ${ex.message}" } } finally { worker.release() diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index ace4ce8da..30bd28d1d 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -14,6 +14,9 @@ dependencies { implementation(project(":ksmt-core")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") + implementation("io.github.oshai:kotlin-logging-jvm:${versions["kotlin-logging-jvm"]}") + implementation("org.slf4j:slf4j-log4j12:${versions["slf4j-log4j12"]}") + testImplementation("org.junit.jupiter:junit-jupiter-api:${versions["junit-jupiter"]}") testImplementation(project(":ksmt-z3")) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 99b8cf9c0..c0c426309 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -1,5 +1,6 @@ package io.ksmt.solver.maxsmt.solvers +import io.github.oshai.kotlinlogging.KotlinLogging import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.solver.KModel @@ -38,12 +39,16 @@ class KPrimalDualMaxResSolver( private var _model: KModel? = null private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) private var collectStatistics = false + private var _iteration = 0 + private val logger = KotlinLogging.logger {} + private var markLoggingPoint = markNow() private data class WeightedCore(val expressions: List>, val weight: UInt) override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val clockStart = System.currentTimeMillis() val markCheckMaxSMTStart = markNow() + markLoggingPoint = markCheckMaxSMTStart if (TimerUtils.timeoutExceeded(timeout)) { error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") @@ -81,6 +86,11 @@ class KPrimalDualMaxResSolver( val assumptions = softConstraints.toMutableList() while (_lower < _upper) { + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] Iteration number: $_iteration" + } + markLoggingPoint = markNow() + val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { if (collectStatistics) { @@ -146,11 +156,16 @@ class KPrimalDualMaxResSolver( throw NotImplementedError() } } + + ++_iteration } _lower = _upper val result = KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, true) + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning result" + } solver.pop() @@ -323,6 +338,11 @@ class KPrimalDualMaxResSolver( _model = model _upper = upper + + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper) --- model is updated" + } + markLoggingPoint = markNow() } private fun maxResolve(weightedCore: WeightedCore, assumptions: MutableList) = with(ctx) { @@ -452,6 +472,13 @@ class KPrimalDualMaxResSolver( _maxUpper = _upper _correctionSetSize = 0 + _iteration = 0 + + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper) --- model is initialized with null" + } + markLoggingPoint = markNow() + _model = null _correctionSetModel = null _minimalUnsatCore.reset() diff --git a/ksmt-maxsmt/src/main/resources/log4j.xml b/ksmt-maxsmt/src/main/resources/log4j.xml new file mode 100644 index 000000000..80ab56d31 --- /dev/null +++ b/ksmt-maxsmt/src/main/resources/log4j.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/version.properties b/version.properties index 074ae9108..9d581871a 100644 --- a/version.properties +++ b/version.properties @@ -2,4 +2,9 @@ kotlin=1.9.0 detekt=1.20.0 junit-jupiter=5.8.2 -kotlinx-coroutines=1.7.2 \ No newline at end of file + +kotlinx-coroutines=1.7.2 + +# Logging +kotlin-logging-jvm=6.0.3 +slf4j-log4j12=2.0.11 \ No newline at end of file From 238f9bd9703c6de268fd6620653ee5844c00506d Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 16 Jan 2024 17:19:39 +0300 Subject: [PATCH 131/228] Upload test logs in MaxSMT workflow --- .github/workflows/run-long-maxsmt-tests.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 952bc572a..61d27331c 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -119,6 +119,13 @@ jobs: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* + - name: Upload ${{ matrix.logics.NAME }} test logs + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_logs + path: ksmt-maxsmt-test/logs/* + merge-and-analyze-maxsmt-statistics: needs: run-maxsmt-tests runs-on: ubuntu-20.04 From dd3b16bb93391c12140ff637626a9b0cffcaccbc Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 17 Jan 2024 15:30:43 +0300 Subject: [PATCH 132/228] Use ValueTimeMark for time measurement --- .../solver/maxsmt/solvers/KPMResSolver.kt | 14 +++---- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 38 +++++++++---------- .../maxsmt/solvers/utils/MinimalUnsatCore.kt | 4 +- .../io/ksmt/solver/maxsmt/utils/TimerUtils.kt | 8 ++-- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 2a9de047d..9b206afa0 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -57,7 +57,7 @@ class KPMResSolver(private val ctx: KContext, private if (softConstraints.isEmpty()) { if (this.collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return maxSMTResult @@ -67,7 +67,7 @@ class KPMResSolver(private val ctx: KContext, private if (!maxSMTResult.maxSMTSucceeded) { val (solverStatus, _, model) = currentMaxSMTResult if (this.collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return when (solverStatus) { @@ -79,21 +79,19 @@ class KPMResSolver(private val ctx: KContext, private } if (this.collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return maxSMTResult } private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { - val clockStart = System.currentTimeMillis() - val markHardConstraintsCheckStart = markNow() val hardConstraintsStatus = solver.check(timeout) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds + maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds } if (softConstraints.isEmpty()) { @@ -114,7 +112,7 @@ class KPMResSolver(private val ctx: KContext, private var formula = softConstraints.toMutableList() while (true) { - val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, markHardConstraintsCheckStart) if (TimerUtils.timeoutExceeded(checkRemainingTime)) { return KMaxSMTResult(listOf(), hardConstraintsStatus, false) @@ -126,7 +124,7 @@ class KPMResSolver(private val ctx: KContext, private if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckSatStart).inWholeMilliseconds + maxSMTStatistics.timeInSolverQueriesMs += markCheckSatStart.elapsedNow().inWholeMilliseconds } if (solverStatus == UNKNOWN) { diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index c0c426309..71b982a02 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -46,7 +46,6 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { - val clockStart = System.currentTimeMillis() val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart @@ -65,17 +64,17 @@ class KPrimalDualMaxResSolver( val hardConstraintsStatus = solver.check(timeout) if (this.collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markHardConstraintsCheckStart).inWholeMilliseconds + maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds } if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return KMaxSMTResult(listOf(), hardConstraintsStatus, true) } else if (hardConstraintsStatus == UNKNOWN) { if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } @@ -91,10 +90,10 @@ class KPrimalDualMaxResSolver( } markLoggingPoint = markNow() - val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotImplementedError() } @@ -121,11 +120,11 @@ class KPrimalDualMaxResSolver( } UNSAT -> { - val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) if (TimerUtils.timeoutExceeded(remainingTime)) { solver.pop() if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotImplementedError() } @@ -136,13 +135,13 @@ class KPrimalDualMaxResSolver( // TODO: process this case as it can happen when timeout exceeded solver.pop() if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotImplementedError() } else if (processUnsatStatus == UNKNOWN) { solver.pop() if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotImplementedError() } @@ -151,7 +150,7 @@ class KPrimalDualMaxResSolver( UNKNOWN -> { solver.pop() if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotImplementedError() } @@ -170,7 +169,7 @@ class KPrimalDualMaxResSolver( solver.pop() if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = (markNow() - markCheckMaxSMTStart).inWholeMilliseconds + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } return result } @@ -248,7 +247,7 @@ class KPrimalDualMaxResSolver( assumptions: MutableList, timeout: Duration, ): Pair> { - val clockStart = System.currentTimeMillis() + val markStart = markNow() val cores = mutableListOf() var status = UNSAT @@ -257,7 +256,7 @@ class KPrimalDualMaxResSolver( var unsatCore: List if (maxSmtCtx.minimizeCores) { - val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime)) { return Pair(SAT, cores) // TODO: is this status Ok? } @@ -286,7 +285,7 @@ class KPrimalDualMaxResSolver( return Pair(SAT, cores) } - val checkSatRemainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val checkSatRemainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(checkSatRemainingTime)) { return Pair(SAT, cores) // TODO: is this status Ok? } @@ -336,7 +335,6 @@ class KPrimalDualMaxResSolver( } _model = model - _upper = upper logger.info { @@ -494,7 +492,7 @@ class KPrimalDualMaxResSolver( var status = SAT if (maxSmtCtx.preferLargeWeightConstraintsForCores && assumptions.isNotEmpty()) { - val clockStart = System.currentTimeMillis() + val markStart = markNow() // Give preference to cores that have large minimal values. assumptions.sortByDescending { it.weight } @@ -510,7 +508,7 @@ class KPrimalDualMaxResSolver( val assumptionsToCheck = assumptions.subList(0, index) - val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val remainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(remainingTime)) { return UNKNOWN } @@ -519,7 +517,7 @@ class KPrimalDualMaxResSolver( status = checkSat(assumptionsToCheck, assumptionsToCheck.size == assumptions.size, remainingTime) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckAssumptionsStart).inWholeMilliseconds + maxSMTStatistics.timeInSolverQueriesMs += markCheckAssumptionsStart.elapsedNow().inWholeMilliseconds } } } else { @@ -527,7 +525,7 @@ class KPrimalDualMaxResSolver( status = checkSat(assumptions, true, timeout) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += (markNow() - markCheckStart).inWholeMilliseconds + maxSMTStatistics.timeInSolverQueriesMs += markCheckStart.elapsedNow().inWholeMilliseconds } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index b2369b52c..e8ba59376 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -30,7 +30,7 @@ internal class MinimalUnsatCore( timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false, ): List = with(ctx) { - val clockStart = System.currentTimeMillis() + val markStart = markNow() if (collectStatistics) { coreStatistics = MinimalCoreStatistics() @@ -47,7 +47,7 @@ internal class MinimalUnsatCore( val unknown = unsatCore.toMutableList() while (unknown.isNotEmpty()) { - val remainingTime = TimerUtils.computeRemainingTime(timeout, clockStart) + val remainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(remainingTime)) { return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt index e27021c8b..6b1885de7 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt @@ -1,13 +1,11 @@ package io.ksmt.solver.maxsmt.utils import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.TimeSource.Monotonic.ValueTimeMark internal object TimerUtils { - fun computeRemainingTime(timeout: Duration, clockStart: Long): Duration { - val msUnit = DurationUnit.MILLISECONDS - return timeout - (System.currentTimeMillis().toDuration(msUnit) - clockStart.toDuration(msUnit)) + fun computeRemainingTime(timeout: Duration, markStart: ValueTimeMark): Duration { + return timeout - markStart.elapsedNow() } fun timeoutExceeded(timeout: Duration): Boolean = From 5c9144d4fccdf53cc577c964a213e13e0196f0a3 Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 17 Jan 2024 15:35:23 +0300 Subject: [PATCH 133/228] Log more events --- .../solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 12 +++++++++++- .../solver/maxsmt/solvers/utils/MinimalUnsatCore.kt | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 71b982a02..5b945ba8f 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -163,7 +163,7 @@ class KPrimalDualMaxResSolver( val result = KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, true) logger.info { - "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning result" + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" } solver.pop() @@ -202,6 +202,8 @@ class KPrimalDualMaxResSolver( } private fun processUnsatCore(weightedCore: WeightedCore, assumptions: MutableList) = with(ctx) { + logger.info { "processing unsat core --- started" } + val core = weightedCore.expressions require(core.isNotEmpty()) { "Core should not be empty here" } @@ -226,6 +228,7 @@ class KPrimalDualMaxResSolver( if (_correctionSetModel != null && _correctionSetSize < core.size) { val correctionSet = getCorrectionSet(_correctionSetModel!!, assumptions) if (correctionSet.size >= core.size) { + logger.info { "processing unsat core --- ended" } return } @@ -233,6 +236,7 @@ class KPrimalDualMaxResSolver( for (asm in assumptions) { val weight1 = asm.weight if (weight != 0u && weight1 != weight) { + logger.info { "processing unsat core --- ended" } return } @@ -241,6 +245,8 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } + + logger.info { "processing unsat core --- ended" } } private fun getCores( @@ -489,6 +495,8 @@ class KPrimalDualMaxResSolver( } private fun checkSatHillClimb(assumptions: MutableList, timeout: Duration): KSolverStatus { + logger.info { "checking formula on satisfiability --- started" } + var status = SAT if (maxSmtCtx.preferLargeWeightConstraintsForCores && assumptions.isNotEmpty()) { @@ -510,6 +518,7 @@ class KPrimalDualMaxResSolver( val remainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(remainingTime)) { + logger.info { "checking formula on satisfiability --- ended --- solver returned UNKNOWN" } return UNKNOWN } @@ -529,6 +538,7 @@ class KPrimalDualMaxResSolver( } } + logger.info { "checking formula on satisfiability --- ended" } return status } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index e8ba59376..c825f47d5 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -1,5 +1,6 @@ package io.ksmt.solver.maxsmt.solvers.utils +import io.github.oshai.kotlinlogging.KotlinLogging import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.solver.KModel @@ -21,6 +22,7 @@ internal class MinimalUnsatCore( ) { private val _minimalUnsatCoreModel = MinimalUnsatCoreModel(ctx, solver) private lateinit var coreStatistics: MinimalCoreStatistics + private val logger = KotlinLogging.logger {} fun getBestModel(): Pair = _minimalUnsatCoreModel.getBestModel() @@ -30,6 +32,8 @@ internal class MinimalUnsatCore( timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false, ): List = with(ctx) { + logger.info { "core minimization --- started" } + val markStart = markNow() if (collectStatistics) { @@ -39,6 +43,7 @@ internal class MinimalUnsatCore( val unsatCore = solver.unsatCore() if (unsatCore.isEmpty()) { + logger.info { "core minimization ended --- unsat core is empty" } return emptyList() } @@ -49,6 +54,7 @@ internal class MinimalUnsatCore( while (unknown.isNotEmpty()) { val remainingTime = TimerUtils.computeRemainingTime(timeout, markStart) if (TimerUtils.timeoutExceeded(remainingTime)) { + logger.info { "core minimization ended --- timeout exceeded" } return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) } @@ -64,7 +70,10 @@ internal class MinimalUnsatCore( } when (status) { - UNKNOWN -> return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + UNKNOWN -> { + logger.info { "core minimization ended --- solver returned UNKNOWN" } + return CoreUtils.coreToSoftConstraints(unsatCore, assumptions) + } SAT -> { minimalUnsatCore.add(expr) @@ -75,6 +84,7 @@ internal class MinimalUnsatCore( } } + logger.info { "core minimization ended --- core is minimized" } return CoreUtils.coreToSoftConstraints(minimalUnsatCore, assumptions) } From c33d2bd6711cd23999d27cb776693d9f8236073e Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 18 Jan 2024 11:39:21 +0300 Subject: [PATCH 134/228] Log unsucceeded MaxSMT execution --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index af6442266..b2bb68851 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -185,7 +185,10 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", ) testStatistics.passed = true - } finally { + } catch (ex: Exception) { + logger.error { ex.message + System.lineSeparator() } + } + finally { jsonHelper.appendTestStatisticsToFile(testStatistics) } } From 211e2af5272d7719365bbf42c00426156220ab7b Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 18 Jan 2024 12:26:47 +0300 Subject: [PATCH 135/228] Update default value for KMaxSMTContext --- .../run-long-maxsmt-tests-pdmres.yml | 34 +++++++++---------- .../test/smt/KDualMaxRes2SMTBenchmarkTest.kt | 2 +- .../test/smt/KDualMaxRes3SMTBenchmarkTest.kt | 2 +- .../test/smt/KDualMaxRes4SMTBenchmarkTest.kt | 2 +- .../smt/KPrimalMaxRes2SMTBenchmarkTest.kt | 2 +- .../smt/KPrimalMaxRes3SMTBenchmarkTest.kt | 2 +- .../smt/KPrimalMaxRes4SMTBenchmarkTest.kt | 2 +- .../io/ksmt/solver/maxsmt/KMaxSMTContext.kt | 6 ++-- .../maxsmt/KPrimalDualMaxResSolver2Test.kt | 2 +- .../maxsmt/KPrimalDualMaxResSolver3Test.kt | 2 +- .../maxsmt/KPrimalDualMaxResSolver4Test.kt | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 94424392d..d1cbac69f 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -24,19 +24,19 @@ on: - Primal minimizeCores: type: boolean - description: Core minimization option (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + description: Core minimization option (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: true + default: false preferLargeWeightCores: type: boolean - description: Prefer large weight constraints for cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + description: Prefer large weight constraints for cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: true + default: false getMultipleCores: type: boolean - description: Get multiple cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false) + description: Get multiple cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: true + default: false commitSHA: description: Commit SHA (latest commit is default) required: false @@ -48,14 +48,14 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Verify inputs - # Check only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] is false + # Check only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] is true # (supported tests configurations) run: | - if ([[ "${{ inputs.minimizeCores }}" == "false" ]] && [[ "${{ inputs.preferLargeWeightCores }}" == "false" ]]) || \ - ([[ "${{ inputs.minimizeCores }}" == "false" ]] && [[ "${{ inputs.getMultipleCores }}" == "false" ]]) || \ - ([[ "${{ inputs.preferLargeWeightCores }}" == "false" ]] && [[ "${{ inputs.getMultipleCores }}" == "false" ]]) + if ([[ "${{ inputs.minimizeCores }}" == "true" ]] && [[ "${{ inputs.preferLargeWeightCores }}" == "true" ]]) || \ + ([[ "${{ inputs.minimizeCores }}" == "true" ]] && [[ "${{ inputs.getMultipleCores }}" == "true" ]]) || \ + ([[ "${{ inputs.preferLargeWeightCores }}" == "true" ]] && [[ "${{ inputs.getMultipleCores }}" == "true" ]]) then - echo "Only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be false" + echo "Only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true" exit 1 fi @@ -67,18 +67,18 @@ jobs: steps: - name: Construct solver name run: | - if [[ "${{ inputs.minimizeCores }}" == true ]] && \ - [[ "${{ inputs.preferLargeWeightCores }}" == true ]] && \ - [[ "${{ inputs.getMultipleCores }}" == true ]] + if [[ "${{ inputs.minimizeCores }}" == false ]] && \ + [[ "${{ inputs.preferLargeWeightCores }}" == false ]] && \ + [[ "${{ inputs.getMultipleCores }}" == false ]] then export OPTION_TO_VALUE="" - elif [[ "${{ inputs.preferLargeWeightCores }}" == false ]] + elif [[ "${{ inputs.preferLargeWeightCores }}" == true ]] then export OPTION_TO_VALUE="2" - elif [[ "${{ inputs.minimizeCores }}" == false ]] + elif [[ "${{ inputs.minimizeCores }}" == true ]] then export OPTION_TO_VALUE="3" - elif [[ "${{ inputs.getMultipleCores }}" == false ]] + elif [[ "${{ inputs.getMultipleCores }}" == true ]] then export OPTION_TO_VALUE="4" fi diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt index dafad3562..5952cf118 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt @@ -9,6 +9,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt index 517264856..400280342 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt @@ -9,6 +9,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = false)) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = true)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt index 6c02d060b..ffbc01614 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt @@ -9,6 +9,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = false)) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = true)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt index a13a70c63..022079f69 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt @@ -15,7 +15,7 @@ class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { smtSolver, KMaxSMTContext( strategy = PrimalMaxRes, - preferLargeWeightConstraintsForCores = false, + preferLargeWeightConstraintsForCores = true, ), ) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt index 48b91062a..9331cdb46 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt @@ -10,6 +10,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = false)) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = true)) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt index 7e9674856..5a954c79f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt @@ -13,7 +13,7 @@ class KPrimalMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { return KPrimalDualMaxResSolver( this, smtSolver, - KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = false), + KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = true), ) } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt index 95ffa24ec..8336efef7 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTContext.kt @@ -4,9 +4,9 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes class KMaxSMTContext( val strategy: Strategy = PrimalDualMaxRes, - val preferLargeWeightConstraintsForCores: Boolean = true, - val minimizeCores: Boolean = true, - val getMultipleCores: Boolean = true, + val preferLargeWeightConstraintsForCores: Boolean = false, + val minimizeCores: Boolean = false, + val getMultipleCores: Boolean = false, ) { enum class Strategy { diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt index 99fc3f1e5..57668b153 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt @@ -8,6 +8,6 @@ import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver2Test : KMaxSMTSolverTest() { override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = false)) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) } } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt index 7a89d9202..1c8429708 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt @@ -8,6 +8,6 @@ import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver3Test : KMaxSMTSolverTest() { override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(minimizeCores = false)) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(minimizeCores = true)) } } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt index f1b0405c5..b3e861892 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt @@ -8,6 +8,6 @@ import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver4Test : KMaxSMTSolverTest() { override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(getMultipleCores = false)) + return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(getMultipleCores = true)) } } From 6fdb93004f6fe35997dbffb33fd2f77a504e8166 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 18 Jan 2024 15:59:34 +0300 Subject: [PATCH 136/228] Update logs about models --- .../io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 5b945ba8f..ffb6070e4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -337,6 +337,7 @@ class KPrimalDualMaxResSolver( val upper = ModelUtils.getModelCost(ctx, model, softConstraints) if (upper > _upper) { + logger.info { "Found model has less weight --- model is not updated" } return } @@ -479,7 +480,8 @@ class KPrimalDualMaxResSolver( _iteration = 0 logger.info { - "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper) --- model is initialized with null" + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper)" + + " --- model is initialized with null" } markLoggingPoint = markNow() From adc89ade76c60ec1bd859c2478feb7ead90014f1 Mon Sep 17 00:00:00 2001 From: Victoria Date: Fri, 19 Jan 2024 17:23:38 +0300 Subject: [PATCH 137/228] Fix hill climb option --- .../solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index ffb6070e4..4ddd776c4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -161,7 +161,7 @@ class KPrimalDualMaxResSolver( _lower = _upper - val result = KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, true) + val result = KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, true) logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" } @@ -525,7 +525,7 @@ class KPrimalDualMaxResSolver( } val markCheckAssumptionsStart = markNow() - status = checkSat(assumptionsToCheck, assumptionsToCheck.size == assumptions.size, remainingTime) + status = checkSat(assumptionsToCheck, remainingTime) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += markCheckAssumptionsStart.elapsedNow().inWholeMilliseconds @@ -533,7 +533,7 @@ class KPrimalDualMaxResSolver( } } else { val markCheckStart = markNow() - status = checkSat(assumptions, true, timeout) + status = checkSat(assumptions, timeout) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += markCheckStart.elapsedNow().inWholeMilliseconds @@ -559,12 +559,11 @@ class KPrimalDualMaxResSolver( private fun checkSat( assumptions: List, - passedAllAssumptions: Boolean, timeout: Duration, ): KSolverStatus { val status = solver.checkWithAssumptions(assumptions.map { it.expression }, timeout) - if (passedAllAssumptions && status == SAT) { + if (status == SAT) { updateAssignment(solver.model().detach(), assumptions) } From 354affaf4037c2a741ee8dfd5bd1cb5a7c804dab Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 23 Jan 2024 15:41:23 +0300 Subject: [PATCH 138/228] Update getting disjoint unsat cores option --- .../io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 4ddd776c4..e22b20fac 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -34,7 +34,7 @@ class KPrimalDualMaxResSolver( private var _upper: UInt = 0u // Current upper frontier private var _maxUpper = 0u // Max possible upper frontier private var _correctionSetSize: Int = 0 // Current corrections set size - private val _maxCoreSize = if (maxSmtCtx.getMultipleCores) 3 else 1 + private val _maxCoreSize = 3 private var _correctionSetModel: KModel? = null private var _model: KModel? = null private var _minimalUnsatCore = MinimalUnsatCore(ctx, solver) @@ -287,7 +287,7 @@ class KPrimalDualMaxResSolver( removeCoreAssumptions(unsatCore, assumptions) splitCore(unsatCore, assumptions) - if (unsatCore.size >= _maxCoreSize) { + if (unsatCore.size >= _maxCoreSize || !maxSmtCtx.getMultipleCores) { return Pair(SAT, cores) } From 5d14b5a15f622f56cceb78b3f2e578da16a668f5 Mon Sep 17 00:00:00 2001 From: Victoria Date: Tue, 23 Jan 2024 15:42:47 +0300 Subject: [PATCH 139/228] Log lower bound updates --- .../io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index e22b20fac..e2d3a0815 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -214,9 +214,11 @@ class KPrimalDualMaxResSolver( assert(fml) _lower += weightedCore.weight + logger.info { "Unsat core weight: ${weightedCore.weight}" } if (maxSmtCtx.strategy == PrimalDualMaxRes) { _lower = minOf(_lower, _upper) + logger.info { "(lower bound: $_lower, upper bound: $_upper) --- lower bound is updated" } } if (_correctionSetModel != null && _correctionSetSize > 0) { From ffea92df0895d224de4b5cbb0c512dad147c515e Mon Sep 17 00:00:00 2001 From: Victoria Date: Wed, 24 Jan 2024 17:06:49 +0300 Subject: [PATCH 140/228] Add NotYetImplementedException --- .../io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt | 2 +- .../solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 11 ++++++----- .../solvers/exceptions/NotImplementedException.kt | 6 ++++++ settings.gradle.kts | 1 + 4 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 9b206afa0..d64628601 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -74,7 +74,7 @@ class KPMResSolver(private val ctx: KContext, private SAT -> processSat(model!!) UNSAT -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving UNKNOWN -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving - else -> error("Unexpected status: $solverStatus") + null -> error("Unexpected status: null") } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index e2d3a0815..bc2fc4dd2 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -15,6 +15,7 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.solvers.exceptions.NotYetImplementedException import io.ksmt.solver.maxsmt.solvers.utils.MinimalUnsatCore import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils @@ -95,7 +96,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotImplementedError() + throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") } val status = checkSatHillClimb(assumptions, timeout) @@ -126,7 +127,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotImplementedError() + throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") } val processUnsatStatus = processUnsat(assumptions, remainingTime) @@ -137,13 +138,13 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotImplementedError() + throw NotYetImplementedException("MaxSMT solution was not found --- processUnsat returned UNSAT") } else if (processUnsatStatus == UNKNOWN) { solver.pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotImplementedError() + throw NotYetImplementedException("MaxSMT solution was not found --- processUnsat returned UNKNOWN") } } @@ -152,7 +153,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotImplementedError() + throw NotYetImplementedException("MaxSMT solution was not found --- solver returned UNKNOWN") } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt new file mode 100644 index 000000000..c6724b962 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt @@ -0,0 +1,6 @@ +package io.ksmt.solver.maxsmt.solvers.exceptions + +internal class NotYetImplementedException : RuntimeException { + constructor() : super() + constructor(message: String) : super(message) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 48ed5ac48..6a28d286c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,6 +22,7 @@ include("ksmt-runner:solver-generator") include("ksmt-test") include("ksmt-symfpu") + include("ksmt-maxsmt") include("ksmt-maxsmt-test") From 0f161f44ca5ca9ac730670125c7ad9fb2228267c Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 25 Jan 2024 12:23:20 +0300 Subject: [PATCH 141/228] Refactor KPMResSolver --- .../solver/maxsmt/solvers/KPMResSolver.kt | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index d64628601..f95e8c972 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -13,6 +13,7 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.solvers.exceptions.NotYetImplementedException import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.TimerUtils @@ -23,8 +24,6 @@ import kotlin.time.TimeSource.Monotonic.markNow class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : KMaxResSolver(ctx, solver) { - private var currentMaxSMTResult: Triple>, KModel?> = - Triple(null, listOf(), null) private var collectStatistics = false private val maxSmtCtx = KMaxSMTContext( strategy = PrimalMaxRes, @@ -47,14 +46,8 @@ class KPMResSolver(private val ctx: KContext, private maxSMTStatistics.timeoutMs = timeout.inWholeMilliseconds } - solver.push() - val maxSMTResult = runMaxSMTLogic(timeout) - solver.pop() - - currentMaxSMTResult = Triple(UNKNOWN, listOf(), null) - if (softConstraints.isEmpty()) { if (this.collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds @@ -65,17 +58,11 @@ class KPMResSolver(private val ctx: KContext, private // TODO: get max SAT soft constraints subset if (!maxSMTResult.maxSMTSucceeded) { - val (solverStatus, _, model) = currentMaxSMTResult if (this.collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return when (solverStatus) { - SAT -> processSat(model!!) - UNSAT -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving - UNKNOWN -> KMaxSMTResult(listOf(), SAT, false) // TODO: support anytime solving - null -> error("Unexpected status: null") - } + throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") } if (this.collectStatistics) { @@ -108,6 +95,8 @@ class KPMResSolver(private val ctx: KContext, private return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } + solver.push() + var i = 0 var formula = softConstraints.toMutableList() @@ -115,6 +104,7 @@ class KPMResSolver(private val ctx: KContext, private val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, markHardConstraintsCheckStart) if (TimerUtils.timeoutExceeded(checkRemainingTime)) { + solver.pop() return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } @@ -127,16 +117,15 @@ class KPMResSolver(private val ctx: KContext, private maxSMTStatistics.timeInSolverQueriesMs += markCheckSatStart.elapsedNow().inWholeMilliseconds } - if (solverStatus == UNKNOWN) { - // TODO: get max SAT soft constraints subset - return KMaxSMTResult(listOf(), hardConstraintsStatus, false) - } - - currentMaxSMTResult = Triple(solverStatus, unsatCore, model) - if (solverStatus == SAT) { + solver.pop() return processSat(model!!) } + else if (solverStatus == UNKNOWN) { + // TODO: get max SAT soft constraints subset + solver.pop() + throw NotYetImplementedException("MaxSMT solution was not found --- solver returned UNKNOWN") + } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) From fe3b2728a016fbda19c14755a31f81daa33347a8 Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 25 Jan 2024 12:25:00 +0300 Subject: [PATCH 142/228] Fix a bug with a core to soft constraints transformation --- .../main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt index e54a6f6c0..136951d5a 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/CoreUtils.kt @@ -8,11 +8,13 @@ internal object CoreUtils { fun coreToSoftConstraints(core: List>, assumptions: List): List { val softs = mutableListOf() - for (element in core) { - val softElement = assumptions.find { it.expression == element } + val potentialSofts = assumptions.filter { core.contains(it.expression) }.toMutableList() - require(softElement != null) { "Assumptions do not contain an element from the core" } + for (element in core) { + val softIndex = potentialSofts.indexOfFirst { it.expression == element } + val softElement = potentialSofts[softIndex] + potentialSofts.removeAt(softIndex) softs.add(softElement) } From d2a0f3e7cbfd8c4139d3eb7c99e20acea09ae60d Mon Sep 17 00:00:00 2001 From: Victoria Date: Thu, 25 Jan 2024 16:27:41 +0300 Subject: [PATCH 143/228] Add GSON version to properties --- ksmt-maxsmt-test/build.gradle.kts | 2 +- version.properties | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index bf23fafa2..500cdeb7b 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { testImplementation("io.github.oshai:kotlin-logging-jvm:${versions["kotlin-logging-jvm"]}") testImplementation("org.slf4j:slf4j-log4j12:${versions["slf4j-log4j12"]}") - testImplementation("com.google.code.gson:gson:2.10.1") + testImplementation("com.google.code.gson:gson:${versions["gson"]}") } val maxSmtBenchmarks = listOfNotNull( diff --git a/version.properties b/version.properties index 9d581871a..fb43dda9b 100644 --- a/version.properties +++ b/version.properties @@ -7,4 +7,7 @@ kotlinx-coroutines=1.7.2 # Logging kotlin-logging-jvm=6.0.3 -slf4j-log4j12=2.0.11 \ No newline at end of file +slf4j-log4j12=2.0.11 + +# Google GSON for JSON +gson=2.10.1 From f03b984a1f97087c9160ca2f3ddff0fc22bfee21 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 28 Apr 2024 21:19:09 +0300 Subject: [PATCH 144/228] Add checkSubOptMaxSMT method to KPrimalDualMaxResSolver --- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 165 +++++++++++++++++- 1 file changed, 162 insertions(+), 3 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index bc2fc4dd2..f5d53c868 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -46,6 +46,155 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) + // TODO: may be we should return KMaxSMTSubOptResult? + fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { + val markCheckMaxSMTStart = markNow() + markLoggingPoint = markCheckMaxSMTStart + + if (TimerUtils.timeoutExceeded(timeout)) { + error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") + } + + this.collectStatistics = collectStatistics + + if (this.collectStatistics) { + maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx) + maxSMTStatistics.timeoutMs = timeout.inWholeMilliseconds + } + + val markHardConstraintsCheckStart = markNow() + val hardConstraintsStatus = solver.check(timeout) + if (this.collectStatistics) { + maxSMTStatistics.queriesToSolverNumber++ + maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds + } + + if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + return KMaxSMTResult(listOf(), hardConstraintsStatus, true) + } else if (hardConstraintsStatus == UNKNOWN) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + return KMaxSMTResult(listOf(), hardConstraintsStatus, false) + } + + solver.push() + initMaxSMT() + + val assumptions = softConstraints.toMutableList() + + while (_lower < _upper) { + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] Iteration number: $_iteration" + } + markLoggingPoint = markNow() + + val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) + if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + return getSubOptMaxSMTResult(true) + } + + val status = checkSatHillClimb(assumptions, timeout) + + when (status) { + SAT -> { + when (maxSmtCtx.strategy) { + PrimalMaxRes -> _upper = _lower + + PrimalDualMaxRes -> { + val correctionSet = getCorrectionSet(solver.model(), assumptions) + if (correctionSet.isEmpty()) { + if (_model != null) { + // Feasible optimum is found by the moment. + _lower = _upper + } + } else { + processSat(correctionSet, assumptions) + } + } + + else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") + } + } + + UNSAT -> { + val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) + if (TimerUtils.timeoutExceeded(remainingTime)) { + solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + return getSubOptMaxSMTResult(true) + } + + val processUnsatStatus = processUnsat(assumptions, remainingTime) + if (processUnsatStatus == UNSAT) { + _lower = _upper + // TODO: process this case as it can happen when timeout exceeded??? + solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + // TODO: is it Ok? + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + return getSubOptMaxSMTResult(true) + } else if (processUnsatStatus == UNKNOWN) { + solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + return getSubOptMaxSMTResult(true) + } + } + + UNKNOWN -> { + solver.pop() + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + return getSubOptMaxSMTResult(true) + } + } + + ++_iteration + } + + _lower = _upper + + val result = KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, true) + logger.info { + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" + } + + solver.pop() + + if (collectStatistics) { + maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds + } + return result + } + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart @@ -117,6 +266,8 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } + + else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } @@ -339,8 +490,8 @@ class KPrimalDualMaxResSolver( val upper = ModelUtils.getModelCost(ctx, model, softConstraints) - if (upper > _upper) { - logger.info { "Found model has less weight --- model is not updated" } + if (_model != null && upper >= _upper) { + logger.info { "Found model has less or equal weight --- model is not updated" } return } @@ -484,7 +635,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper)" + - " --- model is initialized with null" + " --- model is initialized with null" } markLoggingPoint = markNow() @@ -572,4 +723,12 @@ class KPrimalDualMaxResSolver( return status } + + private fun getSubOptMaxSMTResult(maxSMTSucceeded: Boolean): KMaxSMTResult { + return if (_model != null) { + KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, maxSMTSucceeded) + } else { + KMaxSMTResult(listOf(), SAT, maxSMTSucceeded) + } + } } From 60ff5c3bdb7e968cc75c9f5b9cef3034eadfb06e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Tue, 30 Apr 2024 14:00:24 +0300 Subject: [PATCH 145/228] Add test run for Portfolio --- .../KDualMaxRes2SMTBenchmarkTest.kt | 11 +- .../KDualMaxRes3SMTBenchmarkTest.kt | 11 +- .../KDualMaxRes4SMTBenchmarkTest.kt | 11 +- .../KDualMaxResSMTBenchmarkTest.kt | 3 +- .../KPMResSMTBenchmarkTest.kt | 3 +- .../KPrimalMaxRes2SMTBenchmarkTest.kt | 18 +- .../KPrimalMaxRes3SMTBenchmarkTest.kt | 3 +- .../KPrimalMaxRes4SMTBenchmarkTest.kt | 3 +- .../KPrimalMaxResSMTBenchmarkTest.kt | 3 +- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 16 +- .../test/statistics/JsonStatisticsHelper.kt | 21 +- .../statistics/SubOptMaxSMTTestStatistics.kt | 18 ++ .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 290 ++++++++++++++++++ .../ksmt/solver/maxsmt/test/utils/Solver.kt | 2 +- 14 files changed, 394 insertions(+), 19 deletions(-) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KDualMaxRes2SMTBenchmarkTest.kt (52%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KDualMaxRes3SMTBenchmarkTest.kt (52%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KDualMaxRes4SMTBenchmarkTest.kt (52%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KDualMaxResSMTBenchmarkTest.kt (82%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KPMResSMTBenchmarkTest.kt (80%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KPrimalMaxRes2SMTBenchmarkTest.kt (54%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KPrimalMaxRes3SMTBenchmarkTest.kt (85%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KPrimalMaxRes4SMTBenchmarkTest.kt (86%) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/{smt => configurations}/KPrimalMaxResSMTBenchmarkTest.kt (85%) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt similarity index 52% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt index 5952cf118..68079c47c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt @@ -1,9 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { @@ -12,3 +14,10 @@ class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) } } + +class KSubOptDualMaxRes2SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt similarity index 52% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt index 400280342..b97232736 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt @@ -1,9 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { @@ -12,3 +14,10 @@ class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = true)) } } + +class KSubOptDualMaxRes3SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = true)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt similarity index 52% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt index ffbc01614..96237ad54 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt @@ -1,9 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { @@ -12,3 +14,10 @@ class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = true)) } } + +class KSubOptDualMaxRes4SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = true)) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt similarity index 82% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt index 0f261efd2..af969a86e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt @@ -1,9 +1,10 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt similarity index 80% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt index e281ce941..e7c312b2f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt @@ -1,8 +1,9 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt similarity index 54% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt index 022079f69..307acc374 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt @@ -1,10 +1,12 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { @@ -20,3 +22,17 @@ class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { ) } } + +class KSubOptPrimalMaxRes2SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { + override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPrimalDualMaxResSolver( + this, + smtSolver, + KMaxSMTContext( + strategy = PrimalMaxRes, + preferLargeWeightConstraintsForCores = true, + ), + ) + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt similarity index 85% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt index 9331cdb46..c8ca21141 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt @@ -1,10 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt similarity index 86% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt index 5a954c79f..032b76960 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt @@ -1,10 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt similarity index 85% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt index 89db2bcfc..8e875d9f3 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KPrimalMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt @@ -1,10 +1,11 @@ -package io.ksmt.solver.maxsmt.test.smt +package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index b2bb68851..162378444 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -23,9 +23,11 @@ import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -56,6 +58,15 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { BITWUZLA -> KBitwuzlaSolver(this) CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) + PORTFOLIO -> { + val solverManager = KPortfolioSolverManager( + listOf( + // CVC5 often runs out of memory... + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class + ) + ) + solverManager.createPortfolioSolver(this) + } } } @@ -182,13 +193,12 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { maxSmtTestInfo.satSoftConstraintsWeightsSum, satSoftConstraintsWeightsSum.toULong(), "Soft constraints weights sum was [$satSoftConstraintsWeightsSum], " + - "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", ) testStatistics.passed = true } catch (ex: Exception) { logger.error { ex.message + System.lineSeparator() } - } - finally { + } finally { jsonHelper.appendTestStatisticsToFile(testStatistics) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt index 5705c7d4f..42754c561 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt @@ -14,7 +14,22 @@ internal class JsonStatisticsHelper(private val jsonFile: File) { } } + fun appendTestStatisticsToFile(statistics: SubOptMaxSMTTestStatistics) { + processBeforeAppending() + jsonFile.appendText(gson.toJson(statistics)) + } + fun appendTestStatisticsToFile(statistics: MaxSMTTestStatistics) { + processBeforeAppending() + jsonFile.appendText(gson.toJson(statistics)) + } + + fun markLastTestStatisticsAsProcessed() { + lastMet = true + jsonFile.appendText("\n]\n}") + } + + private fun processBeforeAppending() { require(!lastMet) { "It's not allowed to append statistics when the last test is processed" } if (firstMet) { @@ -23,11 +38,5 @@ internal class JsonStatisticsHelper(private val jsonFile: File) { jsonFile.appendText("{\n\"TESTS\": [\n") firstMet = true } - jsonFile.appendText(gson.toJson(statistics)) - } - - fun markLastTestStatisticsAsProcessed() { - lastMet = true - jsonFile.appendText("\n]\n}") } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt new file mode 100644 index 000000000..e4c5cc6c3 --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt @@ -0,0 +1,18 @@ +package io.ksmt.solver.maxsmt.test.statistics + +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics +import io.ksmt.solver.maxsmt.test.utils.Solver + +internal data class SubOptMaxSMTTestStatistics(val name: String, var smtSolver: Solver) { + var maxSMTCallStatistics: KMaxSMTStatistics? = null + var passed = false + var ignoredTest = false + var failedOnParsingOrConvertingExpressions = false + var exceptionMessage: String? = null + /** + * It's wrong when it's more than optimal. + */ + var checkedSoftConstraintsSumIsWrong = false + var optimalWeight: ULong = 0U + var foundSoFarWeight: ULong = 0U +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt new file mode 100644 index 000000000..bff5c2aca --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -0,0 +1,290 @@ +package io.ksmt.solver.maxsmt.test.subopt + +import io.github.oshai.kotlinlogging.KotlinLogging +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.core.KsmtWorkerArgs +import io.ksmt.runner.core.KsmtWorkerFactory +import io.ksmt.runner.core.KsmtWorkerPool +import io.ksmt.runner.core.RdServer +import io.ksmt.runner.core.WorkerInitializationFailedException +import io.ksmt.runner.generated.models.TestProtocolModel +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo +import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper +import io.ksmt.solver.maxsmt.test.statistics.SubOptMaxSMTTestStatistics +import io.ksmt.solver.maxsmt.test.utils.Solver +import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA +import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 +import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO +import io.ksmt.solver.maxsmt.test.utils.Solver.YICES +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 +import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.portfolio.KPortfolioSolverManager +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.sort.KBoolSort +import io.ksmt.test.TestRunner +import io.ksmt.test.TestWorker +import io.ksmt.test.TestWorkerProcess +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.extension +import kotlin.system.measureTimeMillis +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { + return when (solver) { + Z3 -> KZ3Solver(this) + BITWUZLA -> KBitwuzlaSolver(this) + CVC5 -> KCvc5Solver(this) + YICES -> KYicesSolver(this) + PORTFOLIO -> { + val solverManager = KPortfolioSolverManager( + listOf( + // CVC5 often runs out of memory... + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class + ) + ) + solverManager.createPortfolioSolver(this) + } + } + } + + abstract fun getSolver(solver: Solver): KPrimalDualMaxResSolver + + protected val ctx: KContext = KContext() + private lateinit var maxSMTSolver: KPrimalDualMaxResSolver + private val logger = KotlinLogging.logger {} + + private fun initSolver(solver: Solver) { + maxSMTSolver = getSolver(solver) + } + + @AfterEach + fun closeSolver() = maxSMTSolver.close() + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTZ3Test(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> assertions }, Z3) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTCvc5Test(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTYicesTest(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTPortfolioTest(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, PORTFOLIO) + } + + private fun testMaxSMTSolver( + name: String, + samplePath: Path, + mkKsmtAssertions: suspend TestRunner.(List>) -> List>, + solver: Solver = Z3, + ) { + initSolver(solver) + + val extension = "smt2" + require(samplePath.extension == extension) { + "File extension cannot be '${samplePath.extension}' as it must be $extension" + } + + logger.info { "Test name: [$name]" } + + lateinit var ksmtAssertions: List> + val testStatistics = SubOptMaxSMTTestStatistics(name, solver) + + try { + testWorkers.withWorker(ctx) { worker -> + val assertions = worker.parseFile(samplePath) + val convertedAssertions = worker.convertAssertions(assertions) + ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + } + } catch (ex: IgnoreTestException) { + testStatistics.ignoredTest = true + testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } + throw ex + } catch (ex: Exception) { + testStatistics.failedOnParsingOrConvertingExpressions = true + testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } + throw ex + } + + val maxSmtTestPath = File(samplePath.toString().removeSuffix(extension) + "maxsmt").toPath() + val maxSmtTestInfo = parseMaxSMTTestInfo(maxSmtTestPath) + + val optimalWeight = maxSmtTestInfo.satSoftConstraintsWeightsSum + testStatistics.optimalWeight = optimalWeight + + val softConstraintsSize = maxSmtTestInfo.softConstraintsWeights.size + + val softExpressions = + ksmtAssertions.subList(ksmtAssertions.size - softConstraintsSize, ksmtAssertions.size) + val hardExpressions = ksmtAssertions.subList(0, ksmtAssertions.size - softConstraintsSize) + + hardExpressions.forEach { + maxSMTSolver.assert(it) + } + + maxSmtTestInfo.softConstraintsWeights + .zip(softExpressions) + .forEach { (weight, expr) -> + maxSMTSolver.assertSoft(expr, weight) + } + + lateinit var maxSMTResult: KMaxSMTResult + val elapsedTime = measureTimeMillis { + try { + maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(10.seconds, true) + } catch (ex: Exception) { + testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() + testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } + throw ex + } + } + val foundSoFarWeight = maxSMTResult.satSoftConstraints.sumOf { it.weight.toULong() } + testStatistics.foundSoFarWeight = foundSoFarWeight + + testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() + + logger.info { "Elapsed time: $elapsedTime ms --- SubOpt MaxSMT call${System.lineSeparator()}" } + + try { + assertTrue(maxSMTResult.maxSMTSucceeded, "SubOpt MaxSMT was not successful [$name]") + assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") + + if (optimalWeight >= foundSoFarWeight) { + testStatistics.checkedSoftConstraintsSumIsWrong = true + } + assertTrue( + optimalWeight >= foundSoFarWeight, + "Soft constraints weights sum was [$foundSoFarWeight], " + + "but must be no more than [${optimalWeight}]", + ) + + testStatistics.passed = true + } catch (ex: Exception) { + logger.error { ex.message + System.lineSeparator() } + } finally { + jsonHelper.appendTestStatisticsToFile(testStatistics) + } + } + + private fun KsmtWorkerPool.withWorker( + ctx: KContext, + body: suspend (TestRunner) -> Unit, + ) = runBlocking { + val worker = try { + getOrCreateFreeWorker() + } catch (ex: WorkerInitializationFailedException) { + logger.error { ex.message + System.lineSeparator() } + ignoreTest { "worker initialization failed -- ${ex.message}" } + } + worker.astSerializationCtx.initCtx(ctx) + worker.lifetime.onTermination { + worker.astSerializationCtx.resetCtx() + } + try { + TestRunner(ctx, TEST_WORKER_SINGLE_OPERATION_TIMEOUT, worker).let { + try { + it.init() + body(it) + } finally { + it.delete() + } + } + } catch (ex: TimeoutCancellationException) { + logger.error { ex.message + System.lineSeparator() } + ignoreTest { "worker timeout -- ${ex.message}" } + } finally { + worker.release() + } + } + + // See [handleIgnoredTests] + private inline fun ignoreTest(message: () -> String?): Nothing { + throw IgnoreTestException(message()) + } + + class IgnoreTestException(message: String?) : Exception(message) + + companion object { + val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 25.seconds + + internal lateinit var testWorkers: KsmtWorkerPool + private lateinit var jsonHelper: JsonStatisticsHelper + + @BeforeAll + @JvmStatic + fun initWorkerPools() { + testWorkers = KsmtWorkerPool( + maxWorkerPoolSize = 4, + workerProcessIdleTimeout = 10.minutes, + workerFactory = object : KsmtWorkerFactory { + override val childProcessEntrypoint = TestWorkerProcess::class + override fun updateArgs(args: KsmtWorkerArgs): KsmtWorkerArgs = args + override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) + }, + ) + + jsonHelper = + JsonStatisticsHelper( + File( + "${ + Paths.get("").toAbsolutePath() + }/src/test/resources/subopt-maxsmt-statistics-${getRandomString(16)}.json", + ), + ) + } + + @AfterAll + @JvmStatic + fun closeWorkerPools() { + testWorkers.terminate() + jsonHelper.markLastTestStatisticsAsProcessed() + } + } +} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt index dcfbc03b9..568e2453a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt @@ -1,5 +1,5 @@ package io.ksmt.solver.maxsmt.test.utils enum class Solver { - Z3, BITWUZLA, CVC5, YICES + Z3, BITWUZLA, CVC5, YICES, PORTFOLIO } From ae059fc26e08b89f0b55854092026289e8f79948 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Tue, 30 Apr 2024 14:02:21 +0300 Subject: [PATCH 146/228] Fix comment for MinimalUnsatCore --- .../io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt index c825f47d5..48630ba7f 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/utils/MinimalUnsatCore.kt @@ -26,7 +26,7 @@ internal class MinimalUnsatCore( fun getBestModel(): Pair = _minimalUnsatCoreModel.getBestModel() - // If solver starts returning unknown or exceeds the timeout, we return non minimized unsat core. + // If solver starts returning unknown or exceeds the timeout, non-minimized unsat core is returned. fun tryGetMinimalUnsatCore( assumptions: List, timeout: Duration = Duration.INFINITE, From 4aa0762475163935a5db8b40ebf7db34ac13211a Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Tue, 30 Apr 2024 14:05:01 +0300 Subject: [PATCH 147/228] Add default params to checkSubOptMaxSMT --- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index f5d53c868..ad29130d4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -29,8 +29,7 @@ class KPrimalDualMaxResSolver( private val ctx: KContext, private val solver: KSolver, private val maxSmtCtx: KMaxSMTContext, -) : - KMaxResSolver(ctx, solver) { +) : KMaxResSolver(ctx, solver) { private var _lower: UInt = 0u // Current lower frontier private var _upper: UInt = 0u // Current upper frontier private var _maxUpper = 0u // Max possible upper frontier @@ -47,7 +46,7 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) // TODO: may be we should return KMaxSMTSubOptResult? - fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { + fun checkSubOptMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult { val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart @@ -634,8 +633,7 @@ class KPrimalDualMaxResSolver( _iteration = 0 logger.info { - "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper)" + - " --- model is initialized with null" + "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] (lower bound: $_lower, upper bound: $_upper)" + " --- model is initialized with null" } markLoggingPoint = markNow() @@ -724,11 +722,6 @@ class KPrimalDualMaxResSolver( return status } - private fun getSubOptMaxSMTResult(maxSMTSucceeded: Boolean): KMaxSMTResult { - return if (_model != null) { - KMaxSMTResult(getSatSoftConstraintsByModel(_model!!), SAT, maxSMTSucceeded) - } else { - KMaxSMTResult(listOf(), SAT, maxSMTSucceeded) - } - } + private fun getSubOptMaxSMTResult(maxSMTSucceeded: Boolean): KMaxSMTResult = + KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, maxSMTSucceeded) } From 448f1f03ff3be970dd6530d20b760827479cb931 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 1 May 2024 02:36:46 +0300 Subject: [PATCH 148/228] Implement portfolio mode with MaxSMT logic in the backend process --- .../KDualMaxRes2SMTBenchmarkTest.kt | 13 +++- .../KDualMaxRes3SMTBenchmarkTest.kt | 12 +++- .../KDualMaxRes4SMTBenchmarkTest.kt | 12 +++- .../KDualMaxResSMTBenchmarkTest.kt | 4 +- .../configurations/KPMResSMTBenchmarkTest.kt | 4 ++ .../KPrimalMaxRes2SMTBenchmarkTest.kt | 32 ++++----- .../KPrimalMaxRes3SMTBenchmarkTest.kt | 4 +- .../KPrimalMaxRes4SMTBenchmarkTest.kt | 8 +-- .../KPrimalMaxResSMTBenchmarkTest.kt | 2 + .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 8 ++- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 34 +++++++-- .../test/utils/{Solver.kt => SolverUtil.kt} | 4 ++ ksmt-maxsmt/build.gradle.kts | 1 + .../solver/maxsmt/solvers/KMaxResSolver.kt | 4 +- .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 23 +++--- .../maxsmt/solvers/KMaxSMTSolverInterface.kt | 19 +++++ .../solver/maxsmt/solvers/KPMResSolver.kt | 4 ++ .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 2 +- .../solvers/runner/KMaxSMTPortfolioSolver.kt | 72 +++++++++++++++++++ .../runner/KMaxSMTPortfolioSolverManager.kt | 39 ++++++++++ .../solvers/runner/KMaxSMTSolverRunner.kt | 45 ++++++++++++ .../runner/KMaxSMTSolverRunnerManager.kt | 72 +++++++++++++++++++ .../ksmt/solver/portfolio/KPortfolioSolver.kt | 30 ++++---- .../portfolio/KPortfolioSolverManager.kt | 2 +- .../io/ksmt/solver/runner/KSolverRunner.kt | 2 +- .../solver/runner/KSolverRunnerManager.kt | 6 +- 26 files changed, 383 insertions(+), 75 deletions(-) rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/{Solver.kt => SolverUtil.kt} (64%) create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt index 68079c47c..8fa668ddb 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt @@ -1,23 +1,30 @@ package io.ksmt.solver.maxsmt.test.configurations +import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(preferLargeWeightConstraintsForCores = true) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } class KSubOptDualMaxRes2SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { - override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + override val maxSmtCtx = KMaxSMTContext(preferLargeWeightConstraintsForCores = true) + + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt index b97232736..ee29243a4 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt @@ -3,21 +3,27 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(minimizeCores = true) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = true)) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } class KSubOptDualMaxRes3SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { - override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + override val maxSmtCtx = KMaxSMTContext(minimizeCores = true) + + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(minimizeCores = true)) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt index 96237ad54..98d55cdf9 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt @@ -3,21 +3,27 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(getMultipleCores = true) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = true)) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } class KSubOptDualMaxRes4SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { - override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + override val maxSmtCtx = KMaxSMTContext(getMultipleCores = true) + + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(getMultipleCores = true)) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt index af969a86e..b14b5ffd0 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt @@ -8,8 +8,10 @@ import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext() + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext()) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt index e7c312b2f..21039b646 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt @@ -1,12 +1,16 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + // TODO: in fact for KPMRes we don't need MaxSMTContext. + override val maxSmtCtx = KMaxSMTContext() + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPMResSolver(this, smtSolver) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt index 307acc374..23faee291 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt @@ -4,35 +4,33 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext( + strategy = PrimalMaxRes, + preferLargeWeightConstraintsForCores = true, + ) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver( - this, - smtSolver, - KMaxSMTContext( - strategy = PrimalMaxRes, - preferLargeWeightConstraintsForCores = true, - ), - ) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } class KSubOptPrimalMaxRes2SMTBenchmarkTest : KSubOptMaxSMTBenchmarkTest() { - override fun getSolver(solver: Solver): KPrimalDualMaxResSolver = with(ctx) { + override val maxSmtCtx = KMaxSMTContext( + strategy = PrimalMaxRes, + preferLargeWeightConstraintsForCores = true, + ) + + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver( - this, - smtSolver, - KMaxSMTContext( - strategy = PrimalMaxRes, - preferLargeWeightConstraintsForCores = true, - ), - ) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt index c8ca21141..bb5b28f93 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt @@ -9,8 +9,10 @@ import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = true) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = true)) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt index 032b76960..8da1e3cab 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt @@ -9,12 +9,10 @@ import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = true) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver( - this, - smtSolver, - KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = true), - ) + return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt index 8e875d9f3..8c76e6d7c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt @@ -9,6 +9,8 @@ import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { + override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes)) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 162378444..a7adde164 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -14,8 +14,10 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.bitwuzla.KBitwuzlaSolver import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolverManager import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper @@ -27,7 +29,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString -import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -59,13 +60,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - val solverManager = KPortfolioSolverManager( + val solverManager = KMaxSMTPortfolioSolverManager( listOf( // CVC5 often runs out of memory... KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class ) ) - solverManager.createPortfolioSolver(this) + solverManager.createMaxSMTPortfolioSolver(this, maxSmtCtx) } } } @@ -73,6 +74,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { abstract fun getSolver(solver: Solver): KMaxSMTSolver protected val ctx: KContext = KContext() + protected abstract val maxSmtCtx: KMaxSMTContext private lateinit var maxSMTSolver: KMaxSMTSolver private val logger = KotlinLogging.logger {} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index bff5c2aca..bb4e5aecd 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -14,12 +14,20 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.bitwuzla.KBitwuzlaSolver import io.ksmt.solver.cvc5.KCvc5Solver +import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolver +import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolverManager +import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTSolverRunner import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.SubOptMaxSMTTestStatistics +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 @@ -27,7 +35,6 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString -import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -52,6 +59,22 @@ import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { + protected fun getMaxSmtSolver( + maxSmtSolver: MaxSmtSolver, + solver: KSolver + ): KMaxSMTSolverInterface { + when (maxSmtSolver) { + MaxSmtSolver.PMRES -> return KPMResSolver(ctx, solver) + MaxSmtSolver.PRIMAL_DUAL_MAXRES -> { + // Thus, MaxSMT algorithm will be executed in the backend process. + if (solver is KMaxSMTPortfolioSolver) { + return solver + } + return KPrimalDualMaxResSolver(ctx, solver, maxSmtCtx) + } + } + } + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { return when (solver) { Z3 -> KZ3Solver(this) @@ -59,21 +82,22 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - val solverManager = KPortfolioSolverManager( + val solverManager = KMaxSMTPortfolioSolverManager( listOf( // CVC5 often runs out of memory... KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class ) ) - solverManager.createPortfolioSolver(this) + solverManager.createMaxSMTPortfolioSolver(this, maxSmtCtx) } } } - abstract fun getSolver(solver: Solver): KPrimalDualMaxResSolver + abstract fun getSolver(solver: Solver): KMaxSMTSolverInterface protected val ctx: KContext = KContext() - private lateinit var maxSMTSolver: KPrimalDualMaxResSolver + protected abstract val maxSmtCtx: KMaxSMTContext + private lateinit var maxSMTSolver: KMaxSMTSolverInterface private val logger = KotlinLogging.logger {} private fun initSolver(solver: Solver) { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt similarity index 64% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt index 568e2453a..901cacb92 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/Solver.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt @@ -3,3 +3,7 @@ package io.ksmt.solver.maxsmt.test.utils enum class Solver { Z3, BITWUZLA, CVC5, YICES, PORTFOLIO } + +enum class MaxSmtSolver { + PMRES, PRIMAL_DUAL_MAXRES +} diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index 30bd28d1d..39a55a0ce 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -12,6 +12,7 @@ val versions = loadProperties(projectDir.parentFile.resolve("version.properties" dependencies { implementation(project(":ksmt-core")) + implementation(project(":ksmt-runner")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") implementation("io.github.oshai:kotlin-logging-jvm:${versions["kotlin-logging-jvm"]}") diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt index c955a8fc8..a68ee7ebd 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt @@ -7,8 +7,8 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.utils.CoreUtils abstract class KMaxResSolver( - private val ctx: KContext, - private val solver: KSolver, + ctx: KContext, + solver: KSolver, ) : KMaxSMTSolver(ctx, solver) where T : KSolverConfiguration { protected fun removeCoreAssumptions(core: List, assumptions: MutableList) { assumptions.removeAll(core) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index c4b918fc2..4bcb6bed7 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -16,8 +16,7 @@ import kotlin.time.Duration abstract class KMaxSMTSolver( private val ctx: KContext, private val solver: KSolver, -) : KSolver - where T : KSolverConfiguration { +) : KMaxSMTSolverInterface where T : KSolverConfiguration { private val scopeManager = MaxSMTScopeManager() protected var softConstraints = mutableListOf() protected lateinit var maxSMTStatistics: KMaxSMTStatistics @@ -27,7 +26,7 @@ abstract class KMaxSMTSolver( * * @see checkMaxSMT * */ - fun assertSoft(expr: KExpr, weight: UInt) { + override fun assertSoft(expr: KExpr, weight: UInt) { require(weight > 0u) { "Soft constraint weight cannot be equal to $weight as it must be greater than 0" } val softConstraint = SoftConstraint(expr, weight) @@ -40,7 +39,7 @@ abstract class KMaxSMTSolver( * * @throws NotImplementedError */ - fun checkMaxSMT(timeout: Duration = Duration.INFINITE): KMaxSMTResult = + fun checkMaxSMT(timeout: Duration): KMaxSMTResult = checkMaxSMT(timeout, collectStatistics = false) /** @@ -50,15 +49,12 @@ abstract class KMaxSMTSolver( * * @throws NotImplementedError */ - abstract fun checkMaxSMT( - timeout: Duration = Duration.INFINITE, - collectStatistics: Boolean, - ): KMaxSMTResult + abstract override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult /** * Get last MaxSMT launch statistics (number of queries to solver, MaxSMT timeout etc.). */ - fun collectMaxSMTStatistics(): KMaxSMTStatistics { + override fun collectMaxSMTStatistics(): KMaxSMTStatistics { require(this::maxSMTStatistics.isInitialized) { "MaxSMT statistics is only available after MaxSMT launches with statistics collection enabled" } @@ -67,10 +63,15 @@ abstract class KMaxSMTSolver( } protected fun getSatSoftConstraintsByModel(model: KModel): List { - return softConstraints.filter { model.eval(it.expression, true) == ctx.trueExpr } + return softConstraints.filter { + model.eval(it.expression, true) == ctx.trueExpr || model.eval( + it.expression, + true, + ) != ctx.falseExpr + } } - override fun configure(configurator: KSolverConfiguration.() -> Unit) { + override fun configure(configurator: T.() -> Unit) { solver.configure(configurator) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt new file mode 100644 index 000000000..e6efbf968 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt @@ -0,0 +1,19 @@ +package io.ksmt.solver.maxsmt.solvers + +import io.ksmt.expr.KExpr +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics +import io.ksmt.sort.KBoolSort +import kotlin.time.Duration + +interface KMaxSMTSolverInterface : KSolver where C : KSolverConfiguration { + fun assertSoft(expr: KExpr, weight: UInt) + + fun checkMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult + + fun checkSubOptMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult + + fun collectMaxSMTStatistics(): KMaxSMTStatistics +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index f95e8c972..ba1884088 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -72,6 +72,10 @@ class KPMResSolver(private val ctx: KContext, private return maxSMTResult } + override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { + TODO("Not yet implemented") + } + private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { val markHardConstraintsCheckStart = markNow() val hardConstraintsStatus = solver.check(timeout) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index ad29130d4..ecba7a275 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -46,7 +46,7 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) // TODO: may be we should return KMaxSMTSubOptResult? - fun checkSubOptMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult { + override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt new file mode 100644 index 000000000..eb3254652 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt @@ -0,0 +1,72 @@ +package io.ksmt.solver.maxsmt.solvers.runner + +import io.ksmt.expr.KExpr +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverException +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics +import io.ksmt.solver.portfolio.KPortfolioSolver +import io.ksmt.solver.runner.KSolverRunner +import io.ksmt.sort.KBoolSort +import kotlinx.coroutines.runBlocking +import kotlin.reflect.KClass +import kotlin.time.Duration + +class KMaxSMTPortfolioSolver( + solverRunners: List>, KMaxSMTSolverRunner<*>>> +) : KPortfolioSolver(solverRunners), KMaxSMTSolverInterface { + override fun assertSoft(expr: KExpr, weight: UInt) = runBlocking { + solverOperation { + (this as KMaxSMTSolverRunner<*>).assertSoft(expr, weight) + } + } + + override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean) + : KMaxSMTResult = runBlocking { + val solverAwaitResult = solverOperationWithResult { + (this as KMaxSMTSolverRunner<*>).checkSubOptMaxSMT(timeout, collectStatistics) + } + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) + : KMaxSMTResult = runBlocking { + val solverAwaitResult = solverOperationWithResult { + (this as KMaxSMTSolverRunner<*>).checkMaxSMT(timeout, collectStatistics) + } + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + + override fun collectMaxSMTStatistics(): KMaxSMTStatistics = runBlocking { + val solverAwaitResult = solverOperationWithResult { + (this as KMaxSMTSolverRunner<*>).collectMaxSMTStatistics() + } + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + + private suspend inline fun solverOperationWithResult( + crossinline block: suspend KSolverRunner<*>.() -> T + ): SolverAwaitResult { + val result = awaitFirstSolver(block) { true } + if (result is SolverAwaitFailure<*>) { + // throw exception if all solvers in portfolio failed with exception + result.findSuccessOrThrow() + } + + return result + } +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt new file mode 100644 index 000000000..f439e40ef --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt @@ -0,0 +1,39 @@ +package io.ksmt.solver.maxsmt.solvers.runner + +import io.ksmt.KContext +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.utils.uncheckedCast +import kotlin.reflect.KClass +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +class KMaxSMTPortfolioSolverManager( + private val solvers: List>>, + portfolioPoolSize: Int = DEFAULT_PORTFOLIO_POOL_SIZE, + hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, + workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT +) : KMaxSMTSolverRunnerManager( + workerPoolSize = portfolioPoolSize * solvers.size, + hardTimeout = hardTimeout, + workerProcessIdleTimeout = workerProcessIdleTimeout +) { + init { + require(solvers.isNotEmpty()) { "Empty MaxSMT solver portfolio" } + } + + fun createMaxSMTPortfolioSolver(ctx: KContext, maxSmtCtx: KMaxSMTContext): KMaxSMTPortfolioSolver { + val solverInstances = solvers.map { + val solverType: KClass> = it.uncheckedCast() + it to createMaxSMTSolver(ctx, maxSmtCtx, solverType) + } + return KMaxSMTPortfolioSolver(solverInstances) + } + + companion object { + const val DEFAULT_PORTFOLIO_POOL_SIZE = 1 + val DEFAULT_HARD_TIMEOUT = 10.seconds + val DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT = 100.seconds + } +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt new file mode 100644 index 000000000..94723e7d1 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt @@ -0,0 +1,45 @@ +package io.ksmt.solver.maxsmt.solvers.runner + +import io.ksmt.KContext +import io.ksmt.expr.KExpr +import io.ksmt.runner.generated.ConfigurationBuilder +import io.ksmt.runner.generated.models.SolverType +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.runner.KSolverRunner +import io.ksmt.solver.runner.KSolverRunnerManager +import io.ksmt.sort.KBoolSort +import kotlin.time.Duration + +class KMaxSMTSolverRunner( + manager: KMaxSMTSolverRunnerManager, + ctx: KContext, + maxSmtCtx: KMaxSMTContext, + configurationBuilder: ConfigurationBuilder, + solverType: SolverType, + customSolverInfo: KSolverRunnerManager.CustomSolverInfo? = null, +) : KSolverRunner(manager, ctx, configurationBuilder, solverType, customSolverInfo), + KMaxSMTSolverInterface { + + private val maxSMTSolver = KPrimalDualMaxResSolver(ctx, this, maxSmtCtx) + + override fun assertSoft(expr: KExpr, weight: UInt) { + maxSMTSolver.assertSoft(expr, weight) + } + + override fun checkSubOptMaxSMT( + timeout: Duration, + collectStatistics: Boolean + ): KMaxSMTResult = maxSMTSolver.checkSubOptMaxSMT(timeout, collectStatistics) + + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) + : KMaxSMTResult = maxSMTSolver.checkMaxSMT(timeout, collectStatistics) + + override fun collectMaxSMTStatistics() = maxSMTSolver.collectMaxSMTStatistics() + + override fun configure(configurator: C.() -> Unit) = + maxSMTSolver.configure(configurator) +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt new file mode 100644 index 000000000..b80303220 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt @@ -0,0 +1,72 @@ +package io.ksmt.solver.maxsmt.solvers.runner + +import com.jetbrains.rd.util.lifetime.isNotAlive +import io.ksmt.KContext +import io.ksmt.runner.generated.ConfigurationBuilder +import io.ksmt.runner.generated.createConfigurationBuilder +import io.ksmt.runner.generated.models.SolverType +import io.ksmt.runner.generated.solverType +import io.ksmt.solver.KSolver +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.KSolverException +import io.ksmt.solver.KSolverUniversalConfigurationBuilder +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.runner.KSolverRunnerManager +import kotlin.reflect.KClass +import kotlin.time.Duration + +open class KMaxSMTSolverRunnerManager( + workerPoolSize: Int = DEFAULT_WORKER_POOL_SIZE, + hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, + workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT +) : KSolverRunnerManager(workerPoolSize, hardTimeout, workerProcessIdleTimeout) { + fun createMaxSMTSolver( + ctx: KContext, + maxSmtCtx: KMaxSMTContext, + solver: KClass> + ): KMaxSMTSolverRunner { + if (workers.lifetime.isNotAlive) { + throw KSolverException("Solver runner manager is terminated") + } + val solverType = solver.solverType + if (solverType != SolverType.Custom) { + return KMaxSMTSolverRunner( + this, ctx, maxSmtCtx, solverType.createConfigurationBuilder(), solverType + ) + } + + return createCustomMaxSMTSolver(ctx, maxSmtCtx, solver) + } + + private fun createCustomMaxSMTSolver( + ctx: KContext, + maxSmtCtx: KMaxSMTContext, + solver: KClass> + ): KMaxSMTSolverRunner { + val solverInfo = customSolvers[solver.java.name] + ?: error("Solver $solver was not registered") + + val configurationBuilderCreator = customSolversConfiguration.getOrPut(solverInfo) { + createConfigConstructor(solverInfo.configurationQualifiedName) + } + + @Suppress("UNCHECKED_CAST") + return KMaxSMTSolverRunner( + manager = this, + ctx = ctx, + maxSmtCtx, + configurationBuilder = configurationBuilderCreator as ConfigurationBuilder, + solverType = SolverType.Custom, + customSolverInfo = solverInfo + ) + } +} + +// TODO: fix as it's copy-pasted from ksmt-runner SolverUtils file. +internal fun createConfigConstructor( + configQualifiedName: String +): (KSolverUniversalConfigurationBuilder) -> KSolverConfiguration { + val cls = Class.forName(configQualifiedName) + val ctor = cls.getConstructor(KSolverUniversalConfigurationBuilder::class.java) + return { builder: KSolverUniversalConfigurationBuilder -> ctor.newInstance(builder) as KSolverConfiguration } +} diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 161a5b1ef..415714310 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -26,14 +26,14 @@ import java.util.concurrent.atomic.AtomicReferenceArray import kotlin.reflect.KClass import kotlin.time.Duration -class KPortfolioSolver( +open class KPortfolioSolver( solverRunners: List>, KSolverRunner<*>>> ) : KAsyncSolver { - private val lastSuccessfulSolver = AtomicReference?>(null) - private val pendingTermination = ConcurrentLinkedQueue>() + protected val lastSuccessfulSolver = AtomicReference?>(null) + protected val pendingTermination = ConcurrentLinkedQueue>() @OptIn(DelicateCoroutinesApi::class) - private inner class SolverOperationState( + protected inner class SolverOperationState( val solverId: Int, val solver: KSolverRunner<*> ) : AutoCloseable { @@ -71,7 +71,7 @@ class KPortfolioSolver( val isActive: Boolean get() = solverIsActive.get() - internal fun logSolverQueryBest() { + fun logSolverQueryBest() { numberOfBestQueries.incrementAndGet() } @@ -87,9 +87,9 @@ class KPortfolioSolver( SolverOperationState(it, solverRunners[it].second) } - private val activeSolvers = AtomicReferenceArray(solverStates) + protected val activeSolvers = AtomicReferenceArray(solverStates) - private val solverStats = Array(solverRunners.size) { + protected val solverStats = Array(solverRunners.size) { SolverStatistic(solverRunners[it].first) } @@ -223,7 +223,7 @@ class KPortfolioSolver( solverStates.forEach { it.close() } } - private suspend inline fun solverOperation( + protected suspend inline fun solverOperation( crossinline block: suspend KSolverRunner<*>.() -> Unit ) { val result = awaitFirstSolver(block) { true } @@ -275,7 +275,7 @@ class KPortfolioSolver( * with a result matching the [predicate]. * */ @Suppress("TooGenericExceptionCaught") - private suspend inline fun awaitFirstSolver( + protected suspend inline fun awaitFirstSolver( crossinline operation: suspend KSolverRunner<*>.() -> T, crossinline predicate: (T) -> Boolean ): SolverAwaitResult { @@ -336,12 +336,12 @@ class KPortfolioSolver( return resultFuture.await() } - private fun removeSolverFromPortfolio(solverId: Int) { + protected fun removeSolverFromPortfolio(solverId: Int) { activeSolvers.set(solverId, null) solverStats[solverId].logSolverRemovedFromPortfolio() } - private fun SolverAwaitFailure.findSuccessOrThrow(): SolverOperationResult { + protected fun SolverAwaitFailure.findSuccessOrThrow(): SolverOperationResult { val exceptions = arrayListOf() results.forEach { result -> result @@ -368,17 +368,17 @@ class KPortfolioSolver( val result: T ) - private sealed interface SolverAwaitResult + protected sealed interface SolverAwaitResult - private class SolverAwaitSuccess( + protected class SolverAwaitSuccess( val result: SolverOperationResult ) : SolverAwaitResult - private class SolverAwaitFailure( + protected class SolverAwaitFailure( val results: Collection>> ) : SolverAwaitResult - private inline fun AtomicReferenceArray.forEach( + protected inline fun AtomicReferenceArray.forEach( onNullValue: (Int) -> Unit = {}, notNullBody: (Int, T) -> Unit ) { diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt index 87d5b3c89..b093fd5c0 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt @@ -9,7 +9,7 @@ import kotlin.reflect.KClass import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds -class KPortfolioSolverManager( +open class KPortfolioSolverManager( private val solvers: List>>, portfolioPoolSize: Int = DEFAULT_PORTFOLIO_POOL_SIZE, hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index e77ee03f7..2df7bc9bc 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -24,7 +24,7 @@ import kotlin.time.Duration * Manages remote solver executor and can fully restore * its state after failures (e.g. hard timeout) to allow incremental usage. * */ -class KSolverRunner( +open class KSolverRunner( private val manager: KSolverRunnerManager, private val ctx: KContext, private val configurationBuilder: ConfigurationBuilder, diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt index e4deea228..ef6203162 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt @@ -27,7 +27,7 @@ open class KSolverRunnerManager( private val hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT ) : AutoCloseable { - private val workers = KsmtWorkerPool( + protected val workers = KsmtWorkerPool( maxWorkerPoolSize = workerPoolSize, initializationTimeout = SOLVER_WORKER_INITIALIZATION_TIMEOUT, workerProcessIdleTimeout = workerProcessIdleTimeout, @@ -38,8 +38,8 @@ open class KSolverRunnerManager( } ) - private val customSolvers = hashMapOf() - private val customSolversConfiguration = hashMapOf>() + protected val customSolvers = hashMapOf() + protected val customSolversConfiguration = hashMapOf>() override fun close() { workers.terminate() From 08010863756a4e3d0e6f4d5eb7e6e2c8dbd70e24 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 19:02:25 +0300 Subject: [PATCH 149/228] Update Portfolio using rd --- .../KDualMaxRes2SMTBenchmarkTest.kt | 1 - .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 10 +- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 47 +++++--- ksmt-maxsmt/build.gradle.kts | 1 - .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 2 +- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 1 + .../solvers/runner/KMaxSMTPortfolioSolver.kt | 72 ------------ .../runner/KMaxSMTPortfolioSolverManager.kt | 39 ------- .../solvers/runner/KMaxSMTSolverRunner.kt | 45 -------- .../runner/KMaxSMTSolverRunnerManager.kt | 72 ------------ ksmt-runner/build.gradle.kts | 1 + .../ksmt/solver/portfolio/KPortfolioSolver.kt | 109 +++++++++++++----- .../portfolio/KPortfolioSolverManager.kt | 5 +- .../io/ksmt/solver/runner/KSolverRunner.kt | 24 +++- .../solver/runner/KSolverRunnerManager.kt | 12 +- .../solver/runner/KSolverWorkerProcess.kt | 36 +++++- .../ksmt/runner/models/SolverProtocolModel.kt | 39 +++++++ 17 files changed, 229 insertions(+), 287 deletions(-) delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt index 8fa668ddb..a080c200c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt @@ -1,6 +1,5 @@ package io.ksmt.solver.maxsmt.test.configurations -import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index a7adde164..13c09ca98 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -17,7 +17,6 @@ import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver -import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolverManager import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper @@ -29,6 +28,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -60,13 +60,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - val solverManager = KMaxSMTPortfolioSolverManager( + // TODO: update this like in KSubOptMaxSMTBenchmarkTest. + val solverManager = KPortfolioSolverManager( listOf( - // CVC5 often runs out of memory... - KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class ) ) - solverManager.createMaxSMTPortfolioSolver(this, maxSmtCtx) + solverManager.createPortfolioSolver(this, maxSmtCtx) } } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index bb4e5aecd..728f7b00e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -16,13 +16,9 @@ import io.ksmt.solver.bitwuzla.KBitwuzlaSolver import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolver -import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTPortfolioSolverManager -import io.ksmt.solver.maxsmt.solvers.runner.KMaxSMTSolverRunner import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper @@ -35,6 +31,8 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.portfolio.KPortfolioSolver +import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort @@ -67,7 +65,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { MaxSmtSolver.PMRES -> return KPMResSolver(ctx, solver) MaxSmtSolver.PRIMAL_DUAL_MAXRES -> { // Thus, MaxSMT algorithm will be executed in the backend process. - if (solver is KMaxSMTPortfolioSolver) { + if (solver is KPortfolioSolver) { return solver } return KPrimalDualMaxResSolver(ctx, solver, maxSmtCtx) @@ -82,13 +80,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - val solverManager = KMaxSMTPortfolioSolverManager( - listOf( - // CVC5 often runs out of memory... - KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class - ) - ) - solverManager.createMaxSMTPortfolioSolver(this, maxSmtCtx) + solverManager.createPortfolioSolver(this, maxSmtCtx) } } } @@ -98,6 +90,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { protected val ctx: KContext = KContext() protected abstract val maxSmtCtx: KMaxSMTContext private lateinit var maxSMTSolver: KMaxSMTSolverInterface + private val logger = KotlinLogging.logger {} private fun initSolver(solver: Solver) { @@ -280,6 +273,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { internal lateinit var testWorkers: KsmtWorkerPool private lateinit var jsonHelper: JsonStatisticsHelper + private lateinit var solverManager: KPortfolioSolverManager @BeforeAll @JvmStatic @@ -293,7 +287,33 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) }, ) + } + + @AfterAll + @JvmStatic + fun closeWorkerPools() { + testWorkers.terminate() + } + + @BeforeAll + @JvmStatic + fun initSolverManager() { + solverManager = KPortfolioSolverManager( + listOf( + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class + ) //, 4 + ) + } + @AfterAll + @JvmStatic + fun closeSolverManager() { + solverManager.close() + } + + @BeforeAll + @JvmStatic + fun initJsonHelper() { jsonHelper = JsonStatisticsHelper( File( @@ -306,8 +326,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @AfterAll @JvmStatic - fun closeWorkerPools() { - testWorkers.terminate() + fun closeJsonHelper() { jsonHelper.markLastTestStatisticsAsProcessed() } } diff --git a/ksmt-maxsmt/build.gradle.kts b/ksmt-maxsmt/build.gradle.kts index 39a55a0ce..30bd28d1d 100644 --- a/ksmt-maxsmt/build.gradle.kts +++ b/ksmt-maxsmt/build.gradle.kts @@ -12,7 +12,6 @@ val versions = loadProperties(projectDir.parentFile.resolve("version.properties" dependencies { implementation(project(":ksmt-core")) - implementation(project(":ksmt-runner")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions["kotlinx-coroutines"]}") implementation("io.github.oshai:kotlin-logging-jvm:${versions["kotlin-logging-jvm"]}") diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index 4bcb6bed7..ea0f40b07 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -68,7 +68,7 @@ abstract class KMaxSMTSolver( it.expression, true, ) != ctx.falseExpr - } + }.also { model.close() } } override fun configure(configurator: T.() -> Unit) { diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index ecba7a275..c7c8dc9c4 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -494,6 +494,7 @@ class KPrimalDualMaxResSolver( return } + _model?.close() _model = model _upper = upper diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt deleted file mode 100644 index eb3254652..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolver.kt +++ /dev/null @@ -1,72 +0,0 @@ -package io.ksmt.solver.maxsmt.solvers.runner - -import io.ksmt.expr.KExpr -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.KSolverException -import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics -import io.ksmt.solver.portfolio.KPortfolioSolver -import io.ksmt.solver.runner.KSolverRunner -import io.ksmt.sort.KBoolSort -import kotlinx.coroutines.runBlocking -import kotlin.reflect.KClass -import kotlin.time.Duration - -class KMaxSMTPortfolioSolver( - solverRunners: List>, KMaxSMTSolverRunner<*>>> -) : KPortfolioSolver(solverRunners), KMaxSMTSolverInterface { - override fun assertSoft(expr: KExpr, weight: UInt) = runBlocking { - solverOperation { - (this as KMaxSMTSolverRunner<*>).assertSoft(expr, weight) - } - } - - override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean) - : KMaxSMTResult = runBlocking { - val solverAwaitResult = solverOperationWithResult { - (this as KMaxSMTSolverRunner<*>).checkSubOptMaxSMT(timeout, collectStatistics) - } - - when (solverAwaitResult) { - is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result - is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") - } - } - - override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) - : KMaxSMTResult = runBlocking { - val solverAwaitResult = solverOperationWithResult { - (this as KMaxSMTSolverRunner<*>).checkMaxSMT(timeout, collectStatistics) - } - - when (solverAwaitResult) { - is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result - is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") - } - } - - override fun collectMaxSMTStatistics(): KMaxSMTStatistics = runBlocking { - val solverAwaitResult = solverOperationWithResult { - (this as KMaxSMTSolverRunner<*>).collectMaxSMTStatistics() - } - - when (solverAwaitResult) { - is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result - is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") - } - } - - private suspend inline fun solverOperationWithResult( - crossinline block: suspend KSolverRunner<*>.() -> T - ): SolverAwaitResult { - val result = awaitFirstSolver(block) { true } - if (result is SolverAwaitFailure<*>) { - // throw exception if all solvers in portfolio failed with exception - result.findSuccessOrThrow() - } - - return result - } -} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt deleted file mode 100644 index f439e40ef..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTPortfolioSolverManager.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.ksmt.solver.maxsmt.solvers.runner - -import io.ksmt.KContext -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.utils.uncheckedCast -import kotlin.reflect.KClass -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -class KMaxSMTPortfolioSolverManager( - private val solvers: List>>, - portfolioPoolSize: Int = DEFAULT_PORTFOLIO_POOL_SIZE, - hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, - workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT -) : KMaxSMTSolverRunnerManager( - workerPoolSize = portfolioPoolSize * solvers.size, - hardTimeout = hardTimeout, - workerProcessIdleTimeout = workerProcessIdleTimeout -) { - init { - require(solvers.isNotEmpty()) { "Empty MaxSMT solver portfolio" } - } - - fun createMaxSMTPortfolioSolver(ctx: KContext, maxSmtCtx: KMaxSMTContext): KMaxSMTPortfolioSolver { - val solverInstances = solvers.map { - val solverType: KClass> = it.uncheckedCast() - it to createMaxSMTSolver(ctx, maxSmtCtx, solverType) - } - return KMaxSMTPortfolioSolver(solverInstances) - } - - companion object { - const val DEFAULT_PORTFOLIO_POOL_SIZE = 1 - val DEFAULT_HARD_TIMEOUT = 10.seconds - val DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT = 100.seconds - } -} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt deleted file mode 100644 index 94723e7d1..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunner.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.ksmt.solver.maxsmt.solvers.runner - -import io.ksmt.KContext -import io.ksmt.expr.KExpr -import io.ksmt.runner.generated.ConfigurationBuilder -import io.ksmt.runner.generated.models.SolverType -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.runner.KSolverRunner -import io.ksmt.solver.runner.KSolverRunnerManager -import io.ksmt.sort.KBoolSort -import kotlin.time.Duration - -class KMaxSMTSolverRunner( - manager: KMaxSMTSolverRunnerManager, - ctx: KContext, - maxSmtCtx: KMaxSMTContext, - configurationBuilder: ConfigurationBuilder, - solverType: SolverType, - customSolverInfo: KSolverRunnerManager.CustomSolverInfo? = null, -) : KSolverRunner(manager, ctx, configurationBuilder, solverType, customSolverInfo), - KMaxSMTSolverInterface { - - private val maxSMTSolver = KPrimalDualMaxResSolver(ctx, this, maxSmtCtx) - - override fun assertSoft(expr: KExpr, weight: UInt) { - maxSMTSolver.assertSoft(expr, weight) - } - - override fun checkSubOptMaxSMT( - timeout: Duration, - collectStatistics: Boolean - ): KMaxSMTResult = maxSMTSolver.checkSubOptMaxSMT(timeout, collectStatistics) - - override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) - : KMaxSMTResult = maxSMTSolver.checkMaxSMT(timeout, collectStatistics) - - override fun collectMaxSMTStatistics() = maxSMTSolver.collectMaxSMTStatistics() - - override fun configure(configurator: C.() -> Unit) = - maxSMTSolver.configure(configurator) -} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt deleted file mode 100644 index b80303220..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/runner/KMaxSMTSolverRunnerManager.kt +++ /dev/null @@ -1,72 +0,0 @@ -package io.ksmt.solver.maxsmt.solvers.runner - -import com.jetbrains.rd.util.lifetime.isNotAlive -import io.ksmt.KContext -import io.ksmt.runner.generated.ConfigurationBuilder -import io.ksmt.runner.generated.createConfigurationBuilder -import io.ksmt.runner.generated.models.SolverType -import io.ksmt.runner.generated.solverType -import io.ksmt.solver.KSolver -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.KSolverException -import io.ksmt.solver.KSolverUniversalConfigurationBuilder -import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.runner.KSolverRunnerManager -import kotlin.reflect.KClass -import kotlin.time.Duration - -open class KMaxSMTSolverRunnerManager( - workerPoolSize: Int = DEFAULT_WORKER_POOL_SIZE, - hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, - workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT -) : KSolverRunnerManager(workerPoolSize, hardTimeout, workerProcessIdleTimeout) { - fun createMaxSMTSolver( - ctx: KContext, - maxSmtCtx: KMaxSMTContext, - solver: KClass> - ): KMaxSMTSolverRunner { - if (workers.lifetime.isNotAlive) { - throw KSolverException("Solver runner manager is terminated") - } - val solverType = solver.solverType - if (solverType != SolverType.Custom) { - return KMaxSMTSolverRunner( - this, ctx, maxSmtCtx, solverType.createConfigurationBuilder(), solverType - ) - } - - return createCustomMaxSMTSolver(ctx, maxSmtCtx, solver) - } - - private fun createCustomMaxSMTSolver( - ctx: KContext, - maxSmtCtx: KMaxSMTContext, - solver: KClass> - ): KMaxSMTSolverRunner { - val solverInfo = customSolvers[solver.java.name] - ?: error("Solver $solver was not registered") - - val configurationBuilderCreator = customSolversConfiguration.getOrPut(solverInfo) { - createConfigConstructor(solverInfo.configurationQualifiedName) - } - - @Suppress("UNCHECKED_CAST") - return KMaxSMTSolverRunner( - manager = this, - ctx = ctx, - maxSmtCtx, - configurationBuilder = configurationBuilderCreator as ConfigurationBuilder, - solverType = SolverType.Custom, - customSolverInfo = solverInfo - ) - } -} - -// TODO: fix as it's copy-pasted from ksmt-runner SolverUtils file. -internal fun createConfigConstructor( - configQualifiedName: String -): (KSolverUniversalConfigurationBuilder) -> KSolverConfiguration { - val cls = Class.forName(configQualifiedName) - val ctor = cls.getConstructor(KSolverUniversalConfigurationBuilder::class.java) - return { builder: KSolverUniversalConfigurationBuilder -> ctor.newInstance(builder) as KSolverConfiguration } -} diff --git a/ksmt-runner/build.gradle.kts b/ksmt-runner/build.gradle.kts index ef9b2c790..1459611bc 100644 --- a/ksmt-runner/build.gradle.kts +++ b/ksmt-runner/build.gradle.kts @@ -23,6 +23,7 @@ kotlin { dependencies { implementation(project(":ksmt-core")) + implementation(project(":ksmt-maxsmt")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 415714310..b250929de 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -2,15 +2,6 @@ package io.ksmt.solver.portfolio import com.jetbrains.rd.util.AtomicInteger import com.jetbrains.rd.util.AtomicReference -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch -import kotlinx.coroutines.newSingleThreadContext -import kotlinx.coroutines.runBlocking import io.ksmt.expr.KExpr import io.ksmt.solver.KModel import io.ksmt.solver.KSolver @@ -18,22 +9,34 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverException import io.ksmt.solver.KSolverStatus import io.ksmt.solver.async.KAsyncSolver +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.runner.KSolverRunner import io.ksmt.sort.KBoolSort +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.runBlocking import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReferenceArray import kotlin.reflect.KClass import kotlin.time.Duration -open class KPortfolioSolver( +class KPortfolioSolver( solverRunners: List>, KSolverRunner<*>>> -) : KAsyncSolver { - protected val lastSuccessfulSolver = AtomicReference?>(null) - protected val pendingTermination = ConcurrentLinkedQueue>() +) : KAsyncSolver, KMaxSMTSolverInterface { + private val lastSuccessfulSolver = AtomicReference?>(null) + private val pendingTermination = ConcurrentLinkedQueue>() @OptIn(DelicateCoroutinesApi::class) - protected inner class SolverOperationState( + private inner class SolverOperationState( val solverId: Int, val solver: KSolverRunner<*> ) : AutoCloseable { @@ -61,7 +64,7 @@ open class KPortfolioSolver( } } - class SolverStatistic(val solver: KClass>) { + private class SolverStatistic(val solver: KClass>) { private val numberOfBestQueries = AtomicInteger(0) private val solverIsActive = AtomicBoolean(true) @@ -75,7 +78,7 @@ open class KPortfolioSolver( numberOfBestQueries.incrementAndGet() } - internal fun logSolverRemovedFromPortfolio() { + fun logSolverRemovedFromPortfolio() { solverIsActive.set(false) } @@ -87,16 +90,16 @@ open class KPortfolioSolver( SolverOperationState(it, solverRunners[it].second) } - protected val activeSolvers = AtomicReferenceArray(solverStates) + private val activeSolvers = AtomicReferenceArray(solverStates) - protected val solverStats = Array(solverRunners.size) { + private val solverStats = Array(solverRunners.size) { SolverStatistic(solverRunners[it].first) } /** * Gather current statistic on the solvers in the portfolio. * */ - fun solverPortfolioStats(): List = solverStats.toList() + private fun solverPortfolioStats(): List = solverStats.toList() override fun configure(configurator: KSolverConfiguration.() -> Unit) = runBlocking { configureAsync(configurator) @@ -110,6 +113,12 @@ open class KPortfolioSolver( assertAsync(exprs) } + override fun assertSoft(expr: KExpr, weight: UInt) = runBlocking { + solverOperation { + assertSoft(expr, weight) + } + } + override fun assertAndTrack(expr: KExpr) = runBlocking { assertAndTrackAsync(expr) } @@ -130,6 +139,39 @@ open class KPortfolioSolver( checkAsync(timeout) } + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { + val solverAwaitResult = solverOperationWithResult( + { checkMaxSMT(timeout, collectStatistics) }, { it.maxSMTSucceeded } + ) + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + + override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { + val solverAwaitResult = solverOperationWithResult( + { checkSubOptMaxSMT(timeout, collectStatistics) }, { it.maxSMTSucceeded } + ) + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + + override fun collectMaxSMTStatistics(): KMaxSMTStatistics = runBlocking { + val solverAwaitResult = solverOperationWithResult( + { this.collectMaxSMTStatistics() } + ) + + when (solverAwaitResult) { + is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result + is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") + } + } + override fun checkWithAssumptions( assumptions: List>, timeout: Duration @@ -223,7 +265,7 @@ open class KPortfolioSolver( solverStates.forEach { it.close() } } - protected suspend inline fun solverOperation( + private suspend inline fun solverOperation( crossinline block: suspend KSolverRunner<*>.() -> Unit ) { val result = awaitFirstSolver(block) { true } @@ -233,6 +275,19 @@ open class KPortfolioSolver( } } + private suspend inline fun solverOperationWithResult( + crossinline block: suspend KSolverRunner<*>.() -> T, + crossinline predicate: (T) -> Boolean = { true } + ): SolverAwaitResult { + val result = awaitFirstSolver(block, predicate) + if (result is SolverAwaitFailure<*>) { + // throw exception if all solvers in portfolio failed with exception + result.findSuccessOrThrow() + } + + return result + } + private suspend inline fun solverQuery( crossinline block: suspend KSolverRunner<*>.() -> KSolverStatus ): KSolverStatus { @@ -275,7 +330,7 @@ open class KPortfolioSolver( * with a result matching the [predicate]. * */ @Suppress("TooGenericExceptionCaught") - protected suspend inline fun awaitFirstSolver( + private suspend inline fun awaitFirstSolver( crossinline operation: suspend KSolverRunner<*>.() -> T, crossinline predicate: (T) -> Boolean ): SolverAwaitResult { @@ -336,12 +391,12 @@ open class KPortfolioSolver( return resultFuture.await() } - protected fun removeSolverFromPortfolio(solverId: Int) { + private fun removeSolverFromPortfolio(solverId: Int) { activeSolvers.set(solverId, null) solverStats[solverId].logSolverRemovedFromPortfolio() } - protected fun SolverAwaitFailure.findSuccessOrThrow(): SolverOperationResult { + private fun SolverAwaitFailure.findSuccessOrThrow(): SolverOperationResult { val exceptions = arrayListOf() results.forEach { result -> result @@ -368,17 +423,17 @@ open class KPortfolioSolver( val result: T ) - protected sealed interface SolverAwaitResult + private sealed interface SolverAwaitResult - protected class SolverAwaitSuccess( + private class SolverAwaitSuccess( val result: SolverOperationResult ) : SolverAwaitResult - protected class SolverAwaitFailure( + private class SolverAwaitFailure( val results: Collection>> ) : SolverAwaitResult - protected inline fun AtomicReferenceArray.forEach( + private inline fun AtomicReferenceArray.forEach( onNullValue: (Int) -> Unit = {}, notNullBody: (Int, T) -> Unit ) { diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt index b093fd5c0..f2ec7b064 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt @@ -3,6 +3,7 @@ package io.ksmt.solver.portfolio import io.ksmt.KContext import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.runner.KSolverRunnerManager import io.ksmt.utils.uncheckedCast import kotlin.reflect.KClass @@ -23,10 +24,10 @@ open class KPortfolioSolverManager( require(solvers.isNotEmpty()) { "Empty solver portfolio" } } - fun createPortfolioSolver(ctx: KContext): KPortfolioSolver { + fun createPortfolioSolver(ctx: KContext, maxsmtCtx: KMaxSMTContext = KMaxSMTContext()): KPortfolioSolver { val solverInstances = solvers.map { val solverType: KClass> = it.uncheckedCast() - it to createSolver(ctx, solverType) + it to createSolver(ctx, maxsmtCtx, solverType) } return KPortfolioSolver(solverInstances) } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index 2df7bc9bc..ee326c238 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -2,7 +2,6 @@ package io.ksmt.solver.runner import com.jetbrains.rd.util.AtomicReference import com.jetbrains.rd.util.threading.SpinWait -import kotlinx.coroutines.sync.Mutex import io.ksmt.KContext import io.ksmt.expr.KExpr import io.ksmt.runner.generated.ConfigurationBuilder @@ -13,8 +12,12 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverException import io.ksmt.solver.KSolverStatus import io.ksmt.solver.async.KAsyncSolver +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.runner.KSolverRunnerManager.CustomSolverInfo import io.ksmt.sort.KBoolSort +import kotlinx.coroutines.sync.Mutex import java.util.concurrent.atomic.AtomicBoolean import kotlin.time.Duration @@ -24,9 +27,10 @@ import kotlin.time.Duration * Manages remote solver executor and can fully restore * its state after failures (e.g. hard timeout) to allow incremental usage. * */ -open class KSolverRunner( +class KSolverRunner( private val manager: KSolverRunnerManager, private val ctx: KContext, + maxSmtCtx: KMaxSMTContext = KMaxSMTContext(), private val configurationBuilder: ConfigurationBuilder, private val solverType: SolverType, private val customSolverInfo: CustomSolverInfo? = null, @@ -41,6 +45,8 @@ open class KSolverRunner( private val solverState = KSolverState() + private val maxSMTSolver = KPrimalDualMaxResSolver(ctx, this, maxSmtCtx) + override fun close() { deleteSolverSync() } @@ -115,6 +121,10 @@ open class KSolverRunner( } } + fun assertSoft(expr: KExpr, weight: UInt) { + maxSMTSolver.assertSoft(expr, weight) + } + private inline fun bulkAssert( exprs: List>, execute: (List>) -> Unit @@ -232,6 +242,16 @@ open class KSolverRunner( checkSync(timeout) } + fun checkSubOptMaxSMT( + timeout: Duration, + collectStatistics: Boolean + ): KMaxSMTResult = maxSMTSolver.checkSubOptMaxSMT(timeout, collectStatistics) + + fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) + : KMaxSMTResult = maxSMTSolver.checkMaxSMT(timeout, collectStatistics) + + fun collectMaxSMTStatistics() = maxSMTSolver.collectMaxSMTStatistics() + override suspend fun checkWithAssumptionsAsync( assumptions: List>, timeout: Duration diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt index ef6203162..90557bb5c 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerManager.kt @@ -1,7 +1,6 @@ package io.ksmt.solver.runner import com.jetbrains.rd.util.lifetime.isNotAlive -import kotlinx.coroutines.runBlocking import io.ksmt.KContext import io.ksmt.runner.core.KsmtWorkerArgs import io.ksmt.runner.core.KsmtWorkerFactory @@ -18,6 +17,8 @@ import io.ksmt.runner.generated.solverType import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverException +import io.ksmt.solver.maxsmt.KMaxSMTContext +import kotlinx.coroutines.runBlocking import kotlin.reflect.KClass import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -27,7 +28,7 @@ open class KSolverRunnerManager( private val hardTimeout: Duration = DEFAULT_HARD_TIMEOUT, workerProcessIdleTimeout: Duration = DEFAULT_WORKER_PROCESS_IDLE_TIMEOUT ) : AutoCloseable { - protected val workers = KsmtWorkerPool( + private val workers = KsmtWorkerPool( maxWorkerPoolSize = workerPoolSize, initializationTimeout = SOLVER_WORKER_INITIALIZATION_TIMEOUT, workerProcessIdleTimeout = workerProcessIdleTimeout, @@ -38,8 +39,8 @@ open class KSolverRunnerManager( } ) - protected val customSolvers = hashMapOf() - protected val customSolversConfiguration = hashMapOf>() + private val customSolvers = hashMapOf() + private val customSolversConfiguration = hashMapOf>() override fun close() { workers.terminate() @@ -47,6 +48,7 @@ open class KSolverRunnerManager( fun createSolver( ctx: KContext, + maxsmtCtx: KMaxSMTContext = KMaxSMTContext(), solver: KClass> ): KSolverRunner { if (workers.lifetime.isNotAlive) { @@ -54,7 +56,7 @@ open class KSolverRunnerManager( } val solverType = solver.solverType if (solverType != SolverType.Custom) { - return KSolverRunner(this, ctx, solverType.createConfigurationBuilder(), solverType) + return KSolverRunner(this, ctx, maxsmtCtx, solverType.createConfigurationBuilder(), solverType) } return createCustomSolver(ctx, solver) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 48ff7c29b..31613c947 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -8,22 +8,26 @@ import io.ksmt.runner.core.ChildProcessBase import io.ksmt.runner.core.KsmtWorkerArgs import io.ksmt.runner.generated.createInstance import io.ksmt.runner.generated.createSolverConstructor +import io.ksmt.runner.generated.models.CheckMaxSMTResult import io.ksmt.runner.generated.models.CheckResult +import io.ksmt.runner.generated.models.CollectMaxSMTStatisticsResult import io.ksmt.runner.generated.models.ContextSimplificationMode import io.ksmt.runner.generated.models.ModelEntry import io.ksmt.runner.generated.models.ModelFuncInterpEntry import io.ksmt.runner.generated.models.ModelResult import io.ksmt.runner.generated.models.ModelUninterpretedSortUniverse import io.ksmt.runner.generated.models.ReasonUnknownResult +import io.ksmt.runner.generated.models.SoftConstraint import io.ksmt.runner.generated.models.SolverProtocolModel import io.ksmt.runner.generated.models.SolverType import io.ksmt.runner.generated.models.UnsatCoreResult import io.ksmt.runner.generated.models.solverProtocolModel import io.ksmt.runner.serializer.AstSerializationCtx +import io.ksmt.solver.KSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.model.KFuncInterp import io.ksmt.solver.model.KFuncInterpEntry import io.ksmt.solver.model.KFuncInterpEntryWithVars -import io.ksmt.solver.KSolver import io.ksmt.solver.model.KFuncInterpWithVars import io.ksmt.sort.KBoolSort import kotlin.time.Duration.Companion.milliseconds @@ -86,6 +90,10 @@ class KSolverWorkerProcess : ChildProcessBase() { @Suppress("UNCHECKED_CAST") solver.assert(params.expression as KExpr) } + assertSoft.measureExecutionForTermination { params -> + @Suppress("UNCHECKED_CAST") + (solver as KMaxSMTSolverInterface).assertSoft(params.expression as KExpr, params.weight) + } bulkAssert.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") solver.assert(params.expressions as List>) @@ -117,6 +125,32 @@ class KSolverWorkerProcess : ChildProcessBase() { CheckResult(status) } + checkMaxSMT.measureExecutionForTermination { params -> + val timeout = params.timeout.milliseconds + + val result = (solver as KMaxSMTSolverInterface).checkMaxSMT(timeout) + + @Suppress("UNCHECKED_CAST") + CheckMaxSMTResult(result.satSoftConstraints as List, result.hardConstraintsSatStatus, result.maxSMTSucceeded) + } + checkSubOptMaxSMT.measureExecutionForTermination { params -> + val timeout = params.timeout.milliseconds + + val result = (solver as KMaxSMTSolverInterface).checkSubOptMaxSMT(timeout) + + @Suppress("UNCHECKED_CAST") + CheckMaxSMTResult(result.satSoftConstraints as List, result.hardConstraintsSatStatus, result.maxSMTSucceeded) + } + collectMaxSMTStatistics.measureExecutionForTermination { + val statistics = (solver as KMaxSMTSolverInterface).collectMaxSMTStatistics() + + CollectMaxSMTStatisticsResult( + statistics.timeoutMs, + statistics.elapsedTimeMs, + statistics.timeInSolverQueriesMs, + statistics.queriesToSolverNumber + ) + } model.measureExecutionForTermination { val model = solver.model().detach() val declarations = model.declarations.toList() diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index f6c1dfa79..9d09fcadc 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -44,6 +44,11 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { field("value", PredefinedType.string) } + private val softConstraint = structdef { + field("expression", kastType) + field("weight", PredefinedType.uint) + } + private val assertParams = structdef { field("expression", kastType) } @@ -64,11 +69,29 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { field("status", statusType) } + private val checkMaxSMTParams = structdef { + field("timeout", PredefinedType.long) + field("collectStatistics", PredefinedType.bool) + } + + private val checkMaxSMTResult = structdef { + field("satSoftConstraints", immutableList(softConstraint)) + field("hardConstraintsSatStatus", statusType) + field("maxSMTSucceeded", PredefinedType.bool) + } + private val checkWithAssumptionsParams = structdef { field("assumptions", immutableList(kastType)) field("timeout", PredefinedType.long) } + private val collectMaxSMTStatisticsResult = structdef { + field("timeoutMs", PredefinedType.long) + field("elapsedTimeMs", PredefinedType.long) + field("timeInSolverQueriesMs", PredefinedType.long) + field("queriesToSolverNumber", PredefinedType.int) + } + private val unsatCoreResult = structdef { field("core", immutableList(kastType)) } @@ -118,6 +141,10 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { async documentation = "Assert expression" } + call("assertSoft", softConstraint, PredefinedType.void).apply { + async + documentation = "Assert expression softly" + } call("bulkAssert", bulkAssertParams, PredefinedType.void).apply { async documentation = "Assert multiple expressions" @@ -142,6 +169,18 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { async documentation = "Check SAT" } + call("checkMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { + async + documentation = "Check MaxSMT" + } + call("checkSubOptMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { + async + documentation = "Check SubOptMaxSMT" + } + call("collectMaxSMTStatistics", PredefinedType.void, collectMaxSMTStatisticsResult).apply { + async + documentation = "Collect MaxSMT statistics" + } call("checkWithAssumptions", checkWithAssumptionsParams, checkResult).apply { async documentation = "Check SAT with assumptions" From 64e17c955979c45b355450373f42893ec255c95a Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 19:39:15 +0300 Subject: [PATCH 150/228] Transfer solverManager creation/closing to before/after All --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 42 +++++++++++++++---- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 7 +++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 13c09ca98..e123b4fb2 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -21,6 +21,7 @@ import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics +import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 @@ -60,12 +61,6 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - // TODO: update this like in KSubOptMaxSMTBenchmarkTest. - val solverManager = KPortfolioSolverManager( - listOf( - KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class - ) - ) solverManager.createPortfolioSolver(this, maxSmtCtx) } } @@ -83,7 +78,10 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } @AfterEach - fun closeSolver() = maxSMTSolver.close() + fun close() { + maxSMTSolver.close() + ctx.close() + } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") @@ -248,6 +246,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { internal lateinit var testWorkers: KsmtWorkerPool private lateinit var jsonHelper: JsonStatisticsHelper + private lateinit var solverManager: KPortfolioSolverManager @BeforeAll @JvmStatic @@ -261,7 +260,33 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { override fun mkWorker(id: Int, process: RdServer) = TestWorker(id, process) }, ) + } + + @AfterAll + @JvmStatic + fun closeWorkerPools() { + testWorkers.terminate() + } + @BeforeAll + @JvmStatic + fun initSolverManager() { + solverManager = KPortfolioSolverManager( + listOf( + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class + ) + ) + } + + @AfterAll + @JvmStatic + fun closeSolverManager() { + solverManager.close() + } + + @BeforeAll + @JvmStatic + fun initJsonHelper() { jsonHelper = JsonStatisticsHelper( File( @@ -274,8 +299,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @AfterAll @JvmStatic - fun closeWorkerPools() { - testWorkers.terminate() + fun closeJsonHelper() { jsonHelper.markLastTestStatisticsAsProcessed() } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 728f7b00e..3e6e3a083 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -98,7 +98,10 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } @AfterEach - fun closeSolver() = maxSMTSolver.close() + fun close() { + maxSMTSolver.close() + ctx.close() + } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") @@ -301,7 +304,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { solverManager = KPortfolioSolverManager( listOf( KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class - ) //, 4 + ), 4 ) } From 0937081f3c52d3f0c740ceb560d197ebcfb0e540 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 20:26:48 +0300 Subject: [PATCH 151/228] Add Portfolio solver to KMaxSMTBenchmarkTest --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 6 ++++++ .../kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index e123b4fb2..6420bb4e8 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -107,6 +107,12 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) } + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTPortfolioTest(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, PORTFOLIO) + } + private fun testMaxSMTSolver( name: String, samplePath: Path, diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index b250929de..2df674b8c 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -141,7 +141,8 @@ class KPortfolioSolver( override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { val solverAwaitResult = solverOperationWithResult( - { checkMaxSMT(timeout, collectStatistics) }, { it.maxSMTSucceeded } + { checkMaxSMT(timeout, collectStatistics) }, + { it.maxSMTSucceeded } ) when (solverAwaitResult) { @@ -152,7 +153,8 @@ class KPortfolioSolver( override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { val solverAwaitResult = solverOperationWithResult( - { checkSubOptMaxSMT(timeout, collectStatistics) }, { it.maxSMTSucceeded } + { checkSubOptMaxSMT(timeout, collectStatistics) }, + { it.maxSMTSucceeded } ) when (solverAwaitResult) { From 2f4236eefd56b81d28affaad2eccb91dc508a1f3 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 23:54:30 +0300 Subject: [PATCH 152/228] Support portfolio for statistics on CI --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 13 ++++++++++++- .github/workflows/run-long-maxsmt-tests-pmres.yml | 1 + .github/workflows/run-long-maxsmt-tests.yml | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index d1cbac69f..999dc4845 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -12,10 +12,15 @@ on: type: choice description: Chosen SMT solver options: + - Portfolio - Z3 - Bitwuzla - Cvc5 - Yices + optimality: + type: boolean + description: Chosen optimal (otherwise suboptimal) configuration + default: false strategy: type: choice description: Chosen MaxSMT solving strategy @@ -85,9 +90,15 @@ jobs: echo "OPTION_TO_VALUE=$OPTION_TO_VALUE" >> $GITHUB_ENV + if [[ "${{ inputs.optimality }}" == false ]] + then + export SUB_OPT="SubOpt" + + echo "SUB_OPT" >> $GITHUB_ENV + - name: Set solver name to output id: set-output - run: echo "solver=K${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT + run: echo "solver=K${{ env.SUB_OPT }}${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT run-long-maxsmt-tests: needs: construct-solver-name diff --git a/.github/workflows/run-long-maxsmt-tests-pmres.yml b/.github/workflows/run-long-maxsmt-tests-pmres.yml index 202570ee8..27ab72aec 100644 --- a/.github/workflows/run-long-maxsmt-tests-pmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pmres.yml @@ -28,4 +28,5 @@ jobs: with: solver: ${{ inputs.solver }} smtSolver: ${{ inputs.smtSolver }} + optimality: true commitSHA: ${{ inputs.commitSHA }} diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 61d27331c..e7c2031ea 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -11,6 +11,10 @@ on: type: string description: Chosen SMT solver required: true + optimality: + type: boolean + description: Chosen optimal (otherwise suboptimal) configuration + required: true commitSHA: description: Commit SHA (latest commit is default) required: false From 14280b65dc6fcda64df3f80289b0171958b2695a Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 23:57:41 +0300 Subject: [PATCH 153/228] Fix path to configurations --- .github/workflows/run-long-maxsmt-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index e7c2031ea..6203a1187 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -101,7 +101,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.smt.${{ inputs.solver }}SMTBenchmarkTest.maxSMT${{ inputs.smtSolver }}Test" + --tests "io.ksmt.solver.maxsmt.test.configurations.${{ inputs.solver }}SMTBenchmarkTest.maxSMT${{ inputs.smtSolver }}Test" - name: Add logic name to test run statistics if: ${{ always() }} From 3610a0b7ae8ba458b52f003b5990535c18b01828 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 2 May 2024 23:59:12 +0300 Subject: [PATCH 154/228] Pass optimality from PDMRes workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 999dc4845..c4f64c48d 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -106,4 +106,5 @@ jobs: with: solver: ${{ needs.construct-solver-name.outputs.solver }} smtSolver: ${{ inputs.smtSolver }} + optimality: ${{ inputs.optimality }} commitSHA: ${{ inputs.commitSHA }} From abcd7d4e47cbf3c7028312416113a50391b47c15 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 00:01:41 +0300 Subject: [PATCH 155/228] Fix syntax error in workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index c4f64c48d..e3fe8ac58 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -93,6 +93,7 @@ jobs: if [[ "${{ inputs.optimality }}" == false ]] then export SUB_OPT="SubOpt" + fi echo "SUB_OPT" >> $GITHUB_ENV From 55a3df0445a356d259fffc02ee2671875a10b685 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 00:03:47 +0300 Subject: [PATCH 156/228] Fix workflow error --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index e3fe8ac58..232bc0056 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -95,7 +95,7 @@ jobs: export SUB_OPT="SubOpt" fi - echo "SUB_OPT" >> $GITHUB_ENV + echo "SUB_OPT=$SUB_OPT" >> $GITHUB_ENV - name: Set solver name to output id: set-output From b8757e4ab4a622278826ab5d63b76e0af2eb8dbe Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 00:38:07 +0300 Subject: [PATCH 157/228] Update statistics path --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 3e6e3a083..a59d55757 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -322,7 +322,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { File( "${ Paths.get("").toAbsolutePath() - }/src/test/resources/subopt-maxsmt-statistics-${getRandomString(16)}.json", + }/src/test/resources/maxsmt-statistics-${getRandomString(16)}.json", ), ) } From 0b87c8f3525193dc73b17d52c968f2d49bce16fa Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 00:55:10 +0300 Subject: [PATCH 158/228] Downgrade upload-action version in order to use wilcard path --- .github/workflows/run-long-maxsmt-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 6203a1187..3942ad35f 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -111,21 +111,21 @@ jobs: - name: Upload ${{ matrix.logics.NAME }} test statistics if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v2 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics path: ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json - name: Upload ${{ matrix.logics.NAME }} test report if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v2 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* - name: Upload ${{ matrix.logics.NAME }} test logs if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v2 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_logs path: ksmt-maxsmt-test/logs/* From 97c2acec1e205f2d87b766535abf57bd1b474302 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 01:11:01 +0300 Subject: [PATCH 159/228] Upgrade upload-artifact version --- .github/workflows/run-long-maxsat-tests.yml | 2 +- .github/workflows/run-long-maxsmt-tests.yml | 10 +++++----- .gitignore | 4 ++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index f8e67163e..a77bb6359 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -53,7 +53,7 @@ jobs: - name: Upload test report if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ksmt-maxsat-test-report-${{ matrix.BENCHMARK_NUMBER }} path: ksmt-maxsmt-test/build/reports/tests/test/* diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 3942ad35f..12ccf3cc1 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -111,21 +111,21 @@ jobs: - name: Upload ${{ matrix.logics.NAME }} test statistics if: ${{ always() }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics path: ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json - name: Upload ${{ matrix.logics.NAME }} test report if: ${{ always() }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_report path: ksmt-maxsmt-test/build/reports/tests/test/* - name: Upload ${{ matrix.logics.NAME }} test logs if: ${{ always() }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.logics.NAME }}_${{ inputs.solver }}_${{ inputs.smtSolver }}_test_logs path: ksmt-maxsmt-test/logs/* @@ -162,7 +162,7 @@ jobs: cat ${{ env.MERGED_STATISTICS_PATH }}_temp | jq '{ "logics": . }' > ${{ env.MERGED_STATISTICS_PATH }} - name: Upload test run statistics - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ inputs.solver }}_${{ inputs.smtSolver }}_test_statistics path: ${{ env.MERGED_STATISTICS_PATH }} @@ -171,7 +171,7 @@ jobs: run: python ./scripts/analyze_maxsmt_statistics.py ${{ env.MERGED_STATISTICS_PATH }} ${{ env.ANALYZED_STATISTICS_PATH }} - name: Upload analyzed test run statistics - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ inputs.solver }}_${{ inputs.smtSolver }}_analyzed_test_statistics path: ${{ env.ANALYZED_STATISTICS_PATH }} diff --git a/.gitignore b/.gitignore index 4dddda5e0..ba0cbcdbb 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ ksmt-maxsmt-test/src/test/resources/maxsmt-statistics-*.json **/logs/ +ksmt-maxsmt-test/src/test/resources/ + +ksmt-maxsmt-test/*.log + From ab8cb9dabc5114b0e49ad4629f0b946e213d9694 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 01:18:42 +0300 Subject: [PATCH 160/228] Upgrade Ubuntu version --- .github/workflows/run-long-maxsat-tests.yml | 2 +- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 4 ++-- .github/workflows/run-long-maxsmt-tests.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index a77bb6359..23d6e5143 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -8,7 +8,7 @@ env: jobs: run-maxsat-tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 232bc0056..734e181bb 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -50,7 +50,7 @@ on: jobs: verify-inputs: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Verify inputs # Check only one option of [minimizeCores, preferLargeWeightCores, getMultipleCores] is true @@ -66,7 +66,7 @@ jobs: construct-solver-name: needs: verify-inputs - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: solver: ${{ steps.set-output.outputs.solver }} steps: diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 12ccf3cc1..0011e7546 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -27,7 +27,7 @@ env: jobs: prepare-matrix: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: logics-matrix: ${{ steps.set-matrix.outputs.maxsmt-tests-matrix }} steps: @@ -60,7 +60,7 @@ jobs: run-maxsmt-tests: needs: prepare-matrix continue-on-error: true - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: ${{ fromJSON(needs.prepare-matrix.outputs.logics-matrix) }} @@ -132,7 +132,7 @@ jobs: merge-and-analyze-maxsmt-statistics: needs: run-maxsmt-tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repository uses: actions/checkout@v4 From 12e87235b98f71b4d764998c9113068d4a4bdf7d Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 3 May 2024 02:16:46 +0300 Subject: [PATCH 161/228] Remove redundant lines --- .../io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index c7c8dc9c4..f45a5db89 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -121,8 +121,6 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } - - else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } @@ -265,8 +263,6 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } - - else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } From fef02cd5f02174da03d80a0a02dab26610c679a1 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 10 May 2024 15:28:04 +0300 Subject: [PATCH 162/228] Update SolverProtocolModel --- .../models/SolverProtocolModel.Generated.kt | 343 +++++++++++++++++- 1 file changed, 330 insertions(+), 13 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index 89b598d49..9c573517e 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -22,12 +22,16 @@ class SolverProtocolModel private constructor( private val _deleteSolver: RdCall, private val _configure: RdCall, Unit>, private val _assert: RdCall, + private val _assertSoft: RdCall, private val _bulkAssert: RdCall, private val _assertAndTrack: RdCall, private val _bulkAssertAndTrack: RdCall, private val _push: RdCall, private val _pop: RdCall, private val _check: RdCall, + private val _checkMaxSMT: RdCall, + private val _checkSubOptMaxSMT: RdCall, + private val _collectMaxSMTStatistics: RdCall, private val _checkWithAssumptions: RdCall, private val _model: RdCall, private val _unsatCore: RdCall, @@ -41,12 +45,16 @@ class SolverProtocolModel private constructor( override fun registerSerializersCore(serializers: ISerializers) { serializers.register(CreateSolverParams) serializers.register(SolverConfigurationParam) + serializers.register(SoftConstraint) serializers.register(AssertParams) serializers.register(BulkAssertParams) serializers.register(PopParams) serializers.register(CheckParams) serializers.register(CheckResult) + serializers.register(CheckMaxSMTParams) + serializers.register(CheckMaxSMTResult) serializers.register(CheckWithAssumptionsParams) + serializers.register(CollectMaxSMTStatisticsResult) serializers.register(UnsatCoreResult) serializers.register(ReasonUnknownResult) serializers.register(ModelFuncInterpEntry) @@ -80,7 +88,7 @@ class SolverProtocolModel private constructor( private val __SolverConfigurationParamListSerializer = SolverConfigurationParam.list() - const val serializationHash = 6588187342629653927L + const val serializationHash = 283104004146364238L } override val serializersOwner: ISerializersOwner get() = SolverProtocolModel @@ -108,6 +116,11 @@ class SolverProtocolModel private constructor( */ val assert: RdCall get() = _assert + /** + * Assert expression softly + */ + val assertSoft: RdCall get() = _assertSoft + /** * Assert multiple expressions */ @@ -138,6 +151,21 @@ class SolverProtocolModel private constructor( */ val check: RdCall get() = _check + /** + * Check MaxSMT + */ + val checkMaxSMT: RdCall get() = _checkMaxSMT + + /** + * Check SubOptMaxSMT + */ + val checkSubOptMaxSMT: RdCall get() = _checkSubOptMaxSMT + + /** + * Collect MaxSMT statistics + */ + val collectMaxSMTStatistics: RdCall get() = _collectMaxSMTStatistics + /** * Check SAT with assumptions */ @@ -169,12 +197,15 @@ class SolverProtocolModel private constructor( _deleteSolver.async = true _configure.async = true _assert.async = true + _assertSoft.async = true _bulkAssert.async = true _assertAndTrack.async = true _bulkAssertAndTrack.async = true _push.async = true _pop.async = true _check.async = true + _checkMaxSMT.async = true + _checkSubOptMaxSMT.async = true _checkWithAssumptions.async = true _model.async = true _unsatCore.async = true @@ -187,12 +218,16 @@ class SolverProtocolModel private constructor( bindableChildren.add("deleteSolver" to _deleteSolver) bindableChildren.add("configure" to _configure) bindableChildren.add("assert" to _assert) + bindableChildren.add("assertSoft" to _assertSoft) bindableChildren.add("bulkAssert" to _bulkAssert) bindableChildren.add("assertAndTrack" to _assertAndTrack) bindableChildren.add("bulkAssertAndTrack" to _bulkAssertAndTrack) bindableChildren.add("push" to _push) bindableChildren.add("pop" to _pop) bindableChildren.add("check" to _check) + bindableChildren.add("checkMaxSMT" to _checkMaxSMT) + bindableChildren.add("checkSubOptMaxSMT" to _checkSubOptMaxSMT) + bindableChildren.add("collectMaxSMTStatistics" to _collectMaxSMTStatistics) bindableChildren.add("checkWithAssumptions" to _checkWithAssumptions) bindableChildren.add("model" to _model) bindableChildren.add("unsatCore" to _unsatCore) @@ -207,12 +242,16 @@ class SolverProtocolModel private constructor( RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void), RdCall, Unit>(__SolverConfigurationParamListSerializer, FrameworkMarshallers.Void), RdCall(AssertParams, FrameworkMarshallers.Void), + RdCall(SoftConstraint, FrameworkMarshallers.Void), RdCall(BulkAssertParams, FrameworkMarshallers.Void), RdCall(AssertParams, FrameworkMarshallers.Void), RdCall(BulkAssertParams, FrameworkMarshallers.Void), RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void), RdCall(PopParams, FrameworkMarshallers.Void), RdCall(CheckParams, CheckResult), + RdCall(CheckMaxSMTParams, CheckMaxSMTResult), + RdCall(CheckMaxSMTParams, CheckMaxSMTResult), + RdCall(FrameworkMarshallers.Void, CollectMaxSMTStatisticsResult), RdCall(CheckWithAssumptionsParams, CheckResult), RdCall(FrameworkMarshallers.Void, ModelResult), RdCall(FrameworkMarshallers.Void, UnsatCoreResult), @@ -230,12 +269,16 @@ class SolverProtocolModel private constructor( print("deleteSolver = "); _deleteSolver.print(printer); println() print("configure = "); _configure.print(printer); println() print("assert = "); _assert.print(printer); println() + print("assertSoft = "); _assertSoft.print(printer); println() print("bulkAssert = "); _bulkAssert.print(printer); println() print("assertAndTrack = "); _assertAndTrack.print(printer); println() print("bulkAssertAndTrack = "); _bulkAssertAndTrack.print(printer); println() print("push = "); _push.print(printer); println() print("pop = "); _pop.print(printer); println() print("check = "); _check.print(printer); println() + print("checkMaxSMT = "); _checkMaxSMT.print(printer); println() + print("checkSubOptMaxSMT = "); _checkSubOptMaxSMT.print(printer); println() + print("collectMaxSMTStatistics = "); _collectMaxSMTStatistics.print(printer); println() print("checkWithAssumptions = "); _checkWithAssumptions.print(printer); println() print("model = "); _model.print(printer); println() print("unsatCore = "); _unsatCore.print(printer); println() @@ -251,12 +294,16 @@ class SolverProtocolModel private constructor( _deleteSolver.deepClonePolymorphic(), _configure.deepClonePolymorphic(), _assert.deepClonePolymorphic(), + _assertSoft.deepClonePolymorphic(), _bulkAssert.deepClonePolymorphic(), _assertAndTrack.deepClonePolymorphic(), _bulkAssertAndTrack.deepClonePolymorphic(), _push.deepClonePolymorphic(), _pop.deepClonePolymorphic(), _check.deepClonePolymorphic(), + _checkMaxSMT.deepClonePolymorphic(), + _checkSubOptMaxSMT.deepClonePolymorphic(), + _collectMaxSMTStatistics.deepClonePolymorphic(), _checkWithAssumptions.deepClonePolymorphic(), _model.deepClonePolymorphic(), _unsatCore.deepClonePolymorphic(), @@ -271,7 +318,7 @@ val IProtocol.solverProtocolModel get() = getOrCreateExtension(SolverProtocolMod /** - * #### Generated from [SolverProtocolModel.kt:47] + * #### Generated from [SolverProtocolModel.kt:52] */ data class AssertParams ( val expression: io.ksmt.KAst @@ -328,7 +375,7 @@ data class AssertParams ( /** - * #### Generated from [SolverProtocolModel.kt:51] + * #### Generated from [SolverProtocolModel.kt:56] */ data class BulkAssertParams ( val expressions: List @@ -385,7 +432,139 @@ data class BulkAssertParams ( /** - * #### Generated from [SolverProtocolModel.kt:59] + * #### Generated from [SolverProtocolModel.kt:72] + */ +data class CheckMaxSMTParams ( + val timeout: Long, + val collectStatistics: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = CheckMaxSMTParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): CheckMaxSMTParams { + val timeout = buffer.readLong() + val collectStatistics = buffer.readBool() + return CheckMaxSMTParams(timeout, collectStatistics) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CheckMaxSMTParams) { + buffer.writeLong(value.timeout) + buffer.writeBool(value.collectStatistics) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as CheckMaxSMTParams + + if (timeout != other.timeout) return false + if (collectStatistics != other.collectStatistics) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + timeout.hashCode() + __r = __r*31 + collectStatistics.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("CheckMaxSMTParams (") + printer.indent { + print("timeout = "); timeout.print(printer); println() + print("collectStatistics = "); collectStatistics.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [SolverProtocolModel.kt:77] + */ +data class CheckMaxSMTResult ( + val satSoftConstraints: List, + val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, + val maxSMTSucceeded: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = CheckMaxSMTResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): CheckMaxSMTResult { + val satSoftConstraints = buffer.readList { SoftConstraint.read(ctx, buffer) } + val hardConstraintsSatStatus = buffer.readEnum() + val maxSMTSucceeded = buffer.readBool() + return CheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, maxSMTSucceeded) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CheckMaxSMTResult) { + buffer.writeList(value.satSoftConstraints) { v -> SoftConstraint.write(ctx, buffer, v) } + buffer.writeEnum(value.hardConstraintsSatStatus) + buffer.writeBool(value.maxSMTSucceeded) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as CheckMaxSMTResult + + if (satSoftConstraints != other.satSoftConstraints) return false + if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false + if (maxSMTSucceeded != other.maxSMTSucceeded) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + satSoftConstraints.hashCode() + __r = __r*31 + hardConstraintsSatStatus.hashCode() + __r = __r*31 + maxSMTSucceeded.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("CheckMaxSMTResult (") + printer.indent { + print("satSoftConstraints = "); satSoftConstraints.print(printer); println() + print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() + print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [SolverProtocolModel.kt:64] */ data class CheckParams ( val timeout: Long @@ -442,7 +621,7 @@ data class CheckParams ( /** - * #### Generated from [SolverProtocolModel.kt:63] + * #### Generated from [SolverProtocolModel.kt:68] */ data class CheckResult ( val status: io.ksmt.solver.KSolverStatus @@ -499,7 +678,7 @@ data class CheckResult ( /** - * #### Generated from [SolverProtocolModel.kt:67] + * #### Generated from [SolverProtocolModel.kt:83] */ data class CheckWithAssumptionsParams ( val assumptions: List, @@ -561,6 +740,81 @@ data class CheckWithAssumptionsParams ( } +/** + * #### Generated from [SolverProtocolModel.kt:88] + */ +data class CollectMaxSMTStatisticsResult ( + val timeoutMs: Long, + val elapsedTimeMs: Long, + val timeInSolverQueriesMs: Long, + val queriesToSolverNumber: Int +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = CollectMaxSMTStatisticsResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): CollectMaxSMTStatisticsResult { + val timeoutMs = buffer.readLong() + val elapsedTimeMs = buffer.readLong() + val timeInSolverQueriesMs = buffer.readLong() + val queriesToSolverNumber = buffer.readInt() + return CollectMaxSMTStatisticsResult(timeoutMs, elapsedTimeMs, timeInSolverQueriesMs, queriesToSolverNumber) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CollectMaxSMTStatisticsResult) { + buffer.writeLong(value.timeoutMs) + buffer.writeLong(value.elapsedTimeMs) + buffer.writeLong(value.timeInSolverQueriesMs) + buffer.writeInt(value.queriesToSolverNumber) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as CollectMaxSMTStatisticsResult + + if (timeoutMs != other.timeoutMs) return false + if (elapsedTimeMs != other.elapsedTimeMs) return false + if (timeInSolverQueriesMs != other.timeInSolverQueriesMs) return false + if (queriesToSolverNumber != other.queriesToSolverNumber) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + timeoutMs.hashCode() + __r = __r*31 + elapsedTimeMs.hashCode() + __r = __r*31 + timeInSolverQueriesMs.hashCode() + __r = __r*31 + queriesToSolverNumber.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("CollectMaxSMTStatisticsResult (") + printer.indent { + print("timeoutMs = "); timeoutMs.print(printer); println() + print("elapsedTimeMs = "); elapsedTimeMs.print(printer); println() + print("timeInSolverQueriesMs = "); timeInSolverQueriesMs.print(printer); println() + print("queriesToSolverNumber = "); queriesToSolverNumber.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + /** * #### Generated from [SolverProtocolModel.kt:37] */ @@ -667,7 +921,7 @@ data class CreateSolverParams ( /** - * #### Generated from [SolverProtocolModel.kt:86] + * #### Generated from [SolverProtocolModel.kt:109] */ data class ModelEntry ( val decl: io.ksmt.KAst, @@ -742,7 +996,7 @@ data class ModelEntry ( /** - * #### Generated from [SolverProtocolModel.kt:80] + * #### Generated from [SolverProtocolModel.kt:103] */ data class ModelFuncInterpEntry ( val hasVars: Boolean, @@ -811,7 +1065,7 @@ data class ModelFuncInterpEntry ( /** - * #### Generated from [SolverProtocolModel.kt:98] + * #### Generated from [SolverProtocolModel.kt:121] */ data class ModelResult ( val declarations: List, @@ -880,7 +1134,7 @@ data class ModelResult ( /** - * #### Generated from [SolverProtocolModel.kt:93] + * #### Generated from [SolverProtocolModel.kt:116] */ data class ModelUninterpretedSortUniverse ( val sort: io.ksmt.KAst, @@ -943,7 +1197,7 @@ data class ModelUninterpretedSortUniverse ( /** - * #### Generated from [SolverProtocolModel.kt:55] + * #### Generated from [SolverProtocolModel.kt:60] */ data class PopParams ( val levels: UInt @@ -1000,7 +1254,7 @@ data class PopParams ( /** - * #### Generated from [SolverProtocolModel.kt:76] + * #### Generated from [SolverProtocolModel.kt:99] */ data class ReasonUnknownResult ( val reasonUnknown: String @@ -1056,6 +1310,69 @@ data class ReasonUnknownResult ( } +/** + * #### Generated from [SolverProtocolModel.kt:47] + */ +data class SoftConstraint ( + val expression: io.ksmt.KAst, + val weight: UInt +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SoftConstraint::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SoftConstraint { + val expression = (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) + val weight = buffer.readUInt() + return SoftConstraint(expression, weight) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SoftConstraint) { + (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, value.expression) + buffer.writeUInt(value.weight) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SoftConstraint + + if (expression != other.expression) return false + if (weight != other.weight) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + expression.hashCode() + __r = __r*31 + weight.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SoftConstraint (") + printer.indent { + print("expression = "); expression.print(printer); println() + print("weight = "); weight.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + /** * #### Generated from [SolverProtocolModel.kt:36] */ @@ -1143,7 +1460,7 @@ enum class SolverType { /** - * #### Generated from [SolverProtocolModel.kt:72] + * #### Generated from [SolverProtocolModel.kt:95] */ data class UnsatCoreResult ( val core: List From af14e36a2382eaf3cb8d120196c8f63d58df6a2d Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 10 May 2024 16:37:03 +0300 Subject: [PATCH 163/228] Process UNKNOWN cases in KPMResSolver --- .../solver/maxsmt/solvers/KPMResSolver.kt | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index ba1884088..4fb00b82c 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -13,7 +13,6 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint -import io.ksmt.solver.maxsmt.solvers.exceptions.NotYetImplementedException import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.TimerUtils @@ -48,23 +47,6 @@ class KPMResSolver(private val ctx: KContext, private val maxSMTResult = runMaxSMTLogic(timeout) - if (softConstraints.isEmpty()) { - if (this.collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - - return maxSMTResult - } - - // TODO: get max SAT soft constraints subset - if (!maxSMTResult.maxSMTSucceeded) { - if (this.collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - - throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") - } - if (this.collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -126,9 +108,8 @@ class KPMResSolver(private val ctx: KContext, private return processSat(model!!) } else if (solverStatus == UNKNOWN) { - // TODO: get max SAT soft constraints subset solver.pop() - throw NotYetImplementedException("MaxSMT solution was not found --- solver returned UNKNOWN") + return KMaxSMTResult(emptyList(), SAT, false) } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) From b114218fbf06c2ee2681b54ad9bf2fbcd50913c2 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 10 May 2024 17:24:31 +0300 Subject: [PATCH 164/228] Add debug step to workflow --- .github/workflows/run-long-maxsmt-tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 0011e7546..8ce5d4fe3 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -151,6 +151,10 @@ jobs: export ANALYZED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ inputs.solver }}_${{ inputs.smtSolver }}_analyzed_test_statistics.json echo "ANALYZED_STATISTICS_PATH=$ANALYZED_STATISTICS_PATH" >> $GITHUB_ENV + - name: Debug step + run: | + ls ${{steps.download.outputs.download-path}} + - name: Merge test run statistics # 1. Merge all test run statistics files into a single file: # - merge all test run statistics files (${{steps.download.outputs.download-path}}/*_test_statistics/*.json) for different theories From 1697ec0e04594bafaadec469a237b593f47371c1 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 10 May 2024 18:24:01 +0300 Subject: [PATCH 165/228] Upgrade download action version --- .github/workflows/run-long-maxsmt-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 8ce5d4fe3..cbdc40d10 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -138,7 +138,7 @@ jobs: uses: actions/checkout@v4 - name: Download test statistics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 id: download with: path: downloads From b1d0979b5bc6fe689bd23d294aa34740f8917b72 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 00:03:49 +0300 Subject: [PATCH 166/228] Remove debug step in workflow --- .github/workflows/run-long-maxsmt-tests.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index cbdc40d10..7e349f31c 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -151,10 +151,6 @@ jobs: export ANALYZED_STATISTICS_PATH=${{steps.download.outputs.download-path}}/${{ inputs.solver }}_${{ inputs.smtSolver }}_analyzed_test_statistics.json echo "ANALYZED_STATISTICS_PATH=$ANALYZED_STATISTICS_PATH" >> $GITHUB_ENV - - name: Debug step - run: | - ls ${{steps.download.outputs.download-path}} - - name: Merge test run statistics # 1. Merge all test run statistics files into a single file: # - merge all test run statistics files (${{steps.download.outputs.download-path}}/*_test_statistics/*.json) for different theories From 6be8ce98235a979ee2fe386729fd9214177a2ad3 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 00:05:01 +0300 Subject: [PATCH 167/228] Collect statistics about all theories --- scripts/analyze_maxsmt_statistics.py | 123 ++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index 228601758..8764e6ac6 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -28,6 +28,11 @@ def obj_dict(obj): logic_stat_str = json.dumps(logic_stat, default=obj_dict, indent=2, separators=(',', ': ')) print(logic_stat_str + "\n") + all_tests_stat = create_all_tests_statistics(logics_statistics) + logics_statistics.append(all_tests_stat) + all_tests_stat_str = json.dumps(all_tests_stat, default=obj_dict, indent=2, separators=(',', ': ')) + print(all_tests_stat_str + "\n") + with open(analyzed_stat_file_to_save, "w", encoding="utf-8") as f: json.dump(logics_statistics, f, default=obj_dict, indent=2, separators=(',', ': ')) @@ -35,8 +40,9 @@ def obj_dict(obj): def create_tests_size_statistics(tests): tests_size = len(tests) tests_executed_maxsmt_size = len(list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests))) + tests_executed_maxsmt_passed_size = len(list(filter(lambda x: x["passed"], tests))) tests_executed_maxsmt_passed_tests_percent = 0 if tests_executed_maxsmt_size == 0 \ - else len(list(filter(lambda x: x["passed"], tests))) / tests_executed_maxsmt_size * 100 + else tests_executed_maxsmt_passed_size / tests_executed_maxsmt_size * 100 failed_or_ignored_tests_size = len(list(filter(lambda x: not x["passed"], tests))) failed_tests_wrong_soft_constr_sum_size = len(list(filter(lambda x: x["checkedSoftConstraintsSumIsWrong"], tests))) ignored_tests_size = len(list(filter(lambda x: x["ignoredTest"], tests))) @@ -53,6 +59,7 @@ def get_unique_exception_messages(collection): list(filter(lambda x: x["failedOnParsingOrConvertingExpressions"], tests))) return TestsSizeStatistics(tests_size, tests_executed_maxsmt_size, + tests_executed_maxsmt_passed_size, tests_executed_maxsmt_passed_tests_percent, failed_or_ignored_tests_size, ignored_tests_size, failed_on_parsing_or_converting_expressions_size, @@ -156,6 +163,23 @@ def elapsed_time_ms(test): avg_elapsed_failed_tests_time_ms) +def create_tests_score_statistics(tests): + tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) + + passed_tests = list(filter(lambda x: x["passed"], tests_executed_maxsmt)) + passed_tests_size = len(passed_tests) + + def score(x): + if isinstance(x, int) or isinstance(x, float): + return x + return 1 if x["foundSoFarWeight"] == 0 else x["optimalWeight"] / x["foundSoFarWeight"] + + avg_score_passed_tests = reduce( + lambda x, y: score(x) + score(y), passed_tests, 0) / passed_tests_size + + return TestsScoreStatistics(avg_score_passed_tests) + + class MaxSMTContext: def __int__(self, strategy, prefer_large_weight_constraints_for_cores, minimize_cores, get_multiple_cores): self.strategy = strategy @@ -166,6 +190,7 @@ def __int__(self, strategy, prefer_large_weight_constraints_for_cores, minimize_ class TestsSizeStatistics: def __init__(self, tests_size, tests_executed_maxsmt_size, + tests_executed_maxsmt_passed_size, tests_executed_maxsmt_passed_tests_percent, failed_tests_size, ignored_tests_size, @@ -174,6 +199,7 @@ def __init__(self, tests_size, tests_executed_maxsmt_size, failed_tests_wrong_soft_constr_sum_size): self.tests_size = tests_size self.tests_executed_maxsmt_size = tests_executed_maxsmt_size + self.tests_executed_maxsmt_passed_size = tests_executed_maxsmt_passed_size self.tests_executed_maxsmt_passed_tests_percent = tests_executed_maxsmt_passed_tests_percent, self.failed_tests_size = failed_tests_size self.ignored_tests_size = ignored_tests_size @@ -183,6 +209,11 @@ def __init__(self, tests_size, tests_executed_maxsmt_size, self.failed_tests_wrong_soft_constr_sum_size = failed_tests_wrong_soft_constr_sum_size +class TestsScoreStatistics: + def __init__(self, avg_score_passed_tests): + self.avg_score_passed_tests = avg_score_passed_tests + + class TestsQueriesToSolverStatistics: def __init__(self, avg_queries_to_solver_number, avg_queries_to_solver_passed_tests_number, avg_queries_to_solver_failed_tests_number, avg_time_per_solver_queries_percent, @@ -205,6 +236,7 @@ def __init__(self, avg_elapsed_time_ms, avg_elapsed_passed_tests_time_ms, class LogicTestsStatistics: def __init__(self, smt_solver, name, timeout_ms, max_smt_ctx, tests_size_stat: TestsSizeStatistics, + tests_score_stat: TestsScoreStatistics, tests_queries_to_solver_stat: TestsQueriesToSolverStatistics, tests_elapsed_time_stat: TestsElapsedTimeStatistics): self.smt_solver = smt_solver @@ -212,10 +244,98 @@ def __init__(self, smt_solver, name, timeout_ms, max_smt_ctx, tests_size_stat: T self.timeout_ms = timeout_ms self.max_smt_ctx = max_smt_ctx self.tests_size_stat = tests_size_stat + self.tests_score_stat = tests_score_stat self.tests_queries_to_solver_stat = tests_queries_to_solver_stat self.tests_elapsed_time_stat = tests_elapsed_time_stat +class AllTestsStatistics: + def __init__(self, timeout_ms, max_smt_ctx, tests_size_stat: TestsSizeStatistics, + tests_score_stat: TestsScoreStatistics, tests_elapsed_time_stat: TestsElapsedTimeStatistics): + self.timeout_ms = timeout_ms + self.max_smt_ctx = max_smt_ctx + self.tests_size_stat = tests_size_stat + self.tests_score_stat = tests_score_stat + self.tests_elapsed_time_stat = tests_elapsed_time_stat + + +def create_all_tests_statistics(logics_statistics): + first_logic: LogicTestsStatistics = logics_statistics[0] + + timeout_ms = first_logic.timeout_ms + max_smt_ctx = first_logic.max_smt_ctx + + # For test size statistics. + tests_size = 0 + tests_executed_maxsmt_size = 0 + tests_executed_maxsmt_passed_size = 0 + failed_tests_size = 0 + ignored_tests_size = 0 + failed_on_parsing_or_converting_expressions_size = 0 + failed_on_parsing_or_converting_expressions_exception_messages = [] + failed_tests_wrong_soft_constr_sum_size = 0 + + # For test score statistics. + avg_score_passed_tests = 0.0 + + # For elapsed time statistics. + avg_elapsed_time_ms = 0.0 + avg_elapsed_passed_tests_time_ms = 0.0 + avg_elapsed_failed_tests_time_ms = 0.0 + + for logic in logics_statistics: + size_statistics = logic.tests_size_stat + score_statistics = logic.tests_score_stat + elapsed_time_statistics = logic.tests_elapsed_time_stat + + tests_size += size_statistics.tests_size + tests_executed_maxsmt_size += size_statistics.tests_executed_maxsmt_size + tests_executed_maxsmt_passed_size += size_statistics.tests_executed_maxsmt_passed_size + failed_tests_size += size_statistics.failed_tests_size + ignored_tests_size += size_statistics.ignored_tests_size + failed_on_parsing_or_converting_expressions_size += ( + size_statistics.failed_on_parsing_or_converting_expressions_size) + failed_on_parsing_or_converting_expressions_exception_messages += ( + size_statistics.failed_on_parsing_or_converting_expressions_exception_messages) + failed_tests_wrong_soft_constr_sum_size += size_statistics.failed_tests_wrong_soft_constr_sum_size + + avg_score_passed_tests += (score_statistics.avg_score_passed_tests * + size_statistics.tests_executed_maxsmt_passed_size) + + avg_elapsed_time_ms += (elapsed_time_statistics.avg_elapsed_time_ms * + size_statistics.tests_executed_maxsmt_size) + avg_elapsed_passed_tests_time_ms += ( + elapsed_time_statistics.avg_elapsed_passed_tests_time_ms * + size_statistics.tests_executed_maxsmt_passed_size) + avg_elapsed_failed_tests_time_ms += elapsed_time_statistics.avg_elapsed_failed_tests_time_ms * ( + size_statistics.tests_executed_maxsmt_size - size_statistics.tests_executed_maxsmt_passed_size) + + avg_score_passed_tests /= tests_executed_maxsmt_passed_size \ + if tests_executed_maxsmt_passed_size != 0 else "No tests" + + avg_elapsed_time_ms /= tests_executed_maxsmt_size \ + if tests_executed_maxsmt_size != 0 else "No tests" + avg_elapsed_passed_tests_time_ms /= tests_executed_maxsmt_passed_size \ + if tests_executed_maxsmt_passed_size != 0 else "No tests" + avg_elapsed_failed_tests_time_ms /= (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) \ + if (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) != 0 else "No tests" + + return AllTestsStatistics(timeout_ms, max_smt_ctx, + TestsSizeStatistics(tests_size, tests_executed_maxsmt_size, + tests_executed_maxsmt_passed_size, + tests_executed_maxsmt_passed_size / tests_executed_maxsmt_size, + failed_tests_size, ignored_tests_size, + failed_on_parsing_or_converting_expressions_size, + failed_on_parsing_or_converting_expressions_exception_messages, + failed_tests_wrong_soft_constr_sum_size + ), + TestsScoreStatistics(avg_score_passed_tests), + TestsElapsedTimeStatistics(avg_elapsed_time_ms, + avg_elapsed_passed_tests_time_ms, + avg_elapsed_failed_tests_time_ms) + ) + + def create_logic_statistics(logic): tests = logic["TESTS"] first_test = tests[0] @@ -224,6 +344,7 @@ def create_logic_statistics(logic): "maxSMTCallStatistics"] return LogicTestsStatistics(first_test["smtSolver"], logic["NAME"], first_max_smt_call_stat["timeoutMs"], first_max_smt_call_stat["maxSmtCtx"], create_tests_size_statistics(tests), + create_tests_score_statistics(tests), create_tests_queries_to_solver_statistics(tests), create_tests_elapsed_time_statistics(tests)) From 506ba0cf68b38b448e4d1c6bb281e8ec2d676b52 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 00:06:19 +0300 Subject: [PATCH 168/228] Fix a bug with optimalWeight in KSubOptMaxSMTBenchmarkTest --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index a59d55757..c988946ad 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -196,7 +196,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(10.seconds, true) + maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(1.seconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() @@ -216,7 +216,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { assertTrue(maxSMTResult.maxSMTSucceeded, "SubOpt MaxSMT was not successful [$name]") assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") - if (optimalWeight >= foundSoFarWeight) { + if (foundSoFarWeight > optimalWeight) { testStatistics.checkedSoftConstraintsSumIsWrong = true } assertTrue( From cb5d1b4ba0fccd5ef000c3e60c41c264c8ce1769 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 01:19:18 +0300 Subject: [PATCH 169/228] Fix a bug with score collection --- scripts/analyze_maxsmt_statistics.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index 8764e6ac6..925b99e7c 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -172,7 +172,16 @@ def create_tests_score_statistics(tests): def score(x): if isinstance(x, int) or isinstance(x, float): return x - return 1 if x["foundSoFarWeight"] == 0 else x["optimalWeight"] / x["foundSoFarWeight"] + found_so_far_weight = x["foundSoFarWeight"] + optimal_weight = x["optimalWeight"] + + if optimal_weight == 0: + if found_so_far_weight == 0: + return 1 + else: + return 0 + else: + return found_so_far_weight / optimal_weight avg_score_passed_tests = reduce( lambda x, y: score(x) + score(y), passed_tests, 0) / passed_tests_size From b3053c38f0769fa0c85799203b066cc1419a43a5 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 16:02:46 +0300 Subject: [PATCH 170/228] Use solverQuery for checkMaxSMT in KPortfolioSolver --- .../ksmt/solver/portfolio/KPortfolioSolver.kt | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 2df674b8c..4a3b55b18 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -140,27 +140,17 @@ class KPortfolioSolver( } override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - val solverAwaitResult = solverOperationWithResult( + solverQuery( { checkMaxSMT(timeout, collectStatistics) }, - { it.maxSMTSucceeded } + { this.maxSMTSucceeded } ) - - when (solverAwaitResult) { - is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result - is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") - } } override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - val solverAwaitResult = solverOperationWithResult( + solverQuery( { checkSubOptMaxSMT(timeout, collectStatistics) }, - { it.maxSMTSucceeded } + { this.maxSMTSucceeded } ) - - when (solverAwaitResult) { - is SolverAwaitSuccess -> return@runBlocking solverAwaitResult.result.result - is SolverAwaitFailure -> throw KSolverException("MaxSMT portfolio solver failed") - } } override fun collectMaxSMTStatistics(): KMaxSMTStatistics = runBlocking { @@ -225,16 +215,19 @@ class KPortfolioSolver( popAsync(n) } - override suspend fun checkAsync(timeout: Duration): KSolverStatus = solverQuery { - checkAsync(timeout) - } + override suspend fun checkAsync(timeout: Duration): KSolverStatus = solverQuery( + { checkAsync(timeout) }, + { this != KSolverStatus.UNKNOWN } + ) override suspend fun checkWithAssumptionsAsync( assumptions: List>, timeout: Duration - ): KSolverStatus = solverQuery { - checkWithAssumptionsAsync(assumptions, timeout) - } + ): KSolverStatus = solverQuery( + { checkWithAssumptionsAsync(assumptions, timeout) }, + { this != KSolverStatus.UNKNOWN } + + ) override suspend fun modelAsync(): KModel = lastSuccessfulSolver.get()?.modelAsync() @@ -290,15 +283,16 @@ class KPortfolioSolver( return result } - private suspend inline fun solverQuery( - crossinline block: suspend KSolverRunner<*>.() -> KSolverStatus - ): KSolverStatus { + private suspend inline fun solverQuery( + crossinline block: suspend KSolverRunner<*>.() -> T, + crossinline solverPredicate: T.() -> Boolean + ): T { terminateIfNeeded() lastSuccessfulSolver.getAndSet(null) val awaitResult = awaitFirstSolver(block) { - it != KSolverStatus.UNKNOWN + solverPredicate(it) } val result = when (awaitResult) { From c0241a0011df87f9f66882f7b00167b75b0c2ea6 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 11 May 2024 16:40:34 +0300 Subject: [PATCH 171/228] Update workers number --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index c988946ad..49c08a272 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -282,7 +282,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @JvmStatic fun initWorkerPools() { testWorkers = KsmtWorkerPool( - maxWorkerPoolSize = 4, + maxWorkerPoolSize = 1, workerProcessIdleTimeout = 10.minutes, workerFactory = object : KsmtWorkerFactory { override val childProcessEntrypoint = TestWorkerProcess::class @@ -304,7 +304,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { solverManager = KPortfolioSolverManager( listOf( KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class - ), 4 + ), 2 ) } From 7bec8749339ade148f413bb14fd4768ba2abbb49 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 13:46:32 +0300 Subject: [PATCH 172/228] Fix a bug with internalizing assertions for Portfolio --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 2 +- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 6420bb4e8..2f203fe01 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -110,7 +110,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTPortfolioTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, PORTFOLIO) + testMaxSMTSolver(name, samplePath, { assertions -> assertions }, PORTFOLIO) } private fun testMaxSMTSolver( diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 49c08a272..0416adb17 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -130,7 +130,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTPortfolioTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, PORTFOLIO) + testMaxSMTSolver(name, samplePath, { assertions -> assertions }, PORTFOLIO) } private fun testMaxSMTSolver( From 53c303c59e265f5468374901f778c7080b02d919 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 14:12:04 +0300 Subject: [PATCH 173/228] Update timeout to 10 seconds --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 0416adb17..3d5f5c553 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -196,7 +196,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(1.seconds, true) + maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(10.seconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() From c8cb725e585a29aeb5e49d96d489f1d652fc3aa0 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 15:33:21 +0300 Subject: [PATCH 174/228] Update timeout for MaxSAT tests --- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 690bb60c5..5b5399971 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -51,7 +51,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - val maxSATResult = maxSATSolver.checkMaxSMT(20.seconds) + val maxSATResult = maxSATSolver.checkMaxSMT(30.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second From 8670b7f160748a8da49856abd55f53e8b2f3e1e4 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 16:26:27 +0300 Subject: [PATCH 175/228] Run KPrimalDualMaxRes solver for corectness check --- .github/workflows/run-long-maxsat-tests.yml | 2 +- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 2 +- .../maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index 23d6e5143..3711a39d2 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -49,7 +49,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.sat.KPMResSATBenchmarkTest" + --tests "io.ksmt.solver.maxsmt.test.sat.KPrimalDualMaxResSATBenchmarkTest" - name: Upload test report if: always() diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 5b5399971..8400f67ef 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -51,7 +51,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - val maxSATResult = maxSATSolver.checkMaxSMT(30.seconds) + val maxSATResult = maxSATSolver.checkMaxSMT(60.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt index d3bd8373a..064eeea77 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -9,6 +9,9 @@ import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { override fun getSolver(): KMaxSMTSolver = with(ctx) { val z3Solver = KZ3Solver(this) - return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) + return KPrimalDualMaxResSolver( + this, z3Solver, + KMaxSMTContext(preferLargeWeightConstraintsForCores = true) + ) } } From 664b0e61e17905457f7e1819201c556258d97726 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 16:59:32 +0300 Subject: [PATCH 176/228] Update MaxSAT tests for multiple solvers --- .../maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 55 ++++++++++++++++--- .../maxsmt/test/sat/KPMResSATBenchmarkTest.kt | 8 +-- .../sat/KPrimalDualMaxResSATBenchmarkTest.kt | 8 +-- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 8400f67ef..841f5b18b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -1,15 +1,20 @@ package io.ksmt.solver.maxsmt.test.sat import io.ksmt.KContext +import io.ksmt.solver.KSolver import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT +import io.ksmt.solver.bitwuzla.KBitwuzlaSolver +import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.constraints.HardConstraint import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSATTest +import io.ksmt.solver.maxsmt.test.utils.Solver +import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.z3.KZ3Solver import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Path @@ -19,14 +24,24 @@ import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { - abstract fun getSolver(): KMaxSMTSolver + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { + return when (solver) { + Solver.Z3 -> KZ3Solver(this) + Solver.BITWUZLA -> KBitwuzlaSolver(this) + Solver.CVC5 -> KCvc5Solver(this) + Solver.YICES -> KYicesSolver(this) + Solver.PORTFOLIO -> + throw NotImplementedError("Portfolio solver for MaxSAT is not supported in tests") + } + } + + abstract fun getSolver(solver: Solver): KMaxSMTSolver protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSMTSolver + private lateinit var maxSATSolver: KMaxSMTSolver - @BeforeEach - fun initSolver() { - maxSATSolver = getSolver() + private fun initSolver(solver: Solver) { + maxSATSolver = getSolver(solver) } @AfterEach @@ -34,7 +49,29 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSATTestData") - fun maxSATTest(name: String, samplePath: Path) = with(ctx) { + fun maxSATZ3Test(name: String, samplePath: Path) { + testMaxSATSolver(name, samplePath, Solver.Z3) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSATTestData") + fun maxSATBitwuzlaTest(name: String, samplePath: Path) { + testMaxSATSolver(name, samplePath, Solver.BITWUZLA) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSATTestData") + fun maxSATCvc5Test(name: String, samplePath: Path) { + testMaxSATSolver(name, samplePath, Solver.CVC5) + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSATTestData") + fun maxSATYicesTest(name: String, samplePath: Path) { + testMaxSATSolver(name, samplePath, Solver.YICES) + } + + private fun testMaxSATSolver(name: String, samplePath: Path, solver: Solver) = with(ctx) { val testData = maxSATTestNameToExpectedResult.find { it.first == samplePath.name } require(testData != null) { "Test [$name] expected result must be specified" } @@ -42,6 +79,8 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { var sumOfSoftConstraintsWeights = 0u + initSolver(solver) + constraints.forEach { if (it is HardConstraint) { maxSATSolver.assert(it.expression) @@ -63,7 +102,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { expectedSatConstraintsScore, satConstraintsScore.toULong(), "Soft constraints score was [$satConstraintsScore], " + - "but must be [$expectedSatConstraintsScore]", + "but must be [$expectedSatConstraintsScore]", ) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt index 626b7ce09..3a5c83951 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt @@ -3,11 +3,11 @@ package io.ksmt.solver.maxsmt.test.sat import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPMResSolver -import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { - val z3Solver = KZ3Solver(this) - return KPMResSolver(this, z3Solver) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) + return KPMResSolver(this, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt index 064eeea77..65ba61b11 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -4,13 +4,13 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.z3.KZ3Solver +import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { - val z3Solver = KZ3Solver(this) + override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver( - this, z3Solver, + this, smtSolver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true) ) } From 22db2e2e8f1a7de68f89d08ae2fc66ab163915b3 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 17:02:35 +0300 Subject: [PATCH 177/228] Update MaxSAT workflow for multiple solvers --- .github/workflows/run-long-maxsat-tests.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsat-tests.yml b/.github/workflows/run-long-maxsat-tests.yml index 3711a39d2..db4969ddc 100644 --- a/.github/workflows/run-long-maxsat-tests.yml +++ b/.github/workflows/run-long-maxsat-tests.yml @@ -2,6 +2,15 @@ name: Build and run long ksmt MaxSAT tests on: workflow_dispatch: + inputs: + smtSolver: + type: choice + description: Chosen SMT solver + options: + - Z3 + - Bitwuzla + - Cvc5 + - Yices env: TEST_DATA_REVISION: 0.0.0 @@ -49,7 +58,7 @@ jobs: arguments: | :ksmt-maxsmt-test:test --no-daemon - --tests "io.ksmt.solver.maxsmt.test.sat.KPrimalDualMaxResSATBenchmarkTest" + --tests "io.ksmt.solver.maxsmt.test.sat.KPrimalDualMaxResSATBenchmarkTest.maxSAT${{ inputs.smtSolver }}Test" - name: Upload test report if: always() From 99a0bc3152ff2cdc28953fd199235b710a20e433 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 21:48:10 +0300 Subject: [PATCH 178/228] Fix a bug with integer overflow --- .../maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 748 +++++++++--------- 1 file changed, 374 insertions(+), 374 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 841f5b18b..3c347ed08 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -77,7 +77,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { val constraints = parseMaxSATTest(samplePath, this) - var sumOfSoftConstraintsWeights = 0u + var sumOfSoftConstraintsWeights = 0uL initSolver(solver) @@ -91,7 +91,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } val maxSATResult = maxSATSolver.checkMaxSMT(60.seconds) - val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight } + val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight.toULong() } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second @@ -100,383 +100,383 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { assertTrue(maxSATResult.satSoftConstraints.isNotEmpty(), "Soft constraints size should not be 0") assertEquals( expectedSatConstraintsScore, - satConstraintsScore.toULong(), + satConstraintsScore, "Soft constraints score was [$satConstraintsScore], " + "but must be [$expectedSatConstraintsScore]", ) } // Elements are pairs of test name and expected test result (cost equal to the excluded soft constraints sum). - private val maxSATTestNameToExpectedResult = hashSetOf>( - "ae_5_20_ibmq-casablanca_7.wcnf" to 15u, - "af-synthesis_stb_50_20_8.wcnf" to 120u, - "af-synthesis_stb_50_40_0.wcnf" to 111u, - "af-synthesis_stb_50_40_8.wcnf" to 117u, - "af-synthesis_stb_50_60_3.wcnf" to 115u, - "af-synthesis_stb_50_80_7.wcnf" to 115u, - "af-synthesis_stb_50_80_8.wcnf" to 116u, - "af-synthesis_stb_50_100_3.wcnf" to 104u, - "af-synthesis_stb_50_120_3.wcnf" to 100u, - "af-synthesis_stb_50_140_1.wcnf" to 127u, - "af-synthesis_stb_50_140_3.wcnf" to 96u, - "af-synthesis_stb_50_140_8.wcnf" to 113u, - "af-synthesis_stb_50_160_5.wcnf" to 113u, - "af-synthesis_stb_50_180_1.wcnf" to 130u, - "af-synthesis_stb_50_200_4.wcnf" to 105u, - "af-synthesis_stb_50_200_5.wcnf" to 102u, - "af-synthesis_stb_50_200_6.wcnf" to 111u, - "amazon.dimacs.wcnf" to 113575u, - "ar-3.wcnf" to 43814u, - "archlinux.dimacs.wcnf" to 11744u, - "auc.cat_paths_60_100_0006.txt.wcnf" to 70929u, - "auc.cat_paths_60_150_0007.txt.wcnf" to 100364u, - "auc.cat_paths_60_170_0007.txt.wcnf" to 94165u, - "auc.cat_paths_60_200_0009.txt.wcnf" to 157873u, - "auc.cat_reg_60_110_0004.txt.wcnf" to 75602u, - "auc.cat_reg_60_130_0001.txt.wcnf" to 104032u, - "auc.cat_reg_60_160_0002.txt.wcnf" to 164897u, - "auc.cat_sched_60_80_0005.txt.wcnf" to 34950u, - "auc.cat_sched_60_90_0001.txt.wcnf" to 82847u, - "auc.cat_sched_60_100_0003.txt.wcnf" to 34771u, - "auc.cat_sched_60_120_0004.txt.wcnf" to 82385u, - "auc.cat_sched_60_150_0001.txt.wcnf" to 155865u, - "auc.cat_sched_60_150_0005.txt.wcnf" to 72866u, - "auc.cat_sched_60_150_0009.txt.wcnf" to 58699u, - "auc.cat_sched_60_160_0003.txt.wcnf" to 118883u, - "auc.cat_sched_60_200_0005.txt.wcnf" to 172768u, - "cap71.wcsp.wcnf" to 9326144u, - "cap72.wcsp.wcnf" to 9777981u, - "cap91.wcsp.wcnf" to 7966472u, - "cap92.wcsp.wcnf" to 8547029u, - "cap131.wcsp.wcnf" to 7934385u, - "cap132.wcsp.wcnf" to 8514942u, - "car.formula_0.8_2021_atleast_15_max-3_reduced_incomplete_adaboost_2.wcnf" to 232u, - "causal_Meta_7_528.wcnf" to 55120u, - "causal_n5_i5_N1000_uai13_log_int.wcnf" to 46030289u, - "causal_n6_i1_N10000_uai14_log_int.wcnf" to 1510725680u, - "causal_n6_i6_N1000_uai14_log_int.wcnf" to 126257527700u, - "causal_n7_i8_N10000_uai14_log_int.wcnf" to 11486104693u, - "causal_n7_i10_N1000_uai14_log_int.wcnf" to 3246397504u, - "causal_Pigs_6_10000.wcnf" to 25539892u, - "causal_Statlog_7_752.wcnf" to 380356u, - "causal_Voting_7_435.wcnf" to 930263u, - "causal_Water_7_380.wcnf" to 414473u, - "comp04.wcnf" to 35u, - "comp06.wcnf" to 27u, - "CSG40-40-95.wcnf" to 8847u, - "CSG60-60-88.wcnf" to 7714u, - "CSGNaive60-60-53.wcnf" to 9829u, - "CSGNaive70-70-91.wcnf" to 11177u, - "dblp.dimacs.wcnf" to 25014u, - "dim.brock800_3.clq.wcnf" to 1079u, - "dim.c-fat200-1.clq.wcnf" to 14u, - "dim.c-fat500-1.clq.wcnf" to 10u, - "dim.san400_0.7_3.clq.wcnf" to 1201u, - "dir.5.wcsp.dir.wcnf" to 261u, - "dir.28.wcsp.dir.wcnf" to 270105u, - "dir.54.wcsp.dir.wcnf" to 37u, - "dir.404.wcsp.dir.wcnf" to 114u, - "dir.408.wcsp.dir.wcnf" to 6228u, - "dir.507.wcsp.dir.wcnf" to 27390u, - "dir.509.wcsp.dir.wcnf" to 27390u, - "dir.1403.wcsp.dir.wcnf" to 459246u, - "dir.1502.wcsp.dir.wcnf" to 28042u, - "dir.1506.wcsp.dir.wcnf" to 354517u, - "drmx-am12-outof-40-ecardn-w.wcnf" to 28u, - "drmx-am12-outof-40-esortn-w.wcnf" to 28u, - "drmx-am16-outof-45-emtot-w.wcnf" to 29u, - "drmx-am16-outof-45-eseqc-w.wcnf" to 29u, - "drmx-am20-outof-50-ekmtot-w.wcnf" to 30u, - "drmx-am20-outof-50-emtot-w.wcnf" to 30u, - "drmx-am20-outof-50-eseqc-w.wcnf" to 30u, - "drmx-am24-outof-55-emtot-w.wcnf" to 31u, - "drmx-am24-outof-55-etot-w.wcnf" to 31u, - "drmx-am28-outof-60-emtot-w.wcnf" to 32u, - "drmx-am28-outof-60-eseqc-w.wcnf" to 32u, - "drmx-am28-outof-60-etot-w.wcnf" to 32u, - "drmx-am32-outof-70-ecardn-w.wcnf" to 38u, - "drmx-am32-outof-70-ekmtot-w.wcnf" to 38u, - "drmx-am32-outof-70-emtot-w.wcnf" to 38u, - "drmx-am32-outof-70-etot-w.wcnf" to 38u, - "eas.310-15.wcnf" to 30501u, - "eas.310-28.wcnf" to 33151u, - "eas.310-29.wcnf" to 34431u, - "eas.310-33.wcnf" to 35463u, - "eas.310-43.wcnf" to 33138u, - "eas.310-44.wcnf" to 50871u, - "eas.310-55.wcnf" to 21952u, - "eas.310-74.wcnf" to 21867u, - "eas.310-91.wcnf" to 37183u, - "eas.310-93.wcnf" to 19146u, - "eas.310-94.wcnf" to 26160u, - "eas.310-95.wcnf" to 25854u, - "eas.310-97.wcnf" to 35249u, - "ebay.dimacs.wcnf" to 123941u, - "f1-DataDisplay_0_order4.seq-A-2-1-EDCBAir.wcnf" to 6223203u, - "f1-DataDisplay_0_order4.seq-A-2-2-abcdeir.wcnf" to 481429u, - "f1-DataDisplay_0_order4.seq-A-2-2-irabcde.wcnf" to 2220415u, - "f1-DataDisplay_0_order4.seq-A-3-1-EDCBAir.wcnf" to 6240245u, - "f1-DataDisplay_0_order4.seq-A-3-1-irabcde.wcnf" to 5960556u, - "f1-DataDisplay_0_order4.seq-A-3-2-irabcde.wcnf" to 5955300u, - "f1-DataDisplay_0_order4.seq-B-2-2-abcdeir.wcnf" to 53533u, - "f1-DataDisplay_0_order4.seq-B-2-2-irEDCBA.wcnf" to 2273346u, - "f49-DC_TotalLoss.seq-A-2-1-abcdeir.wcnf" to 27698412327u, - "f49-DC_TotalLoss.seq-A-2-1-irEDCBA.wcnf" to 14779649425u, - "f49-DC_TotalLoss.seq-A-2-combined-irabcde.wcnf" to 14735114187u, - "f49-DC_TotalLoss.seq-A-3-2-irEDCBA.wcnf" to 87222797189u, - "f49-DC_TotalLoss.seq-B-2-2-abcdeir.wcnf" to 44321234u, - "f49-DC_TotalLoss.seq-B-2-combined-EDCBAir.wcnf" to 83838199998u, - "f49-DC_TotalLoss.seq-B-3-combined-EDCBAir.wcnf" to 117355113043u, - "f49-DC_TotalLoss.seq-B-3-combined-irabcde.wcnf" to 87177360578u, - "facebook1.dimacs.wcnf" to 45581u, - "github.dimacs.wcnf" to 187405u, - "graphstate_6_6_rigetti-agave_8.wcnf" to 6u, - "grover-noancilla_4_52_rigetti-agave_8.wcnf" to 42u, - "grover-v-chain_4_52_ibmq-casablanca_7.wcnf" to 27u, - "guardian.dimacs.wcnf" to 160777u, - "inst2.lp.sm-extracted.wcnf" to 97u, - "inst10.lp.sm-extracted.wcnf" to 105u, - "inst22.lp.sm-extracted.wcnf" to 180u, - "instance1.wcnf" to 607u, - "instance2.wcnf" to 828u, - "ItalyInstance1.xml.wcnf" to 12u, - "k50-18-30.rna.pre.wcnf" to 462u, - "k50-21-38.rna.pre.wcnf" to 497u, - "k100-14-38.rna.pre.wcnf" to 1953u, - "k100-20-63.rna.pre.wcnf" to 2030u, - "k100-38-60.rna.pre.wcnf" to 1878u, - "k100-40-52.rna.pre.wcnf" to 1861u, - "k100-73-76.rna.pre.wcnf" to 2008u, - "k100-78-85.rna.pre.wcnf" to 1744u, - "lisbon-wedding-1-18.wcnf" to 961u, - "lisbon-wedding-2-18.wcnf" to 1137u, - "lisbon-wedding-3-17.wcnf" to 1035u, - "lisbon-wedding-4-18.wcnf" to 803u, - "lisbon-wedding-5-17.wcnf" to 802u, - "lisbon-wedding-9-17.wcnf" to 394u, - "lisbon-wedding-10-17.wcnf" to 377u, - "log.8.wcsp.log.wcnf" to 2u, - "log.28.wcsp.log.wcnf" to 270105u, - "log.408.wcsp.log.wcnf" to 6228u, - "log.505.wcsp.log.wcnf" to 21253u, - "log.1401.wcsp.log.wcnf" to 459106u, - "londonist.dimacs.wcnf" to 70703u, - "metro_8_8_5_20_10_6_500_1_0.lp.sm-extracted.wcnf" to 82u, - "metro_8_8_5_20_10_6_500_1_7.lp.sm-extracted.wcnf" to 89u, - "metro_8_8_5_20_10_6_500_1_9.lp.sm-extracted.wcnf" to 105u, - "metro_9_8_7_22_10_6_500_1_1.lp.sm-extracted.wcnf" to 52u, - "metro_9_8_7_22_10_6_500_1_2.lp.sm-extracted.wcnf" to 60u, - "metro_9_8_7_22_10_6_500_1_3.lp.sm-extracted.wcnf" to 44u, - "metro_9_8_7_30_10_6_500_1_5.lp.sm-extracted.wcnf" to 47u, - "metro_9_8_7_30_10_6_500_1_6.lp.sm-extracted.wcnf" to 31u, - "metro_9_8_7_30_10_6_500_1_7.lp.sm-extracted.wcnf" to 47u, - "metro_9_8_7_30_10_6_500_1_8.lp.sm-extracted.wcnf" to 55u, - "metro_9_9_10_35_13_7_500_2_7.lp.sm-extracted.wcnf" to 37u, - "MinWidthCB_milan_100_12_1k_1s_2t_3.wcnf" to 109520u, - "MinWidthCB_milan_200_12_1k_4s_1t_4.wcnf" to 108863u, - "MinWidthCB_mitdbsample_100_43_1k_2s_2t_2.wcnf" to 38570u, - "MinWidthCB_mitdbsample_100_64_1k_2s_1t_2.wcnf" to 66045u, - "MinWidthCB_mitdbsample_200_43_1k_2s_2t_2.wcnf" to 50615u, - "MinWidthCB_mitdbsample_200_64_1k_2s_1t_2.wcnf" to 78400u, - "MinWidthCB_mitdbsample_200_64_1k_2s_3t_2.wcnf" to 73730u, - "MinWidthCB_mitdbsample_300_26_1k_3s_2t_3.wcnf" to 32420u, - "MLI.ilpd_train_0_DNF_5_5.wcnf" to 700u, - "mul.role_smallcomp_multiple_0.3_6.wcnf" to 139251u, - "mul.role_smallcomp_multiple_1.0_6.wcnf" to 295598u, - "openstreetmap.dimacs.wcnf" to 65915u, - "pac.80cfe9a6-9b1b-11df-965e-00163e46d37a_l1.wcnf" to 1924238u, - "pac.fa3d0fb2-db9e-11df-a0ec-00163e3d3b7c_l1.wcnf" to 4569599u, - "pac.rand179_l1.wcnf" to 493118u, - "pac.rand892_l1.wcnf" to 224702u, - "pac.rand984_l1.wcnf" to 345082u, - "ped2.B.recomb1-0.01-2.wcnf" to 7u, - "ped2.B.recomb1-0.10-7.wcnf" to 588u, - "ped3.D.recomb10-0.20-12.wcnf" to 349u, - "ped3.D.recomb10-0.20-14.wcnf" to 7u, - "portfoliovqe_4_18_rigetti-agave_8.wcnf" to 33u, - "power-distribution_1_2.wcnf" to 3u, - "power-distribution_1_4.wcnf" to 3u, - "power-distribution_1_6.wcnf" to 3u, - "power-distribution_1_8.wcnf" to 3u, - "power-distribution_2_2.wcnf" to 10u, - "power-distribution_2_8.wcnf" to 10u, - "power-distribution_3_4.wcnf" to 1u, - "power-distribution_7_6.wcnf" to 18u, - "power-distribution_8_4.wcnf" to 40u, - "power-distribution_8_7.wcnf" to 40u, - "power-distribution_9_2.wcnf" to 18u, - "power-distribution_11_6.wcnf" to 126u, - "power-distribution_12_2.wcnf" to 216u, - "power-distribution_12_5.wcnf" to 216u, - "qaoa_4_16_ibmq-casablanca_7.wcnf" to 12u, - "qft_5_26_ibmq-casablanca_7.wcnf" to 15u, - "qftentangled_4_21_ibmq-casablanca_7.wcnf" to 15u, - "qftentangled_4_39_rigetti-agave_8.wcnf" to 18u, - "qftentangled_5_30_ibmq-london_5.wcnf" to 27u, - "qftentangled_5_48_rigetti-agave_8.wcnf" to 24u, - "qgan_6_15_ibmq-casablanca_7.wcnf" to 24u, - "qpeexact_5_26_ibmq-casablanca_7.wcnf" to 15u, - "qwalk-v-chain_3_30_ibmq-casablanca_7.wcnf" to 30u, - "qwalk-v-chain_5_102_ibmq-london_5.wcnf" to 81u, - "rail507.wcnf" to 174u, - "rail516.wcnf" to 182u, - "rail582.wcnf" to 211u, - "ran.max_cut_60_420_2.asc.wcnf" to 703u, - "ran.max_cut_60_420_5.asc.wcnf" to 715u, - "ran.max_cut_60_420_9.asc.wcnf" to 674u, - "ran.max_cut_60_500_2.asc.wcnf" to 900u, - "ran.max_cut_60_560_3.asc.wcnf" to 1054u, - "ran.max_cut_60_560_7.asc.wcnf" to 1053u, - "ran.max_cut_60_600_1.asc.wcnf" to 1156u, - "ran.max_cut_60_600_9.asc.wcnf" to 1149u, - "random-dif-2.rna.pre.wcnf" to 929u, - "random-dif-9.rna.pre.wcnf" to 456u, - "random-dif-16.rna.pre.wcnf" to 768u, - "random-dif-25.rna.pre.wcnf" to 512u, - "random-net-20-5_network-4.net.wcnf" to 19602u, - "random-net-30-3_network-2.net.wcnf" to 27606u, - "random-net-30-4_network-3.net.wcnf" to 24925u, - "random-net-40-2_network-8.net.wcnf" to 38289u, - "random-net-40-2_network-9.net.wcnf" to 35951u, - "random-net-40-3_network-5.net.wcnf" to 35488u, - "random-net-40-4_network-2.net.wcnf" to 36427u, - "random-net-50-3_network-5.net.wcnf" to 41356u, - "random-net-50-4_network-8.net.wcnf" to 43243u, - "random-net-60-3_network-3.net" to 50929u, - "random-net-100-1_network-3.net.wcnf" to 91570u, - "random-net-120-1_network-5.net.wcnf" to 117198u, - "random-net-220-1_network-7.net.wcnf" to 203783u, - "random-net-240-1_network-7.net.wcnf" to 219252u, - "random-net-260-1_network-4.net.wcnf" to 238131u, - "random-same-5.rna.pre.wcnf" to 456u, - "random-same-12.rna.pre.wcnf" to 597u, - "random-same-19.rna.pre.wcnf" to 337u, - "random-same-25.rna.pre.wcnf" to 224u, - "ran-scp.scp41_weighted.wcnf" to 429u, - "ran-scp.scp48_weighted.wcnf" to 492u, - "ran-scp.scp49_weighted.wcnf" to 641u, - "ran-scp.scp51_weighted.wcnf" to 253u, - "ran-scp.scp54_weighted.wcnf" to 242u, - "ran-scp.scp56_weighted.wcnf" to 213u, - "ran-scp.scp58_weighted.wcnf" to 288u, - "ran-scp.scp65_weighted.wcnf" to 161u, - "ran-scp.scp410_weighted.wcnf" to 514u, - "ran-scp.scpnre5_weighted.wcnf" to 28u, - "ran-scp.scpnrf1_weighted.wcnf" to 14u, - "ran-scp.scpnrf4_weighted.wcnf" to 14u, - "ran-scp.scpnrf5_weighted.wcnf" to 13u, - "realamprandom_4_72_rigetti-agave_8.wcnf" to 36u, - "role_smallcomp_0.7_11.wcnf" to 333834u, - "role_smallcomp_0.75_8.wcnf" to 348219u, - "role_smallcomp_0.85_4.wcnf" to 369639u, - "role_smallcomp_0.85_7.wcnf" to 369639u, - "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_2.wcnf" to 24564427u, - "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_3.wcnf" to 24564427u, - "Rounded_BTWBNSL_asia_10000_1_3.scores_TWBound_2.wcnf" to 2247208255u, - "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_2.wcnf" to 602126938u, - "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_3.wcnf" to 601946991u, - "Rounded_BTWBNSL_Heart.BIC_TWBound_2.wcnf" to 239742296u, - "Rounded_BTWBNSL_insurance_100_1_3.scores_TWBound_2.wcnf" to 170760179u, - "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_2.wcnf" to 1389279780u, - "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_3.wcnf" to 1388734978u, - "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_4.wcnf" to 1388734978u, - "Rounded_BTWBNSL_Water_1000_1_2.scores_TWBound_4.wcnf" to 1326306453u, - "Rounded_CorrelationClustering_Ionosphere_BINARY_N200_D0.200.wcnf" to 4604640u, - "Rounded_CorrelationClustering_Orl_BINARY_N320_D0.200.wcnf" to 4429109u, - "Rounded_CorrelationClustering_Protein1_BINARY_N360.wcnf" to 27536228u, - "Rounded_CorrelationClustering_Protein2_BINARY_N220.wcnf" to 13727551u, - "Rounded_CorrelationClustering_Protein2_UNARY_N100.wcnf" to 3913145u, - "simNo_1-s_15-m_50-n_50-fp_0.0001-fn_0.20.wcnf" to 11501657324586u, - "simNo_2-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 99635408482313u, - "simNo_3-s_5-m_50-n_50-fp_0.0001-fn_0.05.wcnf" to 18938961942919u, - "simNo_5-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 113321765415159u, - "simNo_6-s_5-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 90981027155327u, - "simNo_6-s_15-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 60142712649443u, - "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 74156301822200u, - "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 131749300472480u, - "simNo_9-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 131749300472480u, - "simNo_10-s_15-m_100-n_50-fp_0.01-fn_0.20.wcnf" to 84803002848794u, - "simNo_10-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 82981983123459u, - "su2random_4_18_ibmq-casablanca_7.wcnf" to 24u, - "su2random_5_30_ibmq-london_5.wcnf" to 51u, - "tcp_students_91_it_2.wcnf" to 3024u, - "tcp_students_91_it_3.wcnf" to 2430u, - "tcp_students_91_it_6.wcnf" to 2877u, - "tcp_students_91_it_7.wcnf" to 2505u, - "tcp_students_91_it_13.wcnf" to 2730u, - "tcp_students_98_it_8.wcnf" to 2727u, - "tcp_students_98_it_9.wcnf" to 2469u, - "tcp_students_98_it_12.wcnf" to 2994u, - "tcp_students_105_it_7.wcnf" to 3024u, - "tcp_students_105_it_13.wcnf" to 3360u, - "tcp_students_105_it_15.wcnf" to 3258u, - "tcp_students_112_it_1.wcnf" to 3513u, - "tcp_students_112_it_3.wcnf" to 2916u, - "tcp_students_112_it_5.wcnf" to 3366u, - "tcp_students_112_it_7.wcnf" to 3513u, - "tcp_students_112_it_15.wcnf" to 3585u, - "test1--n-5000.wcnf" to 20u, - "test2.wcnf" to 16u, - "test2--n-5000.wcnf" to 3u, - "test5--n-5000.wcnf" to 2u, - "test9--n-5000.wcnf" to 2u, - "test18--n-5000.wcnf" to 22u, - "test25--n-10000.wcnf" to 4u, - "test34--n-10000.wcnf" to 3u, - "test41--n-15000.wcnf" to 5u, - "test42--n-15000.wcnf" to 2u, - "test53--n-15000.wcnf" to 10u, - "test54--n-15000.wcnf" to 45u, - "test66--n-20000.wcnf" to 1u, - "test67--n-20000.wcnf" to 1u, - "test70--n-20000.wcnf" to 5u, - "test75--n-20000.wcnf" to 5u, - "up-.mancoosi-test-i10d0u98-11.wcnf" to 1780771u, - "up-.mancoosi-test-i10d0u98-16.wcnf" to 1780806u, - "up-.mancoosi-test-i20d0u98-9.wcnf" to 1780788u, - "up-.mancoosi-test-i30d0u98-3.wcnf" to 1780860u, - "up-.mancoosi-test-i40d0u98-7.wcnf" to 1780807u, - "up-.mancoosi-test-i40d0u98-17.wcnf" to 1780852u, - "vio.role_smallcomp_violations_0.3_3.wcnf" to 185080u, - "vio.role_smallcomp_violations_0.45_8.wcnf" to 244141u, - "vqe_4_12_ibmq-casablanca_7.wcnf" to 15u, - "vqe_5_20_ibmq-london_5.wcnf" to 33u, - "warehouse0.wcsp.wcnf" to 328u, - "warehouse1.wcsp.wcnf" to 730567u, - "wcn.adult_train_3_DNF_1_5.wcnf" to 24254u, - "wcn.ilpd_test_8_CNF_4_20.wcnf" to 287u, - "wcn.ionosphere_train_5_DNF_2_10.wcnf" to 47u, - "wcn.parkinsons_test_5_CNF_2_10.wcnf" to 58u, - "wcn.pima_test_3_CNF_1_5.wcnf" to 125u, - "wcn.tictactoe_test_8_CNF_2_20.wcnf" to 346u, - "wcn.titanic_test_7_CNF_5_20.wcnf" to 557u, - "wcn.titanic_test_8_DNF_1_20.wcnf" to 449u, - "wcn.titanic_train_7_CNF_5_15.wcnf" to 3262u, - "wcn.titanic_train_8_CNF_5_10.wcnf" to 2201u, - "wcn.transfusion_test_7_DNF_3_5.wcnf" to 96u, - "wcn.transfusion_train_2_CNF_5_10.wcnf" to 1600u, - "wcn.transfusion_train_3_CNF_3_15.wcnf" to 2400u, - "WCNF_pathways_p01.wcnf" to 2u, - "WCNF_pathways_p03.wcnf" to 30u, - "WCNF_pathways_p05.wcnf" to 60u, - "WCNF_pathways_p06.wcnf" to 64u, - "WCNF_pathways_p08.wcnf" to 182u, - "WCNF_pathways_p09.wcnf" to 157u, - "WCNF_pathways_p10.wcnf" to 129u, - "WCNF_pathways_p12.wcnf" to 188u, - "WCNF_pathways_p14.wcnf" to 207u, - "WCNF_pathways_p16.wcnf" to 257u, - "WCNF_storage_p02.wcnf" to 5u, - "WCNF_storage_p06.wcnf" to 173u, - "wei.SingleDay_3_weighted.wcnf" to 35439u, - "wei.Subnetwork_7_weighted.wcnf" to 43213u, - "wei.Subnetwork_9_weighted.wcnf" to 82813u, - "wikipedia.dimacs.wcnf" to 42676u, - "wpm.mancoosi-test-i1000d0u98-15.wcnf" to 92031744u, - "wpm.mancoosi-test-i2000d0u98-25.wcnf" to 332548069u, - "wpm.mancoosi-test-i3000d0u98-50.wcnf" to 422725765u, - "wpm.mancoosi-test-i3000d0u98-70.wcnf" to 512958012u, - "wpm.mancoosi-test-i4000d0u98-76.wcnf" to 738411504u, - "youtube.dimacs.wcnf" to 227167u, + private val maxSATTestNameToExpectedResult = hashSetOf( + "ae_5_20_ibmq-casablanca_7.wcnf" to 15uL, + "af-synthesis_stb_50_20_8.wcnf" to 120uL, + "af-synthesis_stb_50_40_0.wcnf" to 111uL, + "af-synthesis_stb_50_40_8.wcnf" to 117uL, + "af-synthesis_stb_50_60_3.wcnf" to 115uL, + "af-synthesis_stb_50_80_7.wcnf" to 115uL, + "af-synthesis_stb_50_80_8.wcnf" to 116uL, + "af-synthesis_stb_50_100_3.wcnf" to 104uL, + "af-synthesis_stb_50_120_3.wcnf" to 100uL, + "af-synthesis_stb_50_140_1.wcnf" to 127uL, + "af-synthesis_stb_50_140_3.wcnf" to 96uL, + "af-synthesis_stb_50_140_8.wcnf" to 113uL, + "af-synthesis_stb_50_160_5.wcnf" to 113uL, + "af-synthesis_stb_50_180_1.wcnf" to 130uL, + "af-synthesis_stb_50_200_4.wcnf" to 105uL, + "af-synthesis_stb_50_200_5.wcnf" to 102uL, + "af-synthesis_stb_50_200_6.wcnf" to 111uL, + "amazon.dimacs.wcnf" to 113575uL, + "ar-3.wcnf" to 43814uL, + "archlinux.dimacs.wcnf" to 11744uL, + "auc.cat_paths_60_100_0006.txt.wcnf" to 70929uL, + "auc.cat_paths_60_150_0007.txt.wcnf" to 100364uL, + "auc.cat_paths_60_170_0007.txt.wcnf" to 94165uL, + "auc.cat_paths_60_200_0009.txt.wcnf" to 157873uL, + "auc.cat_reg_60_110_0004.txt.wcnf" to 75602uL, + "auc.cat_reg_60_130_0001.txt.wcnf" to 104032uL, + "auc.cat_reg_60_160_0002.txt.wcnf" to 164897uL, + "auc.cat_sched_60_80_0005.txt.wcnf" to 34950uL, + "auc.cat_sched_60_90_0001.txt.wcnf" to 82847uL, + "auc.cat_sched_60_100_0003.txt.wcnf" to 34771uL, + "auc.cat_sched_60_120_0004.txt.wcnf" to 82385uL, + "auc.cat_sched_60_150_0001.txt.wcnf" to 155865uL, + "auc.cat_sched_60_150_0005.txt.wcnf" to 72866uL, + "auc.cat_sched_60_150_0009.txt.wcnf" to 58699uL, + "auc.cat_sched_60_160_0003.txt.wcnf" to 118883uL, + "auc.cat_sched_60_200_0005.txt.wcnf" to 172768uL, + "cap71.wcsp.wcnf" to 9326144uL, + "cap72.wcsp.wcnf" to 9777981uL, + "cap91.wcsp.wcnf" to 7966472uL, + "cap92.wcsp.wcnf" to 8547029uL, + "cap131.wcsp.wcnf" to 7934385uL, + "cap132.wcsp.wcnf" to 8514942uL, + "car.formula_0.8_2021_atleast_15_max-3_reduced_incomplete_adaboost_2.wcnf" to 232uL, + "causal_Meta_7_528.wcnf" to 55120uL, + "causal_n5_i5_N1000_uai13_log_int.wcnf" to 46030289uL, + "causal_n6_i1_N10000_uai14_log_int.wcnf" to 1510725680uL, + "causal_n6_i6_N1000_uai14_log_int.wcnf" to 126257527700uL, + "causal_n7_i8_N10000_uai14_log_int.wcnf" to 11486104693uL, + "causal_n7_i10_N1000_uai14_log_int.wcnf" to 3246397504uL, + "causal_Pigs_6_10000.wcnf" to 25539892uL, + "causal_Statlog_7_752.wcnf" to 380356uL, + "causal_Voting_7_435.wcnf" to 930263uL, + "causal_Water_7_380.wcnf" to 414473uL, + "comp04.wcnf" to 35uL, + "comp06.wcnf" to 27uL, + "CSG40-40-95.wcnf" to 8847uL, + "CSG60-60-88.wcnf" to 7714uL, + "CSGNaive60-60-53.wcnf" to 9829uL, + "CSGNaive70-70-91.wcnf" to 11177uL, + "dblp.dimacs.wcnf" to 25014uL, + "dim.brock800_3.clq.wcnf" to 1079uL, + "dim.c-fat200-1.clq.wcnf" to 14uL, + "dim.c-fat500-1.clq.wcnf" to 10uL, + "dim.san400_0.7_3.clq.wcnf" to 1201uL, + "dir.5.wcsp.dir.wcnf" to 261uL, + "dir.28.wcsp.dir.wcnf" to 270105uL, + "dir.54.wcsp.dir.wcnf" to 37uL, + "dir.404.wcsp.dir.wcnf" to 114uL, + "dir.408.wcsp.dir.wcnf" to 6228uL, + "dir.507.wcsp.dir.wcnf" to 27390uL, + "dir.509.wcsp.dir.wcnf" to 36446uL, + "dir.1403.wcsp.dir.wcnf" to 459246uL, + "dir.1502.wcsp.dir.wcnf" to 28042uL, + "dir.1506.wcsp.dir.wcnf" to 354517uL, + "drmx-am12-outof-40-ecardn-w.wcnf" to 28uL, + "drmx-am12-outof-40-esortn-w.wcnf" to 28uL, + "drmx-am16-outof-45-emtot-w.wcnf" to 29uL, + "drmx-am16-outof-45-eseqc-w.wcnf" to 29uL, + "drmx-am20-outof-50-ekmtot-w.wcnf" to 30uL, + "drmx-am20-outof-50-emtot-w.wcnf" to 30uL, + "drmx-am20-outof-50-eseqc-w.wcnf" to 30uL, + "drmx-am24-outof-55-emtot-w.wcnf" to 31uL, + "drmx-am24-outof-55-etot-w.wcnf" to 31uL, + "drmx-am28-outof-60-emtot-w.wcnf" to 32uL, + "drmx-am28-outof-60-eseqc-w.wcnf" to 32uL, + "drmx-am28-outof-60-etot-w.wcnf" to 32uL, + "drmx-am32-outof-70-ecardn-w.wcnf" to 38uL, + "drmx-am32-outof-70-ekmtot-w.wcnf" to 38uL, + "drmx-am32-outof-70-emtot-w.wcnf" to 38uL, + "drmx-am32-outof-70-etot-w.wcnf" to 38uL, + "eas.310-15.wcnf" to 30501uL, + "eas.310-28.wcnf" to 33151uL, + "eas.310-29.wcnf" to 34431uL, + "eas.310-33.wcnf" to 35463uL, + "eas.310-43.wcnf" to 33138uL, + "eas.310-44.wcnf" to 50871uL, + "eas.310-55.wcnf" to 21952uL, + "eas.310-74.wcnf" to 21867uL, + "eas.310-91.wcnf" to 37183uL, + "eas.310-93.wcnf" to 19146uL, + "eas.310-94.wcnf" to 26160uL, + "eas.310-95.wcnf" to 25854uL, + "eas.310-97.wcnf" to 35249uL, + "ebay.dimacs.wcnf" to 123941uL, + "f1-DataDisplay_0_order4.seq-A-2-1-EDCBAir.wcnf" to 6223203uL, + "f1-DataDisplay_0_order4.seq-A-2-2-abcdeir.wcnf" to 481429uL, + "f1-DataDisplay_0_order4.seq-A-2-2-irabcde.wcnf" to 2220415uL, + "f1-DataDisplay_0_order4.seq-A-3-1-EDCBAir.wcnf" to 6240245uL, + "f1-DataDisplay_0_order4.seq-A-3-1-irabcde.wcnf" to 5960556uL, + "f1-DataDisplay_0_order4.seq-A-3-2-irabcde.wcnf" to 5955300uL, + "f1-DataDisplay_0_order4.seq-B-2-2-abcdeir.wcnf" to 53533uL, + "f1-DataDisplay_0_order4.seq-B-2-2-irEDCBA.wcnf" to 2273346uL, + "f49-DC_TotalLoss.seq-A-2-1-abcdeir.wcnf" to 27698412327uL, + "f49-DC_TotalLoss.seq-A-2-1-irEDCBA.wcnf" to 14779649425uL, + "f49-DC_TotalLoss.seq-A-2-combined-irabcde.wcnf" to 14735114187uL, + "f49-DC_TotalLoss.seq-A-3-2-irEDCBA.wcnf" to 87222797189uL, + "f49-DC_TotalLoss.seq-B-2-2-abcdeir.wcnf" to 44321234uL, + "f49-DC_TotalLoss.seq-B-2-combined-EDCBAir.wcnf" to 83838199998uL, + "f49-DC_TotalLoss.seq-B-3-combined-EDCBAir.wcnf" to 117355113043uL, + "f49-DC_TotalLoss.seq-B-3-combined-irabcde.wcnf" to 87177360578uL, + "facebook1.dimacs.wcnf" to 45581uL, + "github.dimacs.wcnf" to 187405uL, + "graphstate_6_6_rigetti-agave_8.wcnf" to 6uL, + "grover-noancilla_4_52_rigetti-agave_8.wcnf" to 42uL, + "grover-v-chain_4_52_ibmq-casablanca_7.wcnf" to 27uL, + "guardian.dimacs.wcnf" to 160777uL, + "inst2.lp.sm-extracted.wcnf" to 97uL, + "inst10.lp.sm-extracted.wcnf" to 105uL, + "inst22.lp.sm-extracted.wcnf" to 180uL, + "instance1.wcnf" to 607uL, + "instance2.wcnf" to 828uL, + "ItalyInstance1.xml.wcnf" to 12uL, + "k50-18-30.rna.pre.wcnf" to 462uL, + "k50-21-38.rna.pre.wcnf" to 497uL, + "k100-14-38.rna.pre.wcnf" to 1953uL, + "k100-20-63.rna.pre.wcnf" to 2030uL, + "k100-38-60.rna.pre.wcnf" to 1878uL, + "k100-40-52.rna.pre.wcnf" to 1861uL, + "k100-73-76.rna.pre.wcnf" to 2008uL, + "k100-78-85.rna.pre.wcnf" to 1744uL, + "lisbon-wedding-1-18.wcnf" to 961uL, + "lisbon-wedding-2-18.wcnf" to 1137uL, + "lisbon-wedding-3-17.wcnf" to 1035uL, + "lisbon-wedding-4-18.wcnf" to 803uL, + "lisbon-wedding-5-17.wcnf" to 802uL, + "lisbon-wedding-9-17.wcnf" to 394uL, + "lisbon-wedding-10-17.wcnf" to 377uL, + "log.8.wcsp.log.wcnf" to 2uL, + "log.28.wcsp.log.wcnf" to 270105uL, + "log.408.wcsp.log.wcnf" to 6228uL, + "log.505.wcsp.log.wcnf" to 21253uL, + "log.1401.wcsp.log.wcnf" to 459106uL, + "londonist.dimacs.wcnf" to 70703uL, + "metro_8_8_5_20_10_6_500_1_0.lp.sm-extracted.wcnf" to 82uL, + "metro_8_8_5_20_10_6_500_1_7.lp.sm-extracted.wcnf" to 89uL, + "metro_8_8_5_20_10_6_500_1_9.lp.sm-extracted.wcnf" to 105uL, + "metro_9_8_7_22_10_6_500_1_1.lp.sm-extracted.wcnf" to 52uL, + "metro_9_8_7_22_10_6_500_1_2.lp.sm-extracted.wcnf" to 60uL, + "metro_9_8_7_22_10_6_500_1_3.lp.sm-extracted.wcnf" to 44uL, + "metro_9_8_7_30_10_6_500_1_5.lp.sm-extracted.wcnf" to 47uL, + "metro_9_8_7_30_10_6_500_1_6.lp.sm-extracted.wcnf" to 31uL, + "metro_9_8_7_30_10_6_500_1_7.lp.sm-extracted.wcnf" to 47uL, + "metro_9_8_7_30_10_6_500_1_8.lp.sm-extracted.wcnf" to 55uL, + "metro_9_9_10_35_13_7_500_2_7.lp.sm-extracted.wcnf" to 37uL, + "MinWidthCB_milan_100_12_1k_1s_2t_3.wcnf" to 109520uL, + "MinWidthCB_milan_200_12_1k_4s_1t_4.wcnf" to 108863uL, + "MinWidthCB_mitdbsample_100_43_1k_2s_2t_2.wcnf" to 38570uL, + "MinWidthCB_mitdbsample_100_64_1k_2s_1t_2.wcnf" to 66045uL, + "MinWidthCB_mitdbsample_200_43_1k_2s_2t_2.wcnf" to 50615uL, + "MinWidthCB_mitdbsample_200_64_1k_2s_1t_2.wcnf" to 78400uL, + "MinWidthCB_mitdbsample_200_64_1k_2s_3t_2.wcnf" to 73730uL, + "MinWidthCB_mitdbsample_300_26_1k_3s_2t_3.wcnf" to 32420uL, + "MLI.ilpd_train_0_DNF_5_5.wcnf" to 700uL, + "mul.role_smallcomp_multiple_0.3_6.wcnf" to 139251uL, + "mul.role_smallcomp_multiple_1.0_6.wcnf" to 295598uL, + "openstreetmap.dimacs.wcnf" to 65915uL, + "pac.80cfe9a6-9b1b-11df-965e-00163e46d37a_l1.wcnf" to 1924238uL, + "pac.fa3d0fb2-db9e-11df-a0ec-00163e3d3b7c_l1.wcnf" to 4569599uL, + "pac.rand179_l1.wcnf" to 493118uL, + "pac.rand892_l1.wcnf" to 224702uL, + "pac.rand984_l1.wcnf" to 345082uL, + "ped2.B.recomb1-0.01-2.wcnf" to 7uL, + "ped2.B.recomb1-0.10-7.wcnf" to 588uL, + "ped3.D.recomb10-0.20-12.wcnf" to 349uL, + "ped3.D.recomb10-0.20-14.wcnf" to 7uL, + "portfoliovqe_4_18_rigetti-agave_8.wcnf" to 33uL, + "power-distribution_1_2.wcnf" to 3uL, + "power-distribution_1_4.wcnf" to 3uL, + "power-distribution_1_6.wcnf" to 3uL, + "power-distribution_1_8.wcnf" to 3uL, + "power-distribution_2_2.wcnf" to 10uL, + "power-distribution_2_8.wcnf" to 10uL, + "power-distribution_3_4.wcnf" to 1uL, + "power-distribution_7_6.wcnf" to 18uL, + "power-distribution_8_4.wcnf" to 40uL, + "power-distribution_8_7.wcnf" to 40uL, + "power-distribution_9_2.wcnf" to 18uL, + "power-distribution_11_6.wcnf" to 126uL, + "power-distribution_12_2.wcnf" to 216uL, + "power-distribution_12_5.wcnf" to 216uL, + "qaoa_4_16_ibmq-casablanca_7.wcnf" to 12uL, + "qft_5_26_ibmq-casablanca_7.wcnf" to 15uL, + "qftentangled_4_21_ibmq-casablanca_7.wcnf" to 15uL, + "qftentangled_4_39_rigetti-agave_8.wcnf" to 18uL, + "qftentangled_5_30_ibmq-london_5.wcnf" to 27uL, + "qftentangled_5_48_rigetti-agave_8.wcnf" to 24uL, + "qgan_6_15_ibmq-casablanca_7.wcnf" to 24uL, + "qpeexact_5_26_ibmq-casablanca_7.wcnf" to 15uL, + "qwalk-v-chain_3_30_ibmq-casablanca_7.wcnf" to 30uL, + "qwalk-v-chain_5_102_ibmq-london_5.wcnf" to 81uL, + "rail507.wcnf" to 174uL, + "rail516.wcnf" to 182uL, + "rail582.wcnf" to 211uL, + "ran.max_cut_60_420_2.asc.wcnf" to 703uL, + "ran.max_cut_60_420_5.asc.wcnf" to 715uL, + "ran.max_cut_60_420_9.asc.wcnf" to 674uL, + "ran.max_cut_60_500_2.asc.wcnf" to 900uL, + "ran.max_cut_60_560_3.asc.wcnf" to 1054uL, + "ran.max_cut_60_560_7.asc.wcnf" to 1053uL, + "ran.max_cut_60_600_1.asc.wcnf" to 1156uL, + "ran.max_cut_60_600_9.asc.wcnf" to 1149uL, + "random-dif-2.rna.pre.wcnf" to 929uL, + "random-dif-9.rna.pre.wcnf" to 456uL, + "random-dif-16.rna.pre.wcnf" to 768uL, + "random-dif-25.rna.pre.wcnf" to 512uL, + "random-net-20-5_network-4.net.wcnf" to 19602uL, + "random-net-30-3_network-2.net.wcnf" to 27606uL, + "random-net-30-4_network-3.net.wcnf" to 24925uL, + "random-net-40-2_network-8.net.wcnf" to 38289uL, + "random-net-40-2_network-9.net.wcnf" to 35951uL, + "random-net-40-3_network-5.net.wcnf" to 35488uL, + "random-net-40-4_network-2.net.wcnf" to 36427uL, + "random-net-50-3_network-5.net.wcnf" to 41356uL, + "random-net-50-4_network-8.net.wcnf" to 43243uL, + "random-net-60-3_network-3.net" to 50929uL, + "random-net-100-1_network-3.net.wcnf" to 91570uL, + "random-net-120-1_network-5.net.wcnf" to 117198uL, + "random-net-220-1_network-7.net.wcnf" to 203783uL, + "random-net-240-1_network-7.net.wcnf" to 219252uL, + "random-net-260-1_network-4.net.wcnf" to 238131uL, + "random-same-5.rna.pre.wcnf" to 456uL, + "random-same-12.rna.pre.wcnf" to 597uL, + "random-same-19.rna.pre.wcnf" to 337uL, + "random-same-25.rna.pre.wcnf" to 224uL, + "ran-scp.scp41_weighted.wcnf" to 429uL, + "ran-scp.scp48_weighted.wcnf" to 492uL, + "ran-scp.scp49_weighted.wcnf" to 641uL, + "ran-scp.scp51_weighted.wcnf" to 253uL, + "ran-scp.scp54_weighted.wcnf" to 242uL, + "ran-scp.scp56_weighted.wcnf" to 213uL, + "ran-scp.scp58_weighted.wcnf" to 288uL, + "ran-scp.scp65_weighted.wcnf" to 161uL, + "ran-scp.scp410_weighted.wcnf" to 514uL, + "ran-scp.scpnre5_weighted.wcnf" to 28uL, + "ran-scp.scpnrf1_weighted.wcnf" to 14uL, + "ran-scp.scpnrf4_weighted.wcnf" to 14uL, + "ran-scp.scpnrf5_weighted.wcnf" to 13uL, + "realamprandom_4_72_rigetti-agave_8.wcnf" to 36uL, + "role_smallcomp_0.7_11.wcnf" to 333834uL, + "role_smallcomp_0.75_8.wcnf" to 348219uL, + "role_smallcomp_0.85_4.wcnf" to 369639uL, + "role_smallcomp_0.85_7.wcnf" to 369639uL, + "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_2.wcnf" to 24564427uL, + "Rounded_BTWBNSL_asia_100_1_3.scores_TWBound_3.wcnf" to 24564427uL, + "Rounded_BTWBNSL_asia_10000_1_3.scores_TWBound_2.wcnf" to 2247208255uL, + "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_2.wcnf" to 602126938uL, + "Rounded_BTWBNSL_hailfinder_100_1_3.scores_TWBound_3.wcnf" to 601946991uL, + "Rounded_BTWBNSL_Heart.BIC_TWBound_2.wcnf" to 239742296uL, + "Rounded_BTWBNSL_insurance_100_1_3.scores_TWBound_2.wcnf" to 170760179uL, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_2.wcnf" to 1389279780uL, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_3.wcnf" to 1388734978uL, + "Rounded_BTWBNSL_insurance_1000_1_3.scores_TWBound_4.wcnf" to 1388734978uL, + "Rounded_BTWBNSL_Water_1000_1_2.scores_TWBound_4.wcnf" to 1326306453uL, + "Rounded_CorrelationClustering_Ionosphere_BINARY_N200_D0.200.wcnf" to 4604640uL, + "Rounded_CorrelationClustering_Orl_BINARY_N320_D0.200.wcnf" to 4429109uL, + "Rounded_CorrelationClustering_Protein1_BINARY_N360.wcnf" to 27536228uL, + "Rounded_CorrelationClustering_Protein2_BINARY_N220.wcnf" to 13727551uL, + "Rounded_CorrelationClustering_Protein2_UNARY_N100.wcnf" to 3913145uL, + "simNo_1-s_15-m_50-n_50-fp_0.0001-fn_0.20.wcnf" to 11501657324586uL, + "simNo_2-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 99635408482313uL, + "simNo_3-s_5-m_50-n_50-fp_0.0001-fn_0.05.wcnf" to 18938961942919uL, + "simNo_5-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 113321765415159uL, + "simNo_6-s_5-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 90981027155327uL, + "simNo_6-s_15-m_100-n_50-fp_0.01-fn_0.05.wcnf" to 60142712649443uL, + "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 74156301822200uL, + "simNo_8-s_5-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 131749300472480uL, + "simNo_9-s_5-m_100-n_100-fp_0.0001-fn_0.05.wcnf" to 131749300472480uL, + "simNo_10-s_15-m_100-n_50-fp_0.01-fn_0.20.wcnf" to 84803002848794uL, + "simNo_10-s_15-m_100-n_100-fp_0.0001-fn_0.20.wcnf" to 82981983123459uL, + "su2random_4_18_ibmq-casablanca_7.wcnf" to 24uL, + "su2random_5_30_ibmq-london_5.wcnf" to 51uL, + "tcp_students_91_it_2.wcnf" to 3024uL, + "tcp_students_91_it_3.wcnf" to 2430uL, + "tcp_students_91_it_6.wcnf" to 2877uL, + "tcp_students_91_it_7.wcnf" to 2505uL, + "tcp_students_91_it_13.wcnf" to 2730uL, + "tcp_students_98_it_8.wcnf" to 2727uL, + "tcp_students_98_it_9.wcnf" to 2469uL, + "tcp_students_98_it_12.wcnf" to 2994uL, + "tcp_students_105_it_7.wcnf" to 3024uL, + "tcp_students_105_it_13.wcnf" to 3360uL, + "tcp_students_105_it_15.wcnf" to 3258uL, + "tcp_students_112_it_1.wcnf" to 3513uL, + "tcp_students_112_it_3.wcnf" to 2916uL, + "tcp_students_112_it_5.wcnf" to 3366uL, + "tcp_students_112_it_7.wcnf" to 3513uL, + "tcp_students_112_it_15.wcnf" to 3585uL, + "test1--n-5000.wcnf" to 20uL, + "test2.wcnf" to 16uL, + "test2--n-5000.wcnf" to 3uL, + "test5--n-5000.wcnf" to 2uL, + "test9--n-5000.wcnf" to 2uL, + "test18--n-5000.wcnf" to 22uL, + "test25--n-10000.wcnf" to 4uL, + "test34--n-10000.wcnf" to 3uL, + "test41--n-15000.wcnf" to 5uL, + "test42--n-15000.wcnf" to 2uL, + "test53--n-15000.wcnf" to 10uL, + "test54--n-15000.wcnf" to 45uL, + "test66--n-20000.wcnf" to 1uL, + "test67--n-20000.wcnf" to 1uL, + "test70--n-20000.wcnf" to 5uL, + "test75--n-20000.wcnf" to 5uL, + "up-.mancoosi-test-i10d0u98-11.wcnf" to 1780771uL, + "up-.mancoosi-test-i10d0u98-16.wcnf" to 1780806uL, + "up-.mancoosi-test-i20d0u98-9.wcnf" to 1780788uL, + "up-.mancoosi-test-i30d0u98-3.wcnf" to 1780860uL, + "up-.mancoosi-test-i40d0u98-7.wcnf" to 1780807uL, + "up-.mancoosi-test-i40d0u98-17.wcnf" to 1780852uL, + "vio.role_smallcomp_violations_0.3_3.wcnf" to 185080uL, + "vio.role_smallcomp_violations_0.45_8.wcnf" to 244141uL, + "vqe_4_12_ibmq-casablanca_7.wcnf" to 15uL, + "vqe_5_20_ibmq-london_5.wcnf" to 33uL, + "warehouse0.wcsp.wcnf" to 328uL, + "warehouse1.wcsp.wcnf" to 730567uL, + "wcn.adult_train_3_DNF_1_5.wcnf" to 24254uL, + "wcn.ilpd_test_8_CNF_4_20.wcnf" to 287uL, + "wcn.ionosphere_train_5_DNF_2_10.wcnf" to 47uL, + "wcn.parkinsons_test_5_CNF_2_10.wcnf" to 58uL, + "wcn.pima_test_3_CNF_1_5.wcnf" to 125uL, + "wcn.tictactoe_test_8_CNF_2_20.wcnf" to 346uL, + "wcn.titanic_test_7_CNF_5_20.wcnf" to 557uL, + "wcn.titanic_test_8_DNF_1_20.wcnf" to 449uL, + "wcn.titanic_train_7_CNF_5_15.wcnf" to 3262uL, + "wcn.titanic_train_8_CNF_5_10.wcnf" to 2201uL, + "wcn.transfusion_test_7_DNF_3_5.wcnf" to 96uL, + "wcn.transfusion_train_2_CNF_5_10.wcnf" to 1600uL, + "wcn.transfusion_train_3_CNF_3_15.wcnf" to 2400uL, + "WCNF_pathways_p01.wcnf" to 2uL, + "WCNF_pathways_p03.wcnf" to 30uL, + "WCNF_pathways_p05.wcnf" to 60uL, + "WCNF_pathways_p06.wcnf" to 64uL, + "WCNF_pathways_p08.wcnf" to 182uL, + "WCNF_pathways_p09.wcnf" to 157uL, + "WCNF_pathways_p10.wcnf" to 129uL, + "WCNF_pathways_p12.wcnf" to 188uL, + "WCNF_pathways_p14.wcnf" to 207uL, + "WCNF_pathways_p16.wcnf" to 257uL, + "WCNF_storage_p02.wcnf" to 5uL, + "WCNF_storage_p06.wcnf" to 173uL, + "wei.SingleDay_3_weighted.wcnf" to 35439uL, + "wei.Subnetwork_7_weighted.wcnf" to 43213uL, + "wei.Subnetwork_9_weighted.wcnf" to 82813uL, + "wikipedia.dimacs.wcnf" to 42676uL, + "wpm.mancoosi-test-i1000d0u98-15.wcnf" to 92031744uL, + "wpm.mancoosi-test-i2000d0u98-25.wcnf" to 332548069uL, + "wpm.mancoosi-test-i3000d0u98-50.wcnf" to 422725765uL, + "wpm.mancoosi-test-i3000d0u98-70.wcnf" to 512958012uL, + "wpm.mancoosi-test-i4000d0u98-76.wcnf" to 738411504uL, + "youtube.dimacs.wcnf" to 227167uL, ) } From a39e0c071e502ee77feb83de6edf54f852aeea0c Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 21:49:45 +0300 Subject: [PATCH 179/228] Fix integer overflow in solver --- .../ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 8 ++++---- .../main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index f45a5db89..c5c273ebf 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -30,9 +30,9 @@ class KPrimalDualMaxResSolver( private val solver: KSolver, private val maxSmtCtx: KMaxSMTContext, ) : KMaxResSolver(ctx, solver) { - private var _lower: UInt = 0u // Current lower frontier - private var _upper: UInt = 0u // Current upper frontier - private var _maxUpper = 0u // Max possible upper frontier + private var _lower: ULong = 0uL // Current lower frontier + private var _upper: ULong = 0uL // Current upper frontier + private var _maxUpper = 0uL // Max possible upper frontier private var _correctionSetSize: Int = 0 // Current corrections set size private val _maxCoreSize = 3 private var _correctionSetModel: KModel? = null @@ -622,7 +622,7 @@ class KPrimalDualMaxResSolver( private fun initMaxSMT() { _lower = 0u - _upper = softConstraints.sumOf { it.weight } + _upper = softConstraints.sumOf { it.weight.toULong() } _maxUpper = _upper _correctionSetSize = 0 diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt index 6159944c3..ea1ac38f1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/ModelUtils.kt @@ -10,8 +10,8 @@ internal object ModelUtils { fun expressionIsNotTrue(ctx: KContext, model: KModel, expression: KExpr) = model.eval(expression, true) != ctx.trueExpr - fun getModelCost(ctx: KContext, model: KModel, softConstraints: List): UInt { - var upper = 0u + fun getModelCost(ctx: KContext, model: KModel, softConstraints: List): ULong { + var upper = 0uL for (soft in softConstraints) { if (expressionIsNotTrue(ctx, model, soft.expression)) { From 587d9ed640bbde793e543d2598afea05186e44e0 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 12 May 2024 22:08:54 +0300 Subject: [PATCH 180/228] Set up big timeout for MaxSAT tests --- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 3c347ed08..489aa7b8b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -90,7 +90,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - val maxSATResult = maxSATSolver.checkMaxSMT(60.seconds) + val maxSATResult = maxSATSolver.checkMaxSMT(600.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight.toULong() } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second From 8400f2acbf6a6b82ecd679b7bf99357921660c48 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 13 May 2024 03:11:12 +0300 Subject: [PATCH 181/228] Set up new time for MaxSAT tests --- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 489aa7b8b..656276eeb 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -90,7 +90,7 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - val maxSATResult = maxSATSolver.checkMaxSMT(600.seconds) + val maxSATResult = maxSATSolver.checkMaxSMT(420.seconds) val satConstraintsScore = maxSATResult.satSoftConstraints.sumOf { it.weight.toULong() } val expectedSatConstraintsScore = sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second From dca543026babe8b947216c834e0b10ec992e0a2b Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 13 May 2024 03:40:24 +0300 Subject: [PATCH 182/228] Update TestProtocolModel --- .../models/TestProtocolModel.Generated.kt | 331 +++++++++++++++++- .../ksmt/runner/models/TestProtocolModel.kt | 38 ++ 2 files changed, 362 insertions(+), 7 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 1e8a3cc8d..180a53a32 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -27,7 +27,11 @@ class TestProtocolModel private constructor( private val _internalizeAndConvertCvc5: RdCall, private val _createSolver: RdCall, private val _assert: RdCall, + private val _assertSoft: RdCall, private val _check: RdCall, + private val _checkMaxSMT: RdCall, + private val _checkSubOptMaxSMT: RdCall, + private val _collectMaxSMTStatistics: RdCall, private val _exprToString: RdCall, private val _getReasonUnknown: RdCall, private val _addEqualityCheck: RdCall, @@ -41,10 +45,14 @@ class TestProtocolModel private constructor( companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { + serializers.register(SoftConstraint) serializers.register(EqualityCheckParams) serializers.register(EqualityCheckAssumptionsParams) serializers.register(TestAssertParams) serializers.register(TestCheckResult) + serializers.register(TestCheckMaxSMTParams) + serializers.register(TestCheckMaxSMTResult) + serializers.register(TestCollectMaxSMTStatisticsResult) serializers.register(TestConversionResult) serializers.register(TestInternalizeAndConvertParams) } @@ -72,7 +80,7 @@ class TestProtocolModel private constructor( private val __LongListSerializer = FrameworkMarshallers.Long.list() private val __IntNullableSerializer = FrameworkMarshallers.Int.nullable() - const val serializationHash = -2815464060158594303L + const val serializationHash = 4390712550753555496L } override val serializersOwner: ISerializersOwner get() = TestProtocolModel @@ -125,11 +133,31 @@ class TestProtocolModel private constructor( */ val assert: RdCall get() = _assert + /** + * Assert expression softly + */ + val assertSoft: RdCall get() = _assertSoft + /** * Check-sat */ val check: RdCall get() = _check + /** + * Check MaxSMT + */ + val checkMaxSMT: RdCall get() = _checkMaxSMT + + /** + * Check SubOptMaxSMT + */ + val checkSubOptMaxSMT: RdCall get() = _checkSubOptMaxSMT + + /** + * Collect MaxSMT statistics + */ + val collectMaxSMTStatistics: RdCall get() = _collectMaxSMTStatistics + /** * Expression to string */ @@ -176,7 +204,10 @@ class TestProtocolModel private constructor( _internalizeAndConvertCvc5.async = true _createSolver.async = true _assert.async = true + _assertSoft.async = true _check.async = true + _checkMaxSMT.async = true + _checkSubOptMaxSMT.async = true _exprToString.async = true _getReasonUnknown.async = true _addEqualityCheck.async = true @@ -196,7 +227,11 @@ class TestProtocolModel private constructor( bindableChildren.add("internalizeAndConvertCvc5" to _internalizeAndConvertCvc5) bindableChildren.add("createSolver" to _createSolver) bindableChildren.add("assert" to _assert) + bindableChildren.add("assertSoft" to _assertSoft) bindableChildren.add("check" to _check) + bindableChildren.add("checkMaxSMT" to _checkMaxSMT) + bindableChildren.add("checkSubOptMaxSMT" to _checkSubOptMaxSMT) + bindableChildren.add("collectMaxSMTStatistics" to _collectMaxSMTStatistics) bindableChildren.add("exprToString" to _exprToString) bindableChildren.add("getReasonUnknown" to _getReasonUnknown) bindableChildren.add("addEqualityCheck" to _addEqualityCheck) @@ -218,7 +253,11 @@ class TestProtocolModel private constructor( RdCall(TestInternalizeAndConvertParams, TestConversionResult), RdCall(FrameworkMarshallers.Int, FrameworkMarshallers.Int), RdCall(TestAssertParams, FrameworkMarshallers.Void), + RdCall(SoftConstraint, FrameworkMarshallers.Void), RdCall(FrameworkMarshallers.Int, TestCheckResult), + RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), + RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), + RdCall(FrameworkMarshallers.Void, TestCollectMaxSMTStatisticsResult), RdCall(FrameworkMarshallers.Long, FrameworkMarshallers.String), RdCall(FrameworkMarshallers.Int, FrameworkMarshallers.String), RdCall(EqualityCheckParams, FrameworkMarshallers.Void), @@ -243,7 +282,11 @@ class TestProtocolModel private constructor( print("internalizeAndConvertCvc5 = "); _internalizeAndConvertCvc5.print(printer); println() print("createSolver = "); _createSolver.print(printer); println() print("assert = "); _assert.print(printer); println() + print("assertSoft = "); _assertSoft.print(printer); println() print("check = "); _check.print(printer); println() + print("checkMaxSMT = "); _checkMaxSMT.print(printer); println() + print("checkSubOptMaxSMT = "); _checkSubOptMaxSMT.print(printer); println() + print("collectMaxSMTStatistics = "); _collectMaxSMTStatistics.print(printer); println() print("exprToString = "); _exprToString.print(printer); println() print("getReasonUnknown = "); _getReasonUnknown.print(printer); println() print("addEqualityCheck = "); _addEqualityCheck.print(printer); println() @@ -266,7 +309,11 @@ class TestProtocolModel private constructor( _internalizeAndConvertCvc5.deepClonePolymorphic(), _createSolver.deepClonePolymorphic(), _assert.deepClonePolymorphic(), + _assertSoft.deepClonePolymorphic(), _check.deepClonePolymorphic(), + _checkMaxSMT.deepClonePolymorphic(), + _checkSubOptMaxSMT.deepClonePolymorphic(), + _collectMaxSMTStatistics.deepClonePolymorphic(), _exprToString.deepClonePolymorphic(), _getReasonUnknown.deepClonePolymorphic(), _addEqualityCheck.deepClonePolymorphic(), @@ -283,7 +330,7 @@ val IProtocol.testProtocolModel get() = getOrCreateExtension(TestProtocolModel:: /** - * #### Generated from [TestProtocolModel.kt:25] + * #### Generated from [TestProtocolModel.kt:30] */ data class EqualityCheckAssumptionsParams ( val solver: Int, @@ -346,7 +393,7 @@ data class EqualityCheckAssumptionsParams ( /** - * #### Generated from [TestProtocolModel.kt:19] + * #### Generated from [TestProtocolModel.kt:24] */ data class EqualityCheckParams ( val solver: Int, @@ -415,7 +462,70 @@ data class EqualityCheckParams ( /** - * #### Generated from [TestProtocolModel.kt:30] + * #### Generated from [TestProtocolModel.kt:19] + */ +data class SoftConstraint ( + val expression: io.ksmt.KAst, + val weight: UInt +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = SoftConstraint::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SoftConstraint { + val expression = (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) + val weight = buffer.readUInt() + return SoftConstraint(expression, weight) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SoftConstraint) { + (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, value.expression) + buffer.writeUInt(value.weight) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as SoftConstraint + + if (expression != other.expression) return false + if (weight != other.weight) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + expression.hashCode() + __r = __r*31 + weight.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("SoftConstraint (") + printer.indent { + print("expression = "); expression.print(printer); println() + print("weight = "); weight.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [TestProtocolModel.kt:35] */ data class TestAssertParams ( val solver: Int, @@ -478,7 +588,139 @@ data class TestAssertParams ( /** - * #### Generated from [TestProtocolModel.kt:35] + * #### Generated from [TestProtocolModel.kt:44] + */ +data class TestCheckMaxSMTParams ( + val timeout: Long, + val collectStatistics: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = TestCheckMaxSMTParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCheckMaxSMTParams { + val timeout = buffer.readLong() + val collectStatistics = buffer.readBool() + return TestCheckMaxSMTParams(timeout, collectStatistics) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTParams) { + buffer.writeLong(value.timeout) + buffer.writeBool(value.collectStatistics) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as TestCheckMaxSMTParams + + if (timeout != other.timeout) return false + if (collectStatistics != other.collectStatistics) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + timeout.hashCode() + __r = __r*31 + collectStatistics.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("TestCheckMaxSMTParams (") + printer.indent { + print("timeout = "); timeout.print(printer); println() + print("collectStatistics = "); collectStatistics.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [TestProtocolModel.kt:49] + */ +data class TestCheckMaxSMTResult ( + val satSoftConstraints: List, + val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, + val maxSMTSucceeded: Boolean +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = TestCheckMaxSMTResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCheckMaxSMTResult { + val satSoftConstraints = buffer.readList { SoftConstraint.read(ctx, buffer) } + val hardConstraintsSatStatus = buffer.readEnum() + val maxSMTSucceeded = buffer.readBool() + return TestCheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, maxSMTSucceeded) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTResult) { + buffer.writeList(value.satSoftConstraints) { v -> SoftConstraint.write(ctx, buffer, v) } + buffer.writeEnum(value.hardConstraintsSatStatus) + buffer.writeBool(value.maxSMTSucceeded) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as TestCheckMaxSMTResult + + if (satSoftConstraints != other.satSoftConstraints) return false + if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false + if (maxSMTSucceeded != other.maxSMTSucceeded) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + satSoftConstraints.hashCode() + __r = __r*31 + hardConstraintsSatStatus.hashCode() + __r = __r*31 + maxSMTSucceeded.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("TestCheckMaxSMTResult (") + printer.indent { + print("satSoftConstraints = "); satSoftConstraints.print(printer); println() + print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() + print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [TestProtocolModel.kt:40] */ data class TestCheckResult ( val status: io.ksmt.solver.KSolverStatus @@ -535,7 +777,82 @@ data class TestCheckResult ( /** - * #### Generated from [TestProtocolModel.kt:39] + * #### Generated from [TestProtocolModel.kt:55] + */ +data class TestCollectMaxSMTStatisticsResult ( + val timeoutMs: Long, + val elapsedTimeMs: Long, + val timeInSolverQueriesMs: Long, + val queriesToSolverNumber: Int +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = TestCollectMaxSMTStatisticsResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCollectMaxSMTStatisticsResult { + val timeoutMs = buffer.readLong() + val elapsedTimeMs = buffer.readLong() + val timeInSolverQueriesMs = buffer.readLong() + val queriesToSolverNumber = buffer.readInt() + return TestCollectMaxSMTStatisticsResult(timeoutMs, elapsedTimeMs, timeInSolverQueriesMs, queriesToSolverNumber) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCollectMaxSMTStatisticsResult) { + buffer.writeLong(value.timeoutMs) + buffer.writeLong(value.elapsedTimeMs) + buffer.writeLong(value.timeInSolverQueriesMs) + buffer.writeInt(value.queriesToSolverNumber) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as TestCollectMaxSMTStatisticsResult + + if (timeoutMs != other.timeoutMs) return false + if (elapsedTimeMs != other.elapsedTimeMs) return false + if (timeInSolverQueriesMs != other.timeInSolverQueriesMs) return false + if (queriesToSolverNumber != other.queriesToSolverNumber) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + timeoutMs.hashCode() + __r = __r*31 + elapsedTimeMs.hashCode() + __r = __r*31 + timeInSolverQueriesMs.hashCode() + __r = __r*31 + queriesToSolverNumber.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("TestCollectMaxSMTStatisticsResult (") + printer.indent { + print("timeoutMs = "); timeoutMs.print(printer); println() + print("elapsedTimeMs = "); elapsedTimeMs.print(printer); println() + print("timeInSolverQueriesMs = "); timeInSolverQueriesMs.print(printer); println() + print("queriesToSolverNumber = "); queriesToSolverNumber.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [TestProtocolModel.kt:62] */ data class TestConversionResult ( val expressions: List @@ -592,7 +909,7 @@ data class TestConversionResult ( /** - * #### Generated from [TestProtocolModel.kt:43] + * #### Generated from [TestProtocolModel.kt:66] */ data class TestInternalizeAndConvertParams ( val expressions: List diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index 5c3088bb7..cb90c4ecc 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -16,6 +16,11 @@ object TestProtocolModel : Ext(TestProtocolRoot) { private val kastType = kastType() private val statusType = solverStatusType() + private val softConstraint = structdef { + field("expression", kastType) + field("weight", PredefinedType.uint) + } + private val equalityCheckParams = structdef { field("solver", PredefinedType.int) field("actual", kastType) @@ -36,6 +41,24 @@ object TestProtocolModel : Ext(TestProtocolRoot) { field("status", statusType) } + private val testCheckMaxSMTParams = structdef { + field("timeout", PredefinedType.long) + field("collectStatistics", PredefinedType.bool) + } + + private val testCheckMaxSMTResult = structdef { + field("satSoftConstraints", immutableList(softConstraint)) + field("hardConstraintsSatStatus", statusType) + field("maxSMTSucceeded", PredefinedType.bool) + } + + private val testCollectMaxSMTStatisticsResult = structdef { + field("timeoutMs", PredefinedType.long) + field("elapsedTimeMs", PredefinedType.long) + field("timeInSolverQueriesMs", PredefinedType.long) + field("queriesToSolverNumber", PredefinedType.int) + } + private val testConversionResult = structdef { field("expressions", immutableList(kastType)) } @@ -81,10 +104,25 @@ object TestProtocolModel : Ext(TestProtocolRoot) { async documentation = "Assert expr" } + call("assertSoft", softConstraint, PredefinedType.void).apply { + async + documentation = "Assert expression softly" + } call("check", PredefinedType.int, testCheckResult).apply { async documentation = "Check-sat" } + call("checkMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { + async + documentation = "Check MaxSMT" + } + call("checkSubOptMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { + async + documentation = "Check SubOptMaxSMT" + } + call("collectMaxSMTStatistics", PredefinedType.void, testCollectMaxSMTStatisticsResult).apply { + documentation = "Collect MaxSMT statistics" + } call("exprToString", PredefinedType.long, PredefinedType.string).apply { async documentation = "Expression to string" From cc93a567820f5ecaa64373bb579ce7c7f2e1d87e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 13 May 2024 03:55:30 +0300 Subject: [PATCH 183/228] Fix an error with redeclaration SoftConstraint in ProtocolModel --- .../models/TestProtocolModel.Generated.kt | 142 +++++++++--------- .../ksmt/runner/models/TestProtocolModel.kt | 6 +- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 180a53a32..aedf95aea 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -27,7 +27,7 @@ class TestProtocolModel private constructor( private val _internalizeAndConvertCvc5: RdCall, private val _createSolver: RdCall, private val _assert: RdCall, - private val _assertSoft: RdCall, + private val _assertSoft: RdCall, private val _check: RdCall, private val _checkMaxSMT: RdCall, private val _checkSubOptMaxSMT: RdCall, @@ -45,7 +45,7 @@ class TestProtocolModel private constructor( companion object : ISerializersOwner { override fun registerSerializersCore(serializers: ISerializers) { - serializers.register(SoftConstraint) + serializers.register(TestSoftConstraint) serializers.register(EqualityCheckParams) serializers.register(EqualityCheckAssumptionsParams) serializers.register(TestAssertParams) @@ -80,7 +80,7 @@ class TestProtocolModel private constructor( private val __LongListSerializer = FrameworkMarshallers.Long.list() private val __IntNullableSerializer = FrameworkMarshallers.Int.nullable() - const val serializationHash = 4390712550753555496L + const val serializationHash = -5506113420646493100L } override val serializersOwner: ISerializersOwner get() = TestProtocolModel @@ -136,7 +136,7 @@ class TestProtocolModel private constructor( /** * Assert expression softly */ - val assertSoft: RdCall get() = _assertSoft + val assertSoft: RdCall get() = _assertSoft /** * Check-sat @@ -253,7 +253,7 @@ class TestProtocolModel private constructor( RdCall(TestInternalizeAndConvertParams, TestConversionResult), RdCall(FrameworkMarshallers.Int, FrameworkMarshallers.Int), RdCall(TestAssertParams, FrameworkMarshallers.Void), - RdCall(SoftConstraint, FrameworkMarshallers.Void), + RdCall(TestSoftConstraint, FrameworkMarshallers.Void), RdCall(FrameworkMarshallers.Int, TestCheckResult), RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), @@ -461,69 +461,6 @@ data class EqualityCheckParams ( } -/** - * #### Generated from [TestProtocolModel.kt:19] - */ -data class SoftConstraint ( - val expression: io.ksmt.KAst, - val weight: UInt -) : IPrintable { - //companion - - companion object : IMarshaller { - override val _type: KClass = SoftConstraint::class - - @Suppress("UNCHECKED_CAST") - override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SoftConstraint { - val expression = (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) - val weight = buffer.readUInt() - return SoftConstraint(expression, weight) - } - - override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SoftConstraint) { - (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, value.expression) - buffer.writeUInt(value.weight) - } - - - } - //fields - //methods - //initializer - //secondary constructor - //equals trait - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || other::class != this::class) return false - - other as SoftConstraint - - if (expression != other.expression) return false - if (weight != other.weight) return false - - return true - } - //hash code trait - override fun hashCode(): Int { - var __r = 0 - __r = __r*31 + expression.hashCode() - __r = __r*31 + weight.hashCode() - return __r - } - //pretty print - override fun print(printer: PrettyPrinter) { - printer.println("SoftConstraint (") - printer.indent { - print("expression = "); expression.print(printer); println() - print("weight = "); weight.print(printer); println() - } - printer.print(")") - } - //deepClone - //contexts -} - - /** * #### Generated from [TestProtocolModel.kt:35] */ @@ -654,7 +591,7 @@ data class TestCheckMaxSMTParams ( * #### Generated from [TestProtocolModel.kt:49] */ data class TestCheckMaxSMTResult ( - val satSoftConstraints: List, + val satSoftConstraints: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, val maxSMTSucceeded: Boolean ) : IPrintable { @@ -665,14 +602,14 @@ data class TestCheckMaxSMTResult ( @Suppress("UNCHECKED_CAST") override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCheckMaxSMTResult { - val satSoftConstraints = buffer.readList { SoftConstraint.read(ctx, buffer) } + val satSoftConstraints = buffer.readList { TestSoftConstraint.read(ctx, buffer) } val hardConstraintsSatStatus = buffer.readEnum() val maxSMTSucceeded = buffer.readBool() return TestCheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, maxSMTSucceeded) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTResult) { - buffer.writeList(value.satSoftConstraints) { v -> SoftConstraint.write(ctx, buffer, v) } + buffer.writeList(value.satSoftConstraints) { v -> TestSoftConstraint.write(ctx, buffer, v) } buffer.writeEnum(value.hardConstraintsSatStatus) buffer.writeBool(value.maxSMTSucceeded) } @@ -963,3 +900,66 @@ data class TestInternalizeAndConvertParams ( //deepClone //contexts } + + +/** + * #### Generated from [TestProtocolModel.kt:19] + */ +data class TestSoftConstraint ( + val expression: io.ksmt.KAst, + val weight: UInt +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = TestSoftConstraint::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestSoftConstraint { + val expression = (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) + val weight = buffer.readUInt() + return TestSoftConstraint(expression, weight) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestSoftConstraint) { + (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, value.expression) + buffer.writeUInt(value.weight) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as TestSoftConstraint + + if (expression != other.expression) return false + if (weight != other.weight) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + expression.hashCode() + __r = __r*31 + weight.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("TestSoftConstraint (") + printer.indent { + print("expression = "); expression.print(printer); println() + print("weight = "); weight.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index cb90c4ecc..c77fcd64e 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -16,7 +16,7 @@ object TestProtocolModel : Ext(TestProtocolRoot) { private val kastType = kastType() private val statusType = solverStatusType() - private val softConstraint = structdef { + private val testSoftConstraint = structdef { field("expression", kastType) field("weight", PredefinedType.uint) } @@ -47,7 +47,7 @@ object TestProtocolModel : Ext(TestProtocolRoot) { } private val testCheckMaxSMTResult = structdef { - field("satSoftConstraints", immutableList(softConstraint)) + field("satSoftConstraints", immutableList(testSoftConstraint)) field("hardConstraintsSatStatus", statusType) field("maxSMTSucceeded", PredefinedType.bool) } @@ -104,7 +104,7 @@ object TestProtocolModel : Ext(TestProtocolRoot) { async documentation = "Assert expr" } - call("assertSoft", softConstraint, PredefinedType.void).apply { + call("assertSoft", testSoftConstraint, PredefinedType.void).apply { async documentation = "Assert expression softly" } From ab0931e4ac435ca09eb129d6bd7940bf789beabd Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 13 May 2024 12:06:59 +0300 Subject: [PATCH 184/228] Inherit secrets in called workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 1 + .github/workflows/run-long-maxsmt-tests-pmres.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 734e181bb..224be24d9 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -109,3 +109,4 @@ jobs: smtSolver: ${{ inputs.smtSolver }} optimality: ${{ inputs.optimality }} commitSHA: ${{ inputs.commitSHA }} + secrets: inherit diff --git a/.github/workflows/run-long-maxsmt-tests-pmres.yml b/.github/workflows/run-long-maxsmt-tests-pmres.yml index 27ab72aec..1daa28655 100644 --- a/.github/workflows/run-long-maxsmt-tests-pmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pmres.yml @@ -30,3 +30,4 @@ jobs: smtSolver: ${{ inputs.smtSolver }} optimality: true commitSHA: ${{ inputs.commitSHA }} + secrets: inherit From a51a92bdcbd85f607166e06627ebe2c39d582751 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 13 May 2024 12:43:20 +0300 Subject: [PATCH 185/228] Temporary remove UF and UFLIA form run --- .github/workflows/maxsmt-tests-matrix.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 8e5812a27..362ae35d5 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -27,18 +27,10 @@ "NAME": "QF_FP", "TEST_DATA_REVISION": "0.0.0" }, - { - "NAME": "QF_UF-light", - "TEST_DATA_REVISION": "0.0.0" - }, { "NAME": "QF_UFBV-light", "TEST_DATA_REVISION": "0.0.0" }, - { - "NAME": "QF_UFLIA-light", - "TEST_DATA_REVISION": "0.0.0" - }, { "NAME": "QF_UFLRA-light", "TEST_DATA_REVISION": "0.0.0" From aabef32aedde54d022a8cbdabf7327cdf86eb729 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Tue, 14 May 2024 20:10:59 +0300 Subject: [PATCH 186/228] Merge KMaxSMTStatistics and KSubOptMaxSMTStatistics --- .../test/statistics/JsonStatisticsHelper.kt | 5 ----- .../test/statistics/MaxSMTTestStatistics.kt | 5 +++++ .../statistics/SubOptMaxSMTTestStatistics.kt | 18 ------------------ .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 4 ++-- 4 files changed, 7 insertions(+), 25 deletions(-) delete mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt index 42754c561..56f2db22e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/JsonStatisticsHelper.kt @@ -14,11 +14,6 @@ internal class JsonStatisticsHelper(private val jsonFile: File) { } } - fun appendTestStatisticsToFile(statistics: SubOptMaxSMTTestStatistics) { - processBeforeAppending() - jsonFile.appendText(gson.toJson(statistics)) - } - fun appendTestStatisticsToFile(statistics: MaxSMTTestStatistics) { processBeforeAppending() jsonFile.appendText(gson.toJson(statistics)) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index af63c2091..01156b96b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -9,5 +9,10 @@ internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver var ignoredTest = false var failedOnParsingOrConvertingExpressions = false var exceptionMessage: String? = null + /** + * It's wrong when it's more than optimal in case of SubOpt. + */ var checkedSoftConstraintsSumIsWrong = false + var optimalWeight: ULong = 0U + var foundSoFarWeight: ULong = 0U } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt deleted file mode 100644 index e4c5cc6c3..000000000 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/SubOptMaxSMTTestStatistics.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.ksmt.solver.maxsmt.test.statistics - -import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics -import io.ksmt.solver.maxsmt.test.utils.Solver - -internal data class SubOptMaxSMTTestStatistics(val name: String, var smtSolver: Solver) { - var maxSMTCallStatistics: KMaxSMTStatistics? = null - var passed = false - var ignoredTest = false - var failedOnParsingOrConvertingExpressions = false - var exceptionMessage: String? = null - /** - * It's wrong when it's more than optimal. - */ - var checkedSoftConstraintsSumIsWrong = false - var optimalWeight: ULong = 0U - var foundSoFarWeight: ULong = 0U -} diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 3d5f5c553..b59ec273b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -22,7 +22,7 @@ import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper -import io.ksmt.solver.maxsmt.test.statistics.SubOptMaxSMTTestStatistics +import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA @@ -149,7 +149,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Test name: [$name]" } lateinit var ksmtAssertions: List> - val testStatistics = SubOptMaxSMTTestStatistics(name, solver) + val testStatistics = MaxSMTTestStatistics(name, solver) try { testWorkers.withWorker(ctx) { worker -> From a535c7bbbc4ee581fc2dde103fec0e44db6861f2 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 15 May 2024 15:23:27 +0300 Subject: [PATCH 187/228] Run tests for QF_UF and QF_UFLIA --- .github/workflows/maxsmt-tests-matrix.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/maxsmt-tests-matrix.json b/.github/workflows/maxsmt-tests-matrix.json index 362ae35d5..8e5812a27 100644 --- a/.github/workflows/maxsmt-tests-matrix.json +++ b/.github/workflows/maxsmt-tests-matrix.json @@ -27,10 +27,18 @@ "NAME": "QF_FP", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_UF-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UFBV-light", "TEST_DATA_REVISION": "0.0.0" }, + { + "NAME": "QF_UFLIA-light", + "TEST_DATA_REVISION": "0.0.0" + }, { "NAME": "QF_UFLRA-light", "TEST_DATA_REVISION": "0.0.0" From f7ec47e02a640d9d4e6158cf6e328571022bb8ba Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 15 May 2024 15:35:37 +0300 Subject: [PATCH 188/228] Create Portfolio solver in tests correctly --- .../KDualMaxRes2SMTBenchmarkTest.kt | 6 ++--- .../KDualMaxRes3SMTBenchmarkTest.kt | 6 ++--- .../KDualMaxRes4SMTBenchmarkTest.kt | 6 ++--- .../KDualMaxResSMTBenchmarkTest.kt | 6 +++-- .../configurations/KPMResSMTBenchmarkTest.kt | 6 +++-- .../KPrimalMaxRes2SMTBenchmarkTest.kt | 6 ++--- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 25 +++++++++++++++++-- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- 8 files changed, 40 insertions(+), 23 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt index a080c200c..eb3b5cbf6 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes2SMTBenchmarkTest.kt @@ -2,9 +2,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -13,9 +11,9 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(preferLargeWeightConstraintsForCores = true) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt index ee29243a4..ba96c94a4 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes3SMTBenchmarkTest.kt @@ -2,9 +2,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -13,9 +11,9 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(minimizeCores = true) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt index 98d55cdf9..dff5c4953 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxRes4SMTBenchmarkTest.kt @@ -2,9 +2,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -13,9 +11,9 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(getMultipleCores = true) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt index b14b5ffd0..71ea471cb 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt @@ -3,15 +3,17 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KDualMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext() - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt index 21039b646..d0af2d25e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt @@ -3,16 +3,18 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { // TODO: in fact for KPMRes we don't need MaxSMTContext. override val maxSmtCtx = KMaxSMTContext() - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPMResSolver(this, smtSolver) + return getMaxSmtSolver(MaxSmtSolver.PMRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt index 23faee291..48d49a22e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes2SMTBenchmarkTest.kt @@ -3,9 +3,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -17,9 +15,9 @@ class KPrimalMaxRes2SMTBenchmarkTest : KMaxSMTBenchmarkTest() { preferLargeWeightConstraintsForCores = true, ) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverInterface { val smtSolver = getSmtSolver(solver) - return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) + return getMaxSmtSolver(MaxSmtSolver.PRIMAL_DUAL_MAXRES, smtSolver) } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 2f203fe01..47e109bc5 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -17,11 +17,15 @@ import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.solvers.KPMResSolver +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 @@ -29,6 +33,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver import io.ksmt.solver.z3.KZ3Solver @@ -54,6 +59,22 @@ import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { + protected fun getMaxSmtSolver( + maxSmtSolver: MaxSmtSolver, + solver: KSolver + ): KMaxSMTSolverInterface { + when (maxSmtSolver) { + MaxSmtSolver.PMRES -> return KPMResSolver(ctx, solver) + MaxSmtSolver.PRIMAL_DUAL_MAXRES -> { + // Thus, MaxSMT algorithm will be executed in the backend process. + if (solver is KPortfolioSolver) { + return solver + } + return KPrimalDualMaxResSolver(ctx, solver, maxSmtCtx) + } + } + } + protected fun getSmtSolver(solver: Solver): KSolver = with(ctx) { return when (solver) { Z3 -> KZ3Solver(this) @@ -66,11 +87,11 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - abstract fun getSolver(solver: Solver): KMaxSMTSolver + abstract fun getSolver(solver: Solver): KMaxSMTSolverInterface protected val ctx: KContext = KContext() protected abstract val maxSmtCtx: KMaxSMTContext - private lateinit var maxSMTSolver: KMaxSMTSolver + private lateinit var maxSMTSolver: KMaxSMTSolverInterface private val logger = KotlinLogging.logger {} private fun initSolver(solver: Solver) { diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index b59ec273b..88a0c6a9f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -87,7 +87,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { abstract fun getSolver(solver: Solver): KMaxSMTSolverInterface - protected val ctx: KContext = KContext() + private val ctx: KContext = KContext() protected abstract val maxSmtCtx: KMaxSMTContext private lateinit var maxSMTSolver: KMaxSMTSolverInterface From 5972d0d9a0276ecbd057825d50a771e58ce3cbd4 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 15 May 2024 18:39:32 +0300 Subject: [PATCH 189/228] Support interruption for MaxSMT --- .../solver/maxsmt/solvers/KMaxSMTSolver.kt | 2 ++ .../solver/maxsmt/solvers/KPMResSolver.kt | 2 +- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt index ea0f40b07..6441869d5 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt @@ -20,6 +20,7 @@ abstract class KMaxSMTSolver( private val scopeManager = MaxSMTScopeManager() protected var softConstraints = mutableListOf() protected lateinit var maxSMTStatistics: KMaxSMTStatistics + protected var isInterrupted = false /** * Softly assert an expression with weight (aka soft constraint) into solver. @@ -114,6 +115,7 @@ abstract class KMaxSMTSolver( } override fun interrupt() { + isInterrupted = true solver.interrupt() } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 4fb00b82c..ef0ac0a81 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -89,7 +89,7 @@ class KPMResSolver(private val ctx: KContext, private while (true) { val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, markHardConstraintsCheckStart) - if (TimerUtils.timeoutExceeded(checkRemainingTime)) { + if (TimerUtils.timeoutExceeded(checkRemainingTime) || isInterrupted) { solver.pop() return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index c5c273ebf..523b41b9f 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -92,7 +92,7 @@ class KPrimalDualMaxResSolver( markLoggingPoint = markNow() val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { + if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime) || isInterrupted) { if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -126,7 +126,7 @@ class KPrimalDualMaxResSolver( UNSAT -> { val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(remainingTime)) { + if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { solver.pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds @@ -238,7 +238,7 @@ class KPrimalDualMaxResSolver( markLoggingPoint = markNow() val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime)) { + if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime) || isInterrupted) { if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -268,7 +268,7 @@ class KPrimalDualMaxResSolver( UNSAT -> { val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(remainingTime)) { + if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { solver.pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds @@ -412,7 +412,7 @@ class KPrimalDualMaxResSolver( if (maxSmtCtx.minimizeCores) { val minimizeCoreRemainingTime = TimerUtils.computeRemainingTime(timeout, markStart) - if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime)) { + if (TimerUtils.timeoutExceeded(minimizeCoreRemainingTime) || isInterrupted) { return Pair(SAT, cores) // TODO: is this status Ok? } @@ -441,7 +441,7 @@ class KPrimalDualMaxResSolver( } val checkSatRemainingTime = TimerUtils.computeRemainingTime(timeout, markStart) - if (TimerUtils.timeoutExceeded(checkSatRemainingTime)) { + if (TimerUtils.timeoutExceeded(checkSatRemainingTime) || isInterrupted) { return Pair(SAT, cores) // TODO: is this status Ok? } @@ -621,6 +621,8 @@ class KPrimalDualMaxResSolver( } private fun initMaxSMT() { + isInterrupted = false + _lower = 0u _upper = softConstraints.sumOf { it.weight.toULong() } @@ -668,8 +670,11 @@ class KPrimalDualMaxResSolver( val assumptionsToCheck = assumptions.subList(0, index) val remainingTime = TimerUtils.computeRemainingTime(timeout, markStart) - if (TimerUtils.timeoutExceeded(remainingTime)) { - logger.info { "checking formula on satisfiability --- ended --- solver returned UNKNOWN" } + if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { + logger.info { + "checking formula on satisfiability --- ended --- solver returned UNKNOWN" + + "or operation was interrupted" + } return UNKNOWN } From 912e89e3bd221d344d32eeff014ff461670a336f Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 17 May 2024 18:53:42 +0300 Subject: [PATCH 190/228] Implement KSolverRunner and KSolverExecutor in order to interact through ProtocolModel --- .../KDualMaxResSMTBenchmarkTest.kt | 2 - .../configurations/KPMResSMTBenchmarkTest.kt | 2 - .../KPrimalMaxRes3SMTBenchmarkTest.kt | 4 +- .../KPrimalMaxRes4SMTBenchmarkTest.kt | 4 +- .../KPrimalMaxResSMTBenchmarkTest.kt | 4 +- .../maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 6 +- .../maxsmt/test/sat/KPMResSATBenchmarkTest.kt | 4 +- .../sat/KPrimalDualMaxResSATBenchmarkTest.kt | 4 +- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 4 +- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- ...{KMaxResSolver.kt => KMaxResSolverBase.kt} | 4 +- ...{KMaxSMTSolver.kt => KMaxSMTSolverBase.kt} | 2 +- .../solver/maxsmt/solvers/KPMResSolver.kt | 25 +++--- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 43 +++++---- .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 6 +- .../io/ksmt/solver/maxsmt/KPMResSolverTest.kt | 4 +- .../maxsmt/KPrimalDualMaxResSolver2Test.kt | 4 +- .../maxsmt/KPrimalDualMaxResSolver3Test.kt | 4 +- .../maxsmt/KPrimalDualMaxResSolver4Test.kt | 4 +- .../maxsmt/KPrimalDualMaxResSolverTest.kt | 4 +- .../models/SolverProtocolModel.Generated.kt | 3 - .../models/TestProtocolModel.Generated.kt | 3 - .../io/ksmt/solver/runner/KSolverRunner.kt | 54 +++++++++--- .../solver/runner/KSolverRunnerExecutor.kt | 87 +++++++++++++++++-- .../solver/runner/KSolverWorkerProcess.kt | 56 +++++++----- .../ksmt/runner/models/SolverProtocolModel.kt | 4 - .../ksmt/runner/models/TestProtocolModel.kt | 3 - 27 files changed, 217 insertions(+), 129 deletions(-) rename ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/{KMaxResSolver.kt => KMaxResSolverBase.kt} (91%) rename ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/{KMaxSMTSolver.kt => KMaxSMTSolverBase.kt} (99%) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt index 71ea471cb..8daccf61e 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KDualMaxResSMTBenchmarkTest.kt @@ -2,9 +2,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt index d0af2d25e..5bd24d53f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPMResSMTBenchmarkTest.kt @@ -2,9 +2,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface -import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt index bb5b28f93..ac8cced78 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes3SMTBenchmarkTest.kt @@ -3,7 +3,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver @@ -11,7 +11,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes3SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes, minimizeCores = true) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverBase = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt index 8da1e3cab..150af5018 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxRes4SMTBenchmarkTest.kt @@ -3,7 +3,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver @@ -11,7 +11,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxRes4SMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes, getMultipleCores = true) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverBase = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, maxSmtCtx) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt index 8c76e6d7c..1f002eff1 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/KPrimalMaxResSMTBenchmarkTest.kt @@ -3,7 +3,7 @@ package io.ksmt.solver.maxsmt.test.configurations import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.Solver @@ -11,7 +11,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalMaxResSMTBenchmarkTest : KMaxSMTBenchmarkTest() { override val maxSmtCtx = KMaxSMTContext(strategy = PrimalMaxRes) - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverBase = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver(this, smtSolver, KMaxSMTContext(strategy = PrimalMaxRes)) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 656276eeb..c24072aab 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -8,7 +8,7 @@ import io.ksmt.solver.bitwuzla.KBitwuzlaSolver import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.constraints.HardConstraint import io.ksmt.solver.maxsmt.constraints.SoftConstraint -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSATTest import io.ksmt.solver.maxsmt.test.utils.Solver @@ -35,10 +35,10 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } - abstract fun getSolver(solver: Solver): KMaxSMTSolver + abstract fun getSolver(solver: Solver): KMaxSMTSolverBase protected val ctx: KContext = KContext() - private lateinit var maxSATSolver: KMaxSMTSolver + private lateinit var maxSATSolver: KMaxSMTSolverBase private fun initSolver(solver: Solver) { maxSATSolver = getSolver(solver) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt index 3a5c83951..90277789d 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPMResSATBenchmarkTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt.test.sat import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KPMResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverBase = with(ctx) { val smtSolver = getSmtSolver(solver) return KPMResSolver(this, smtSolver) } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt index 65ba61b11..4358d7eaa 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KPrimalDualMaxResSATBenchmarkTest.kt @@ -2,12 +2,12 @@ package io.ksmt.solver.maxsmt.test.sat import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.utils.Solver class KPrimalDualMaxResSATBenchmarkTest : KMaxSATBenchmarkTest() { - override fun getSolver(solver: Solver): KMaxSMTSolver = with(ctx) { + override fun getSolver(solver: Solver): KMaxSMTSolverBase = with(ctx) { val smtSolver = getSmtSolver(solver) return KPrimalDualMaxResSolver( this, smtSolver, diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 47e109bc5..9cdc3172f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -16,7 +16,6 @@ import io.ksmt.solver.bitwuzla.KBitwuzlaSolver import io.ksmt.solver.cvc5.KCvc5Solver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver @@ -24,7 +23,6 @@ import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics -import io.ksmt.solver.maxsmt.test.subopt.KSubOptMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.utils.Solver.BITWUZLA @@ -82,7 +80,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - solverManager.createPortfolioSolver(this, maxSmtCtx) + solverManager.createPortfolioSolver(this) } } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 88a0c6a9f..05b369d96 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -80,7 +80,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { CVC5 -> KCvc5Solver(this) YICES -> KYicesSolver(this) PORTFOLIO -> { - solverManager.createPortfolioSolver(this, maxSmtCtx) + solverManager.createPortfolioSolver(this) } } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolverBase.kt similarity index 91% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolverBase.kt index a68ee7ebd..71abcb990 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxResSolverBase.kt @@ -6,10 +6,10 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.maxsmt.constraints.SoftConstraint import io.ksmt.solver.maxsmt.utils.CoreUtils -abstract class KMaxResSolver( +abstract class KMaxResSolverBase( ctx: KContext, solver: KSolver, -) : KMaxSMTSolver(ctx, solver) where T : KSolverConfiguration { +) : KMaxSMTSolverBase(ctx, solver) where T : KSolverConfiguration { protected fun removeCoreAssumptions(core: List, assumptions: MutableList) { assumptions.removeAll(core) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt similarity index 99% rename from ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt rename to ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt index 6441869d5..a96be3912 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt @@ -13,7 +13,7 @@ import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.sort.KBoolSort import kotlin.time.Duration -abstract class KMaxSMTSolver( +abstract class KMaxSMTSolverBase( private val ctx: KContext, private val solver: KSolver, ) : KMaxSMTSolverInterface where T : KSolverConfiguration { diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index ef0ac0a81..d32cf5ae5 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -21,8 +21,8 @@ import io.ksmt.utils.mkConst import kotlin.time.Duration import kotlin.time.TimeSource.Monotonic.markNow -class KPMResSolver(private val ctx: KContext, private val solver: KSolver) : - KMaxResSolver(ctx, solver) { +class KPMResSolver(private val ctx: KContext, solver: KSolver) : + KMaxResSolverBase(ctx, solver) { private var collectStatistics = false private val maxSmtCtx = KMaxSMTContext( strategy = PrimalMaxRes, @@ -60,7 +60,7 @@ class KPMResSolver(private val ctx: KContext, private private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { val markHardConstraintsCheckStart = markNow() - val hardConstraintsStatus = solver.check(timeout) + val hardConstraintsStatus = check(timeout) if (collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ @@ -81,7 +81,7 @@ class KPMResSolver(private val ctx: KContext, private return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } - solver.push() + push() var i = 0 var formula = softConstraints.toMutableList() @@ -90,7 +90,7 @@ class KPMResSolver(private val ctx: KContext, private val checkRemainingTime = TimerUtils.computeRemainingTime(timeout, markHardConstraintsCheckStart) if (TimerUtils.timeoutExceeded(checkRemainingTime) || isInterrupted) { - solver.pop() + pop() return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } @@ -104,11 +104,10 @@ class KPMResSolver(private val ctx: KContext, private } if (solverStatus == SAT) { - solver.pop() + pop() return processSat(model!!) - } - else if (solverStatus == UNKNOWN) { - solver.pop() + } else if (solverStatus == UNKNOWN) { + pop() return KMaxSMTResult(emptyList(), SAT, false) } @@ -223,10 +222,10 @@ class KPMResSolver(private val ctx: KContext, private * (if exists, null otherwise). */ private fun checkSat(assumptions: List, timeout: Duration): - Triple>, KModel?> = - when (val status = solver.checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { - SAT -> Triple(status, listOf(), solver.model()) - UNSAT -> Triple(status, solver.unsatCore(), null) + Triple>, KModel?> = + when (val status = checkWithAssumptions(assumptions.map { x -> x.expression }, timeout)) { + SAT -> Triple(status, listOf(), model()) + UNSAT -> Triple(status, unsatCore(), null) UNKNOWN -> Triple(status, listOf(), null) } } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 523b41b9f..08c1c937d 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -27,9 +27,9 @@ import kotlin.time.TimeSource.Monotonic.markNow class KPrimalDualMaxResSolver( private val ctx: KContext, - private val solver: KSolver, + solver: KSolver, private val maxSmtCtx: KMaxSMTContext, -) : KMaxResSolver(ctx, solver) { +) : KMaxResSolverBase(ctx, solver) { private var _lower: ULong = 0uL // Current lower frontier private var _upper: ULong = 0uL // Current upper frontier private var _maxUpper = 0uL // Max possible upper frontier @@ -45,7 +45,6 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) - // TODO: may be we should return KMaxSMTSubOptResult? override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart @@ -62,7 +61,7 @@ class KPrimalDualMaxResSolver( } val markHardConstraintsCheckStart = markNow() - val hardConstraintsStatus = solver.check(timeout) + val hardConstraintsStatus = check(timeout) if (this.collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds @@ -80,7 +79,7 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } - solver.push() + push() initMaxSMT() val assumptions = softConstraints.toMutableList() @@ -111,7 +110,7 @@ class KPrimalDualMaxResSolver( PrimalMaxRes -> _upper = _lower PrimalDualMaxRes -> { - val correctionSet = getCorrectionSet(solver.model(), assumptions) + val correctionSet = getCorrectionSet(model(), assumptions) if (correctionSet.isEmpty()) { if (_model != null) { // Feasible optimum is found by the moment. @@ -127,7 +126,7 @@ class KPrimalDualMaxResSolver( UNSAT -> { val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -141,7 +140,7 @@ class KPrimalDualMaxResSolver( if (processUnsatStatus == UNSAT) { _lower = _upper // TODO: process this case as it can happen when timeout exceeded??? - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -151,7 +150,7 @@ class KPrimalDualMaxResSolver( } return getSubOptMaxSMTResult(true) } else if (processUnsatStatus == UNKNOWN) { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -163,7 +162,7 @@ class KPrimalDualMaxResSolver( } UNKNOWN -> { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -184,7 +183,7 @@ class KPrimalDualMaxResSolver( "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds @@ -208,7 +207,7 @@ class KPrimalDualMaxResSolver( } val markHardConstraintsCheckStart = markNow() - val hardConstraintsStatus = solver.check(timeout) + val hardConstraintsStatus = check(timeout) if (this.collectStatistics) { maxSMTStatistics.queriesToSolverNumber++ maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds @@ -226,7 +225,7 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult(listOf(), hardConstraintsStatus, false) } - solver.push() + push() initMaxSMT() val assumptions = softConstraints.toMutableList() @@ -253,7 +252,7 @@ class KPrimalDualMaxResSolver( PrimalMaxRes -> _upper = _lower PrimalDualMaxRes -> { - val correctionSet = getCorrectionSet(solver.model(), assumptions) + val correctionSet = getCorrectionSet(model(), assumptions) if (correctionSet.isEmpty()) { if (_model != null) { // Feasible optimum is found by the moment. @@ -269,7 +268,7 @@ class KPrimalDualMaxResSolver( UNSAT -> { val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -280,13 +279,13 @@ class KPrimalDualMaxResSolver( if (processUnsatStatus == UNSAT) { _lower = _upper // TODO: process this case as it can happen when timeout exceeded - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } throw NotYetImplementedException("MaxSMT solution was not found --- processUnsat returned UNSAT") } else if (processUnsatStatus == UNKNOWN) { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -295,7 +294,7 @@ class KPrimalDualMaxResSolver( } UNKNOWN -> { - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } @@ -313,7 +312,7 @@ class KPrimalDualMaxResSolver( "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" } - solver.pop() + pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds @@ -419,7 +418,7 @@ class KPrimalDualMaxResSolver( unsatCore = minimizeCore(assumptions, minimizeCoreRemainingTime) updateMinimalUnsatCoreModel(assumptions) } else { - unsatCore = CoreUtils.coreToSoftConstraints(solver.unsatCore(), assumptions) + unsatCore = CoreUtils.coreToSoftConstraints(unsatCore(), assumptions) } if (unsatCore.isEmpty()) { @@ -715,10 +714,10 @@ class KPrimalDualMaxResSolver( assumptions: List, timeout: Duration, ): KSolverStatus { - val status = solver.checkWithAssumptions(assumptions.map { it.expression }, timeout) + val status = checkWithAssumptions(assumptions.map { it.expression }, timeout) if (status == SAT) { - updateAssignment(solver.model().detach(), assumptions) + updateAssignment(model().detach(), assumptions) } return status diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index 81109f0bc..a9a523a65 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -5,7 +5,7 @@ import io.ksmt.solver.KSolverConfiguration import io.ksmt.solver.KSolverStatus.SAT import io.ksmt.solver.KSolverStatus.UNSAT import io.ksmt.solver.maxsmt.constraints.SoftConstraint -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.utils.getValue import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals @@ -14,10 +14,10 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test abstract class KMaxSMTSolverTest { - abstract fun getSolver(): KMaxSMTSolver + abstract fun getSolver(): KMaxSMTSolverBase protected val ctx: KContext = KContext() - private lateinit var maxSMTSolver: KMaxSMTSolver + private lateinit var maxSMTSolver: KMaxSMTSolverBase @BeforeEach fun initSolver() { diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt index b4b4b0244..77e2b040a 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPMResSolverTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.z3.KZ3Solver class KPMResSolverTest : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolverBase = with(ctx) { val z3Solver = KZ3Solver(this) return KPMResSolver(this, z3Solver) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt index 57668b153..e42617af6 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver2Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver2Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolverBase = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt index 1c8429708..486d24e98 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver3Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver3Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolverBase = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(minimizeCores = true)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt index b3e861892..dee45f28f 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolver4Test.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolver4Test : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolverBase = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext(getMultipleCores = true)) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt index 0e9674d7d..53dd34da2 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KPrimalDualMaxResSolverTest.kt @@ -1,12 +1,12 @@ package io.ksmt.solver.maxsmt import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.z3.KZ3Solver class KPrimalDualMaxResSolverTest : KMaxSMTSolverTest() { - override fun getSolver(): KMaxSMTSolver = with(ctx) { + override fun getSolver(): KMaxSMTSolverBase = with(ctx) { val z3Solver = KZ3Solver(this) return KPrimalDualMaxResSolver(this, z3Solver, KMaxSMTContext()) } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index 9c573517e..18fd57fe0 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -197,15 +197,12 @@ class SolverProtocolModel private constructor( _deleteSolver.async = true _configure.async = true _assert.async = true - _assertSoft.async = true _bulkAssert.async = true _assertAndTrack.async = true _bulkAssertAndTrack.async = true _push.async = true _pop.async = true _check.async = true - _checkMaxSMT.async = true - _checkSubOptMaxSMT.async = true _checkWithAssumptions.async = true _model.async = true _unsatCore.async = true diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index aedf95aea..275757733 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -204,10 +204,7 @@ class TestProtocolModel private constructor( _internalizeAndConvertCvc5.async = true _createSolver.async = true _assert.async = true - _assertSoft.async = true _check.async = true - _checkMaxSMT.async = true - _checkSubOptMaxSMT.async = true _exprToString.async = true _getReasonUnknown.async = true _addEqualityCheck.async = true diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index ee326c238..7129bcb79 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -14,7 +14,8 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.async.KAsyncSolver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.runner.KSolverRunnerManager.CustomSolverInfo import io.ksmt.sort.KBoolSort import kotlinx.coroutines.sync.Mutex @@ -30,11 +31,11 @@ import kotlin.time.Duration class KSolverRunner( private val manager: KSolverRunnerManager, private val ctx: KContext, - maxSmtCtx: KMaxSMTContext = KMaxSMTContext(), + private val maxSmtCtx: KMaxSMTContext = KMaxSMTContext(), private val configurationBuilder: ConfigurationBuilder, private val solverType: SolverType, private val customSolverInfo: CustomSolverInfo? = null, -) : KAsyncSolver { +) : KAsyncSolver, KMaxSMTSolverInterface { private val isActive = AtomicBoolean(true) private val executorInitializationLock = Mutex() private val executorRef = AtomicReference(null) @@ -45,8 +46,6 @@ class KSolverRunner( private val solverState = KSolverState() - private val maxSMTSolver = KPrimalDualMaxResSolver(ctx, this, maxSmtCtx) - override fun close() { deleteSolverSync() } @@ -121,8 +120,12 @@ class KSolverRunner( } } - fun assertSoft(expr: KExpr, weight: UInt) { - maxSMTSolver.assertSoft(expr, weight) + override fun assertSoft(expr: KExpr, weight: UInt) { + ctx.ensureContextMatch(expr) + + ensureInitializedAndExecuteSync(onException = {}) { + assertSoft(expr, weight) + } } private inline fun bulkAssert( @@ -242,15 +245,20 @@ class KSolverRunner( checkSync(timeout) } - fun checkSubOptMaxSMT( - timeout: Duration, - collectStatistics: Boolean - ): KMaxSMTResult = maxSMTSolver.checkSubOptMaxSMT(timeout, collectStatistics) + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + handleCheckAnyMaxSMTExceptionAsNotSucceededSync { + checkMaxSMT(timeout, collectStatistics) + } - fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean) - : KMaxSMTResult = maxSMTSolver.checkMaxSMT(timeout, collectStatistics) + override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + handleCheckAnyMaxSMTExceptionAsNotSucceededSync { + checkSubOptMaxSMT(timeout, collectStatistics) + } - fun collectMaxSMTStatistics() = maxSMTSolver.collectMaxSMTStatistics() + override fun collectMaxSMTStatistics(): KMaxSMTStatistics = + handleCollectStatisticsSync { + collectMaxSMTStatistics() + } override suspend fun checkWithAssumptionsAsync( assumptions: List>, @@ -445,6 +453,24 @@ class KSolverRunner( ) } + private inline fun handleCheckAnyMaxSMTExceptionAsNotSucceededSync( + crossinline body: KSolverRunnerExecutor.() -> KMaxSMTResult + ): KMaxSMTResult { + return ensureInitializedAndExecuteSync( + body = body, + onException = { KMaxSMTResult(emptyList(), KSolverStatus.UNKNOWN, false) } + ) + } + + private inline fun handleCollectStatisticsSync( + crossinline body: KSolverRunnerExecutor.() -> KMaxSMTStatistics + ): KMaxSMTStatistics { + return ensureInitializedAndExecuteSync( + body = body, + onException = { KMaxSMTStatistics(maxSmtCtx) } + ) + } + private inline fun handleCheckSatExceptionAsUnknown( execute: ((KSolverExecutorException) -> KSolverStatus) -> KSolverStatus ): KSolverStatus { diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt index 328cc9cbf..04c411289 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt @@ -8,8 +8,6 @@ import com.jetbrains.rd.util.TimeoutException import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.reactive.RdFault import com.jetbrains.rd.util.threading.SynchronousScheduler -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.withTimeout import io.ksmt.KContext import io.ksmt.decl.KDecl import io.ksmt.expr.KExpr @@ -17,45 +15,55 @@ import io.ksmt.expr.KUninterpretedSortValue import io.ksmt.runner.core.KsmtWorkerSession import io.ksmt.runner.generated.models.AssertParams import io.ksmt.runner.generated.models.BulkAssertParams +import io.ksmt.runner.generated.models.CheckMaxSMTParams +import io.ksmt.runner.generated.models.CheckMaxSMTResult import io.ksmt.runner.generated.models.CheckParams import io.ksmt.runner.generated.models.CheckResult import io.ksmt.runner.generated.models.CheckWithAssumptionsParams import io.ksmt.runner.generated.models.ContextSimplificationMode import io.ksmt.runner.generated.models.CreateSolverParams -import io.ksmt.runner.generated.models.ModelResult import io.ksmt.runner.generated.models.ModelEntry import io.ksmt.runner.generated.models.ModelFuncInterpEntry +import io.ksmt.runner.generated.models.ModelResult import io.ksmt.runner.generated.models.PopParams import io.ksmt.runner.generated.models.ReasonUnknownResult +import io.ksmt.runner.generated.models.SoftConstraint import io.ksmt.runner.generated.models.SolverConfigurationParam import io.ksmt.runner.generated.models.SolverProtocolModel import io.ksmt.runner.generated.models.SolverType import io.ksmt.runner.generated.models.UnsatCoreResult +import io.ksmt.solver.KModel +import io.ksmt.solver.KSolverException +import io.ksmt.solver.KSolverStatus +import io.ksmt.solver.KSolverUnsupportedFeatureException +import io.ksmt.solver.KSolverUnsupportedParameterException +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.KMaxSMTResult +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.model.KFuncInterp import io.ksmt.solver.model.KFuncInterpEntry import io.ksmt.solver.model.KFuncInterpEntryVarsFree import io.ksmt.solver.model.KFuncInterpEntryWithVars import io.ksmt.solver.model.KFuncInterpVarsFree import io.ksmt.solver.model.KFuncInterpWithVars -import io.ksmt.solver.KModel -import io.ksmt.solver.KSolverException -import io.ksmt.solver.KSolverStatus -import io.ksmt.solver.KSolverUnsupportedFeatureException -import io.ksmt.solver.KSolverUnsupportedParameterException import io.ksmt.solver.model.KModelImpl import io.ksmt.solver.runner.KSolverRunnerManager.CustomSolverInfo import io.ksmt.sort.KBoolSort import io.ksmt.sort.KSort import io.ksmt.sort.KUninterpretedSort +import io.ksmt.utils.uncheckedCast +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.withTimeout import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit -import io.ksmt.utils.uncheckedCast import kotlin.time.Duration class KSolverRunnerExecutor( private val hardTimeout: Duration, private val worker: KsmtWorkerSession, ) { + private val maxSmtCtx = KMaxSMTContext(preferLargeWeightConstraintsForCores = true) + fun configureSync(config: List) = configure(config) { cfg -> queryWithTimeoutAndExceptionHandlingSync { configure.querySync(cfg) @@ -88,11 +96,22 @@ class KSolverRunnerExecutor( } } + fun assertSoft(expr: KExpr, weight: UInt) = assertSoft(expr, weight) { params -> + queryWithTimeoutAndExceptionHandlingSync { + assertSoft.querySync(params) + } + } + private inline fun assert(expr: KExpr, query: (AssertParams) -> Unit) { ensureActive() query(AssertParams(expr)) } + private inline fun assertSoft(expr: KExpr, weight: UInt, query: (SoftConstraint) -> Unit) { + ensureActive() + query(SoftConstraint(expr, weight)) + } + fun bulkAssertSync(exprs: List>) = bulkAssert(exprs) { params -> queryWithTimeoutAndExceptionHandlingSync { bulkAssert.querySync(params) @@ -206,6 +225,56 @@ class KSolverRunnerExecutor( return result.status } + fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + checkAnyMaxSMT(timeout, collectStatistics) { params -> + queryWithTimeoutAndExceptionHandlingSync { + checkMaxSMT.querySync(params) + } + } + + fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + checkAnyMaxSMT(timeout, collectStatistics) { params -> + queryWithTimeoutAndExceptionHandlingSync { + checkSubOptMaxSMT.querySync(params) + } + } + + /** + * Can be used with both optimal and suboptimal versions. + */ + private inline fun checkAnyMaxSMT( + timeout: Duration, + collectStatistics: Boolean, + query: (CheckMaxSMTParams) -> CheckMaxSMTResult + ): KMaxSMTResult { + ensureActive() + + val params = CheckMaxSMTParams(timeout.inWholeMilliseconds, collectStatistics) + val result = query(params) + @Suppress("UNCHECKED_CAST") + return KMaxSMTResult( + result.satSoftConstraints as List, + result.hardConstraintsSatStatus, + result.maxSMTSucceeded + ) + } + + fun collectMaxSMTStatistics(): KMaxSMTStatistics { + ensureActive() + + val result = queryWithTimeoutAndExceptionHandlingSync { + collectMaxSMTStatistics.querySync(Unit) + } + + val maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx).apply { + timeoutMs = result.timeoutMs + elapsedTimeMs = result.elapsedTimeMs + queriesToSolverNumber = result.queriesToSolverNumber + result.timeInSolverQueriesMs + } + return maxSMTStatistics + } + fun checkWithAssumptionsSync( assumptions: List>, timeout: Duration diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 31613c947..dda802be6 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -24,7 +24,10 @@ import io.ksmt.runner.generated.models.UnsatCoreResult import io.ksmt.runner.generated.models.solverProtocolModel import io.ksmt.runner.serializer.AstSerializationCtx import io.ksmt.solver.KSolver -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.model.KFuncInterp import io.ksmt.solver.model.KFuncInterpEntry import io.ksmt.solver.model.KFuncInterpEntryWithVars @@ -43,6 +46,9 @@ class KSolverWorkerProcess : ChildProcessBase() { private val solver: KSolver<*> get() = workerSolver ?: error("Solver is not initialized") + private val maxSmtSolver: KMaxSMTSolverBase + get() = KPrimalDualMaxResSolver(ctx, solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) + override fun parseArgs(args: Array) = KsmtWorkerArgs.fromList(args.toList()) override fun initProtocolModel(protocol: IProtocol): SolverProtocolModel = @@ -75,74 +81,82 @@ class KSolverWorkerProcess : ChildProcessBase() { } } deleteSolver.measureExecutionForTermination { - solver.close() + maxSmtSolver.close() ctx.close() astSerializationCtx.resetCtx() workerSolver = null workerCtx = null } configure.measureExecutionForTermination { config -> - solver.configure { + maxSmtSolver.configure { config.forEach { addUniversalParam(it) } } } assert.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") - solver.assert(params.expression as KExpr) + maxSmtSolver.assert(params.expression as KExpr) } assertSoft.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") - (solver as KMaxSMTSolverInterface).assertSoft(params.expression as KExpr, params.weight) + maxSmtSolver.assertSoft(params.expression as KExpr, params.weight) } bulkAssert.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") - solver.assert(params.expressions as List>) + maxSmtSolver.assert(params.expressions as List>) } assertAndTrack.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") - solver.assertAndTrack(params.expression as KExpr) + maxSmtSolver.assertAndTrack(params.expression as KExpr) } bulkAssertAndTrack.measureExecutionForTermination { params -> @Suppress("UNCHECKED_CAST") - solver.assertAndTrack(params.expressions as List>) + maxSmtSolver.assertAndTrack(params.expressions as List>) } push.measureExecutionForTermination { - solver.push() + maxSmtSolver.push() } pop.measureExecutionForTermination { params -> - solver.pop(params.levels) + maxSmtSolver.pop(params.levels) } check.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds - val status = solver.check(timeout) + val status = maxSmtSolver.check(timeout) CheckResult(status) } checkWithAssumptions.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds @Suppress("UNCHECKED_CAST") - val status = solver.checkWithAssumptions(params.assumptions as List>, timeout) + val status = maxSmtSolver.checkWithAssumptions(params.assumptions as List>, timeout) CheckResult(status) } checkMaxSMT.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds - val result = (solver as KMaxSMTSolverInterface).checkMaxSMT(timeout) + val result = maxSmtSolver.checkMaxSMT(timeout) @Suppress("UNCHECKED_CAST") - CheckMaxSMTResult(result.satSoftConstraints as List, result.hardConstraintsSatStatus, result.maxSMTSucceeded) + CheckMaxSMTResult( + result.satSoftConstraints as List, + result.hardConstraintsSatStatus, + result.maxSMTSucceeded + ) } checkSubOptMaxSMT.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds - val result = (solver as KMaxSMTSolverInterface).checkSubOptMaxSMT(timeout) + val result = maxSmtSolver.checkSubOptMaxSMT(timeout) @Suppress("UNCHECKED_CAST") - CheckMaxSMTResult(result.satSoftConstraints as List, result.hardConstraintsSatStatus, result.maxSMTSucceeded) + CheckMaxSMTResult( + result.satSoftConstraints as List, + result.hardConstraintsSatStatus, + result.maxSMTSucceeded + ) } collectMaxSMTStatistics.measureExecutionForTermination { - val statistics = (solver as KMaxSMTSolverInterface).collectMaxSMTStatistics() + val statistics = maxSmtSolver.collectMaxSMTStatistics() CollectMaxSMTStatisticsResult( statistics.timeoutMs, @@ -152,7 +166,7 @@ class KSolverWorkerProcess : ChildProcessBase() { ) } model.measureExecutionForTermination { - val model = solver.model().detach() + val model = maxSmtSolver.model().detach() val declarations = model.declarations.toList() val interpretations = declarations.map { val interp = model.interpretation(it) ?: error("No interpretation for model declaration $it") @@ -166,15 +180,15 @@ class KSolverWorkerProcess : ChildProcessBase() { ModelResult(declarations, interpretations, uninterpretedSortUniverse) } unsatCore.measureExecutionForTermination { - val core = solver.unsatCore() + val core = maxSmtSolver.unsatCore() UnsatCoreResult(core) } reasonOfUnknown.measureExecutionForTermination { - val reason = solver.reasonOfUnknown() + val reason = maxSmtSolver.reasonOfUnknown() ReasonUnknownResult(reason) } interrupt.measureExecutionForTermination { - solver.interrupt() + maxSmtSolver.interrupt() } lifetime.onTermination { workerSolver?.close() diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index 9d09fcadc..ec7aeb17b 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -142,7 +142,6 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { documentation = "Assert expression" } call("assertSoft", softConstraint, PredefinedType.void).apply { - async documentation = "Assert expression softly" } call("bulkAssert", bulkAssertParams, PredefinedType.void).apply { @@ -170,15 +169,12 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { documentation = "Check SAT" } call("checkMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { - async documentation = "Check MaxSMT" } call("checkSubOptMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { - async documentation = "Check SubOptMaxSMT" } call("collectMaxSMTStatistics", PredefinedType.void, collectMaxSMTStatisticsResult).apply { - async documentation = "Collect MaxSMT statistics" } call("checkWithAssumptions", checkWithAssumptionsParams, checkResult).apply { diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index c77fcd64e..30435ac4f 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -105,7 +105,6 @@ object TestProtocolModel : Ext(TestProtocolRoot) { documentation = "Assert expr" } call("assertSoft", testSoftConstraint, PredefinedType.void).apply { - async documentation = "Assert expression softly" } call("check", PredefinedType.int, testCheckResult).apply { @@ -113,11 +112,9 @@ object TestProtocolModel : Ext(TestProtocolRoot) { documentation = "Check-sat" } call("checkMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { - async documentation = "Check MaxSMT" } call("checkSubOptMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { - async documentation = "Check SubOptMaxSMT" } call("collectMaxSMTStatistics", PredefinedType.void, testCollectMaxSMTStatisticsResult).apply { From 7e196e8f850d5d00be349157ff13c3265aa92cd0 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 17 May 2024 20:15:59 +0300 Subject: [PATCH 191/228] Run Portfolio is async mode --- .../models/SolverProtocolModel.Generated.kt | 4 +++ .../models/TestProtocolModel.Generated.kt | 4 +++ .../io/ksmt/solver/runner/KSolverRunner.kt | 30 +++++++++---------- .../solver/runner/KSolverRunnerExecutor.kt | 24 +++++++-------- .../solver/runner/KSolverWorkerProcess.kt | 6 ++-- .../ksmt/runner/models/SolverProtocolModel.kt | 4 +++ .../ksmt/runner/models/TestProtocolModel.kt | 4 +++ 7 files changed, 47 insertions(+), 29 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index 18fd57fe0..aab315324 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -197,12 +197,16 @@ class SolverProtocolModel private constructor( _deleteSolver.async = true _configure.async = true _assert.async = true + _assertSoft.async = true _bulkAssert.async = true _assertAndTrack.async = true _bulkAssertAndTrack.async = true _push.async = true _pop.async = true _check.async = true + _checkMaxSMT.async = true + _checkSubOptMaxSMT.async = true + _collectMaxSMTStatistics.async = true _checkWithAssumptions.async = true _model.async = true _unsatCore.async = true diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 275757733..7156e0d25 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -204,7 +204,11 @@ class TestProtocolModel private constructor( _internalizeAndConvertCvc5.async = true _createSolver.async = true _assert.async = true + _assertSoft.async = true _check.async = true + _checkMaxSMT.async = true + _checkSubOptMaxSMT.async = true + _collectMaxSMTStatistics.async = true _exprToString.async = true _getReasonUnknown.async = true _addEqualityCheck.async = true diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index 7129bcb79..f3f682ffd 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -35,7 +35,7 @@ class KSolverRunner( private val configurationBuilder: ConfigurationBuilder, private val solverType: SolverType, private val customSolverInfo: CustomSolverInfo? = null, -) : KAsyncSolver, KMaxSMTSolverInterface { +) : KAsyncSolver { private val isActive = AtomicBoolean(true) private val executorInitializationLock = Mutex() private val executorRef = AtomicReference(null) @@ -120,10 +120,10 @@ class KSolverRunner( } } - override fun assertSoft(expr: KExpr, weight: UInt) { + suspend fun assertSoft(expr: KExpr, weight: UInt) { ctx.ensureContextMatch(expr) - ensureInitializedAndExecuteSync(onException = {}) { + ensureInitializedAndExecuteAsync(onException = {}) { assertSoft(expr, weight) } } @@ -245,18 +245,18 @@ class KSolverRunner( checkSync(timeout) } - override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = - handleCheckAnyMaxSMTExceptionAsNotSucceededSync { + suspend fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + handleCheckAnyMaxSMTExceptionAsNotSucceededAsync { checkMaxSMT(timeout, collectStatistics) } - override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = - handleCheckAnyMaxSMTExceptionAsNotSucceededSync { + suspend fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + handleCheckAnyMaxSMTExceptionAsNotSucceededAsync { checkSubOptMaxSMT(timeout, collectStatistics) } - override fun collectMaxSMTStatistics(): KMaxSMTStatistics = - handleCollectStatisticsSync { + suspend fun collectMaxSMTStatistics(): KMaxSMTStatistics = + handleCollectStatisticsAsync { collectMaxSMTStatistics() } @@ -453,19 +453,19 @@ class KSolverRunner( ) } - private inline fun handleCheckAnyMaxSMTExceptionAsNotSucceededSync( - crossinline body: KSolverRunnerExecutor.() -> KMaxSMTResult + private suspend inline fun handleCheckAnyMaxSMTExceptionAsNotSucceededAsync( + crossinline body: suspend KSolverRunnerExecutor.() -> KMaxSMTResult ): KMaxSMTResult { - return ensureInitializedAndExecuteSync( + return ensureInitializedAndExecuteAsync( body = body, onException = { KMaxSMTResult(emptyList(), KSolverStatus.UNKNOWN, false) } ) } - private inline fun handleCollectStatisticsSync( - crossinline body: KSolverRunnerExecutor.() -> KMaxSMTStatistics + private suspend inline fun handleCollectStatisticsAsync( + crossinline body: suspend KSolverRunnerExecutor.() -> KMaxSMTStatistics ): KMaxSMTStatistics { - return ensureInitializedAndExecuteSync( + return ensureInitializedAndExecuteAsync( body = body, onException = { KMaxSMTStatistics(maxSmtCtx) } ) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt index 04c411289..6cbbdd489 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt @@ -96,9 +96,9 @@ class KSolverRunnerExecutor( } } - fun assertSoft(expr: KExpr, weight: UInt) = assertSoft(expr, weight) { params -> - queryWithTimeoutAndExceptionHandlingSync { - assertSoft.querySync(params) + suspend fun assertSoft(expr: KExpr, weight: UInt) = assertSoft(expr, weight) { params -> + queryWithTimeoutAndExceptionHandlingAsync { + assertSoft.queryAsync(params) } } @@ -225,17 +225,17 @@ class KSolverRunnerExecutor( return result.status } - fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + suspend fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = checkAnyMaxSMT(timeout, collectStatistics) { params -> - queryWithTimeoutAndExceptionHandlingSync { - checkMaxSMT.querySync(params) + queryWithTimeoutAndExceptionHandlingAsync { + checkMaxSMT.queryAsync(params) } } - fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = + suspend fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = checkAnyMaxSMT(timeout, collectStatistics) { params -> - queryWithTimeoutAndExceptionHandlingSync { - checkSubOptMaxSMT.querySync(params) + queryWithTimeoutAndExceptionHandlingAsync { + checkSubOptMaxSMT.queryAsync(params) } } @@ -259,11 +259,11 @@ class KSolverRunnerExecutor( ) } - fun collectMaxSMTStatistics(): KMaxSMTStatistics { + suspend fun collectMaxSMTStatistics(): KMaxSMTStatistics { ensureActive() - val result = queryWithTimeoutAndExceptionHandlingSync { - collectMaxSMTStatistics.querySync(Unit) + val result = queryWithTimeoutAndExceptionHandlingAsync { + collectMaxSMTStatistics.queryAsync(Unit) } val maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx).apply { diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index dda802be6..91aea992b 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -133,8 +133,9 @@ class KSolverWorkerProcess : ChildProcessBase() { } checkMaxSMT.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds + val collectStatistics = params.collectStatistics - val result = maxSmtSolver.checkMaxSMT(timeout) + val result = maxSmtSolver.checkMaxSMT(timeout, collectStatistics) @Suppress("UNCHECKED_CAST") CheckMaxSMTResult( @@ -145,8 +146,9 @@ class KSolverWorkerProcess : ChildProcessBase() { } checkSubOptMaxSMT.measureExecutionForTermination { params -> val timeout = params.timeout.milliseconds + val collectStatistics = params.collectStatistics - val result = maxSmtSolver.checkSubOptMaxSMT(timeout) + val result = maxSmtSolver.checkSubOptMaxSMT(timeout, collectStatistics) @Suppress("UNCHECKED_CAST") CheckMaxSMTResult( diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index ec7aeb17b..9d09fcadc 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -142,6 +142,7 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { documentation = "Assert expression" } call("assertSoft", softConstraint, PredefinedType.void).apply { + async documentation = "Assert expression softly" } call("bulkAssert", bulkAssertParams, PredefinedType.void).apply { @@ -169,12 +170,15 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { documentation = "Check SAT" } call("checkMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { + async documentation = "Check MaxSMT" } call("checkSubOptMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { + async documentation = "Check SubOptMaxSMT" } call("collectMaxSMTStatistics", PredefinedType.void, collectMaxSMTStatisticsResult).apply { + async documentation = "Collect MaxSMT statistics" } call("checkWithAssumptions", checkWithAssumptionsParams, checkResult).apply { diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index 30435ac4f..dd442253e 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -105,6 +105,7 @@ object TestProtocolModel : Ext(TestProtocolRoot) { documentation = "Assert expr" } call("assertSoft", testSoftConstraint, PredefinedType.void).apply { + async documentation = "Assert expression softly" } call("check", PredefinedType.int, testCheckResult).apply { @@ -112,12 +113,15 @@ object TestProtocolModel : Ext(TestProtocolRoot) { documentation = "Check-sat" } call("checkMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { + async documentation = "Check MaxSMT" } call("checkSubOptMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { + async documentation = "Check SubOptMaxSMT" } call("collectMaxSMTStatistics", PredefinedType.void, testCollectMaxSMTStatisticsResult).apply { + async documentation = "Collect MaxSMT statistics" } call("exprToString", PredefinedType.long, PredefinedType.string).apply { From e75f2ab4596f27e8ce10dc017add862b3c55330e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 18 May 2024 17:14:35 +0300 Subject: [PATCH 192/228] Update KMaxSMTResult with timeoutExceededOrUnknown --- .../io/ksmt/solver/maxsmt/KMaxSMTResult.kt | 7 +- .../solver/maxsmt/solvers/KPMResSolver.kt | 23 ++++-- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 73 +++++++++++++++---- .../io/ksmt/solver/maxsmt/utils/TimerUtils.kt | 5 ++ 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt index f9aa8fd3d..1e97dba97 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt @@ -11,6 +11,10 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint * @property hardConstraintsSatStatus * Shows satisfiability status of hardly asserted constraints' conjunction. * + * @property timeoutExceededOrUnknown + * Shows whether timeout has exceeded, solver was interrupted or returned UNKNOWN (can happen when timeout has exceeded + * or by some other reason). + * * @property maxSMTSucceeded * Shows whether MaxSMT calculation has succeeded or not. * @@ -20,5 +24,6 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint class KMaxSMTResult( val satSoftConstraints: List, val hardConstraintsSatStatus: KSolverStatus, - val maxSMTSucceeded: Boolean, + val timeoutExceededOrUnknown: Boolean, + val maxSMTSucceeded: Boolean ) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index d32cf5ae5..9459bda8c 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -71,14 +71,23 @@ class KPMResSolver(private val ctx: KContext, solver: return KMaxSMTResult( listOf(), hardConstraintsStatus, + hardConstraintsStatus == UNKNOWN, hardConstraintsStatus != UNKNOWN, ) } if (hardConstraintsStatus == UNSAT) { - return KMaxSMTResult(listOf(), hardConstraintsStatus, true) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = false, + maxSMTSucceeded = true + ) } else if (hardConstraintsStatus == UNKNOWN) { - return KMaxSMTResult(listOf(), hardConstraintsStatus, false) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = true, + maxSMTSucceeded = false + ) } push() @@ -91,7 +100,11 @@ class KPMResSolver(private val ctx: KContext, solver: if (TimerUtils.timeoutExceeded(checkRemainingTime) || isInterrupted) { pop() - return KMaxSMTResult(listOf(), hardConstraintsStatus, false) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = true, + maxSMTSucceeded = false + ) } val markCheckSatStart = markNow() @@ -108,7 +121,7 @@ class KPMResSolver(private val ctx: KContext, solver: return processSat(model!!) } else if (solverStatus == UNKNOWN) { pop() - return KMaxSMTResult(emptyList(), SAT, false) + return KMaxSMTResult(emptyList(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) @@ -212,7 +225,7 @@ class KPMResSolver(private val ctx: KContext, solver: private fun processSat(model: KModel): KMaxSMTResult { val satSoftConstraints = getSatSoftConstraintsByModel(model) - return KMaxSMTResult(satSoftConstraints, SAT, true) + return KMaxSMTResult(satSoftConstraints, SAT, timeoutExceededOrUnknown = false, maxSMTSucceeded = true) } /** diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 08c1c937d..6dd59a4d6 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -15,7 +15,6 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalDualMaxRes import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint -import io.ksmt.solver.maxsmt.solvers.exceptions.NotYetImplementedException import io.ksmt.solver.maxsmt.solvers.utils.MinimalUnsatCore import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils @@ -71,12 +70,20 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), hardConstraintsStatus, true) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = false, + maxSMTSucceeded = true + ) } else if (hardConstraintsStatus == UNKNOWN) { if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), hardConstraintsStatus, false) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = true, + maxSMTSucceeded = false + ) } push() @@ -120,6 +127,8 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } + + else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } @@ -148,6 +157,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } + // Something went wrong, so timeout has exceeded, solver was interrupted or returned UNKNOWN. return getSubOptMaxSMTResult(true) } else if (processUnsatStatus == UNKNOWN) { pop() @@ -178,7 +188,11 @@ class KPrimalDualMaxResSolver( _lower = _upper - val result = KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, true) + val result = KMaxSMTResult( + if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, + timeoutExceededOrUnknown = false, + maxSMTSucceeded = true + ) logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } @@ -217,12 +231,20 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), hardConstraintsStatus, true) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = false, + maxSMTSucceeded = true + ) } else if (hardConstraintsStatus == UNKNOWN) { if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), hardConstraintsStatus, false) + return KMaxSMTResult( + listOf(), hardConstraintsStatus, + timeoutExceededOrUnknown = true, + maxSMTSucceeded = false + ) } push() @@ -241,7 +263,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } val status = checkSatHillClimb(assumptions, timeout) @@ -262,6 +284,8 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } + + else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } @@ -272,7 +296,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotYetImplementedException("MaxSMT solution was not found --- timeout exceeded") + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } val processUnsatStatus = processUnsat(assumptions, remainingTime) @@ -283,13 +307,13 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotYetImplementedException("MaxSMT solution was not found --- processUnsat returned UNSAT") + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } else if (processUnsatStatus == UNKNOWN) { pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotYetImplementedException("MaxSMT solution was not found --- processUnsat returned UNKNOWN") + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } } @@ -298,7 +322,8 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - throw NotYetImplementedException("MaxSMT solution was not found --- solver returned UNKNOWN") + // Solver returned UNKNOWN case. + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } } @@ -307,7 +332,11 @@ class KPrimalDualMaxResSolver( _lower = _upper - val result = KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, true) + val result = KMaxSMTResult( + if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, + timeoutExceededOrUnknown = false, + maxSMTSucceeded = true + ) logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" } @@ -330,12 +359,14 @@ class KPrimalDualMaxResSolver( } private fun processUnsat(assumptions: MutableList, timeout: Duration): KSolverStatus { + // We can't get UNSAT from it. val (status, cores) = getCores(assumptions, timeout) if (status != SAT) { return status } + // Timeout has exceeded or some core was empty (how???). if (cores.isEmpty()) { return UNSAT } @@ -397,6 +428,9 @@ class KPrimalDualMaxResSolver( logger.info { "processing unsat core --- ended" } } + /** + * Status is UNKNOWN when timeout has exceeded or solver returned UNKNOWN. + */ private fun getCores( assumptions: MutableList, timeout: Duration, @@ -421,6 +455,7 @@ class KPrimalDualMaxResSolver( unsatCore = CoreUtils.coreToSoftConstraints(unsatCore(), assumptions) } + // TODO: when can we reach it? if (unsatCore.isEmpty()) { cores.clear() _lower = _upper @@ -447,6 +482,8 @@ class KPrimalDualMaxResSolver( status = checkSatHillClimb(assumptions, checkSatRemainingTime) } + // TODO: for the future, we can improve this processing UNKNOWN case. + // Okay, it's UNKNOWN, but if timeout has not exceeded and we have a core then it's Okay, we can do something. return Pair(status, cores) } @@ -723,6 +760,14 @@ class KPrimalDualMaxResSolver( return status } - private fun getSubOptMaxSMTResult(maxSMTSucceeded: Boolean): KMaxSMTResult = - KMaxSMTResult(if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, maxSMTSucceeded) + private fun getSubOptMaxSMTResult( + maxSMTSucceeded: Boolean, + timeoutExceededOrInterrupted: Boolean = true + ): KMaxSMTResult = + KMaxSMTResult( + if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), + SAT, + timeoutExceededOrInterrupted, + maxSMTSucceeded + ) } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt index 6b1885de7..e5d54b68c 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt @@ -10,4 +10,9 @@ internal object TimerUtils { fun timeoutExceeded(timeout: Duration): Boolean = timeout.isNegative() || timeout == Duration.ZERO + + fun timeoutExceeded(timeout: Duration, markStart: ValueTimeMark): Boolean { + val remainingTime = computeRemainingTime(timeout, markStart) + return timeoutExceeded(remainingTime) + } } From 74737691065a64a31d3820db5d39867e6133ee67 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 18 May 2024 17:31:41 +0300 Subject: [PATCH 193/228] Update protocol models etc for timeoutExceededOrUnknown support --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 5 +++- .../models/SolverProtocolModel.Generated.kt | 26 ++++++++++++------- .../models/TestProtocolModel.Generated.kt | 16 ++++++++---- .../io/ksmt/solver/runner/KSolverRunner.kt | 9 +++++-- .../solver/runner/KSolverRunnerExecutor.kt | 1 + .../solver/runner/KSolverWorkerProcess.kt | 2 ++ .../ksmt/runner/models/SolverProtocolModel.kt | 1 + .../ksmt/runner/models/TestProtocolModel.kt | 1 + 8 files changed, 43 insertions(+), 18 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 9cdc3172f..fb807803b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -207,7 +207,10 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Elapsed time: $elapsedTime ms --- MaxSMT call${System.lineSeparator()}" } try { - assertTrue(maxSMTResult.maxSMTSucceeded, "MaxSMT was not successful [$name]") + assertTrue( + !maxSMTResult.timeoutExceededOrUnknown, "MaxSMT was not successful [$name] as timeout" + + "has exceeded, solver was interrupted or returned UNKNOWN" + ) assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") val satSoftConstraintsWeightsSum = maxSMTResult.satSoftConstraints.sumOf { it.weight } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index aab315324..4343396fe 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -88,7 +88,7 @@ class SolverProtocolModel private constructor( private val __SolverConfigurationParamListSerializer = SolverConfigurationParam.list() - const val serializationHash = 283104004146364238L + const val serializationHash = 4749706839366626801L } override val serializersOwner: ISerializersOwner get() = SolverProtocolModel @@ -501,6 +501,7 @@ data class CheckMaxSMTParams ( data class CheckMaxSMTResult ( val satSoftConstraints: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, + val timeoutExceededOrUnknown: Boolean, val maxSMTSucceeded: Boolean ) : IPrintable { //companion @@ -512,13 +513,15 @@ data class CheckMaxSMTResult ( override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): CheckMaxSMTResult { val satSoftConstraints = buffer.readList { SoftConstraint.read(ctx, buffer) } val hardConstraintsSatStatus = buffer.readEnum() + val timeoutExceededOrUnknown = buffer.readBool() val maxSMTSucceeded = buffer.readBool() - return CheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, maxSMTSucceeded) + return CheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CheckMaxSMTResult) { buffer.writeList(value.satSoftConstraints) { v -> SoftConstraint.write(ctx, buffer, v) } buffer.writeEnum(value.hardConstraintsSatStatus) + buffer.writeBool(value.timeoutExceededOrUnknown) buffer.writeBool(value.maxSMTSucceeded) } @@ -537,6 +540,7 @@ data class CheckMaxSMTResult ( if (satSoftConstraints != other.satSoftConstraints) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false + if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false if (maxSMTSucceeded != other.maxSMTSucceeded) return false return true @@ -546,6 +550,7 @@ data class CheckMaxSMTResult ( var __r = 0 __r = __r*31 + satSoftConstraints.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() + __r = __r*31 + timeoutExceededOrUnknown.hashCode() __r = __r*31 + maxSMTSucceeded.hashCode() return __r } @@ -555,6 +560,7 @@ data class CheckMaxSMTResult ( printer.indent { print("satSoftConstraints = "); satSoftConstraints.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() + print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() } printer.print(")") @@ -679,7 +685,7 @@ data class CheckResult ( /** - * #### Generated from [SolverProtocolModel.kt:83] + * #### Generated from [SolverProtocolModel.kt:84] */ data class CheckWithAssumptionsParams ( val assumptions: List, @@ -742,7 +748,7 @@ data class CheckWithAssumptionsParams ( /** - * #### Generated from [SolverProtocolModel.kt:88] + * #### Generated from [SolverProtocolModel.kt:89] */ data class CollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -922,7 +928,7 @@ data class CreateSolverParams ( /** - * #### Generated from [SolverProtocolModel.kt:109] + * #### Generated from [SolverProtocolModel.kt:110] */ data class ModelEntry ( val decl: io.ksmt.KAst, @@ -997,7 +1003,7 @@ data class ModelEntry ( /** - * #### Generated from [SolverProtocolModel.kt:103] + * #### Generated from [SolverProtocolModel.kt:104] */ data class ModelFuncInterpEntry ( val hasVars: Boolean, @@ -1066,7 +1072,7 @@ data class ModelFuncInterpEntry ( /** - * #### Generated from [SolverProtocolModel.kt:121] + * #### Generated from [SolverProtocolModel.kt:122] */ data class ModelResult ( val declarations: List, @@ -1135,7 +1141,7 @@ data class ModelResult ( /** - * #### Generated from [SolverProtocolModel.kt:116] + * #### Generated from [SolverProtocolModel.kt:117] */ data class ModelUninterpretedSortUniverse ( val sort: io.ksmt.KAst, @@ -1255,7 +1261,7 @@ data class PopParams ( /** - * #### Generated from [SolverProtocolModel.kt:99] + * #### Generated from [SolverProtocolModel.kt:100] */ data class ReasonUnknownResult ( val reasonUnknown: String @@ -1461,7 +1467,7 @@ enum class SolverType { /** - * #### Generated from [SolverProtocolModel.kt:95] + * #### Generated from [SolverProtocolModel.kt:96] */ data class UnsatCoreResult ( val core: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 7156e0d25..0d32551f2 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -80,7 +80,7 @@ class TestProtocolModel private constructor( private val __LongListSerializer = FrameworkMarshallers.Long.list() private val __IntNullableSerializer = FrameworkMarshallers.Int.nullable() - const val serializationHash = -5506113420646493100L + const val serializationHash = -3623267389865938479L } override val serializersOwner: ISerializersOwner get() = TestProtocolModel @@ -594,6 +594,7 @@ data class TestCheckMaxSMTParams ( data class TestCheckMaxSMTResult ( val satSoftConstraints: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, + val timeoutExceededOrUnknown: Boolean, val maxSMTSucceeded: Boolean ) : IPrintable { //companion @@ -605,13 +606,15 @@ data class TestCheckMaxSMTResult ( override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCheckMaxSMTResult { val satSoftConstraints = buffer.readList { TestSoftConstraint.read(ctx, buffer) } val hardConstraintsSatStatus = buffer.readEnum() + val timeoutExceededOrUnknown = buffer.readBool() val maxSMTSucceeded = buffer.readBool() - return TestCheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, maxSMTSucceeded) + return TestCheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTResult) { buffer.writeList(value.satSoftConstraints) { v -> TestSoftConstraint.write(ctx, buffer, v) } buffer.writeEnum(value.hardConstraintsSatStatus) + buffer.writeBool(value.timeoutExceededOrUnknown) buffer.writeBool(value.maxSMTSucceeded) } @@ -630,6 +633,7 @@ data class TestCheckMaxSMTResult ( if (satSoftConstraints != other.satSoftConstraints) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false + if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false if (maxSMTSucceeded != other.maxSMTSucceeded) return false return true @@ -639,6 +643,7 @@ data class TestCheckMaxSMTResult ( var __r = 0 __r = __r*31 + satSoftConstraints.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() + __r = __r*31 + timeoutExceededOrUnknown.hashCode() __r = __r*31 + maxSMTSucceeded.hashCode() return __r } @@ -648,6 +653,7 @@ data class TestCheckMaxSMTResult ( printer.indent { print("satSoftConstraints = "); satSoftConstraints.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() + print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() } printer.print(")") @@ -715,7 +721,7 @@ data class TestCheckResult ( /** - * #### Generated from [TestProtocolModel.kt:55] + * #### Generated from [TestProtocolModel.kt:56] */ data class TestCollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -790,7 +796,7 @@ data class TestCollectMaxSMTStatisticsResult ( /** - * #### Generated from [TestProtocolModel.kt:62] + * #### Generated from [TestProtocolModel.kt:63] */ data class TestConversionResult ( val expressions: List @@ -847,7 +853,7 @@ data class TestConversionResult ( /** - * #### Generated from [TestProtocolModel.kt:66] + * #### Generated from [TestProtocolModel.kt:67] */ data class TestInternalizeAndConvertParams ( val expressions: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index f3f682ffd..58b7c1675 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -14,7 +14,6 @@ import io.ksmt.solver.KSolverStatus import io.ksmt.solver.async.KAsyncSolver import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTResult -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverInterface import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.runner.KSolverRunnerManager.CustomSolverInfo import io.ksmt.sort.KBoolSort @@ -458,7 +457,13 @@ class KSolverRunner( ): KMaxSMTResult { return ensureInitializedAndExecuteAsync( body = body, - onException = { KMaxSMTResult(emptyList(), KSolverStatus.UNKNOWN, false) } + onException = { + KMaxSMTResult( + emptyList(), KSolverStatus.UNKNOWN, + timeoutExceededOrUnknown = true, + maxSMTSucceeded = false + ) + } ) } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt index 6cbbdd489..b706c15f3 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt @@ -255,6 +255,7 @@ class KSolverRunnerExecutor( return KMaxSMTResult( result.satSoftConstraints as List, result.hardConstraintsSatStatus, + result.timeoutExceededOrUnknown, result.maxSMTSucceeded ) } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 91aea992b..679c08cda 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -141,6 +141,7 @@ class KSolverWorkerProcess : ChildProcessBase() { CheckMaxSMTResult( result.satSoftConstraints as List, result.hardConstraintsSatStatus, + result.timeoutExceededOrUnknown, result.maxSMTSucceeded ) } @@ -154,6 +155,7 @@ class KSolverWorkerProcess : ChildProcessBase() { CheckMaxSMTResult( result.satSoftConstraints as List, result.hardConstraintsSatStatus, + result.timeoutExceededOrUnknown, result.maxSMTSucceeded ) } diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index 9d09fcadc..1a2564e69 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -77,6 +77,7 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { private val checkMaxSMTResult = structdef { field("satSoftConstraints", immutableList(softConstraint)) field("hardConstraintsSatStatus", statusType) + field("timeoutExceededOrUnknown", PredefinedType.bool) field("maxSMTSucceeded", PredefinedType.bool) } diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index dd442253e..635fd5fe6 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -49,6 +49,7 @@ object TestProtocolModel : Ext(TestProtocolRoot) { private val testCheckMaxSMTResult = structdef { field("satSoftConstraints", immutableList(testSoftConstraint)) field("hardConstraintsSatStatus", statusType) + field("timeoutExceededOrUnknown", PredefinedType.bool) field("maxSMTSucceeded", PredefinedType.bool) } From e3533802d4f6c01b01f6d4e74c57d28436d90b05 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 18 May 2024 17:37:07 +0300 Subject: [PATCH 194/228] Descrease test workers number to 1 --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index fb807803b..6be6c18ec 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -280,7 +280,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @JvmStatic fun initWorkerPools() { testWorkers = KsmtWorkerPool( - maxWorkerPoolSize = 4, + maxWorkerPoolSize = 1, workerProcessIdleTimeout = 10.minutes, workerFactory = object : KsmtWorkerFactory { override val childProcessEntrypoint = TestWorkerProcess::class From dcb1b7dd17fef54081817d577ee95511456d5fc0 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 19 May 2024 00:06:07 +0300 Subject: [PATCH 195/228] Update models to use two lists instead of List --- .../models/SolverProtocolModel.Generated.kt | 38 ++++++++------ .../models/TestProtocolModel.Generated.kt | 28 ++++++---- .../ksmt/solver/portfolio/KPortfolioSolver.kt | 52 +++++++++++++++++-- .../solver/runner/KSolverRunnerExecutor.kt | 6 ++- .../solver/runner/KSolverWorkerProcess.kt | 8 +-- .../ksmt/runner/models/SolverProtocolModel.kt | 3 +- .../ksmt/runner/models/TestProtocolModel.kt | 3 +- 7 files changed, 100 insertions(+), 38 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index 4343396fe..0fd3ed291 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -88,7 +88,7 @@ class SolverProtocolModel private constructor( private val __SolverConfigurationParamListSerializer = SolverConfigurationParam.list() - const val serializationHash = 4749706839366626801L + const val serializationHash = 60617290091922470L } override val serializersOwner: ISerializersOwner get() = SolverProtocolModel @@ -499,7 +499,8 @@ data class CheckMaxSMTParams ( * #### Generated from [SolverProtocolModel.kt:77] */ data class CheckMaxSMTResult ( - val satSoftConstraints: List, + val satSoftConstraintExprs: List, + val satSoftConstraintWeights: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, val timeoutExceededOrUnknown: Boolean, val maxSMTSucceeded: Boolean @@ -511,15 +512,17 @@ data class CheckMaxSMTResult ( @Suppress("UNCHECKED_CAST") override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): CheckMaxSMTResult { - val satSoftConstraints = buffer.readList { SoftConstraint.read(ctx, buffer) } + val satSoftConstraintExprs = buffer.readList { (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) } + val satSoftConstraintWeights = buffer.readList { buffer.readUInt() } val hardConstraintsSatStatus = buffer.readEnum() val timeoutExceededOrUnknown = buffer.readBool() val maxSMTSucceeded = buffer.readBool() - return CheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) + return CheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CheckMaxSMTResult) { - buffer.writeList(value.satSoftConstraints) { v -> SoftConstraint.write(ctx, buffer, v) } + buffer.writeList(value.satSoftConstraintExprs) { v -> (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, v) } + buffer.writeList(value.satSoftConstraintWeights) { v -> buffer.writeUInt(v) } buffer.writeEnum(value.hardConstraintsSatStatus) buffer.writeBool(value.timeoutExceededOrUnknown) buffer.writeBool(value.maxSMTSucceeded) @@ -538,7 +541,8 @@ data class CheckMaxSMTResult ( other as CheckMaxSMTResult - if (satSoftConstraints != other.satSoftConstraints) return false + if (satSoftConstraintExprs != other.satSoftConstraintExprs) return false + if (satSoftConstraintWeights != other.satSoftConstraintWeights) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false if (maxSMTSucceeded != other.maxSMTSucceeded) return false @@ -548,7 +552,8 @@ data class CheckMaxSMTResult ( //hash code trait override fun hashCode(): Int { var __r = 0 - __r = __r*31 + satSoftConstraints.hashCode() + __r = __r*31 + satSoftConstraintExprs.hashCode() + __r = __r*31 + satSoftConstraintWeights.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() __r = __r*31 + timeoutExceededOrUnknown.hashCode() __r = __r*31 + maxSMTSucceeded.hashCode() @@ -558,7 +563,8 @@ data class CheckMaxSMTResult ( override fun print(printer: PrettyPrinter) { printer.println("CheckMaxSMTResult (") printer.indent { - print("satSoftConstraints = "); satSoftConstraints.print(printer); println() + print("satSoftConstraintExprs = "); satSoftConstraintExprs.print(printer); println() + print("satSoftConstraintWeights = "); satSoftConstraintWeights.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() @@ -685,7 +691,7 @@ data class CheckResult ( /** - * #### Generated from [SolverProtocolModel.kt:84] + * #### Generated from [SolverProtocolModel.kt:85] */ data class CheckWithAssumptionsParams ( val assumptions: List, @@ -748,7 +754,7 @@ data class CheckWithAssumptionsParams ( /** - * #### Generated from [SolverProtocolModel.kt:89] + * #### Generated from [SolverProtocolModel.kt:90] */ data class CollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -928,7 +934,7 @@ data class CreateSolverParams ( /** - * #### Generated from [SolverProtocolModel.kt:110] + * #### Generated from [SolverProtocolModel.kt:111] */ data class ModelEntry ( val decl: io.ksmt.KAst, @@ -1003,7 +1009,7 @@ data class ModelEntry ( /** - * #### Generated from [SolverProtocolModel.kt:104] + * #### Generated from [SolverProtocolModel.kt:105] */ data class ModelFuncInterpEntry ( val hasVars: Boolean, @@ -1072,7 +1078,7 @@ data class ModelFuncInterpEntry ( /** - * #### Generated from [SolverProtocolModel.kt:122] + * #### Generated from [SolverProtocolModel.kt:123] */ data class ModelResult ( val declarations: List, @@ -1141,7 +1147,7 @@ data class ModelResult ( /** - * #### Generated from [SolverProtocolModel.kt:117] + * #### Generated from [SolverProtocolModel.kt:118] */ data class ModelUninterpretedSortUniverse ( val sort: io.ksmt.KAst, @@ -1261,7 +1267,7 @@ data class PopParams ( /** - * #### Generated from [SolverProtocolModel.kt:100] + * #### Generated from [SolverProtocolModel.kt:101] */ data class ReasonUnknownResult ( val reasonUnknown: String @@ -1467,7 +1473,7 @@ enum class SolverType { /** - * #### Generated from [SolverProtocolModel.kt:96] + * #### Generated from [SolverProtocolModel.kt:97] */ data class UnsatCoreResult ( val core: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 0d32551f2..207b3cb74 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -80,7 +80,7 @@ class TestProtocolModel private constructor( private val __LongListSerializer = FrameworkMarshallers.Long.list() private val __IntNullableSerializer = FrameworkMarshallers.Int.nullable() - const val serializationHash = -3623267389865938479L + const val serializationHash = -1203465621073210600L } override val serializersOwner: ISerializersOwner get() = TestProtocolModel @@ -592,7 +592,8 @@ data class TestCheckMaxSMTParams ( * #### Generated from [TestProtocolModel.kt:49] */ data class TestCheckMaxSMTResult ( - val satSoftConstraints: List, + val satSoftConstraintExprs: List, + val satSoftConstraintWeights: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, val timeoutExceededOrUnknown: Boolean, val maxSMTSucceeded: Boolean @@ -604,15 +605,17 @@ data class TestCheckMaxSMTResult ( @Suppress("UNCHECKED_CAST") override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): TestCheckMaxSMTResult { - val satSoftConstraints = buffer.readList { TestSoftConstraint.read(ctx, buffer) } + val satSoftConstraintExprs = buffer.readList { (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).read(ctx, buffer) } + val satSoftConstraintWeights = buffer.readList { buffer.readUInt() } val hardConstraintsSatStatus = buffer.readEnum() val timeoutExceededOrUnknown = buffer.readBool() val maxSMTSucceeded = buffer.readBool() - return TestCheckMaxSMTResult(satSoftConstraints, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) + return TestCheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTResult) { - buffer.writeList(value.satSoftConstraints) { v -> TestSoftConstraint.write(ctx, buffer, v) } + buffer.writeList(value.satSoftConstraintExprs) { v -> (ctx.serializers.get(io.ksmt.runner.serializer.AstSerializationCtx.marshallerId)!! as IMarshaller).write(ctx,buffer, v) } + buffer.writeList(value.satSoftConstraintWeights) { v -> buffer.writeUInt(v) } buffer.writeEnum(value.hardConstraintsSatStatus) buffer.writeBool(value.timeoutExceededOrUnknown) buffer.writeBool(value.maxSMTSucceeded) @@ -631,7 +634,8 @@ data class TestCheckMaxSMTResult ( other as TestCheckMaxSMTResult - if (satSoftConstraints != other.satSoftConstraints) return false + if (satSoftConstraintExprs != other.satSoftConstraintExprs) return false + if (satSoftConstraintWeights != other.satSoftConstraintWeights) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false if (maxSMTSucceeded != other.maxSMTSucceeded) return false @@ -641,7 +645,8 @@ data class TestCheckMaxSMTResult ( //hash code trait override fun hashCode(): Int { var __r = 0 - __r = __r*31 + satSoftConstraints.hashCode() + __r = __r*31 + satSoftConstraintExprs.hashCode() + __r = __r*31 + satSoftConstraintWeights.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() __r = __r*31 + timeoutExceededOrUnknown.hashCode() __r = __r*31 + maxSMTSucceeded.hashCode() @@ -651,7 +656,8 @@ data class TestCheckMaxSMTResult ( override fun print(printer: PrettyPrinter) { printer.println("TestCheckMaxSMTResult (") printer.indent { - print("satSoftConstraints = "); satSoftConstraints.print(printer); println() + print("satSoftConstraintExprs = "); satSoftConstraintExprs.print(printer); println() + print("satSoftConstraintWeights = "); satSoftConstraintWeights.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() @@ -721,7 +727,7 @@ data class TestCheckResult ( /** - * #### Generated from [TestProtocolModel.kt:56] + * #### Generated from [TestProtocolModel.kt:57] */ data class TestCollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -796,7 +802,7 @@ data class TestCollectMaxSMTStatisticsResult ( /** - * #### Generated from [TestProtocolModel.kt:63] + * #### Generated from [TestProtocolModel.kt:64] */ data class TestConversionResult ( val expressions: List @@ -853,7 +859,7 @@ data class TestConversionResult ( /** - * #### Generated from [TestProtocolModel.kt:67] + * #### Generated from [TestProtocolModel.kt:68] */ data class TestInternalizeAndConvertParams ( val expressions: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 4a3b55b18..5bb01cae6 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -140,16 +140,21 @@ class KPortfolioSolver( } override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - solverQuery( + solverMaxSmtQuery( { checkMaxSMT(timeout, collectStatistics) }, - { this.maxSMTSucceeded } + { !this.timeoutExceededOrUnknown } ) } override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - solverQuery( + solverMaxSmtQuery( { checkSubOptMaxSMT(timeout, collectStatistics) }, - { this.maxSMTSucceeded } + solverPredicate = { false }, + onFailure = { results -> + results.results + .mapNotNull { it.getOrNull() } + .maxByOrNull { it.result.satSoftConstraints.sumOf { constr -> constr.weight } }!! + } ) } @@ -283,6 +288,45 @@ class KPortfolioSolver( return result } + private suspend inline fun solverMaxSmtQuery( + crossinline block: suspend KSolverRunner<*>.() -> T, + crossinline solverPredicate: T.() -> Boolean, + crossinline onFailure: (SolverAwaitFailure) -> SolverOperationResult = { it.findSuccessOrThrow() } + ): T { + terminateIfNeeded() + + lastSuccessfulSolver.getAndSet(null) + + val awaitResult = awaitFirstSolver(block) { + solverPredicate(it) + } + + val result = when (awaitResult) { + is SolverAwaitSuccess -> awaitResult.result.also { + solverStats[awaitResult.result.solverId].logSolverQueryBest() + } + /** + * All solvers finished with Unknown or failed with exception. + * If some solver ends up with Unknown we can treat this result as successful. + * */ + is SolverAwaitFailure -> onFailure(awaitResult) + } + + lastSuccessfulSolver.getAndSet(result.solver) + + activeSolvers.forEach { _, solverOperationState -> + if (solverOperationState.solver != result.solver) { + val failedSolver = solverOperationState.solver + solverOperationState.operationScope.launch { + failedSolver.interruptAsync() + } + pendingTermination.offer(failedSolver) + } + } + + return result.result + } + private suspend inline fun solverQuery( crossinline block: suspend KSolverRunner<*>.() -> T, crossinline solverPredicate: T.() -> Boolean diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt index b706c15f3..138958a8d 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt @@ -252,8 +252,12 @@ class KSolverRunnerExecutor( val params = CheckMaxSMTParams(timeout.inWholeMilliseconds, collectStatistics) val result = query(params) @Suppress("UNCHECKED_CAST") + val satSoftConstraints = (result.satSoftConstraintExprs as List>) + .zip(result.satSoftConstraintWeights) + .map { io.ksmt.solver.maxsmt.constraints.SoftConstraint(it.first, it.second) } + return KMaxSMTResult( - result.satSoftConstraints as List, + satSoftConstraints, result.hardConstraintsSatStatus, result.timeoutExceededOrUnknown, result.maxSMTSucceeded diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 679c08cda..d42c70bc1 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -137,9 +137,9 @@ class KSolverWorkerProcess : ChildProcessBase() { val result = maxSmtSolver.checkMaxSMT(timeout, collectStatistics) - @Suppress("UNCHECKED_CAST") CheckMaxSMTResult( - result.satSoftConstraints as List, + result.satSoftConstraints.map { it.expression }, + result.satSoftConstraints.map { it.weight }, result.hardConstraintsSatStatus, result.timeoutExceededOrUnknown, result.maxSMTSucceeded @@ -151,9 +151,9 @@ class KSolverWorkerProcess : ChildProcessBase() { val result = maxSmtSolver.checkSubOptMaxSMT(timeout, collectStatistics) - @Suppress("UNCHECKED_CAST") CheckMaxSMTResult( - result.satSoftConstraints as List, + result.satSoftConstraints.map { it.expression }, + result.satSoftConstraints.map { it.weight }, result.hardConstraintsSatStatus, result.timeoutExceededOrUnknown, result.maxSMTSucceeded diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index 1a2564e69..fe9fe9464 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -75,7 +75,8 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { } private val checkMaxSMTResult = structdef { - field("satSoftConstraints", immutableList(softConstraint)) + field("satSoftConstraintExprs", immutableList(kastType)) + field("satSoftConstraintWeights", immutableList(PredefinedType.uint)) field("hardConstraintsSatStatus", statusType) field("timeoutExceededOrUnknown", PredefinedType.bool) field("maxSMTSucceeded", PredefinedType.bool) diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index 635fd5fe6..82f7b6285 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -47,7 +47,8 @@ object TestProtocolModel : Ext(TestProtocolRoot) { } private val testCheckMaxSMTResult = structdef { - field("satSoftConstraints", immutableList(testSoftConstraint)) + field("satSoftConstraintExprs", immutableList(kastType)) + field("satSoftConstraintWeights", immutableList(PredefinedType.uint)) field("hardConstraintsSatStatus", statusType) field("timeoutExceededOrUnknown", PredefinedType.bool) field("maxSMTSucceeded", PredefinedType.bool) From c7f6f41642bf4a5e0317dfde2e8aeb3277073c33 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 19 May 2024 14:48:10 +0300 Subject: [PATCH 196/228] Fix a bug with initializing KPrimalDualMaxResSolverBase in KSolverWorkerProcess --- .../io/ksmt/solver/runner/KSolverWorkerProcess.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index d42c70bc1..300a86758 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -32,6 +32,7 @@ import io.ksmt.solver.model.KFuncInterp import io.ksmt.solver.model.KFuncInterpEntry import io.ksmt.solver.model.KFuncInterpEntryWithVars import io.ksmt.solver.model.KFuncInterpWithVars +import io.ksmt.solver.model.KNativeSolverModel import io.ksmt.sort.KBoolSort import kotlin.time.Duration.Companion.milliseconds @@ -39,6 +40,15 @@ class KSolverWorkerProcess : ChildProcessBase() { private var workerCtx: KContext? = null private var workerSolver: KSolver<*>? = null private val customSolverCreators = hashMapOf KSolver<*>>() + private var _maxSmtSolver: KMaxSMTSolverBase? = null + private val maxSmtSolver: KMaxSMTSolverBase + get() { + if (_maxSmtSolver == null) { + _maxSmtSolver = KPrimalDualMaxResSolver(ctx, solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) + return _maxSmtSolver!! + } + return _maxSmtSolver!! + } private val ctx: KContext get() = workerCtx ?: error("Solver is not initialized") @@ -46,9 +56,6 @@ class KSolverWorkerProcess : ChildProcessBase() { private val solver: KSolver<*> get() = workerSolver ?: error("Solver is not initialized") - private val maxSmtSolver: KMaxSMTSolverBase - get() = KPrimalDualMaxResSolver(ctx, solver, KMaxSMTContext(preferLargeWeightConstraintsForCores = true)) - override fun parseArgs(args: Array) = KsmtWorkerArgs.fromList(args.toList()) override fun initProtocolModel(protocol: IProtocol): SolverProtocolModel = From 9638af4cf87f4a8e1d8d971c0202f0c8c43173ef Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 19 May 2024 16:29:23 +0300 Subject: [PATCH 197/228] Add elapsed time to general stat --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 1 + .../ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt | 1 + .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 ++ 3 files changed, 4 insertions(+) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 6be6c18ec..26f62f824 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -201,6 +201,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { throw ex } } + testStatistics.elapsedTimeMs = elapsedTime testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index 01156b96b..9a009546d 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -9,6 +9,7 @@ internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver var ignoredTest = false var failedOnParsingOrConvertingExpressions = false var exceptionMessage: String? = null + var elapsedTimeMs: Long = 0 /** * It's wrong when it's more than optimal in case of SubOpt. */ diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 05b369d96..ffff7132b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -205,6 +205,8 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { throw ex } } + testStatistics.elapsedTimeMs = elapsedTime + val foundSoFarWeight = maxSMTResult.satSoftConstraints.sumOf { it.weight.toULong() } testStatistics.foundSoFarWeight = foundSoFarWeight From faadc6253ea8871c2ce6de30224af4f044c4984e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 19 May 2024 16:32:16 +0300 Subject: [PATCH 198/228] Update statistics collection in script --- scripts/analyze_maxsmt_statistics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index 925b99e7c..5e6d0a923 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -82,7 +82,7 @@ def time_in_solver_queries_ms(test): return max_smt_stat(test)["timeInSolverQueriesMs"] def elapsed_time_ms(test): - return max_smt_stat(test)["elapsedTimeMs"] + return test["elapsedTimeMs"] tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) tests_executed_maxsmt_size = len(tests_executed_maxsmt) @@ -140,7 +140,7 @@ def max_smt_stat(test): def elapsed_time_ms(test): if isinstance(test, int): return test - return max_smt_stat(test)["elapsedTimeMs"] + return test["elapsedTimeMs"] tests_executed_maxsmt = list(filter(lambda x: x.get("maxSMTCallStatistics") is not None, tests)) tests_executed_maxsmt_size = len(tests_executed_maxsmt) From 9f869c854137bb0bb12b67d7d350cff3b4bee739 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sun, 19 May 2024 16:42:20 +0300 Subject: [PATCH 199/228] Remove redundant else --- .../io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 6dd59a4d6..b0b243825 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -127,8 +127,6 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } - - else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } @@ -284,8 +282,6 @@ class KPrimalDualMaxResSolver( processSat(correctionSet, assumptions) } } - - else -> error("Unexpected strategy: ${maxSmtCtx.strategy}") } } From c759217d5d71b110868b23e0fbdec3357d9e27ff Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 11:37:28 +0300 Subject: [PATCH 200/228] Update predicate for checkSubOptMaxSMT in KPortfolioSolver --- .../main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 5bb01cae6..c5d55f998 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -149,7 +149,7 @@ class KPortfolioSolver( override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { solverMaxSmtQuery( { checkSubOptMaxSMT(timeout, collectStatistics) }, - solverPredicate = { false }, + solverPredicate = { !this.timeoutExceededOrUnknown }, onFailure = { results -> results.results .mapNotNull { it.getOrNull() } From 31f65a734137e916fa0da71454d84694e3bcda2d Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 12:16:26 +0300 Subject: [PATCH 201/228] Fix Portfolio --- .../main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 300a86758..04b943c62 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -93,6 +93,7 @@ class KSolverWorkerProcess : ChildProcessBase() { astSerializationCtx.resetCtx() workerSolver = null workerCtx = null + _maxSmtSolver = null } configure.measureExecutionForTermination { config -> maxSmtSolver.configure { From b729853015e38f3cc2e8e247501afd8469d3ad01 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 13:16:38 +0300 Subject: [PATCH 202/228] Implement native z3 tests --- .../maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt new file mode 100644 index 000000000..2f10d6bea --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt @@ -0,0 +1,179 @@ +package io.ksmt.solver.maxsmt.test.z3 + +import com.microsoft.z3.BoolExpr +import com.microsoft.z3.Context +import com.microsoft.z3.Optimize +import com.microsoft.z3.Status +import io.github.oshai.kotlinlogging.KotlinLogging +import io.ksmt.KContext +import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest +import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo +import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper +import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3_NATIVE +import io.ksmt.solver.maxsmt.test.utils.getRandomString +import io.ksmt.solver.z3.KZ3Solver +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.extension +import kotlin.system.measureTimeMillis +import kotlin.test.assertEquals + +class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { + private lateinit var z3Ctx: Context + private lateinit var maxSMTSolver: Optimize + private val logger = KotlinLogging.logger {} + + @BeforeEach + fun initSolver() { + z3Ctx = Context() + maxSMTSolver = z3Ctx.mkOptimize() + } + + @AfterEach + fun close() { + z3Ctx.close() + } + + @ParameterizedTest(name = "{0}") + @MethodSource("maxSMTTestData") + fun maxSMTZ3NativeTest(name: String, samplePath: Path) { + testMaxSMTSolver(name, samplePath) + } + + private fun testMaxSMTSolver(name: String, samplePath: Path) { + val extension = "smt2" + require(samplePath.extension == extension) { + "File extension cannot be '${samplePath.extension}' as it must be $extension" + } + + logger.info { "Test name: [$name]" } + + val testStatistics = MaxSMTTestStatistics(name, Z3_NATIVE) + lateinit var expressions: List + + try { + expressions = z3Ctx.parseSMTLIB2File( + samplePath.toString(), + emptyArray(), + emptyArray(), + emptyArray(), + emptyArray(), + ).toList() + } catch (t: Throwable) { + testStatistics.failedOnParsingOrConvertingExpressions = true + testStatistics.exceptionMessage = t.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { t.message + System.lineSeparator() } + throw t + } + + val maxSmtTestIntoPath = samplePath.toString().removeSuffix(".smt2") + ".maxsmt" + val maxSmtTestInfo = parseMaxSMTTestInfo(File(maxSmtTestIntoPath).toPath()) + + val softConstraintsSize = maxSmtTestInfo.softConstraintsWeights.size + + val softExpressions = + expressions.subList( + expressions.lastIndex + 1 - softConstraintsSize, + expressions.lastIndex + 1, + ) + val hardExpressions = + expressions.subList(0, expressions.lastIndex + 1 - softConstraintsSize) + + hardExpressions.forEach { + maxSMTSolver.Assert(it) + } + + var softConstraintsWeightsSum = 0u + + maxSmtTestInfo.softConstraintsWeights + .zip(softExpressions) + .forEach { (weight, expr) -> + maxSMTSolver.AssertSoft(expr, weight.toInt(), "s") + softConstraintsWeightsSum += weight + } + + // Setting parameters (timeout). + // Solver tries to find an optimal solution by default and suboptimal if timeout is set. + val params = z3Ctx.mkParams() + params.add("timeout", 60000) // 1-minute timeout (in ms) + maxSMTSolver.setParameters(params) + + var maxSMTResult: Status? + val elapsedTimeMs: Long + + try { + elapsedTimeMs = measureTimeMillis { + maxSMTResult = maxSMTSolver.Check() + } + } catch (ex: Exception) { + testStatistics.exceptionMessage = ex.message.toString() + jsonHelper.appendTestStatisticsToFile(testStatistics) + logger.error { ex.message + System.lineSeparator() } + throw ex + } + + logger.info { "Elapsed time: $elapsedTimeMs ms --- MaxSMT call${System.lineSeparator()}" } + testStatistics.elapsedTimeMs = elapsedTimeMs + + val actualSatSoftConstraintsWeightsSum = maxSmtTestInfo.softConstraintsWeights + .zip(softExpressions) + .fold(0uL) { acc, expr -> + acc + if (maxSMTSolver.model.eval(expr.second, true).isTrue) expr.first.toULong() else 0uL + } + + if (maxSMTResult == null || maxSMTResult == Status.UNKNOWN) { + // TODO: ... + } + + try { + assertEquals(Status.SATISFIABLE, maxSMTResult, "MaxSMT returned $maxSMTResult status") + assertEquals( + maxSmtTestInfo.satSoftConstraintsWeightsSum, + actualSatSoftConstraintsWeightsSum, + "Soft constraints weights sum was [$actualSatSoftConstraintsWeightsSum], " + + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", + ) + testStatistics.passed = true + } catch (ex: Exception) { + logger.error { ex.message + System.lineSeparator() } + } finally { + jsonHelper.appendTestStatisticsToFile(testStatistics) + } + } + + companion object { + init { + KZ3Solver(KContext()).close() + } + + private lateinit var jsonHelper: JsonStatisticsHelper + + @BeforeAll + @JvmStatic + fun initJsonHelper() { + jsonHelper = + JsonStatisticsHelper( + File( + "${ + Paths.get("").toAbsolutePath() + }/src/test/resources/maxsmt-statistics-${getRandomString(16)}.json", + ), + ) + } + + @AfterAll + @JvmStatic + fun closeJsonHelper() { + jsonHelper.markLastTestStatisticsAsProcessed() + } + } +} From 5a4bb779e66d15de51d84ce518fc7512882af6c5 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 15:22:32 +0300 Subject: [PATCH 203/228] Set an algorithm for Z3 native --- .../io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt index 2f10d6bea..565fbecd2 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt @@ -104,7 +104,10 @@ class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { // Setting parameters (timeout). // Solver tries to find an optimal solution by default and suboptimal if timeout is set. val params = z3Ctx.mkParams() - params.add("timeout", 60000) // 1-minute timeout (in ms) + // 1-minute timeout (in ms) + params.add("timeout", 60000) + // Setting an algorithm. + params.add("maxsat_engine", "pd-maxres") maxSMTSolver.setParameters(params) var maxSMTResult: Status? From ff69d4ca9f65d40a2b42aff93d7f823ae1175160 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 15:59:48 +0300 Subject: [PATCH 204/228] Run Z3 native on CI --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 9 ++++++++- .../test/configurations/Z3NativeSMTBenchmarkTest.kt | 11 +++++++++++ ...nchmarkTest.kt => KZ3NativeMaxSMTBenchmarkTest.kt} | 8 ++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt rename ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/{Z3MaxSMTBenchmarkTest.kt => KZ3NativeMaxSMTBenchmarkTest.kt} (95%) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 224be24d9..aa616bbd4 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -17,6 +17,7 @@ on: - Bitwuzla - Cvc5 - Yices + - Z3Native optimality: type: boolean description: Chosen optimal (otherwise suboptimal) configuration @@ -99,7 +100,13 @@ jobs: - name: Set solver name to output id: set-output - run: echo "solver=K${{ env.SUB_OPT }}${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT + run: | + if [[ "${{ inputs.smtSolver }}" == "Z3Native" ]] + then + echo "solver=Z3Native" + else + echo "solver=K${{ env.SUB_OPT }}${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT + fi run-long-maxsmt-tests: needs: construct-solver-name diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt new file mode 100644 index 000000000..3c26810fc --- /dev/null +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt @@ -0,0 +1,11 @@ +package io.ksmt.solver.maxsmt.test.configurations + +import io.ksmt.solver.KSolverConfiguration +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase +import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest +import io.ksmt.solver.maxsmt.test.utils.Solver +import io.ksmt.solver.maxsmt.test.z3.KZ3NativeMaxSMTBenchmarkTest + +class Z3NativeSMTBenchmarkTest : KZ3NativeMaxSMTBenchmarkTest() \ No newline at end of file diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt similarity index 95% rename from ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt rename to ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index 565fbecd2..60b3c0f06 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/Z3MaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -26,7 +26,7 @@ import kotlin.io.path.extension import kotlin.system.measureTimeMillis import kotlin.test.assertEquals -class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { +abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { private lateinit var z3Ctx: Context private lateinit var maxSMTSolver: Optimize private val logger = KotlinLogging.logger {} @@ -103,11 +103,15 @@ class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { // Setting parameters (timeout). // Solver tries to find an optimal solution by default and suboptimal if timeout is set. + // Cores are non-minimized by default. val params = z3Ctx.mkParams() // 1-minute timeout (in ms) params.add("timeout", 60000) - // Setting an algorithm. + // Choose an algorithm. params.add("maxsat_engine", "pd-maxres") + // Prefer larger cores. + params.add("maxres.hill_climb", true) + params.add("maxres.max_core_size", 3) maxSMTSolver.setParameters(params) var maxSMTResult: Status? From ed462d34168274e141afb0a5ebd57387fe7b54c3 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:09:29 +0300 Subject: [PATCH 205/228] Updare solver enum for Z3_Native support --- .../test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt index 901cacb92..64aefdbf1 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/utils/SolverUtil.kt @@ -1,7 +1,7 @@ package io.ksmt.solver.maxsmt.test.utils enum class Solver { - Z3, BITWUZLA, CVC5, YICES, PORTFOLIO + Z3, BITWUZLA, CVC5, YICES, PORTFOLIO, Z3_NATIVE } enum class MaxSmtSolver { From 8062b7111ed6586e3c8d7213d6b77e78c96a8bb2 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:23:51 +0300 Subject: [PATCH 206/228] Fix compilation --- .../io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 2 ++ .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 2 ++ .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 3 +++ 3 files changed, 7 insertions(+) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index c24072aab..95264c233 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -32,6 +32,8 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { Solver.YICES -> KYicesSolver(this) Solver.PORTFOLIO -> throw NotImplementedError("Portfolio solver for MaxSAT is not supported in tests") + + Solver.Z3_NATIVE -> error("Unexpected solver type: Z3_NATIVE") } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 26f62f824..31bedca30 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -30,6 +30,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3_NATIVE import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager @@ -82,6 +83,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { PORTFOLIO -> { solverManager.createPortfolioSolver(this) } + Z3_NATIVE -> error("Unexpected solver type: Z3_NATIVE") } } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index ffff7132b..779dab4cc 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -30,6 +30,7 @@ import io.ksmt.solver.maxsmt.test.utils.Solver.CVC5 import io.ksmt.solver.maxsmt.test.utils.Solver.PORTFOLIO import io.ksmt.solver.maxsmt.test.utils.Solver.YICES import io.ksmt.solver.maxsmt.test.utils.Solver.Z3 +import io.ksmt.solver.maxsmt.test.utils.Solver.Z3_NATIVE import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager @@ -82,6 +83,8 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { PORTFOLIO -> { solverManager.createPortfolioSolver(this) } + + Z3_NATIVE -> error("Unexpected solver type: Z3_NATIVE") } } From 2855efffbe2b70dce7497ffa83cc214e6e17de90 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:37:08 +0300 Subject: [PATCH 207/228] Update statistics analyzer --- scripts/analyze_maxsmt_statistics.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/scripts/analyze_maxsmt_statistics.py b/scripts/analyze_maxsmt_statistics.py index 5e6d0a923..a7744b81b 100644 --- a/scripts/analyze_maxsmt_statistics.py +++ b/scripts/analyze_maxsmt_statistics.py @@ -319,15 +319,25 @@ def create_all_tests_statistics(logics_statistics): avg_elapsed_failed_tests_time_ms += elapsed_time_statistics.avg_elapsed_failed_tests_time_ms * ( size_statistics.tests_executed_maxsmt_size - size_statistics.tests_executed_maxsmt_passed_size) - avg_score_passed_tests /= tests_executed_maxsmt_passed_size \ - if tests_executed_maxsmt_passed_size != 0 else "No tests" - - avg_elapsed_time_ms /= tests_executed_maxsmt_size \ - if tests_executed_maxsmt_size != 0 else "No tests" - avg_elapsed_passed_tests_time_ms /= tests_executed_maxsmt_passed_size \ - if tests_executed_maxsmt_passed_size != 0 else "No tests" - avg_elapsed_failed_tests_time_ms /= (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) \ - if (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) != 0 else "No tests" + if tests_executed_maxsmt_passed_size != 0: + avg_score_passed_tests /= tests_executed_maxsmt_passed_size + else: + avg_score_passed_tests = "No tests" + + if tests_executed_maxsmt_size != 0: + avg_elapsed_time_ms /= tests_executed_maxsmt_size + else: + avg_elapsed_time_ms = "No tests" + + if tests_executed_maxsmt_passed_size != 0: + avg_elapsed_passed_tests_time_ms /= tests_executed_maxsmt_passed_size + else: + avg_elapsed_passed_tests_time_ms = "No tests" + + if (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) != 0: + avg_elapsed_failed_tests_time_ms /= (tests_executed_maxsmt_size - tests_executed_maxsmt_passed_size) + else: + avg_elapsed_failed_tests_time_ms = "No tests" return AllTestsStatistics(timeout_ms, max_smt_ctx, TestsSizeStatistics(tests_size, tests_executed_maxsmt_size, From 29d283ecca3807f3d524c1285accc2433777e286 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:39:59 +0300 Subject: [PATCH 208/228] Fix compilation --- .../maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt | 6 ------ .../solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt index 3c26810fc..bd3c5010c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/configurations/Z3NativeSMTBenchmarkTest.kt @@ -1,11 +1,5 @@ package io.ksmt.solver.maxsmt.test.configurations -import io.ksmt.solver.KSolverConfiguration -import io.ksmt.solver.maxsmt.KMaxSMTContext -import io.ksmt.solver.maxsmt.solvers.KMaxSMTSolverBase -import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver -import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest -import io.ksmt.solver.maxsmt.test.utils.Solver import io.ksmt.solver.maxsmt.test.z3.KZ3NativeMaxSMTBenchmarkTest class Z3NativeSMTBenchmarkTest : KZ3NativeMaxSMTBenchmarkTest() \ No newline at end of file diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index 60b3c0f06..fc8b0e880 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -26,7 +26,7 @@ import kotlin.io.path.extension import kotlin.system.measureTimeMillis import kotlin.test.assertEquals -abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { +open class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { private lateinit var z3Ctx: Context private lateinit var maxSMTSolver: Optimize private val logger = KotlinLogging.logger {} From d427dc33e5b1a422bb99827bbb9d10897d861a99 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:41:39 +0300 Subject: [PATCH 209/228] Update default value in workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index aa616bbd4..ee662b48b 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -32,7 +32,7 @@ on: type: boolean description: Core minimization option (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: false + default: true preferLargeWeightCores: type: boolean description: Prefer large weight constraints for cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) From cc1441b43d7e75f40e0a71033ec78cf186646324 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:42:06 +0300 Subject: [PATCH 210/228] Update default value in workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index ee662b48b..2866470f6 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -32,12 +32,12 @@ on: type: boolean description: Core minimization option (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: true + default: false preferLargeWeightCores: type: boolean description: Prefer large weight constraints for cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) required: true - default: false + default: true getMultipleCores: type: boolean description: Get multiple cores (only one of [minimizeCores, preferLargeWeightCores, getMultipleCores] can be true) From 7788f68bf3b7998e5af483c9b15ced544c216a56 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 16:47:35 +0300 Subject: [PATCH 211/228] Fix setting solver in workflow --- .github/workflows/run-long-maxsmt-tests-pdmres.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-long-maxsmt-tests-pdmres.yml b/.github/workflows/run-long-maxsmt-tests-pdmres.yml index 2866470f6..dd273ca23 100644 --- a/.github/workflows/run-long-maxsmt-tests-pdmres.yml +++ b/.github/workflows/run-long-maxsmt-tests-pdmres.yml @@ -103,7 +103,7 @@ jobs: run: | if [[ "${{ inputs.smtSolver }}" == "Z3Native" ]] then - echo "solver=Z3Native" + echo "solver=Z3Native" >> $GITHUB_OUTPUT else echo "solver=K${{ env.SUB_OPT }}${{ inputs.strategy }}MaxRes${{ env.OPTION_TO_VALUE }}" >> $GITHUB_OUTPUT fi From 0d55a166b3d357fea8c3978a8479f36cf72447a7 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 18:00:18 +0300 Subject: [PATCH 212/228] Add MaxSMTCallStatistics to Z3Native --- .../test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index fc8b0e880..f27615890 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -6,6 +6,8 @@ import com.microsoft.z3.Optimize import com.microsoft.z3.Status import io.github.oshai.kotlinlogging.KotlinLogging import io.ksmt.KContext +import io.ksmt.solver.maxsmt.KMaxSMTContext +import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper @@ -26,7 +28,7 @@ import kotlin.io.path.extension import kotlin.system.measureTimeMillis import kotlin.test.assertEquals -open class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { +abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { private lateinit var z3Ctx: Context private lateinit var maxSMTSolver: Optimize private val logger = KotlinLogging.logger {} @@ -106,7 +108,8 @@ open class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { // Cores are non-minimized by default. val params = z3Ctx.mkParams() // 1-minute timeout (in ms) - params.add("timeout", 60000) + val timeoutMs = 60000 + params.add("timeout", timeoutMs) // Choose an algorithm. params.add("maxsat_engine", "pd-maxres") // Prefer larger cores. @@ -114,6 +117,17 @@ open class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { params.add("maxres.max_core_size", 3) maxSMTSolver.setParameters(params) + val maxSmtCallStatistics = KMaxSMTStatistics( + KMaxSMTContext( + KMaxSMTContext.Strategy.PrimalDualMaxRes, + preferLargeWeightConstraintsForCores = true + ) + ).apply { + this.timeoutMs = timeoutMs.toLong() + } + + testStatistics.maxSMTCallStatistics = maxSmtCallStatistics + var maxSMTResult: Status? val elapsedTimeMs: Long From 5d7cddbad27ad52e2a5fae345d54a0d32fbaa5da Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 20 May 2024 21:36:44 +0300 Subject: [PATCH 213/228] Add to test stat timeoutExceededOrUnknown option --- .../io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 1 + .../solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt | 4 ++++ .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 1 + .../solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 1 + 4 files changed, 7 insertions(+) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 31bedca30..9705f3338 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -206,6 +206,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { testStatistics.elapsedTimeMs = elapsedTime testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() + testStatistics.timeoutExceededOrUnknown = maxSMTResult.timeoutExceededOrUnknown logger.info { "Elapsed time: $elapsedTime ms --- MaxSMT call${System.lineSeparator()}" } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index 9a009546d..d2ffc5725 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -16,4 +16,8 @@ internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver var checkedSoftConstraintsSumIsWrong = false var optimalWeight: ULong = 0U var foundSoFarWeight: ULong = 0U + /** + * Shows whether timeout has been exceeded, solver was terminated or returned UNKNOWN. + */ + var timeoutExceededOrUnknown: Boolean = true } diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 779dab4cc..f8e605950 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -209,6 +209,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } } testStatistics.elapsedTimeMs = elapsedTime + testStatistics.timeoutExceededOrUnknown = maxSMTResult.timeoutExceededOrUnknown val foundSoFarWeight = maxSMTResult.satSoftConstraints.sumOf { it.weight.toULong() } testStatistics.foundSoFarWeight = foundSoFarWeight diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index f27615890..25fe5576f 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -144,6 +144,7 @@ abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Elapsed time: $elapsedTimeMs ms --- MaxSMT call${System.lineSeparator()}" } testStatistics.elapsedTimeMs = elapsedTimeMs + testStatistics.timeoutExceededOrUnknown = (maxSMTResult == Status.UNKNOWN) val actualSatSoftConstraintsWeightsSum = maxSmtTestInfo.softConstraintsWeights .zip(softExpressions) From 8dd2dc59d5b2a4d63027fdc258c8e53a64521a9e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 23 May 2024 16:04:12 +0300 Subject: [PATCH 214/228] Wrap Yices into SymFpu in tests --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 40 ++++++++++++------- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 39 +++++++++++------- .../ksmt/solver/portfolio/KPortfolioSolver.kt | 6 +-- .../portfolio/KPortfolioSolverManager.kt | 5 ++- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index 9705f3338..cdf83e0c7 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -35,8 +35,12 @@ import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.yices.KYicesSolverConfiguration +import io.ksmt.solver.yices.KYicesSolverConfigurationImpl +import io.ksmt.solver.yices.KYicesSolverUniversalConfiguration import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort +import io.ksmt.symfpu.solver.KSymFpuSolver import io.ksmt.test.TestRunner import io.ksmt.test.TestWorker import io.ksmt.test.TestWorkerProcess @@ -79,10 +83,11 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { Z3 -> KZ3Solver(this) BITWUZLA -> KBitwuzlaSolver(this) CVC5 -> KCvc5Solver(this) - YICES -> KYicesSolver(this) + YICES -> KSymFpuSolver(KYicesSolver(this), this) PORTFOLIO -> { solverManager.createPortfolioSolver(this) } + Z3_NATIVE -> error("Unexpected solver type: Z3_NATIVE") } } @@ -107,37 +112,36 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTZ3Test(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> assertions }, Z3) + testMaxSMTSolver(name, samplePath, Z3) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + testMaxSMTSolver(name, samplePath, BITWUZLA) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTCvc5Test(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + testMaxSMTSolver(name, samplePath, CVC5) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTYicesTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + testMaxSMTSolver(name, samplePath, YICES) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTPortfolioTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> assertions }, PORTFOLIO) + testMaxSMTSolver(name, samplePath, PORTFOLIO) } private fun testMaxSMTSolver( name: String, samplePath: Path, - mkKsmtAssertions: suspend TestRunner.(List>) -> List>, solver: Solver = Z3, ) { initSolver(solver) @@ -149,14 +153,13 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Test name: [$name]" } - lateinit var ksmtAssertions: List> + lateinit var convertedAssertions: List> val testStatistics = MaxSMTTestStatistics(name, solver) try { testWorkers.withWorker(ctx) { worker -> val assertions = worker.parseFile(samplePath) - val convertedAssertions = worker.convertAssertions(assertions) - ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + convertedAssertions = worker.convertAssertions(assertions) } } catch (ex: IgnoreTestException) { testStatistics.ignoredTest = true @@ -178,8 +181,8 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val softConstraintsSize = maxSmtTestInfo.softConstraintsWeights.size val softExpressions = - ksmtAssertions.subList(ksmtAssertions.size - softConstraintsSize, ksmtAssertions.size) - val hardExpressions = ksmtAssertions.subList(0, ksmtAssertions.size - softConstraintsSize) + convertedAssertions.subList(convertedAssertions.size - softConstraintsSize, convertedAssertions.size) + val hardExpressions = convertedAssertions.subList(0, convertedAssertions.size - softConstraintsSize) hardExpressions.forEach { maxSMTSolver.assert(it) @@ -274,7 +277,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { class IgnoreTestException(message: String?) : Exception(message) companion object { - val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 25.seconds + val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 45.seconds internal lateinit var testWorkers: KsmtWorkerPool private lateinit var jsonHelper: JsonStatisticsHelper @@ -303,11 +306,20 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @BeforeAll @JvmStatic fun initSolverManager() { + class SymFpuYicesSolver(ctx: KContext) : KSymFpuSolver(KYicesSolver(ctx), ctx) solverManager = KPortfolioSolverManager( listOf( - KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class + KZ3Solver::class, KBitwuzlaSolver::class, SymFpuYicesSolver::class ) ) + solverManager.registerSolver( + SymFpuYicesSolver::class, + KYicesSolverUniversalConfiguration::class + ) + solverManager.createSolver( + KContext(), KMaxSMTContext(preferLargeWeightConstraintsForCores = false), + SymFpuYicesSolver::class + ) } @AfterAll diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index f8e605950..9a5489533 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -21,6 +21,7 @@ import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo +import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -35,8 +36,11 @@ import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver +import io.ksmt.solver.yices.KYicesSolverConfiguration +import io.ksmt.solver.yices.KYicesSolverUniversalConfiguration import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort +import io.ksmt.symfpu.solver.KSymFpuSolver import io.ksmt.test.TestRunner import io.ksmt.test.TestWorker import io.ksmt.test.TestWorkerProcess @@ -79,7 +83,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { Z3 -> KZ3Solver(this) BITWUZLA -> KBitwuzlaSolver(this) CVC5 -> KCvc5Solver(this) - YICES -> KYicesSolver(this) + YICES -> KSymFpuSolver(KYicesSolver(this), this) PORTFOLIO -> { solverManager.createPortfolioSolver(this) } @@ -109,37 +113,36 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTZ3Test(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> assertions }, Z3) + testMaxSMTSolver(name, samplePath, Z3) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTBitwuzlaTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertBitwuzla(assertions) }, BITWUZLA) + testMaxSMTSolver(name, samplePath, BITWUZLA) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTCvc5Test(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertCvc5(assertions) }, CVC5) + testMaxSMTSolver(name, samplePath, CVC5) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTYicesTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> internalizeAndConvertYices(assertions) }, YICES) + testMaxSMTSolver(name, samplePath, YICES) } @ParameterizedTest(name = "{0}") @MethodSource("maxSMTTestData") fun maxSMTPortfolioTest(name: String, samplePath: Path) { - testMaxSMTSolver(name, samplePath, { assertions -> assertions }, PORTFOLIO) + testMaxSMTSolver(name, samplePath, PORTFOLIO) } private fun testMaxSMTSolver( name: String, samplePath: Path, - mkKsmtAssertions: suspend TestRunner.(List>) -> List>, solver: Solver = Z3, ) { initSolver(solver) @@ -151,14 +154,13 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Test name: [$name]" } - lateinit var ksmtAssertions: List> + lateinit var convertedAssertions: List> val testStatistics = MaxSMTTestStatistics(name, solver) try { testWorkers.withWorker(ctx) { worker -> val assertions = worker.parseFile(samplePath) - val convertedAssertions = worker.convertAssertions(assertions) - ksmtAssertions = worker.mkKsmtAssertions(convertedAssertions) + convertedAssertions = worker.convertAssertions(assertions) } } catch (ex: IgnoreTestException) { testStatistics.ignoredTest = true @@ -183,8 +185,8 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { val softConstraintsSize = maxSmtTestInfo.softConstraintsWeights.size val softExpressions = - ksmtAssertions.subList(ksmtAssertions.size - softConstraintsSize, ksmtAssertions.size) - val hardExpressions = ksmtAssertions.subList(0, ksmtAssertions.size - softConstraintsSize) + convertedAssertions.subList(convertedAssertions.size - softConstraintsSize, convertedAssertions.size) + val hardExpressions = convertedAssertions.subList(0, convertedAssertions.size - softConstraintsSize) hardExpressions.forEach { maxSMTSolver.assert(it) @@ -278,7 +280,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { class IgnoreTestException(message: String?) : Exception(message) companion object { - val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 25.seconds + val TEST_WORKER_SINGLE_OPERATION_TIMEOUT = 45.seconds internal lateinit var testWorkers: KsmtWorkerPool private lateinit var jsonHelper: JsonStatisticsHelper @@ -307,11 +309,20 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @BeforeAll @JvmStatic fun initSolverManager() { + class SymFpuYicesSolver(ctx: KContext) : KSymFpuSolver(KYicesSolver(ctx), ctx) solverManager = KPortfolioSolverManager( listOf( - KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class, KCvc5Solver::class + KZ3Solver::class, KBitwuzlaSolver::class, SymFpuYicesSolver::class ), 2 ) + solverManager.registerSolver( + SymFpuYicesSolver::class, + KYicesSolverUniversalConfiguration::class + ) + solverManager.createSolver( + KContext(), KMaxSMTContext(preferLargeWeightConstraintsForCores = false), + SymFpuYicesSolver::class + ) } @AfterAll diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index c5d55f998..1ff532caf 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -140,14 +140,14 @@ class KPortfolioSolver( } override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - solverMaxSmtQuery( + solverMaxSmtQueryAsync( { checkMaxSMT(timeout, collectStatistics) }, { !this.timeoutExceededOrUnknown } ) } override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - solverMaxSmtQuery( + solverMaxSmtQueryAsync( { checkSubOptMaxSMT(timeout, collectStatistics) }, solverPredicate = { !this.timeoutExceededOrUnknown }, onFailure = { results -> @@ -288,7 +288,7 @@ class KPortfolioSolver( return result } - private suspend inline fun solverMaxSmtQuery( + private suspend inline fun solverMaxSmtQueryAsync( crossinline block: suspend KSolverRunner<*>.() -> T, crossinline solverPredicate: T.() -> Boolean, crossinline onFailure: (SolverAwaitFailure) -> SolverOperationResult = { it.findSuccessOrThrow() } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt index f2ec7b064..db099264d 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolverManager.kt @@ -24,7 +24,10 @@ open class KPortfolioSolverManager( require(solvers.isNotEmpty()) { "Empty solver portfolio" } } - fun createPortfolioSolver(ctx: KContext, maxsmtCtx: KMaxSMTContext = KMaxSMTContext()): KPortfolioSolver { + fun createPortfolioSolver( + ctx: KContext, + maxsmtCtx: KMaxSMTContext = KMaxSMTContext(preferLargeWeightConstraintsForCores = true) + ): KPortfolioSolver { val solverInstances = solvers.map { val solverType: KClass> = it.uncheckedCast() it to createSolver(ctx, maxsmtCtx, solverType) From b85b2f38fb25c34b24e411919d004cf7ec67be8f Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 23 May 2024 19:57:40 +0300 Subject: [PATCH 215/228] Add symfpu dependency to tests --- ksmt-maxsmt-test/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index 500cdeb7b..d17406ffd 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { testImplementation(project(":ksmt-bitwuzla")) testImplementation(project(":ksmt-cvc5")) testImplementation(project(":ksmt-yices")) + testImplementation(project(":ksmt-symfpu")) testImplementation(project(":ksmt-test")) testImplementation(project(":ksmt-runner")) @@ -62,7 +63,6 @@ val runMaxSatBenchmarkTests = project.booleanProperty("runMaxSatBenchmarkTests") // Use benchmarks from maxSmtBenchmark directory (test resources) instead of downloading val usePreparedBenchmarks = project.booleanProperty("usePreparedBenchmarks") ?: true -val testDataRevision = project.stringProperty("testDataRevision") ?: "no-revision" val downloadPreparedMaxSmtBenchmarkTestData = maxSmtBenchmarks.map { maxSmtBenchmarkTestData(it, testDataRevision) } From 657a4e7710952c874d4160fa72fa774848f16cfd Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 23 May 2024 20:06:25 +0300 Subject: [PATCH 216/228] Hardcode test data revision --- ksmt-maxsmt-test/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/ksmt-maxsmt-test/build.gradle.kts b/ksmt-maxsmt-test/build.gradle.kts index d17406ffd..728218844 100644 --- a/ksmt-maxsmt-test/build.gradle.kts +++ b/ksmt-maxsmt-test/build.gradle.kts @@ -63,6 +63,7 @@ val runMaxSatBenchmarkTests = project.booleanProperty("runMaxSatBenchmarkTests") // Use benchmarks from maxSmtBenchmark directory (test resources) instead of downloading val usePreparedBenchmarks = project.booleanProperty("usePreparedBenchmarks") ?: true +val testDataRevision = project.stringProperty("testDataRevision") ?: "0.0.0" val downloadPreparedMaxSmtBenchmarkTestData = maxSmtBenchmarks.map { maxSmtBenchmarkTestData(it, testDataRevision) } From d1fcfb5b8e0294e0b41f5541a1f73133debbce6b Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Thu, 23 May 2024 21:26:49 +0300 Subject: [PATCH 217/228] Let Yices-SymFpu run on FP --- .github/workflows/run-long-maxsmt-tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 7e349f31c..91045783b 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -47,9 +47,6 @@ jobs: if ${{ inputs.smtSolver == 'Bitwuzla' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("LIA") or contains("LRA") | not) ] }')) - elif ${{ inputs.smtSolver == 'Yices' }} - then - MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("FP") | not) ] }')) else MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] ] }')) fi From 1e515453d6de3efe6ed6602e13235bc707052823 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 27 May 2024 13:41:49 +0300 Subject: [PATCH 218/228] Update benchmark tests solver list --- .../maxsmt/test/smt/KMaxSMTBenchmarkTest.kt | 17 ++--------------- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 17 ++--------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt index cdf83e0c7..b658a1576 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/smt/KMaxSMTBenchmarkTest.kt @@ -35,12 +35,8 @@ import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.yices.KYicesSolverConfiguration -import io.ksmt.solver.yices.KYicesSolverConfigurationImpl -import io.ksmt.solver.yices.KYicesSolverUniversalConfiguration import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort -import io.ksmt.symfpu.solver.KSymFpuSolver import io.ksmt.test.TestRunner import io.ksmt.test.TestWorker import io.ksmt.test.TestWorkerProcess @@ -83,7 +79,7 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { Z3 -> KZ3Solver(this) BITWUZLA -> KBitwuzlaSolver(this) CVC5 -> KCvc5Solver(this) - YICES -> KSymFpuSolver(KYicesSolver(this), this) + YICES -> KYicesSolver(this) PORTFOLIO -> { solverManager.createPortfolioSolver(this) } @@ -306,20 +302,11 @@ abstract class KMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @BeforeAll @JvmStatic fun initSolverManager() { - class SymFpuYicesSolver(ctx: KContext) : KSymFpuSolver(KYicesSolver(ctx), ctx) solverManager = KPortfolioSolverManager( listOf( - KZ3Solver::class, KBitwuzlaSolver::class, SymFpuYicesSolver::class + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class ) ) - solverManager.registerSolver( - SymFpuYicesSolver::class, - KYicesSolverUniversalConfiguration::class - ) - solverManager.createSolver( - KContext(), KMaxSMTContext(preferLargeWeightConstraintsForCores = false), - SymFpuYicesSolver::class - ) } @AfterAll diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 9a5489533..7ade506d6 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -21,7 +21,6 @@ import io.ksmt.solver.maxsmt.solvers.KPMResSolver import io.ksmt.solver.maxsmt.solvers.KPrimalDualMaxResSolver import io.ksmt.solver.maxsmt.test.KMaxSMTBenchmarkBasedTest import io.ksmt.solver.maxsmt.test.parseMaxSMTTestInfo -import io.ksmt.solver.maxsmt.test.smt.KMaxSMTBenchmarkTest import io.ksmt.solver.maxsmt.test.statistics.JsonStatisticsHelper import io.ksmt.solver.maxsmt.test.statistics.MaxSMTTestStatistics import io.ksmt.solver.maxsmt.test.utils.MaxSmtSolver @@ -36,11 +35,8 @@ import io.ksmt.solver.maxsmt.test.utils.getRandomString import io.ksmt.solver.portfolio.KPortfolioSolver import io.ksmt.solver.portfolio.KPortfolioSolverManager import io.ksmt.solver.yices.KYicesSolver -import io.ksmt.solver.yices.KYicesSolverConfiguration -import io.ksmt.solver.yices.KYicesSolverUniversalConfiguration import io.ksmt.solver.z3.KZ3Solver import io.ksmt.sort.KBoolSort -import io.ksmt.symfpu.solver.KSymFpuSolver import io.ksmt.test.TestRunner import io.ksmt.test.TestWorker import io.ksmt.test.TestWorkerProcess @@ -83,7 +79,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { Z3 -> KZ3Solver(this) BITWUZLA -> KBitwuzlaSolver(this) CVC5 -> KCvc5Solver(this) - YICES -> KSymFpuSolver(KYicesSolver(this), this) + YICES -> KYicesSolver(this) PORTFOLIO -> { solverManager.createPortfolioSolver(this) } @@ -309,20 +305,11 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { @BeforeAll @JvmStatic fun initSolverManager() { - class SymFpuYicesSolver(ctx: KContext) : KSymFpuSolver(KYicesSolver(ctx), ctx) solverManager = KPortfolioSolverManager( listOf( - KZ3Solver::class, KBitwuzlaSolver::class, SymFpuYicesSolver::class + KZ3Solver::class, KBitwuzlaSolver::class, KYicesSolver::class ), 2 ) - solverManager.registerSolver( - SymFpuYicesSolver::class, - KYicesSolverUniversalConfiguration::class - ) - solverManager.createSolver( - KContext(), KMaxSMTContext(preferLargeWeightConstraintsForCores = false), - SymFpuYicesSolver::class - ) } @AfterAll From bca994a37547077b944a2d22dd93f29375358b3d Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Mon, 27 May 2024 14:26:44 +0300 Subject: [PATCH 219/228] Run Yices on corresponding theories --- .github/workflows/run-long-maxsmt-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-long-maxsmt-tests.yml b/.github/workflows/run-long-maxsmt-tests.yml index 91045783b..7e349f31c 100644 --- a/.github/workflows/run-long-maxsmt-tests.yml +++ b/.github/workflows/run-long-maxsmt-tests.yml @@ -47,6 +47,9 @@ jobs: if ${{ inputs.smtSolver == 'Bitwuzla' }} then MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("LIA") or contains("LRA") | not) ] }')) + elif ${{ inputs.smtSolver == 'Yices' }} + then + MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] | select(.NAME | contains("FP") | not) ] }')) else MAXSMT_TESTS=$(echo $(cat .github/workflows/maxsmt-tests-matrix.json | jq '{ "logics": [ .[] ] }')) fi From 8daaae1900f7b4442c4fe20a526edc6702d2f756 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Tue, 28 May 2024 17:13:44 +0300 Subject: [PATCH 220/228] Set up suboptimal mode for Z3 native --- .../maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index 25fe5576f..2d8f05d5c 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -103,12 +103,11 @@ abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { softConstraintsWeightsSum += weight } - // Setting parameters (timeout). // Solver tries to find an optimal solution by default and suboptimal if timeout is set. // Cores are non-minimized by default. val params = z3Ctx.mkParams() - // 1-minute timeout (in ms) - val timeoutMs = 60000 + // Setting parameters (timeout). + val timeoutMs = 10000 params.add("timeout", timeoutMs) // Choose an algorithm. params.add("maxsat_engine", "pd-maxres") @@ -157,14 +156,17 @@ abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { } try { - assertEquals(Status.SATISFIABLE, maxSMTResult, "MaxSMT returned $maxSMTResult status") + testStatistics.passed = true + testStatistics.optimalWeight = maxSmtTestInfo.satSoftConstraintsWeightsSum + testStatistics.foundSoFarWeight = actualSatSoftConstraintsWeightsSum + + // assertEquals(Status.SATISFIABLE, maxSMTResult, "MaxSMT returned $maxSMTResult status") assertEquals( maxSmtTestInfo.satSoftConstraintsWeightsSum, actualSatSoftConstraintsWeightsSum, "Soft constraints weights sum was [$actualSatSoftConstraintsWeightsSum], " + "but must be [${maxSmtTestInfo.satSoftConstraintsWeightsSum}]", ) - testStatistics.passed = true } catch (ex: Exception) { logger.error { ex.message + System.lineSeparator() } } finally { From 2dbee6efee69047599f6b1ab01c1c7f5543e475e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 29 May 2024 15:54:18 +0300 Subject: [PATCH 221/228] Set timeout to 1s --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- .../ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 7ade506d6..b1d5a82ee 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -197,7 +197,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(10.seconds, true) + maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(1.seconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index 2d8f05d5c..c094bfbcf 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -107,7 +107,7 @@ abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { // Cores are non-minimized by default. val params = z3Ctx.mkParams() // Setting parameters (timeout). - val timeoutMs = 10000 + val timeoutMs = 1000 params.add("timeout", timeoutMs) // Choose an algorithm. params.add("maxsat_engine", "pd-maxres") From 3d8e9de56726229f9842f5727bacecbbac4b538c Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Wed, 29 May 2024 17:20:35 +0300 Subject: [PATCH 222/228] Downgrade timeout to 500 ms --- .../solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 3 ++- .../ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index b1d5a82ee..cbc55d429 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -54,6 +54,7 @@ import kotlin.io.path.extension import kotlin.system.measureTimeMillis import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -197,7 +198,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(1.seconds, true) + maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(500.milliseconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt index c094bfbcf..b3a62829b 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/z3/KZ3NativeMaxSMTBenchmarkTest.kt @@ -107,7 +107,7 @@ abstract class KZ3NativeMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { // Cores are non-minimized by default. val params = z3Ctx.mkParams() // Setting parameters (timeout). - val timeoutMs = 1000 + val timeoutMs = 500 params.add("timeout", timeoutMs) // Choose an algorithm. params.add("maxsat_engine", "pd-maxres") From 1c1f60ac07b3a934292d9ed117bd55b5e13ff883 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 14 Jun 2024 22:25:32 +0300 Subject: [PATCH 223/228] Remove useless NotImplementedException --- .../maxsmt/solvers/exceptions/NotImplementedException.kt | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt deleted file mode 100644 index c6724b962..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/exceptions/NotImplementedException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.ksmt.solver.maxsmt.solvers.exceptions - -internal class NotYetImplementedException : RuntimeException { - constructor() : super() - constructor(message: String) : super(message) -} From 1576cf4d41429312d64a43d7d4cf1b322bccfb3a Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 14 Jun 2024 22:57:33 +0300 Subject: [PATCH 224/228] Remove useless `maxSMTSuceeded` from `KMaxSMTResult` --- .../maxsmt/test/sat/KMaxSATBenchmarkTest.kt | 5 +++- .../test/statistics/MaxSMTTestStatistics.kt | 3 +- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 1 - .../io/ksmt/solver/maxsmt/KMaxSMTResult.kt | 9 ++---- .../solver/maxsmt/solvers/KPMResSolver.kt | 8 ++---- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 17 +++-------- .../ksmt/solver/maxsmt/KMaxSMTSolverTest.kt | 28 +++++++++---------- 7 files changed, 28 insertions(+), 43 deletions(-) diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt index 95264c233..32bc88226 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/sat/KMaxSATBenchmarkTest.kt @@ -98,7 +98,10 @@ abstract class KMaxSATBenchmarkTest : KMaxSMTBenchmarkBasedTest { sumOfSoftConstraintsWeights - maxSATTestNameToExpectedResult.first { it.first == samplePath.name }.second assertEquals(SAT, maxSATResult.hardConstraintsSatStatus, "Hard constraints must be SAT") - assertTrue(maxSATResult.maxSMTSucceeded, "MaxSAT was not successful [$name]") + assertTrue( + !maxSATResult.timeoutExceededOrUnknown, "Timeout exceeded, solver returned UNKNOWN" + + "or something else happened [$name]" + ) assertTrue(maxSATResult.satSoftConstraints.isNotEmpty(), "Soft constraints size should not be 0") assertEquals( expectedSatConstraintsScore, diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt index d2ffc5725..2b6711f71 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/statistics/MaxSMTTestStatistics.kt @@ -11,7 +11,8 @@ internal data class MaxSMTTestStatistics(val name: String, var smtSolver: Solver var exceptionMessage: String? = null var elapsedTimeMs: Long = 0 /** - * It's wrong when it's more than optimal in case of SubOpt. + * It's false when a sum is more than optimal in case of SubOpt + * or is different from expected in case of Opt. */ var checkedSoftConstraintsSumIsWrong = false var optimalWeight: ULong = 0U diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index cbc55d429..91af65f9a 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -218,7 +218,6 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { logger.info { "Elapsed time: $elapsedTime ms --- SubOpt MaxSMT call${System.lineSeparator()}" } try { - assertTrue(maxSMTResult.maxSMTSucceeded, "SubOpt MaxSMT was not successful [$name]") assertEquals(SAT, maxSMTResult.hardConstraintsSatStatus, "Hard constraints must be SAT") if (foundSoFarWeight > optimalWeight) { diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt index 1e97dba97..52d09f3d6 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/KMaxSMTResult.kt @@ -5,8 +5,7 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint /** * @property satSoftConstraints - * - MaxSMT has succeeded -> contains soft constraints from MaxSMT solution. - * - MaxSMT has not succeeded -> contains soft constraints algorithm considered as satisfiable (suboptimal solution). + * Contains soft constraints algorithm considered as the best solution found by the moment. * * @property hardConstraintsSatStatus * Shows satisfiability status of hardly asserted constraints' conjunction. @@ -15,15 +14,11 @@ import io.ksmt.solver.maxsmt.constraints.SoftConstraint * Shows whether timeout has exceeded, solver was interrupted or returned UNKNOWN (can happen when timeout has exceeded * or by some other reason). * - * @property maxSMTSucceeded - * Shows whether MaxSMT calculation has succeeded or not. - * * It may end without success in case of exceeding the timeout or in case solver started returning UNKNOWN during * MaxSAT calculation. */ class KMaxSMTResult( val satSoftConstraints: List, val hardConstraintsSatStatus: KSolverStatus, - val timeoutExceededOrUnknown: Boolean, - val maxSMTSucceeded: Boolean + val timeoutExceededOrUnknown: Boolean ) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 9459bda8c..905298933 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -72,7 +72,6 @@ class KPMResSolver(private val ctx: KContext, solver: listOf(), hardConstraintsStatus, hardConstraintsStatus == UNKNOWN, - hardConstraintsStatus != UNKNOWN, ) } @@ -80,13 +79,11 @@ class KPMResSolver(private val ctx: KContext, solver: return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = false, - maxSMTSucceeded = true ) } else if (hardConstraintsStatus == UNKNOWN) { return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = true, - maxSMTSucceeded = false ) } @@ -103,7 +100,6 @@ class KPMResSolver(private val ctx: KContext, solver: return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = true, - maxSMTSucceeded = false ) } @@ -121,7 +117,7 @@ class KPMResSolver(private val ctx: KContext, solver: return processSat(model!!) } else if (solverStatus == UNKNOWN) { pop() - return KMaxSMTResult(emptyList(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) + return KMaxSMTResult(emptyList(), SAT, timeoutExceededOrUnknown = true) } val (weight, splitUnsatCore) = splitUnsatCore(formula, unsatCore) @@ -225,7 +221,7 @@ class KPMResSolver(private val ctx: KContext, solver: private fun processSat(model: KModel): KMaxSMTResult { val satSoftConstraints = getSatSoftConstraintsByModel(model) - return KMaxSMTResult(satSoftConstraints, SAT, timeoutExceededOrUnknown = false, maxSMTSucceeded = true) + return KMaxSMTResult(satSoftConstraints, SAT, timeoutExceededOrUnknown = false) } /** diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index b0b243825..8c3e9b675 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -73,7 +73,6 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = false, - maxSMTSucceeded = true ) } else if (hardConstraintsStatus == UNKNOWN) { if (collectStatistics) { @@ -82,7 +81,6 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = true, - maxSMTSucceeded = false ) } @@ -189,7 +187,6 @@ class KPrimalDualMaxResSolver( val result = KMaxSMTResult( if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, timeoutExceededOrUnknown = false, - maxSMTSucceeded = true ) logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" @@ -232,7 +229,6 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = false, - maxSMTSucceeded = true ) } else if (hardConstraintsStatus == UNKNOWN) { if (collectStatistics) { @@ -241,7 +237,6 @@ class KPrimalDualMaxResSolver( return KMaxSMTResult( listOf(), hardConstraintsStatus, timeoutExceededOrUnknown = true, - maxSMTSucceeded = false ) } @@ -261,7 +256,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) } val status = checkSatHillClimb(assumptions, timeout) @@ -292,7 +287,7 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) } val processUnsatStatus = processUnsat(assumptions, remainingTime) @@ -303,13 +298,13 @@ class KPrimalDualMaxResSolver( if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) } else if (processUnsatStatus == UNKNOWN) { pop() if (collectStatistics) { maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) } } @@ -319,7 +314,6 @@ class KPrimalDualMaxResSolver( maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } // Solver returned UNKNOWN case. - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true, maxSMTSucceeded = false) } } @@ -331,7 +325,6 @@ class KPrimalDualMaxResSolver( val result = KMaxSMTResult( if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, timeoutExceededOrUnknown = false, - maxSMTSucceeded = true ) logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" @@ -757,13 +750,11 @@ class KPrimalDualMaxResSolver( } private fun getSubOptMaxSMTResult( - maxSMTSucceeded: Boolean, timeoutExceededOrInterrupted: Boolean = true ): KMaxSMTResult = KMaxSMTResult( if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, timeoutExceededOrInterrupted, - maxSMTSucceeded ) } diff --git a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt index a9a523a65..d80bf1b25 100644 --- a/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt +++ b/ksmt-maxsmt/src/test/kotlin/io/ksmt/solver/maxsmt/KMaxSMTSolverTest.kt @@ -37,7 +37,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == UNSAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) } @@ -55,7 +55,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == UNSAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) } @@ -71,7 +71,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) } @@ -89,7 +89,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.isEmpty()) } @@ -107,7 +107,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 1) assertSoftConstraintsSat(listOf(SoftConstraint(!a or !b, 3u)), maxSMTResult.satSoftConstraints) } @@ -132,7 +132,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 2) assertSoftConstraintsSat( listOf(SoftConstraint(!a or !b, 3u), SoftConstraint(!c or !d, 3u)), @@ -159,7 +159,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 6) assertEquals(42u, maxSMTResult.satSoftConstraints.sumOf { it.weight }) } @@ -178,7 +178,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 2) val softConstraintsToAssertSAT = listOf(SoftConstraint(!a or b, 4u), SoftConstraint(a or !b, 6u)) @@ -200,7 +200,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 3) assertSoftConstraintsSat( listOf( @@ -227,7 +227,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 2) assertSoftConstraintsSat( listOf(SoftConstraint(!x or y, 6u), SoftConstraint(x or !y, 6u)), @@ -249,7 +249,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 1) assertSoftConstraintsSat(listOf(SoftConstraint(!a and !b, 5u)), maxSMTResult.satSoftConstraints) } @@ -310,7 +310,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResult.satSoftConstraints.size == 2) assertSoftConstraintsSat( listOf(SoftConstraint(!a3, 5u), SoftConstraint(!a1, 10u)), @@ -329,7 +329,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResult = maxSMTSolver.checkMaxSMT() - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertSoftConstraintsSat( listOf(SoftConstraint(y, 783u), SoftConstraint(!x and !y or !x or !y, 859u)), maxSMTResult.satSoftConstraints, @@ -356,7 +356,7 @@ abstract class KMaxSMTSolverTest { val maxSMTResultScoped = maxSMTSolver.checkMaxSMT() assertTrue(maxSMTResult.hardConstraintsSatStatus == SAT) - assertTrue(maxSMTResult.maxSMTSucceeded) + assertTrue(!maxSMTResult.timeoutExceededOrUnknown) assertTrue(maxSMTResultScoped.satSoftConstraints.size == 2) maxSMTSolver.pop() From 78fcf85d68414b3031c6be8ebfd6176248fe8386 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 14 Jun 2024 22:59:25 +0300 Subject: [PATCH 225/228] Remove useless `timeoutExceeded` method --- .../main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt index e5d54b68c..6b1885de7 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/utils/TimerUtils.kt @@ -10,9 +10,4 @@ internal object TimerUtils { fun timeoutExceeded(timeout: Duration): Boolean = timeout.isNegative() || timeout == Duration.ZERO - - fun timeoutExceeded(timeout: Duration, markStart: ValueTimeMark): Boolean { - val remainingTime = computeRemainingTime(timeout, markStart) - return timeoutExceeded(remainingTime) - } } From 3d2a15a62fe13aa8c997380e95089d8a62c3641e Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Fri, 14 Jun 2024 23:33:24 +0300 Subject: [PATCH 226/228] Refactor --- .../UnsupportedSolverOperationException.kt | 7 +++++++ .../solver/maxsmt/solvers/KMaxSMTSolverBase.kt | 7 +++---- .../ksmt/solver/maxsmt/solvers/KPMResSolver.kt | 3 ++- .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 17 ++++++++--------- 4 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt new file mode 100644 index 000000000..8c95b84d8 --- /dev/null +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt @@ -0,0 +1,7 @@ +package io.ksmt.solver.maxsmt.exception + +class UnsupportedSolverOperationException : Exception { + constructor() : super() + + constructor(message: String) : super(message) +} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt index a96be3912..fca97536c 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverBase.kt @@ -13,6 +13,9 @@ import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.sort.KBoolSort import kotlin.time.Duration +/** + * This class implements a logic general for any MaxSMT solver. + */ abstract class KMaxSMTSolverBase( private val ctx: KContext, private val solver: KSolver, @@ -37,8 +40,6 @@ abstract class KMaxSMTSolverBase( /** * Solve maximum satisfiability modulo theories problem. - * - * @throws NotImplementedError */ fun checkMaxSMT(timeout: Duration): KMaxSMTResult = checkMaxSMT(timeout, collectStatistics = false) @@ -47,8 +48,6 @@ abstract class KMaxSMTSolverBase( * Solve maximum satisfiability modulo theories problem. * * @param collectStatistics specifies whether statistics (elapsed time to execute method etc.) should be collected or not. - * - * @throws NotImplementedError */ abstract override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index 905298933..a09c18ad1 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -13,6 +13,7 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint +import io.ksmt.solver.maxsmt.exception.UnsupportedSolverOperationException import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.TimerUtils @@ -55,7 +56,7 @@ class KPMResSolver(private val ctx: KContext, solver: } override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { - TODO("Not yet implemented") + throw UnsupportedSolverOperationException("PMResSolver is not able to return suboptimal solutions") } private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 8c3e9b675..425965faa 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -104,7 +104,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } - return getSubOptMaxSMTResult(true) + return getSubOptMaxSMTResult() } val status = checkSatHillClimb(assumptions, timeout) @@ -138,7 +138,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } - return getSubOptMaxSMTResult(true) + return getSubOptMaxSMTResult() } val processUnsatStatus = processUnsat(assumptions, remainingTime) @@ -154,7 +154,7 @@ class KPrimalDualMaxResSolver( "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } // Something went wrong, so timeout has exceeded, solver was interrupted or returned UNKNOWN. - return getSubOptMaxSMTResult(true) + return getSubOptMaxSMTResult() } else if (processUnsatStatus == UNKNOWN) { pop() if (collectStatistics) { @@ -163,7 +163,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } - return getSubOptMaxSMTResult(true) + return getSubOptMaxSMTResult() } } @@ -175,7 +175,7 @@ class KPrimalDualMaxResSolver( logger.info { "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning SubOpt MaxSMT result" } - return getSubOptMaxSMTResult(true) + return getSubOptMaxSMTResult() } } @@ -314,6 +314,7 @@ class KPrimalDualMaxResSolver( maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds } // Solver returned UNKNOWN case. + return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) } } @@ -749,12 +750,10 @@ class KPrimalDualMaxResSolver( return status } - private fun getSubOptMaxSMTResult( - timeoutExceededOrInterrupted: Boolean = true - ): KMaxSMTResult = + private fun getSubOptMaxSMTResult(): KMaxSMTResult = KMaxSMTResult( if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, - timeoutExceededOrInterrupted, + true ) } From e0e6689e65a8650a590a04b2f6ad0deb5a31213d Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 15 Jun 2024 00:43:54 +0300 Subject: [PATCH 227/228] Split checkMaxSMT and checkSubOptMaxSmt (part1) --- .../test/subopt/KSubOptMaxSMTBenchmarkTest.kt | 2 +- .../UnsupportedSolverOperationException.kt | 7 - .../maxsmt/solvers/KMaxSMTSolverInterface.kt | 2 - .../solver/maxsmt/solvers/KPMResSolver.kt | 5 - .../maxsmt/solvers/KPrimalDualMaxResSolver.kt | 141 +----------------- 5 files changed, 2 insertions(+), 155 deletions(-) delete mode 100644 ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt diff --git a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt index 91af65f9a..0618467ec 100644 --- a/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt +++ b/ksmt-maxsmt-test/src/test/kotlin/io/ksmt/solver/maxsmt/test/subopt/KSubOptMaxSMTBenchmarkTest.kt @@ -198,7 +198,7 @@ abstract class KSubOptMaxSMTBenchmarkTest : KMaxSMTBenchmarkBasedTest { lateinit var maxSMTResult: KMaxSMTResult val elapsedTime = measureTimeMillis { try { - maxSMTResult = maxSMTSolver.checkSubOptMaxSMT(500.milliseconds, true) + maxSMTResult = maxSMTSolver.checkMaxSMT(500.milliseconds, true) } catch (ex: Exception) { testStatistics.maxSMTCallStatistics = maxSMTSolver.collectMaxSMTStatistics() testStatistics.exceptionMessage = ex.message.toString() diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt deleted file mode 100644 index 8c95b84d8..000000000 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/exception/UnsupportedSolverOperationException.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.ksmt.solver.maxsmt.exception - -class UnsupportedSolverOperationException : Exception { - constructor() : super() - - constructor(message: String) : super(message) -} diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt index e6efbf968..a3813d189 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KMaxSMTSolverInterface.kt @@ -13,7 +13,5 @@ interface KMaxSMTSolverInterface : KSolver where C : KSolverConfiguration fun checkMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult - fun checkSubOptMaxSMT(timeout: Duration = Duration.INFINITE, collectStatistics: Boolean = false): KMaxSMTResult - fun collectMaxSMTStatistics(): KMaxSMTStatistics } diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt index a09c18ad1..702a4cb76 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPMResSolver.kt @@ -13,7 +13,6 @@ import io.ksmt.solver.maxsmt.KMaxSMTContext import io.ksmt.solver.maxsmt.KMaxSMTContext.Strategy.PrimalMaxRes import io.ksmt.solver.maxsmt.KMaxSMTResult import io.ksmt.solver.maxsmt.constraints.SoftConstraint -import io.ksmt.solver.maxsmt.exception.UnsupportedSolverOperationException import io.ksmt.solver.maxsmt.statistics.KMaxSMTStatistics import io.ksmt.solver.maxsmt.utils.CoreUtils import io.ksmt.solver.maxsmt.utils.TimerUtils @@ -55,10 +54,6 @@ class KPMResSolver(private val ctx: KContext, solver: return maxSMTResult } - override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { - throw UnsupportedSolverOperationException("PMResSolver is not able to return suboptimal solutions") - } - private fun runMaxSMTLogic(timeout: Duration): KMaxSMTResult { val markHardConstraintsCheckStart = markNow() val hardConstraintsStatus = check(timeout) diff --git a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt index 425965faa..645a6d1e3 100644 --- a/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt +++ b/ksmt-maxsmt/src/main/kotlin/io/ksmt/solver/maxsmt/solvers/KPrimalDualMaxResSolver.kt @@ -44,7 +44,7 @@ class KPrimalDualMaxResSolver( private data class WeightedCore(val expressions: List>, val weight: UInt) - override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { + override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { val markCheckMaxSMTStart = markNow() markLoggingPoint = markCheckMaxSMTStart @@ -200,145 +200,6 @@ class KPrimalDualMaxResSolver( return result } - override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult { - val markCheckMaxSMTStart = markNow() - markLoggingPoint = markCheckMaxSMTStart - - if (TimerUtils.timeoutExceeded(timeout)) { - error("Timeout must be positive but was [${timeout.inWholeSeconds} s]") - } - - this.collectStatistics = collectStatistics - - if (this.collectStatistics) { - maxSMTStatistics = KMaxSMTStatistics(maxSmtCtx) - maxSMTStatistics.timeoutMs = timeout.inWholeMilliseconds - } - - val markHardConstraintsCheckStart = markNow() - val hardConstraintsStatus = check(timeout) - if (this.collectStatistics) { - maxSMTStatistics.queriesToSolverNumber++ - maxSMTStatistics.timeInSolverQueriesMs += markHardConstraintsCheckStart.elapsedNow().inWholeMilliseconds - } - - if (hardConstraintsStatus == UNSAT || softConstraints.isEmpty()) { - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult( - listOf(), hardConstraintsStatus, - timeoutExceededOrUnknown = false, - ) - } else if (hardConstraintsStatus == UNKNOWN) { - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult( - listOf(), hardConstraintsStatus, - timeoutExceededOrUnknown = true, - ) - } - - push() - initMaxSMT() - - val assumptions = softConstraints.toMutableList() - - while (_lower < _upper) { - logger.info { - "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] Iteration number: $_iteration" - } - markLoggingPoint = markNow() - - val softConstraintsCheckRemainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(softConstraintsCheckRemainingTime) || isInterrupted) { - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) - } - - val status = checkSatHillClimb(assumptions, timeout) - - when (status) { - SAT -> { - when (maxSmtCtx.strategy) { - PrimalMaxRes -> _upper = _lower - - PrimalDualMaxRes -> { - val correctionSet = getCorrectionSet(model(), assumptions) - if (correctionSet.isEmpty()) { - if (_model != null) { - // Feasible optimum is found by the moment. - _lower = _upper - } - } else { - processSat(correctionSet, assumptions) - } - } - } - } - - UNSAT -> { - val remainingTime = TimerUtils.computeRemainingTime(timeout, markCheckMaxSMTStart) - if (TimerUtils.timeoutExceeded(remainingTime) || isInterrupted) { - pop() - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) - } - - val processUnsatStatus = processUnsat(assumptions, remainingTime) - if (processUnsatStatus == UNSAT) { - _lower = _upper - // TODO: process this case as it can happen when timeout exceeded - pop() - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) - } else if (processUnsatStatus == UNKNOWN) { - pop() - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) - } - } - - UNKNOWN -> { - pop() - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - // Solver returned UNKNOWN case. - return KMaxSMTResult(listOf(), SAT, timeoutExceededOrUnknown = true) - } - } - - ++_iteration - } - - _lower = _upper - - val result = KMaxSMTResult( - if (_model != null) getSatSoftConstraintsByModel(_model!!) else listOf(), SAT, - timeoutExceededOrUnknown = false, - ) - logger.info { - "[${markLoggingPoint.elapsedNow().inWholeMicroseconds} mcs] --- returning MaxSMT result" - } - - pop() - - if (collectStatistics) { - maxSMTStatistics.elapsedTimeMs = markCheckMaxSMTStart.elapsedNow().inWholeMilliseconds - } - return result - } - private fun processSat(correctionSet: List, assumptions: MutableList) { removeCoreAssumptions(correctionSet, assumptions) val (minWeight, _) = splitCore(correctionSet, assumptions) From 070cbcb609b6289d8f7d0295b23efaad8c966980 Mon Sep 17 00:00:00 2001 From: Viktoriia Fomina Date: Sat, 15 Jun 2024 21:25:47 +0300 Subject: [PATCH 228/228] Refactor Portfolio: split CheckMaxSMT and CheckSubOptMaxSMT --- .../models/SolverProtocolModel.Generated.kt | 39 ++++++------------- .../models/TestProtocolModel.Generated.kt | 29 +++----------- .../ksmt/solver/portfolio/KPortfolioSolver.kt | 7 ---- .../io/ksmt/solver/runner/KSolverRunner.kt | 11 ++---- .../solver/runner/KSolverRunnerExecutor.kt | 10 +---- .../solver/runner/KSolverWorkerProcess.kt | 17 +------- .../ksmt/runner/models/SolverProtocolModel.kt | 5 --- .../ksmt/runner/models/TestProtocolModel.kt | 5 --- 8 files changed, 22 insertions(+), 101 deletions(-) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt index 0fd3ed291..105fd6412 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/SolverProtocolModel.Generated.kt @@ -30,7 +30,6 @@ class SolverProtocolModel private constructor( private val _pop: RdCall, private val _check: RdCall, private val _checkMaxSMT: RdCall, - private val _checkSubOptMaxSMT: RdCall, private val _collectMaxSMTStatistics: RdCall, private val _checkWithAssumptions: RdCall, private val _model: RdCall, @@ -88,7 +87,7 @@ class SolverProtocolModel private constructor( private val __SolverConfigurationParamListSerializer = SolverConfigurationParam.list() - const val serializationHash = 60617290091922470L + const val serializationHash = -1861632144359942979L } override val serializersOwner: ISerializersOwner get() = SolverProtocolModel @@ -156,11 +155,6 @@ class SolverProtocolModel private constructor( */ val checkMaxSMT: RdCall get() = _checkMaxSMT - /** - * Check SubOptMaxSMT - */ - val checkSubOptMaxSMT: RdCall get() = _checkSubOptMaxSMT - /** * Collect MaxSMT statistics */ @@ -205,7 +199,6 @@ class SolverProtocolModel private constructor( _pop.async = true _check.async = true _checkMaxSMT.async = true - _checkSubOptMaxSMT.async = true _collectMaxSMTStatistics.async = true _checkWithAssumptions.async = true _model.async = true @@ -227,7 +220,6 @@ class SolverProtocolModel private constructor( bindableChildren.add("pop" to _pop) bindableChildren.add("check" to _check) bindableChildren.add("checkMaxSMT" to _checkMaxSMT) - bindableChildren.add("checkSubOptMaxSMT" to _checkSubOptMaxSMT) bindableChildren.add("collectMaxSMTStatistics" to _collectMaxSMTStatistics) bindableChildren.add("checkWithAssumptions" to _checkWithAssumptions) bindableChildren.add("model" to _model) @@ -251,7 +243,6 @@ class SolverProtocolModel private constructor( RdCall(PopParams, FrameworkMarshallers.Void), RdCall(CheckParams, CheckResult), RdCall(CheckMaxSMTParams, CheckMaxSMTResult), - RdCall(CheckMaxSMTParams, CheckMaxSMTResult), RdCall(FrameworkMarshallers.Void, CollectMaxSMTStatisticsResult), RdCall(CheckWithAssumptionsParams, CheckResult), RdCall(FrameworkMarshallers.Void, ModelResult), @@ -278,7 +269,6 @@ class SolverProtocolModel private constructor( print("pop = "); _pop.print(printer); println() print("check = "); _check.print(printer); println() print("checkMaxSMT = "); _checkMaxSMT.print(printer); println() - print("checkSubOptMaxSMT = "); _checkSubOptMaxSMT.print(printer); println() print("collectMaxSMTStatistics = "); _collectMaxSMTStatistics.print(printer); println() print("checkWithAssumptions = "); _checkWithAssumptions.print(printer); println() print("model = "); _model.print(printer); println() @@ -303,7 +293,6 @@ class SolverProtocolModel private constructor( _pop.deepClonePolymorphic(), _check.deepClonePolymorphic(), _checkMaxSMT.deepClonePolymorphic(), - _checkSubOptMaxSMT.deepClonePolymorphic(), _collectMaxSMTStatistics.deepClonePolymorphic(), _checkWithAssumptions.deepClonePolymorphic(), _model.deepClonePolymorphic(), @@ -502,8 +491,7 @@ data class CheckMaxSMTResult ( val satSoftConstraintExprs: List, val satSoftConstraintWeights: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, - val timeoutExceededOrUnknown: Boolean, - val maxSMTSucceeded: Boolean + val timeoutExceededOrUnknown: Boolean ) : IPrintable { //companion @@ -516,8 +504,7 @@ data class CheckMaxSMTResult ( val satSoftConstraintWeights = buffer.readList { buffer.readUInt() } val hardConstraintsSatStatus = buffer.readEnum() val timeoutExceededOrUnknown = buffer.readBool() - val maxSMTSucceeded = buffer.readBool() - return CheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) + return CheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: CheckMaxSMTResult) { @@ -525,7 +512,6 @@ data class CheckMaxSMTResult ( buffer.writeList(value.satSoftConstraintWeights) { v -> buffer.writeUInt(v) } buffer.writeEnum(value.hardConstraintsSatStatus) buffer.writeBool(value.timeoutExceededOrUnknown) - buffer.writeBool(value.maxSMTSucceeded) } @@ -545,7 +531,6 @@ data class CheckMaxSMTResult ( if (satSoftConstraintWeights != other.satSoftConstraintWeights) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false - if (maxSMTSucceeded != other.maxSMTSucceeded) return false return true } @@ -556,7 +541,6 @@ data class CheckMaxSMTResult ( __r = __r*31 + satSoftConstraintWeights.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() __r = __r*31 + timeoutExceededOrUnknown.hashCode() - __r = __r*31 + maxSMTSucceeded.hashCode() return __r } //pretty print @@ -567,7 +551,6 @@ data class CheckMaxSMTResult ( print("satSoftConstraintWeights = "); satSoftConstraintWeights.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() - print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() } printer.print(")") } @@ -691,7 +674,7 @@ data class CheckResult ( /** - * #### Generated from [SolverProtocolModel.kt:85] + * #### Generated from [SolverProtocolModel.kt:84] */ data class CheckWithAssumptionsParams ( val assumptions: List, @@ -754,7 +737,7 @@ data class CheckWithAssumptionsParams ( /** - * #### Generated from [SolverProtocolModel.kt:90] + * #### Generated from [SolverProtocolModel.kt:89] */ data class CollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -934,7 +917,7 @@ data class CreateSolverParams ( /** - * #### Generated from [SolverProtocolModel.kt:111] + * #### Generated from [SolverProtocolModel.kt:110] */ data class ModelEntry ( val decl: io.ksmt.KAst, @@ -1009,7 +992,7 @@ data class ModelEntry ( /** - * #### Generated from [SolverProtocolModel.kt:105] + * #### Generated from [SolverProtocolModel.kt:104] */ data class ModelFuncInterpEntry ( val hasVars: Boolean, @@ -1078,7 +1061,7 @@ data class ModelFuncInterpEntry ( /** - * #### Generated from [SolverProtocolModel.kt:123] + * #### Generated from [SolverProtocolModel.kt:122] */ data class ModelResult ( val declarations: List, @@ -1147,7 +1130,7 @@ data class ModelResult ( /** - * #### Generated from [SolverProtocolModel.kt:118] + * #### Generated from [SolverProtocolModel.kt:117] */ data class ModelUninterpretedSortUniverse ( val sort: io.ksmt.KAst, @@ -1267,7 +1250,7 @@ data class PopParams ( /** - * #### Generated from [SolverProtocolModel.kt:101] + * #### Generated from [SolverProtocolModel.kt:100] */ data class ReasonUnknownResult ( val reasonUnknown: String @@ -1473,7 +1456,7 @@ enum class SolverType { /** - * #### Generated from [SolverProtocolModel.kt:97] + * #### Generated from [SolverProtocolModel.kt:96] */ data class UnsatCoreResult ( val core: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt index 207b3cb74..ef980a754 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/runner/generated/models/TestProtocolModel.Generated.kt @@ -30,7 +30,6 @@ class TestProtocolModel private constructor( private val _assertSoft: RdCall, private val _check: RdCall, private val _checkMaxSMT: RdCall, - private val _checkSubOptMaxSMT: RdCall, private val _collectMaxSMTStatistics: RdCall, private val _exprToString: RdCall, private val _getReasonUnknown: RdCall, @@ -80,7 +79,7 @@ class TestProtocolModel private constructor( private val __LongListSerializer = FrameworkMarshallers.Long.list() private val __IntNullableSerializer = FrameworkMarshallers.Int.nullable() - const val serializationHash = -1203465621073210600L + const val serializationHash = 1052422932439377909L } override val serializersOwner: ISerializersOwner get() = TestProtocolModel @@ -148,11 +147,6 @@ class TestProtocolModel private constructor( */ val checkMaxSMT: RdCall get() = _checkMaxSMT - /** - * Check SubOptMaxSMT - */ - val checkSubOptMaxSMT: RdCall get() = _checkSubOptMaxSMT - /** * Collect MaxSMT statistics */ @@ -207,7 +201,6 @@ class TestProtocolModel private constructor( _assertSoft.async = true _check.async = true _checkMaxSMT.async = true - _checkSubOptMaxSMT.async = true _collectMaxSMTStatistics.async = true _exprToString.async = true _getReasonUnknown.async = true @@ -231,7 +224,6 @@ class TestProtocolModel private constructor( bindableChildren.add("assertSoft" to _assertSoft) bindableChildren.add("check" to _check) bindableChildren.add("checkMaxSMT" to _checkMaxSMT) - bindableChildren.add("checkSubOptMaxSMT" to _checkSubOptMaxSMT) bindableChildren.add("collectMaxSMTStatistics" to _collectMaxSMTStatistics) bindableChildren.add("exprToString" to _exprToString) bindableChildren.add("getReasonUnknown" to _getReasonUnknown) @@ -257,7 +249,6 @@ class TestProtocolModel private constructor( RdCall(TestSoftConstraint, FrameworkMarshallers.Void), RdCall(FrameworkMarshallers.Int, TestCheckResult), RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), - RdCall(TestCheckMaxSMTParams, TestCheckMaxSMTResult), RdCall(FrameworkMarshallers.Void, TestCollectMaxSMTStatisticsResult), RdCall(FrameworkMarshallers.Long, FrameworkMarshallers.String), RdCall(FrameworkMarshallers.Int, FrameworkMarshallers.String), @@ -286,7 +277,6 @@ class TestProtocolModel private constructor( print("assertSoft = "); _assertSoft.print(printer); println() print("check = "); _check.print(printer); println() print("checkMaxSMT = "); _checkMaxSMT.print(printer); println() - print("checkSubOptMaxSMT = "); _checkSubOptMaxSMT.print(printer); println() print("collectMaxSMTStatistics = "); _collectMaxSMTStatistics.print(printer); println() print("exprToString = "); _exprToString.print(printer); println() print("getReasonUnknown = "); _getReasonUnknown.print(printer); println() @@ -313,7 +303,6 @@ class TestProtocolModel private constructor( _assertSoft.deepClonePolymorphic(), _check.deepClonePolymorphic(), _checkMaxSMT.deepClonePolymorphic(), - _checkSubOptMaxSMT.deepClonePolymorphic(), _collectMaxSMTStatistics.deepClonePolymorphic(), _exprToString.deepClonePolymorphic(), _getReasonUnknown.deepClonePolymorphic(), @@ -595,8 +584,7 @@ data class TestCheckMaxSMTResult ( val satSoftConstraintExprs: List, val satSoftConstraintWeights: List, val hardConstraintsSatStatus: io.ksmt.solver.KSolverStatus, - val timeoutExceededOrUnknown: Boolean, - val maxSMTSucceeded: Boolean + val timeoutExceededOrUnknown: Boolean ) : IPrintable { //companion @@ -609,8 +597,7 @@ data class TestCheckMaxSMTResult ( val satSoftConstraintWeights = buffer.readList { buffer.readUInt() } val hardConstraintsSatStatus = buffer.readEnum() val timeoutExceededOrUnknown = buffer.readBool() - val maxSMTSucceeded = buffer.readBool() - return TestCheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown, maxSMTSucceeded) + return TestCheckMaxSMTResult(satSoftConstraintExprs, satSoftConstraintWeights, hardConstraintsSatStatus, timeoutExceededOrUnknown) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestCheckMaxSMTResult) { @@ -618,7 +605,6 @@ data class TestCheckMaxSMTResult ( buffer.writeList(value.satSoftConstraintWeights) { v -> buffer.writeUInt(v) } buffer.writeEnum(value.hardConstraintsSatStatus) buffer.writeBool(value.timeoutExceededOrUnknown) - buffer.writeBool(value.maxSMTSucceeded) } @@ -638,7 +624,6 @@ data class TestCheckMaxSMTResult ( if (satSoftConstraintWeights != other.satSoftConstraintWeights) return false if (hardConstraintsSatStatus != other.hardConstraintsSatStatus) return false if (timeoutExceededOrUnknown != other.timeoutExceededOrUnknown) return false - if (maxSMTSucceeded != other.maxSMTSucceeded) return false return true } @@ -649,7 +634,6 @@ data class TestCheckMaxSMTResult ( __r = __r*31 + satSoftConstraintWeights.hashCode() __r = __r*31 + hardConstraintsSatStatus.hashCode() __r = __r*31 + timeoutExceededOrUnknown.hashCode() - __r = __r*31 + maxSMTSucceeded.hashCode() return __r } //pretty print @@ -660,7 +644,6 @@ data class TestCheckMaxSMTResult ( print("satSoftConstraintWeights = "); satSoftConstraintWeights.print(printer); println() print("hardConstraintsSatStatus = "); hardConstraintsSatStatus.print(printer); println() print("timeoutExceededOrUnknown = "); timeoutExceededOrUnknown.print(printer); println() - print("maxSMTSucceeded = "); maxSMTSucceeded.print(printer); println() } printer.print(")") } @@ -727,7 +710,7 @@ data class TestCheckResult ( /** - * #### Generated from [TestProtocolModel.kt:57] + * #### Generated from [TestProtocolModel.kt:56] */ data class TestCollectMaxSMTStatisticsResult ( val timeoutMs: Long, @@ -802,7 +785,7 @@ data class TestCollectMaxSMTStatisticsResult ( /** - * #### Generated from [TestProtocolModel.kt:64] + * #### Generated from [TestProtocolModel.kt:63] */ data class TestConversionResult ( val expressions: List @@ -859,7 +842,7 @@ data class TestConversionResult ( /** - * #### Generated from [TestProtocolModel.kt:68] + * #### Generated from [TestProtocolModel.kt:67] */ data class TestInternalizeAndConvertParams ( val expressions: List diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt index 1ff532caf..583bcdb37 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/portfolio/KPortfolioSolver.kt @@ -140,13 +140,6 @@ class KPortfolioSolver( } override fun checkMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { - solverMaxSmtQueryAsync( - { checkMaxSMT(timeout, collectStatistics) }, - { !this.timeoutExceededOrUnknown } - ) - } - - override fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = runBlocking { solverMaxSmtQueryAsync( { checkSubOptMaxSMT(timeout, collectStatistics) }, solverPredicate = { !this.timeoutExceededOrUnknown }, diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt index 58b7c1675..a14111ac8 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunner.kt @@ -249,11 +249,6 @@ class KSolverRunner( checkMaxSMT(timeout, collectStatistics) } - suspend fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = - handleCheckAnyMaxSMTExceptionAsNotSucceededAsync { - checkSubOptMaxSMT(timeout, collectStatistics) - } - suspend fun collectMaxSMTStatistics(): KMaxSMTStatistics = handleCollectStatisticsAsync { collectMaxSMTStatistics() @@ -459,9 +454,9 @@ class KSolverRunner( body = body, onException = { KMaxSMTResult( - emptyList(), KSolverStatus.UNKNOWN, - timeoutExceededOrUnknown = true, - maxSMTSucceeded = false + emptyList(), + KSolverStatus.UNKNOWN, + timeoutExceededOrUnknown = true ) } ) diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt index 138958a8d..0ddd254f6 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverRunnerExecutor.kt @@ -232,13 +232,6 @@ class KSolverRunnerExecutor( } } - suspend fun checkSubOptMaxSMT(timeout: Duration, collectStatistics: Boolean): KMaxSMTResult = - checkAnyMaxSMT(timeout, collectStatistics) { params -> - queryWithTimeoutAndExceptionHandlingAsync { - checkSubOptMaxSMT.queryAsync(params) - } - } - /** * Can be used with both optimal and suboptimal versions. */ @@ -259,8 +252,7 @@ class KSolverRunnerExecutor( return KMaxSMTResult( satSoftConstraints, result.hardConstraintsSatStatus, - result.timeoutExceededOrUnknown, - result.maxSMTSucceeded + result.timeoutExceededOrUnknown ) } diff --git a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt index 04b943c62..ba062941e 100644 --- a/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt +++ b/ksmt-runner/src/main/kotlin/io/ksmt/solver/runner/KSolverWorkerProcess.kt @@ -149,22 +149,7 @@ class KSolverWorkerProcess : ChildProcessBase() { result.satSoftConstraints.map { it.expression }, result.satSoftConstraints.map { it.weight }, result.hardConstraintsSatStatus, - result.timeoutExceededOrUnknown, - result.maxSMTSucceeded - ) - } - checkSubOptMaxSMT.measureExecutionForTermination { params -> - val timeout = params.timeout.milliseconds - val collectStatistics = params.collectStatistics - - val result = maxSmtSolver.checkSubOptMaxSMT(timeout, collectStatistics) - - CheckMaxSMTResult( - result.satSoftConstraints.map { it.expression }, - result.satSoftConstraints.map { it.weight }, - result.hardConstraintsSatStatus, - result.timeoutExceededOrUnknown, - result.maxSMTSucceeded + result.timeoutExceededOrUnknown ) } collectMaxSMTStatistics.measureExecutionForTermination { diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt index fe9fe9464..86338a2a5 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/SolverProtocolModel.kt @@ -79,7 +79,6 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { field("satSoftConstraintWeights", immutableList(PredefinedType.uint)) field("hardConstraintsSatStatus", statusType) field("timeoutExceededOrUnknown", PredefinedType.bool) - field("maxSMTSucceeded", PredefinedType.bool) } private val checkWithAssumptionsParams = structdef { @@ -175,10 +174,6 @@ object SolverProtocolModel : Ext(SolverProtocolRoot) { async documentation = "Check MaxSMT" } - call("checkSubOptMaxSMT", checkMaxSMTParams, checkMaxSMTResult).apply { - async - documentation = "Check SubOptMaxSMT" - } call("collectMaxSMTStatistics", PredefinedType.void, collectMaxSMTStatisticsResult).apply { async documentation = "Collect MaxSMT statistics" diff --git a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt index 82f7b6285..9c661dfa7 100644 --- a/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt +++ b/ksmt-runner/src/main/rdgen/io/ksmt/runner/models/TestProtocolModel.kt @@ -51,7 +51,6 @@ object TestProtocolModel : Ext(TestProtocolRoot) { field("satSoftConstraintWeights", immutableList(PredefinedType.uint)) field("hardConstraintsSatStatus", statusType) field("timeoutExceededOrUnknown", PredefinedType.bool) - field("maxSMTSucceeded", PredefinedType.bool) } private val testCollectMaxSMTStatisticsResult = structdef { @@ -118,10 +117,6 @@ object TestProtocolModel : Ext(TestProtocolRoot) { async documentation = "Check MaxSMT" } - call("checkSubOptMaxSMT", testCheckMaxSMTParams, testCheckMaxSMTResult).apply { - async - documentation = "Check SubOptMaxSMT" - } call("collectMaxSMTStatistics", PredefinedType.void, testCollectMaxSMTStatisticsResult).apply { async documentation = "Collect MaxSMT statistics"