Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework model detach #151

Merged
merged 2 commits into from
Feb 1, 2024
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Get the most out of SMT solving with KSMT features:
* Streamlined [solver delivery](#ksmt-distribution) with no need for building a solver or implementing JVM bindings

[![KSMT: build](https://github.com/UnitTestBot/ksmt/actions/workflows/build-and-run-tests.yml/badge.svg)](https://github.com/UnitTestBot/ksmt/actions/workflows/build-and-run-tests.yml)
[![Maven Central](https://img.shields.io/maven-central/v/io.ksmt/ksmt-core)](https://central.sonatype.com/artifact/io.ksmt/ksmt-core/0.5.17)
[![Maven Central](https://img.shields.io/maven-central/v/io.ksmt/ksmt-core)](https://central.sonatype.com/artifact/io.ksmt/ksmt-core/0.5.18)
[![javadoc](https://javadoc.io/badge2/io.ksmt/ksmt-core/javadoc.svg)](https://javadoc.io/doc/io.ksmt/ksmt-core)

## Get started
Expand All @@ -20,9 +20,9 @@ To start using KSMT, install it via [Gradle](https://gradle.org/):

```kotlin
// core
implementation("io.ksmt:ksmt-core:0.5.17")
implementation("io.ksmt:ksmt-core:0.5.18")
// z3 solver
implementation("io.ksmt:ksmt-z3:0.5.17")
implementation("io.ksmt:ksmt-z3:0.5.18")
```

Find basic instructions in the [Getting started](docs/getting-started.md) guide and try it out with the
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/io.ksmt.ksmt-base.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {
}

group = "io.ksmt"
version = "0.5.17"
version = "0.5.18"

repositories {
mavenCentral()
Expand Down
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ repositories {
```kotlin
dependencies {
// core
implementation("io.ksmt:ksmt-core:0.5.17")
implementation("io.ksmt:ksmt-core:0.5.18")
}
```

Expand All @@ -43,9 +43,9 @@ dependencies {
```kotlin
dependencies {
// z3
implementation("io.ksmt:ksmt-z3:0.5.17")
implementation("io.ksmt:ksmt-z3:0.5.18")
// bitwuzla
implementation("io.ksmt:ksmt-bitwuzla:0.5.17")
implementation("io.ksmt:ksmt-bitwuzla:0.5.18")
}
```

Expand Down
6 changes: 3 additions & 3 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ repositories {

dependencies {
// core
implementation("io.ksmt:ksmt-core:0.5.17")
implementation("io.ksmt:ksmt-core:0.5.18")
// z3 solver
implementation("io.ksmt:ksmt-z3:0.5.17")
implementation("io.ksmt:ksmt-z3:0.5.18")
// Runner and portfolio solver
implementation("io.ksmt:ksmt-runner:0.5.17")
implementation("io.ksmt:ksmt-runner:0.5.18")
}

java {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ open class KBitwuzlaModel(
assertedDeclarations: Set<KDecl<*>>,
private val uninterpretedSortDependency: Map<KUninterpretedSort, Set<KDecl<*>>>
) : KModel {

private val modelDeclarations = assertedDeclarations.toHashSet()

override val declarations: Set<KDecl<*>>
Expand Down Expand Up @@ -263,15 +262,14 @@ open class KBitwuzlaModel(
interpretation(it) ?: error("missed interpretation for $it")
}

// The model is detached from the solver and therefore invalid
markInvalid()

return KModelImpl(ctx, interpretations, uninterpretedSortsUniverses)
}

override fun toString(): String = detach().toString()
override fun hashCode(): Int = detach().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KModel) return false
return detach() == other
override fun close() {
markInvalid()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.ksmt.expr.KExpr
import io.ksmt.solver.KModel
import io.ksmt.solver.KSolver
import io.ksmt.solver.KSolverStatus
import io.ksmt.solver.model.KNativeSolverModel
import org.ksmt.solver.bitwuzla.bindings.BitwuzlaNativeException
import org.ksmt.solver.bitwuzla.bindings.BitwuzlaOption
import org.ksmt.solver.bitwuzla.bindings.BitwuzlaResult
Expand All @@ -27,7 +28,8 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
private var lastCheckStatus = KSolverStatus.UNKNOWN
private var lastReasonOfUnknown: String? = null
private var lastAssumptions: TrackedAssumptions? = null
private var lastModel: KBitwuzlaModel? = null
private var lastBitwuzlaModel: KBitwuzlaModel? = null
private var lastModel: KModel? = null

init {
Native.bitwuzlaSetOption(bitwuzlaCtx.bitwuzla, BitwuzlaOption.BITWUZLA_OPT_INCREMENTAL, value = 1)
Expand Down Expand Up @@ -121,13 +123,17 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC

override fun model(): KModel = bitwuzlaCtx.bitwuzlaTry {
require(lastCheckStatus == KSolverStatus.SAT) { "Model are only available after SAT checks" }
val model = lastModel ?: KBitwuzlaModel(
lastModel?.let { return it }

val bitwuzlaModel = KBitwuzlaModel(
ctx, bitwuzlaCtx, exprConverter,
bitwuzlaCtx.declarations(),
bitwuzlaCtx.uninterpretedSortsWithRelevantDecls()
)
lastModel = model
model
return KNativeSolverModel(bitwuzlaModel).also {
lastBitwuzlaModel = bitwuzlaModel
lastModel = it
}
}

override fun unsatCore(): List<KExpr<KBoolSort>> = bitwuzlaCtx.bitwuzlaTry {
Expand Down Expand Up @@ -163,7 +169,8 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
/**
* Bitwuzla model is only valid until the next check-sat call.
* */
lastModel?.markInvalid()
lastBitwuzlaModel?.markInvalid()
lastBitwuzlaModel = null
lastModel = null

lastCheckStatus = KSolverStatus.UNKNOWN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import io.ksmt.sort.KArraySort
import io.ksmt.utils.mkConst
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

Expand Down Expand Up @@ -63,7 +62,6 @@ class Example {

solver.close()

assertFailsWith(IllegalStateException::class) { model.interpretation(b) }
assertEquals(aValue, detachedModel.eval(a))
assertEquals(cValue, detachedModel.eval(c))
}
Expand Down
10 changes: 9 additions & 1 deletion ksmt-core/src/main/kotlin/io/ksmt/solver/KModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import io.ksmt.solver.model.KFuncInterp
import io.ksmt.sort.KSort
import io.ksmt.sort.KUninterpretedSort

interface KModel {
interface KModel : AutoCloseable {
val declarations: Set<KDecl<*>>

val uninterpretedSorts: Set<KUninterpretedSort>
Expand All @@ -21,5 +21,13 @@ interface KModel {
* */
fun uninterpretedSortUniverse(sort: KUninterpretedSort): Set<KUninterpretedSortValue>?

/**
* Detach model from the solver and release native resources.
* */
fun detach(): KModel

/**
* Close model and release acquired native resources.
* */
override fun close()
}
4 changes: 4 additions & 0 deletions ksmt-core/src/main/kotlin/io/ksmt/solver/model/KModelImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ open class KModelImpl(
}

override fun hashCode(): Int = interpretations.hashCode()

override fun close() {
// nothing to close
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.ksmt.solver.model

import io.ksmt.decl.KDecl
import io.ksmt.expr.KExpr
import io.ksmt.expr.KUninterpretedSortValue
import io.ksmt.solver.KModel
import io.ksmt.sort.KSort
import io.ksmt.sort.KUninterpretedSort

class KNativeSolverModel(nativeModel: KModel): KModel {
private var model: KModel = nativeModel

override val declarations: Set<KDecl<*>> get() = model.declarations

override val uninterpretedSorts: Set<KUninterpretedSort> get() = model.uninterpretedSorts

override fun <T : KSort> eval(expr: KExpr<T>, isComplete: Boolean): KExpr<T> =
model.eval(expr, isComplete)

override fun <T : KSort> interpretation(decl: KDecl<T>): KFuncInterp<T>? =
model.interpretation(decl)

override fun uninterpretedSortUniverse(sort: KUninterpretedSort): Set<KUninterpretedSortValue>? =
model.uninterpretedSortUniverse(sort)

override fun detach(): KModel {
model = model.detach()
return model
}

override fun close() {
model.close()
}

override fun toString(): String = detach().toString()
override fun hashCode(): Int = detach().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KModel) return false
return detach() == other
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,17 @@ open class KCvc5Model(
uninterpretedSortUniverse(it) ?: error("missed sort universe for $it")
}

// The model is detached from the solver and therefore invalid
markInvalid()

return KModelImpl(ctx, interpretations, uninterpretedSortsUniverses)
}

private fun ensureContextActive() = check(cvc5Ctx.isActive) { "Context already closed" }

override fun toString(): String = detach().toString()
override fun hashCode(): Int = detach().hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KModel) return false
return detach() == other
override fun close() {
markInvalid()
}

private fun ensureContextActive() = check(cvc5Ctx.isActive) { "Context already closed" }

private fun getUninterpretedSortContext(sort: KUninterpretedSort): UninterpretedSortValueContext =
uninterpretedSortValues.getOrPut(sort) { UninterpretedSortValueContext(sort) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.ksmt.solver.KModel
import io.ksmt.solver.KSolver
import io.ksmt.solver.KSolverException
import io.ksmt.solver.KSolverStatus
import io.ksmt.solver.model.KNativeSolverModel
import io.ksmt.sort.KBoolSort
import io.ksmt.utils.library.NativeLibraryLoaderUtils
import java.util.TreeMap
Expand All @@ -27,7 +28,8 @@ open class KCvc5Solver(private val ctx: KContext) : KSolver<KCvc5SolverConfigura

private var lastCheckStatus = KSolverStatus.UNKNOWN
private var lastReasonOfUnknown: String? = null
private var lastModel: KCvc5Model? = null
private var lastCvcModel: KCvc5Model? = null
private var lastModel: KModel? = null

// we need TreeMap here (hashcode not implemented in Term)
private var cvc5LastAssumptions: TreeMap<Term, KExpr<KBoolSort>>? = null
Expand Down Expand Up @@ -130,16 +132,20 @@ open class KCvc5Solver(private val ctx: KContext) : KSolver<KCvc5SolverConfigura

override fun model(): KModel = cvc5Try {
require(lastCheckStatus == KSolverStatus.SAT) { "Models are only available after SAT checks" }
val model = lastModel ?: KCvc5Model(
lastModel?.let { return it }

val cvcModel = KCvc5Model(
ctx,
cvc5Ctx,
exprInternalizer,
cvc5Ctx.declarations().flatMapTo(hashSetOf()) { it },
cvc5Ctx.uninterpretedSorts().flatMapTo(hashSetOf()) { it },
)
lastModel = model

model
return KNativeSolverModel(cvcModel).also {
lastCvcModel = cvcModel
lastModel = it
}
}

override fun unsatCore(): List<KExpr<KBoolSort>> = cvc5Try {
Expand Down Expand Up @@ -205,7 +211,8 @@ open class KCvc5Solver(private val ctx: KContext) : KSolver<KCvc5SolverConfigura
/**
* Cvc5 model is only valid until the next check-sat call.
* */
lastModel?.markInvalid()
lastCvcModel?.markInvalid()
lastCvcModel = null
lastModel = null

lastCheckStatus = KSolverStatus.UNKNOWN
Expand Down
15 changes: 12 additions & 3 deletions ksmt-symfpu/src/main/kotlin/io/ksmt/symfpu/solver/KSymFpuModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ import io.ksmt.symfpu.operations.pack
import io.ksmt.utils.asExpr
import io.ksmt.utils.uncheckedCast

class KSymFpuModel(private val kModel: KModel, val ctx: KContext, val transformer: FpToBvTransformer) : KModel {
override val declarations: Set<KDecl<*>>
get() = kModel.declarations.mapTo(hashSetOf()) { transformer.findFpDeclByMappedDecl(it) ?: it }
class KSymFpuModel(underlyingModel: KModel, val ctx: KContext, val transformer: FpToBvTransformer) : KModel {
private var kModel: KModel = underlyingModel

override val declarations: Set<KDecl<*>> by lazy {
kModel.declarations.mapTo(hashSetOf()) { transformer.findFpDeclByMappedDecl(it) ?: it }
}

override val uninterpretedSorts
get() = kModel.uninterpretedSorts
Expand Down Expand Up @@ -252,6 +255,8 @@ class KSymFpuModel(private val kModel: KModel, val ctx: KContext, val transforme
}

override fun detach(): KModel {
kModel = kModel.detach()

declarations.forEach {
interpretation(it) ?: error("missed interpretation for $it")
}
Expand All @@ -263,6 +268,10 @@ class KSymFpuModel(private val kModel: KModel, val ctx: KContext, val transforme
return KModelImpl(ctx, interpretations.toMap(), uninterpretedSortsUniverses)
}

override fun close() {
kModel.close()
}

override fun toString(): String = detach().toString()
override fun hashCode(): Int = detach().hashCode()
override fun equals(other: Any?): Boolean {
Expand Down
4 changes: 4 additions & 0 deletions ksmt-symfpu/src/test/kotlin/io/ksmt/symfpu/ModelTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class ModelTest {
return KModelImpl(ctx, interpretations.toMap(), emptyMap())
}

override fun close() {
// ignored
}

override val uninterpretedSorts: Set<KUninterpretedSort> = emptySet()

override fun uninterpretedSortUniverse(sort: KUninterpretedSort): Set<KUninterpretedSortValue>? = null
Expand Down
Loading
Loading