Skip to content

Commit e89ec8f

Browse files
committed
cvc5: Extracted global cache for forking context and delegated to KCvc5ForkingSolverManager
1 parent 6d61643 commit e89ec8f

File tree

5 files changed

+145
-47
lines changed

5 files changed

+145
-47
lines changed

ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5Context.kt

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,24 @@ import io.ksmt.sort.KSortVisitor
3232
import io.ksmt.sort.KUninterpretedSort
3333
import java.util.TreeMap
3434

35-
class KCvc5Context private constructor(
35+
class KCvc5Context internal constructor(
3636
private val solver: Solver,
37+
/**
38+
* Used as context for expressions lifetime separation.
39+
* Exprs which stored in [KCvc5Context], created with [mkExprSolver]
40+
*/
3741
val mkExprSolver: Solver,
3842
private val ctx: KContext,
39-
parent: KCvc5Context?,
40-
val isForking: Boolean
43+
forkingSolverManager: KCvc5ForkingSolverManager? = null
4144
) : AutoCloseable {
42-
constructor(solver: Solver, mkExprSolver: Solver, ctx: KContext, isForking: Boolean = false)
43-
: this(solver, mkExprSolver, ctx, null, isForking)
45+
constructor(solver: Solver, mkExprSolver: Solver, ctx: KContext)
46+
: this(solver, mkExprSolver, ctx, null)
4447

45-
constructor(solver: Solver, ctx: KContext, isForking: Boolean = false)
46-
: this(solver, solver, ctx, null, isForking)
48+
constructor(solver: Solver, ctx: KContext)
49+
: this(solver, solver, ctx, null)
4750

4851
private var isClosed = false
52+
val isForking = forkingSolverManager != null
4953

5054
private val uninterpretedSortCollector = KUninterpretedSortCollector(this)
5155
private var exprCurrentLevelCacheRestorer = KCurrentScopeExprCacheRestorer(uninterpretedSortCollector, ctx)
@@ -72,14 +76,11 @@ class KCvc5Context private constructor(
7276
* that is in global cache, but whose sorts / decls have been erased after pop()
7377
* (and put this expr to the cache of current accumulated scope)
7478
*/
75-
private val currentAccumulatedScopeExpressions: HashMap<KExpr<*>, Term>
79+
private val currentAccumulatedScopeExpressions = HashMap<KExpr<*>, Term>()
7680
private val expressions: HashMap<KExpr<*>, Term>
7781

7882
/**
7983
* We can't use HashMap with Term and Sort (hashcode is not implemented)
80-
*
81-
* Avoid to close cache explicitly due to its sharing between forking hierarchy.
82-
* It will be garbage collected on last solver close in forking hierarchy
8384
*/
8485
private val cvc5Expressions: TreeMap<Term, KExpr<*>>
8586
private val sorts: HashMap<KSort, Sort>
@@ -102,28 +103,26 @@ class KCvc5Context private constructor(
102103

103104
init {
104105
if (isForking) {
105-
uninterpretedSorts = (parent?.uninterpretedSorts as? ScopedLinkedFrame)?.fork()
106-
?: ScopedLinkedFrame(::HashSet, ::HashSet)
107-
declarations = (parent?.declarations as? ScopedLinkedFrame)?.fork()
108-
?: ScopedLinkedFrame(::HashSet, ::HashSet)
106+
uninterpretedSorts = ScopedLinkedFrame(::HashSet, ::HashSet)
107+
declarations = ScopedLinkedFrame(::HashSet, ::HashSet)
109108
} else {
110109
uninterpretedSorts = ScopedArrayFrame(::HashSet)
111110
declarations = ScopedArrayFrame(::HashSet)
112111
}
113112

114-
if (parent != null) {
115-
currentAccumulatedScopeExpressions = parent.currentAccumulatedScopeExpressions.toMap(HashMap())
116-
expressions = parent.expressions
117-
cvc5Expressions = parent.cvc5Expressions
118-
sorts = parent.sorts
119-
cvc5Sorts = parent.cvc5Sorts
120-
decls = parent.decls
121-
cvc5Decls = parent.cvc5Decls
122-
uninterpretedSortValueDescriptors = parent.uninterpretedSortValueDescriptors
123-
uninterpretedSortValueInterpreter = parent.uninterpretedSortValueInterpreter
124-
uninterpretedSortValues = parent.uninterpretedSortValues
113+
if (forkingSolverManager != null) {
114+
with(forkingSolverManager) {
115+
expressions = findExpressionsCache()
116+
cvc5Expressions = findExpressionsReversedCache()
117+
sorts = findSortsCache()
118+
cvc5Sorts = findSortsReversedCache()
119+
decls = findDeclsCache()
120+
cvc5Decls = findDeclsReversedCache()
121+
uninterpretedSortValueDescriptors = findUninterpretedSortsValueDescriptors()
122+
uninterpretedSortValueInterpreter = findUninterpretedSortsValueInterpretersCache()
123+
uninterpretedSortValues = findUninterpretedSortValues()
124+
}
125125
} else {
126-
currentAccumulatedScopeExpressions = HashMap()
127126
expressions = HashMap()
128127
cvc5Expressions = TreeMap()
129128
sorts = HashMap()
@@ -155,12 +154,18 @@ class KCvc5Context private constructor(
155154
val isActive: Boolean
156155
get() = !isClosed
157156

158-
fun fork(solver: Solver, mkExprSolver: Solver): KCvc5Context =
159-
KCvc5Context(solver, mkExprSolver, ctx, this, true).also { forkCtx ->
157+
fun fork(solver: Solver, forkingSolverManager: KCvc5ForkingSolverManager): KCvc5Context {
158+
require(isForking) { "Can't fork non-forking context" }
159+
return KCvc5Context(solver, mkExprSolver, ctx, forkingSolverManager).also { forkCtx ->
160+
forkCtx.currentAccumulatedScopeExpressions += currentAccumulatedScopeExpressions
161+
(forkCtx.uninterpretedSorts as ScopedLinkedFrame).fork(uninterpretedSorts as ScopedLinkedFrame)
162+
(forkCtx.declarations as ScopedLinkedFrame).fork(declarations as ScopedLinkedFrame)
163+
160164
repeat(assertedConstraintLevels.size) {
161165
forkCtx.pushAssertionLevel()
162166
}
163167
}
168+
}
164169

165170
fun push() {
166171
declarations.push()
@@ -262,7 +267,7 @@ class KCvc5Context private constructor(
262267
*
263268
* todo: precise uninterpreted sort values tracking
264269
* */
265-
private data class UninterpretedSortValueDescriptor(
270+
internal data class UninterpretedSortValueDescriptor(
266271
val value: KUninterpretedSortValue,
267272
val nativeUniqueValueDescriptor: Term,
268273
val nativeValueTerm: Term
@@ -414,6 +419,18 @@ class KCvc5Context private constructor(
414419
isClosed = true
415420

416421
currentAccumulatedScopeExpressions.clear()
422+
423+
if (!isForking) {
424+
expressions.clear()
425+
cvc5Expressions.clear()
426+
sorts.clear()
427+
cvc5Sorts.clear()
428+
decls.clear()
429+
cvc5Decls.clear()
430+
uninterpretedSortValueDescriptors.clear()
431+
uninterpretedSortValueInterpreter.clear()
432+
uninterpretedSortValues.clear()
433+
}
417434
}
418435

419436
class KUninterpretedSortCollector(private val cvc5Ctx: KCvc5Context) : KSortVisitor<Unit> {

ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5ForkingSolver.kt

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,27 @@ open class KCvc5ForkingSolver internal constructor(
1616
ctx: KContext,
1717
private val manager: KCvc5ForkingSolverManager,
1818
/** store reference on Solver to separate lifetime of native expressions */
19-
private val mkExprSolver: Solver,
19+
mkExprSolver: Solver,
2020
parent: KCvc5ForkingSolver? = null
2121
) : KCvc5SolverBase(ctx), KForkingSolver<KCvc5SolverConfiguration>, KSolver<KCvc5SolverConfiguration> {
2222

2323
final override val cvc5Ctx: KCvc5Context
2424
private val isChild = parent != null
2525
private var assertionsInitiated = !isChild
2626

27-
private val trackedAssertions: ScopedLinkedFrame<TreeMap<Term, KExpr<KBoolSort>>>
28-
private val cvc5Assertions: ScopedLinkedFrame<TreeSet<Term>>
27+
private val trackedAssertions = ScopedLinkedFrame<TreeMap<Term, KExpr<KBoolSort>>>(::TreeMap, ::TreeMap)
28+
private val cvc5Assertions = ScopedLinkedFrame<TreeSet<Term>>(::TreeSet, ::TreeSet)
2929

3030
override val currentScope: UInt
3131
get() = trackedAssertions.currentScope
3232

3333
init {
3434
if (parent != null) {
35-
cvc5Ctx = parent.cvc5Ctx.fork(solver, this.mkExprSolver)
36-
trackedAssertions = parent.trackedAssertions.fork()
37-
cvc5Assertions = parent.cvc5Assertions.fork()
35+
cvc5Ctx = parent.cvc5Ctx.fork(solver, manager)
36+
trackedAssertions.fork(parent.trackedAssertions)
37+
cvc5Assertions.fork(parent.cvc5Assertions)
3838
} else {
39-
cvc5Ctx = KCvc5Context(solver, this.mkExprSolver, ctx, true)
40-
trackedAssertions = ScopedLinkedFrame(::TreeMap, ::TreeMap)
41-
cvc5Assertions = ScopedLinkedFrame(::TreeSet, ::TreeSet)
39+
cvc5Ctx = KCvc5Context(solver, mkExprSolver, ctx, manager)
4240
}
4341
}
4442

ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5ForkingSolverManager.kt

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,86 @@
11
package io.ksmt.solver.cvc5
22

33
import io.github.cvc5.Solver
4+
import io.github.cvc5.Sort
5+
import io.github.cvc5.Term
46
import io.ksmt.KContext
7+
import io.ksmt.decl.KDecl
8+
import io.ksmt.expr.KExpr
9+
import io.ksmt.expr.KUninterpretedSortValue
510
import io.ksmt.solver.KForkingSolver
611
import io.ksmt.solver.KForkingSolverManager
12+
import io.ksmt.sort.KSort
13+
import io.ksmt.sort.KUninterpretedSort
714
import java.util.IdentityHashMap
15+
import java.util.TreeMap
816
import java.util.concurrent.ConcurrentHashMap
917

1018
open class KCvc5ForkingSolverManager(private val ctx: KContext) : KForkingSolverManager<KCvc5SolverConfiguration> {
1119

1220
private val solvers: MutableSet<KCvc5ForkingSolver> = ConcurrentHashMap.newKeySet()
1321

1422
/**
15-
* for each parent to child hierarchy created only one mkExprSolver,
23+
* for each parent-to-child hierarchy created only one mkExprSolver,
1624
* which is responsible for native expressions lifetime
1725
*/
1826
private val forkingSolverToMkExprSolver = IdentityHashMap<KCvc5ForkingSolver, Solver>()
1927
private val mkExprSolverReferences = IdentityHashMap<Solver, Int>()
2028

29+
// shared cache
30+
private val expressionsCache = IdentityHashMap<Solver, ExpressionsCache>()
31+
private val expressionsReversedCache = IdentityHashMap<Solver, ExpressionsReversedCache>()
32+
private val sortsCache = IdentityHashMap<Solver, SortsCache>()
33+
private val sortsReversedCache = IdentityHashMap<Solver, SortsReversedCache>()
34+
private val declsCache = IdentityHashMap<Solver, DeclsCache>()
35+
private val declsReversedCache = IdentityHashMap<Solver, DeclsReversedCache>()
36+
37+
private val uninterpretedSortValueDescriptors = IdentityHashMap<Solver, UninterpretedSortValueDescriptors>()
38+
private val uninterpretedSortValueInterpretersCache =
39+
IdentityHashMap<Solver, UninterpretedSortValueInterpretersCache>()
40+
private val uninterpretedSortValues = IdentityHashMap<Solver, UninterpretedSortValues>()
41+
42+
private fun Solver.ensureRegisteredAsMkExprSolver() = require(this in mkExprSolverReferences) {
43+
"Solver is not registered by this manager"
44+
}
45+
46+
internal fun KCvc5Context.findExpressionsCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
47+
expressionsCache.getOrPut(mkExprSolver) { ExpressionsCache() }
48+
}
49+
50+
internal fun KCvc5Context.findExpressionsReversedCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
51+
expressionsReversedCache.getOrPut(mkExprSolver) { ExpressionsReversedCache() }
52+
}
53+
54+
internal fun KCvc5Context.findSortsCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
55+
sortsCache.getOrPut(mkExprSolver) { SortsCache() }
56+
}
57+
58+
internal fun KCvc5Context.findSortsReversedCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
59+
sortsReversedCache.getOrPut(mkExprSolver) { SortsReversedCache() }
60+
}
61+
62+
internal fun KCvc5Context.findDeclsCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
63+
declsCache.getOrPut(mkExprSolver) { DeclsCache() }
64+
}
65+
66+
internal fun KCvc5Context.findDeclsReversedCache() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
67+
declsReversedCache.getOrPut(mkExprSolver) { DeclsReversedCache() }
68+
}
69+
70+
internal fun KCvc5Context.findUninterpretedSortsValueDescriptors() = mkExprSolver.ensureRegisteredAsMkExprSolver()
71+
.let {
72+
uninterpretedSortValueDescriptors.getOrPut(mkExprSolver) { UninterpretedSortValueDescriptors() }
73+
}
74+
75+
internal fun KCvc5Context.findUninterpretedSortsValueInterpretersCache() = mkExprSolver
76+
.ensureRegisteredAsMkExprSolver().let {
77+
uninterpretedSortValueInterpretersCache.getOrPut(mkExprSolver) { UninterpretedSortValueInterpretersCache() }
78+
}
79+
80+
internal fun KCvc5Context.findUninterpretedSortValues() = mkExprSolver.ensureRegisteredAsMkExprSolver().let {
81+
uninterpretedSortValues.getOrPut(mkExprSolver) { UninterpretedSortValues() }
82+
}
83+
2184
override fun mkForkingSolver(): KForkingSolver<KCvc5SolverConfiguration> {
2285
val mkExprSolver = Solver()
2386
incRef(mkExprSolver)
@@ -58,6 +121,16 @@ open class KCvc5ForkingSolverManager(private val ctx: KContext) : KForkingSolver
58121
val referencesAfterDec = mkExprSolverReferences.getValue(mkExprSolver) - 1
59122
if (referencesAfterDec == 0) {
60123
mkExprSolverReferences -= mkExprSolver
124+
expressionsCache -= mkExprSolver
125+
expressionsReversedCache -= mkExprSolver
126+
sortsCache -= mkExprSolver
127+
sortsReversedCache -= mkExprSolver
128+
declsCache -= mkExprSolver
129+
declsReversedCache -= mkExprSolver
130+
uninterpretedSortValueDescriptors -= mkExprSolver
131+
uninterpretedSortValueInterpretersCache -= mkExprSolver
132+
uninterpretedSortValues -= mkExprSolver
133+
61134
mkExprSolver.close()
62135
} else {
63136
mkExprSolverReferences[mkExprSolver] = referencesAfterDec
@@ -70,3 +143,14 @@ open class KCvc5ForkingSolverManager(private val ctx: KContext) : KForkingSolver
70143
}
71144
}
72145
}
146+
147+
private typealias ExpressionsCache = HashMap<KExpr<*>, Term>
148+
private typealias ExpressionsReversedCache = TreeMap<Term, KExpr<*>>
149+
private typealias SortsCache = HashMap<KSort, Sort>
150+
private typealias SortsReversedCache = TreeMap<Sort, KSort>
151+
private typealias DeclsCache = HashMap<KDecl<*>, Term>
152+
private typealias DeclsReversedCache = TreeMap<Term, KDecl<*>>
153+
private typealias UninterpretedSortValueDescriptors = ArrayList<KCvc5Context.UninterpretedSortValueDescriptor>
154+
private typealias UninterpretedSortValueInterpretersCache = HashMap<KUninterpretedSort, Term>
155+
@Suppress("MaxLineLength")
156+
private typealias UninterpretedSortValues = HashMap<KUninterpretedSort, MutableList<Pair<Term, KUninterpretedSortValue>>>

ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/ScopedFrame.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,10 @@ internal class ScopedLinkedFrame<T> private constructor(
106106
current = LinkedFrame(newTopFrame, current.previous)
107107
}
108108

109-
fun fork(): ScopedLinkedFrame<T> = ScopedLinkedFrame(
110-
current,
111-
createNewFrame,
112-
copyFrame
113-
).also { it.recreateTopFrame() }
109+
fun fork(parent: ScopedLinkedFrame<T>) {
110+
current = parent.current
111+
recreateTopFrame()
112+
}
114113

115114
private inline fun forEachReversed(action: (T) -> Unit) {
116115
var cur: LinkedFrame<T>? = current

ksmt-test/src/test/kotlin/io/ksmt/test/KForkingSolverTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ class KForkingSolverTest {
284284
with(ctx) {
285285
val parent = mkSolver(ctx)
286286
val x by intSort
287-
val f = x ge 100.expr
287+
val f = x gt 100.expr
288288

289289
parent.assert(f)
290290
parent.check().also { require(it == KSolverStatus.SAT) }
@@ -296,7 +296,7 @@ class KForkingSolverTest {
296296

297297
fork.assert(f and (x eq xVal))
298298
fork.check().also { assertEquals(KSolverStatus.SAT, it) }
299-
assertEquals(fork.model().eval(x), xVal)
299+
assertEquals(xVal, fork.model().eval(x))
300300
}
301301

302302
}

0 commit comments

Comments
 (0)