Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ open class KBitwuzlaContext(val ctx: KContext) : AutoCloseable {
}

override fun close() {
if (isClosed) return
isClosed = true

sorts.clear()
bitwuzlaSorts.clear()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ class KCvc5Context(


override fun close() {
if (isClosed) return
isClosed = true

currentScopeExpressions.clear()
Expand Down
23 changes: 23 additions & 0 deletions ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ class CheckWithAssumptionsTest {
@Test
fun testUnsatCoreGenerationCvc() = testUnsatCoreGeneration { KCvc5Solver(it) }

@Test
fun testTrivialUnsatCoreZ3() = testTrivialUnsatCore { KZ3Solver(it) }

@Test
fun testTrivialUnsatCoreBitwuzla() = testTrivialUnsatCore { KBitwuzlaSolver(it) }

@Test
fun testTrivialUnsatCoreYices() = testTrivialUnsatCore { KYicesSolver(it) }

@Test
fun testTrivialUnsatCoreCvc() = testTrivialUnsatCore { KCvc5Solver(it) }

private fun testComplexAssumption(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) {
mkSolver(this).use { solver ->
val a by bv32Sort
Expand Down Expand Up @@ -76,4 +88,15 @@ class CheckWithAssumptionsTest {
assertTrue(e3 in core)
}
}

private fun testTrivialUnsatCore(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) {
mkSolver(this).use { solver ->
val status = solver.checkWithAssumptions(listOf(falseExpr))
assertEquals(KSolverStatus.UNSAT, status)

val core = solver.unsatCore()
assertEquals(1, core.size)
assertTrue(falseExpr in core)
}
}
}
1 change: 1 addition & 0 deletions ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ class KZ3Context(
}

override fun close() {
if (isClosed) return
isClosed = true

z3Expressions.keys.decRefAll()
Expand Down
42 changes: 35 additions & 7 deletions ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Solver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ import kotlin.time.DurationUnit
open class KZ3Solver(private val ctx: KContext) : KSolver<KZ3SolverConfiguration> {
private val z3Ctx = KZ3Context(ctx)
private val solver = createSolver()

private var lastCheckStatus = KSolverStatus.UNKNOWN
private var lastReasonOfUnknown: String? = null
private var lastModel: KZ3Model? = null
private var lastUnsatCore: List<KExpr<KBoolSort>>? = null

private var currentScope: UInt = 0u

@Suppress("LeakingThis")
Expand Down Expand Up @@ -104,7 +108,20 @@ open class KZ3Solver(private val ctx: KContext) : KSolver<KZ3SolverConfiguration
ctx.ensureContextMatch(assumptions)

val z3Assumptions = with(exprInternalizer) {
LongArray(assumptions.size) { assumptions[it].internalizeExpr() }
LongArray(assumptions.size) {
val assumption = assumptions[it]

/**
* Assumptions are trivially unsat and no check-sat is required.
* */
if (assumption == ctx.falseExpr) {
lastUnsatCore = listOf(ctx.falseExpr)
lastCheckStatus = KSolverStatus.UNSAT
return KSolverStatus.UNSAT
}

assumption.internalizeExpr()
}
}

solver.updateTimeout(timeout)
Expand All @@ -116,19 +133,28 @@ open class KZ3Solver(private val ctx: KContext) : KSolver<KZ3SolverConfiguration
require(lastCheckStatus == KSolverStatus.SAT) {
"Model are only available after SAT checks, current solver status: $lastCheckStatus"
}
val model = solver.model

KZ3Model(model, ctx, z3Ctx, exprInternalizer)
val model = lastModel ?: KZ3Model(
model = solver.model,
ctx = ctx,
z3Ctx = z3Ctx,
internalizer = exprInternalizer
)
lastModel = model

model
}

override fun unsatCore(): List<KExpr<KBoolSort>> = z3Try {
require(lastCheckStatus == KSolverStatus.UNSAT) { "Unsat cores are only available after UNSAT checks" }

val unsatCore = solver.solverGetUnsatCore()

with(exprConverter) {
unsatCore.map { trackedAssertions.get(it) ?: it.convertExpr() }
val unsatCore = lastUnsatCore ?: with(exprConverter) {
val solverUnsatCore = solver.solverGetUnsatCore()
solverUnsatCore.map { trackedAssertions.get(it) ?: it.convertExpr() }
}
lastUnsatCore = unsatCore

unsatCore
}

override fun reasonOfUnknown(): String = z3Try {
Expand Down Expand Up @@ -173,6 +199,8 @@ open class KZ3Solver(private val ctx: KContext) : KSolver<KZ3SolverConfiguration
private fun invalidateSolverState() {
lastReasonOfUnknown = null
lastCheckStatus = KSolverStatus.UNKNOWN
lastModel = null
lastUnsatCore = null
}

private inline fun z3TryCheck(body: () -> KSolverStatus): KSolverStatus = try {
Expand Down