11package org.ksmt.solver.bitwuzla
22
3+ import it.unimi.dsi.fastutil.longs.LongOpenHashSet
34import org.ksmt.KContext
4- import org.ksmt.decl.KConstDecl
55import org.ksmt.expr.KExpr
66import org.ksmt.solver.KModel
77import org.ksmt.solver.KSolver
88import org.ksmt.solver.KSolverStatus
9+ import org.ksmt.solver.bitwuzla.bindings.BitwuzlaNativeException
910import org.ksmt.solver.bitwuzla.bindings.BitwuzlaOption
1011import org.ksmt.solver.bitwuzla.bindings.BitwuzlaResult
1112import org.ksmt.solver.bitwuzla.bindings.BitwuzlaTerm
13+ import org.ksmt.solver.bitwuzla.bindings.BitwuzlaTermArray
1214import org.ksmt.solver.bitwuzla.bindings.Native
1315import org.ksmt.sort.KBoolSort
1416import kotlin.time.Duration
@@ -21,15 +23,19 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
2123 open val exprConverter: KBitwuzlaExprConverter by lazy {
2224 KBitwuzlaExprConverter (ctx, bitwuzlaCtx)
2325 }
26+
2427 private var lastCheckStatus = KSolverStatus .UNKNOWN
28+ private var lastReasonOfUnknown: String? = null
29+ private var lastAssumptions: TrackedAssumptions ? = null
30+ private var lastModel: KBitwuzlaModel ? = null
2531
2632 init {
2733 Native .bitwuzlaSetOption(bitwuzlaCtx.bitwuzla, BitwuzlaOption .BITWUZLA_OPT_INCREMENTAL , value = 1 )
2834 Native .bitwuzlaSetOption(bitwuzlaCtx.bitwuzla, BitwuzlaOption .BITWUZLA_OPT_PRODUCE_MODELS , value = 1 )
2935 }
3036
31- private var trackVars = mutableListOf<Pair <KExpr <KBoolSort >, BitwuzlaTerm >> ()
32- private val trackVarsAssertionFrames = arrayListOf (trackVars )
37+ private var trackedAssertions = mutableListOf<Pair <KExpr <KBoolSort >, BitwuzlaTerm >> ()
38+ private val trackVarsAssertionFrames = arrayListOf (trackedAssertions )
3339
3440 override fun configure (configurator : KBitwuzlaSolverConfiguration .() -> Unit ) {
3541 KBitwuzlaSolverConfigurationImpl (bitwuzlaCtx.bitwuzla).configurator()
@@ -46,23 +52,23 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
4652 Native .bitwuzlaAssert(bitwuzlaCtx.bitwuzla, assertionWithAxioms.assertion)
4753 }
4854
49- override fun assertAndTrack (expr : KExpr <KBoolSort >, trackVar : KConstDecl < KBoolSort > ) = bitwuzlaCtx.bitwuzlaTry {
50- ctx.ensureContextMatch(expr, trackVar )
55+ override fun assertAndTrack (expr : KExpr <KBoolSort >) = bitwuzlaCtx.bitwuzlaTry {
56+ ctx.ensureContextMatch(expr)
5157
52- val trackVarExpr = ctx.mkConstApp(trackVar )
58+ val trackVarExpr = ctx.mkFreshConst( " track " , ctx.boolSort )
5359 val trackedExpr = with (ctx) { ! trackVarExpr or expr }
5460
5561 assert (trackedExpr)
5662
5763 val trackVarTerm = with (exprInternalizer) { trackVarExpr.internalize() }
58- trackVars + = trackVarExpr to trackVarTerm
64+ trackedAssertions + = expr to trackVarTerm
5965 }
6066
6167 override fun push (): Unit = bitwuzlaCtx.bitwuzlaTry {
6268 Native .bitwuzlaPush(bitwuzlaCtx.bitwuzla, nlevels = 1 )
6369
64- trackVars = trackVars .toMutableList()
65- trackVarsAssertionFrames.add(trackVars )
70+ trackedAssertions = trackedAssertions .toMutableList()
71+ trackVarsAssertionFrames.add(trackedAssertions )
6672
6773 bitwuzlaCtx.createNestedDeclarationScope()
6874 }
@@ -80,35 +86,28 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
8086 bitwuzlaCtx.popDeclarationScope()
8187 }
8288
83- trackVars = trackVarsAssertionFrames.last()
89+ trackedAssertions = trackVarsAssertionFrames.last()
8490
8591 Native .bitwuzlaPop(bitwuzlaCtx.bitwuzla, n.toInt())
8692 }
8793
8894 override fun check (timeout : Duration ): KSolverStatus =
8995 checkWithAssumptions(emptyList(), timeout)
9096
91- private val lastAssumptions = arrayListOf<Pair <KExpr <KBoolSort >, BitwuzlaTerm >> ()
92-
93- private fun assumeExpr (expr : KExpr <KBoolSort >, term : BitwuzlaTerm ) {
94- lastAssumptions + = expr to term
95- Native .bitwuzlaAssume(bitwuzlaCtx.bitwuzla, term)
96- }
97-
9897 override fun checkWithAssumptions (assumptions : List <KExpr <KBoolSort >>, timeout : Duration ): KSolverStatus =
99- bitwuzlaCtx.bitwuzlaTry {
98+ bitwuzlaTryCheck {
10099 ctx.ensureContextMatch(assumptions)
101100
102- invalidatePreviousModel()
103- lastAssumptions.clear()
101+ val currentAssumptions = TrackedAssumptions ().also { lastAssumptions = it }
104102
105- trackVars .forEach {
106- assumeExpr (it.first, it.second )
103+ trackedAssertions .forEach {
104+ currentAssumptions.assumeTrackedAssertion (it)
107105 }
108106
109- assumptions.forEach {
110- val assumptionTerm = with (exprInternalizer) { it.internalize() }
111- assumeExpr(it, assumptionTerm)
107+ with (exprInternalizer) {
108+ assumptions.forEach {
109+ currentAssumptions.assumeAssumption(it, it.internalize())
110+ }
112111 }
113112
114113 checkWithTimeout(timeout).processCheckResult()
@@ -120,16 +119,6 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
120119 Native .bitwuzlaCheckSatTimeoutResult(bitwuzlaCtx.bitwuzla, timeout.inWholeMilliseconds)
121120 }
122121
123- private var lastModel: KBitwuzlaModel ? = null
124-
125- /* *
126- * Bitwuzla model is only valid until the next check-sat call.
127- * */
128- private fun invalidatePreviousModel () {
129- lastModel?.markInvalid()
130- lastModel = null
131- }
132-
133122 override fun model (): KModel = bitwuzlaCtx.bitwuzlaTry {
134123 require(lastCheckStatus == KSolverStatus .SAT ) { " Model are only available after SAT checks" }
135124 val model = lastModel ? : KBitwuzlaModel (
@@ -143,9 +132,8 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
143132
144133 override fun unsatCore (): List <KExpr <KBoolSort >> = bitwuzlaCtx.bitwuzlaTry {
145134 require(lastCheckStatus == KSolverStatus .UNSAT ) { " Unsat cores are only available after UNSAT checks" }
146- val unsatCore = Native .bitwuzlaGetUnsatAssumptions(bitwuzlaCtx.bitwuzla).toSet()
147-
148- return lastAssumptions.filter { it.second in unsatCore }.map { it.first }
135+ val unsatAssumptions = Native .bitwuzlaGetUnsatAssumptions(bitwuzlaCtx.bitwuzla)
136+ lastAssumptions?.resolveUnsatCore(unsatAssumptions) ? : emptyList()
149137 }
150138
151139 override fun reasonOfUnknown (): String = bitwuzlaCtx.bitwuzlaTry {
@@ -154,7 +142,7 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
154142 }
155143
156144 // There is no way to retrieve reason of unknown from Bitwuzla in general case.
157- return " unknown"
145+ return lastReasonOfUnknown ? : " unknown"
158146 }
159147
160148 override fun interrupt () = bitwuzlaCtx.bitwuzlaTry {
@@ -170,4 +158,42 @@ open class KBitwuzlaSolver(private val ctx: KContext) : KSolver<KBitwuzlaSolverC
170158 BitwuzlaResult .BITWUZLA_UNSAT -> KSolverStatus .UNSAT
171159 BitwuzlaResult .BITWUZLA_UNKNOWN -> KSolverStatus .UNKNOWN
172160 }.also { lastCheckStatus = it }
161+
162+ private fun invalidateSolverState () {
163+ /* *
164+ * Bitwuzla model is only valid until the next check-sat call.
165+ * */
166+ lastModel?.markInvalid()
167+ lastModel = null
168+
169+ lastCheckStatus = KSolverStatus .UNKNOWN
170+ lastReasonOfUnknown = null
171+
172+ lastAssumptions = null
173+ }
174+
175+ private inline fun bitwuzlaTryCheck (body : () -> KSolverStatus ): KSolverStatus = try {
176+ invalidateSolverState()
177+ body()
178+ } catch (ex: BitwuzlaNativeException ) {
179+ lastReasonOfUnknown = ex.message
180+ KSolverStatus .UNKNOWN .also { lastCheckStatus = it }
181+ }
182+
183+ private inner class TrackedAssumptions {
184+ private val assumedExprs = arrayListOf<Pair <KExpr <KBoolSort >, BitwuzlaTerm >> ()
185+
186+ fun assumeTrackedAssertion (trackedAssertion : Pair <KExpr <KBoolSort >, BitwuzlaTerm >) {
187+ assumedExprs.add(trackedAssertion)
188+ Native .bitwuzlaAssume(bitwuzlaCtx.bitwuzla, trackedAssertion.second)
189+ }
190+
191+ fun assumeAssumption (expr : KExpr <KBoolSort >, term : BitwuzlaTerm ) =
192+ assumeTrackedAssertion(expr to term)
193+
194+ fun resolveUnsatCore (unsatAssumptions : BitwuzlaTermArray ): List <KExpr <KBoolSort >> {
195+ val unsatCoreTerms = LongOpenHashSet (unsatAssumptions)
196+ return assumedExprs.mapNotNull { (expr, term) -> expr.takeIf { unsatCoreTerms.contains(term) } }
197+ }
198+ }
173199}
0 commit comments