diff --git a/README.md b/README.md index a652119be..c2166b9fe 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ repositories { } // core -implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.0") +implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.1") // z3 solver -implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.0") +implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.1") ``` ## Usage diff --git a/Requirements.md b/Requirements.md index 42990e685..272a2982f 100644 --- a/Requirements.md +++ b/Requirements.md @@ -12,7 +12,7 @@ | [Yices2 solver support](#yices2-solver-support) | Done | | [CVC5 solver support](#cvc5-solver-support) | In progress | | [External process runner](#external-process-runner) | Done | -| [Portfolio solver](#portfolio-solver) | In progress | +| [Portfolio solver](#portfolio-solver) | Done | | [Solver configuration API](#solver-configuration-api) | Done | | [Deployment](#deployment) | Done partially | | [Expression simplification / evaluation](#expression-simplification--evaluation) | Done | diff --git a/buildSrc/src/main/kotlin/org.ksmt.ksmt-base.gradle.kts b/buildSrc/src/main/kotlin/org.ksmt.ksmt-base.gradle.kts index 167ffca2a..c1152caaf 100644 --- a/buildSrc/src/main/kotlin/org.ksmt.ksmt-base.gradle.kts +++ b/buildSrc/src/main/kotlin/org.ksmt.ksmt-base.gradle.kts @@ -9,7 +9,7 @@ plugins { } group = "org.ksmt" -version = "0.4.0" +version = "0.4.1" repositories { mavenCentral() diff --git a/docs/getting-started.md b/docs/getting-started.md index 7955709ec..e257b3de8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -18,7 +18,7 @@ repositories { ```kotlin dependencies { // core - implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.0") + implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.1") } ``` @@ -26,9 +26,9 @@ dependencies { ```kotlin dependencies { // z3 - implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.0") + implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.1") // bitwuzla - implementation("com.github.UnitTestBot.ksmt:ksmt-bitwuzla:0.4.0") + implementation("com.github.UnitTestBot.ksmt:ksmt-bitwuzla:0.4.1") } ``` SMT solver specific packages are provided with solver native binaries. diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 43f1605a6..801332a7a 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -10,9 +10,9 @@ repositories { dependencies { // core - implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.0") + implementation("com.github.UnitTestBot.ksmt:ksmt-core:0.4.1") // z3 solver - implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.0") + implementation("com.github.UnitTestBot.ksmt:ksmt-z3:0.4.1") } java { diff --git a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaContext.kt b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaContext.kt index b678fe7b7..43d93a43d 100644 --- a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaContext.kt +++ b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaContext.kt @@ -3,7 +3,10 @@ package org.ksmt.solver.bitwuzla import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.decl.KFuncDecl +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KConst import org.ksmt.expr.KExistentialQuantifier import org.ksmt.expr.KExpr @@ -16,7 +19,11 @@ import org.ksmt.solver.bitwuzla.bindings.BitwuzlaNativeException import org.ksmt.solver.bitwuzla.bindings.BitwuzlaSort import org.ksmt.solver.bitwuzla.bindings.BitwuzlaTerm import org.ksmt.solver.bitwuzla.bindings.Native +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFpRoundingModeSort @@ -277,6 +284,24 @@ open class KBitwuzlaContext(val ctx: KContext) : AutoCloseable { sort.range.accept(this) } + override fun visit(sort: KArray2Sort) { + sort.domain0.accept(this) + sort.domain1.accept(this) + sort.range.accept(this) + } + + override fun visit(sort: KArray3Sort) { + sort.domain0.accept(this) + sort.domain1.accept(this) + sort.domain2.accept(this) + sort.range.accept(this) + } + + override fun visit(sort: KArrayNSort) { + sort.domainSorts.forEach { it.accept(this) } + sort.range.accept(this) + } + override fun visit(sort: KBoolSort) { } @@ -344,7 +369,7 @@ open class KBitwuzlaContext(val ctx: KContext) : AutoCloseable { return super.transform(expr) } - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { registerDeclIfNotIgnored(expr.function) return super.transform(expr) } @@ -365,7 +390,20 @@ open class KBitwuzlaContext(val ctx: KContext) : AutoCloseable { } override fun transform(expr: KArrayLambda): KExpr> = - expr.transformQuantifier(listOf(expr.indexVarDecl), expr.body) + expr.transformQuantifier(expr.indexVarDeclarations, expr.body) + + override fun transform( + expr: KArray2Lambda + ): KExpr> = + expr.transformQuantifier(expr.indexVarDeclarations, expr.body) + + override fun transform( + expr: KArray3Lambda + ): KExpr> = + expr.transformQuantifier(expr.indexVarDeclarations, expr.body) + + override fun transform(expr: KArrayNLambda): KExpr> = + expr.transformQuantifier(expr.indexVarDeclarations, expr.body) override fun transform(expr: KExistentialQuantifier): KExpr = expr.transformQuantifier(expr.bounds, expr.body) diff --git a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprConverter.kt b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprConverter.kt index 65d148c62..a0d82692b 100644 --- a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprConverter.kt +++ b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprConverter.kt @@ -136,7 +136,7 @@ open class KBitwuzlaExprConverter( // array BitwuzlaKind.BITWUZLA_KIND_CONST_ARRAY -> expr.convert { value: KExpr -> - val sort = Native.bitwuzlaTermGetSort(expr).convertSort() as KArraySort<*, *> + val sort: KArraySort = Native.bitwuzlaTermGetSort(expr).convertSort().uncheckedCast() convertConstArrayExpr(sort, value) } @@ -652,7 +652,7 @@ open class KBitwuzlaExprConverter( fun convertConstArrayExpr( currentSort: KArraySort, value: KExpr - ): KArrayConst = with(ctx) { + ): KArrayConst, KSort> = with(ctx) { val expectedValueSort = selectExpectedSort(currentSort.range, value.sort) val expectedArraySort = mkArraySort(currentSort.domain, expectedValueSort) return mkArrayConst(expectedArraySort, value.convertToExpectedIfNeeded(expectedValueSort)) @@ -796,7 +796,8 @@ open class KBitwuzlaExprConverter( check(expectedDomain !is KArraySort<*, *> && expectedRange !is KArraySort<*, *>) { "Bitwuzla doesn't support nested arrays" } - ArrayAdapterExpr(this@ensureArrayExprSortMatch, expectedDomain, expectedRange) + val array: KExpr> = this@ensureArrayExprSortMatch.uncheckedCast() + ArrayAdapterExpr(array, expectedDomain, expectedRange) } } } diff --git a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprInternalizer.kt b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprInternalizer.kt index 7680d39cf..c867b7f89 100644 --- a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprInternalizer.kt +++ b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaExprInternalizer.kt @@ -7,8 +7,17 @@ import org.ksmt.decl.KDeclVisitor import org.ksmt.decl.KFuncDecl import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -148,7 +157,11 @@ import org.ksmt.solver.bitwuzla.bindings.BitwuzlaTerm import org.ksmt.solver.bitwuzla.bindings.Native import org.ksmt.solver.util.KExprInternalizerBase import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv32Sort @@ -696,6 +709,22 @@ open class KBitwuzlaExprInternalizer( } } + override fun transform( + expr: KArray2Store + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Store + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform(expr: KArrayNStore): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + override fun transform(expr: KArraySelect) = with(expr) { transform(array, index) { a: BitwuzlaTerm, i: BitwuzlaTerm -> mkArraySelectTerm(Native.bitwuzlaTermGetBitwuzlaKind(a), a, i) @@ -709,7 +738,21 @@ open class KBitwuzlaExprInternalizer( Native.bitwuzlaMkTerm2(bitwuzlaCtx.bitwuzla, BitwuzlaKind.BITWUZLA_KIND_ARRAY_SELECT, array, idx) } - override fun transform(expr: KArrayConst) = with(expr) { + override fun transform(expr: KArray2Select): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Select + ): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform(expr: KArrayNSelect): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun , R : KSort> transform(expr: KArrayConst) = with(expr) { transform(value) { value: BitwuzlaTerm -> Native.bitwuzlaMkConstArray(bitwuzlaCtx.bitwuzla, sort.internalizeSort(), value) } @@ -732,6 +775,24 @@ open class KBitwuzlaExprInternalizer( private fun mkLambdaTerm(boundVar: BitwuzlaTerm, body: BitwuzlaTerm): BitwuzlaTerm = Native.bitwuzlaMkTerm2(bitwuzlaCtx.bitwuzla, BitwuzlaKind.BITWUZLA_KIND_LAMBDA, boundVar, body) + override fun transform( + expr: KArray2Lambda + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArrayNLambda + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + override fun transform( expr: KExistentialQuantifier ): KExpr = expr.internalizeQuantifier { args -> @@ -1007,7 +1068,7 @@ open class KBitwuzlaExprInternalizer( } } - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { TODO("KFunctionAsArray internalization is not implemented in bitwuzla") } @@ -1079,6 +1140,20 @@ open class KBitwuzlaExprInternalizer( Native.bitwuzlaMkArraySort(bitwuzlaCtx.bitwuzla, domain, range) } + override fun visit(sort: KArray2Sort): BitwuzlaSort { + TODO("Multi-indexed arrays are not supported") + } + + override fun visit( + sort: KArray3Sort + ): BitwuzlaSort { + TODO("Multi-indexed arrays are not supported") + } + + override fun visit(sort: KArrayNSort): BitwuzlaSort { + TODO("Multi-indexed arrays are not supported") + } + override fun visit(sort: S): BitwuzlaSort = bitwuzlaCtx.internalizeSort(sort) { val size = sort.sizeBits.toInt() @@ -1208,6 +1283,16 @@ open class KBitwuzlaExprInternalizer( override fun visit(sort: KArraySort): Boolean = sort.domain.accept(this) || sort.range.accept(this) + override fun visit(sort: KArray2Sort): Boolean = + sort.domain0.accept(this) || sort.domain1.accept(this) || sort.range.accept(this) + + override fun visit(sort: KArray3Sort): Boolean = + sort.domain0.accept(this) || sort.domain1.accept(this) + || sort.domain2.accept(this) || sort.range.accept(this) + + override fun visit(sort: KArrayNSort): Boolean = + sort.domainSorts.any { it.accept(this) } || sort.range.accept(this) + override fun visit(sort: KBoolSort): Boolean = false override fun visit(sort: KIntSort): Boolean = false override fun visit(sort: KRealSort): Boolean = false diff --git a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaModel.kt b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaModel.kt index 13d583a2e..645c36d15 100644 --- a/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaModel.kt +++ b/ksmt-bitwuzla/src/main/kotlin/org/ksmt/solver/bitwuzla/KBitwuzlaModel.kt @@ -142,7 +142,7 @@ open class KBitwuzlaModel( decl: KDecl, term: BitwuzlaTerm ): KModel.KFuncInterp = with(converter) { - val sort = decl.sort as KArraySort<*, *> + val sort: KArraySort = decl.sort.uncheckedCast() val entries = mutableListOf>() val interp = Native.bitwuzlaGetArrayValue(bitwuzlaCtx.bitwuzla, term) @@ -168,7 +168,7 @@ open class KBitwuzlaModel( decl = decl, vars = emptyList(), entries = emptyList(), - default = mkFunctionAsArray(arrayInterpDecl).uncheckedCast() + default = mkFunctionAsArray(sort, arrayInterpDecl).uncheckedCast() ) } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/KContext.kt b/ksmt-core/src/main/kotlin/org/ksmt/KContext.kt index 2749089b6..fd8612688 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/KContext.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/KContext.kt @@ -21,7 +21,13 @@ import org.ksmt.decl.KArithMulDecl import org.ksmt.decl.KArithPowerDecl import org.ksmt.decl.KArithSubDecl import org.ksmt.decl.KArithUnaryMinusDecl +import org.ksmt.decl.KArray2SelectDecl +import org.ksmt.decl.KArray2StoreDecl +import org.ksmt.decl.KArray3SelectDecl +import org.ksmt.decl.KArray3StoreDecl import org.ksmt.decl.KArrayConstDecl +import org.ksmt.decl.KArrayNSelectDecl +import org.ksmt.decl.KArrayNStoreDecl import org.ksmt.decl.KArraySelectDecl import org.ksmt.decl.KArrayStoreDecl import org.ksmt.decl.KBitVec16ValueDecl @@ -139,8 +145,17 @@ import org.ksmt.decl.KZeroExtDecl import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -283,7 +298,9 @@ import org.ksmt.expr.rewrite.simplify.simplifyArithPower import org.ksmt.expr.rewrite.simplify.simplifyArithSub import org.ksmt.expr.rewrite.simplify.simplifyArithUnaryMinus import org.ksmt.expr.rewrite.simplify.simplifyArraySelect +import org.ksmt.expr.rewrite.simplify.simplifyArrayNSelect import org.ksmt.expr.rewrite.simplify.simplifyArrayStore +import org.ksmt.expr.rewrite.simplify.simplifyArrayNStore import org.ksmt.expr.rewrite.simplify.simplifyBv2IntExpr import org.ksmt.expr.rewrite.simplify.simplifyBvAddExpr import org.ksmt.expr.rewrite.simplify.simplifyBvAddNoOverflowExpr @@ -375,7 +392,11 @@ import org.ksmt.expr.rewrite.simplify.simplifyRealToFpExpr import org.ksmt.expr.rewrite.simplify.simplifyRealToInt import org.ksmt.expr.rewrite.simplify.simplifyXor import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort @@ -487,12 +508,41 @@ open class KContext( fun mkBoolSort(): KBoolSort = boolSort - private val arraySortCache = mkCache, KArraySort<*, *>>(operationMode) + private val arraySortCache = mkCache, KArraySort<*, *>>(operationMode) + private val array2SortCache = mkCache, KArray2Sort<*, *, *>>(operationMode) + private val array3SortCache = mkCache, KArray3Sort<*, *, *, *>>(operationMode) + private val arrayNSortCache = mkCache, KArrayNSort<*>>(operationMode) fun mkArraySort(domain: D, range: R): KArraySort = ensureContextActive { ensureContextMatch(domain, range) - arraySortCache.getOrPut(domain to range) { KArraySort(this, domain, range) }.uncheckedCast() + val sort = KArraySort(this, domain, range) + (arraySortCache.putIfAbsent(sort, sort) ?: sort).uncheckedCast() + } + + fun mkArraySort(domain0: D0, domain1: D1, range: R): KArray2Sort = + ensureContextActive { + ensureContextMatch(domain0, domain1, range) + val sort = KArray2Sort(this, domain0, domain1, range) + (array2SortCache.putIfAbsent(sort, sort) ?: sort).uncheckedCast() + } + + fun mkArraySort( + domain0: D0, domain1: D1, domain2: D2, range: R + ): KArray3Sort = + ensureContextActive { + ensureContextMatch(domain0, domain1, domain2, range) + val sort = KArray3Sort(this, domain0, domain1, domain2, range) + (array3SortCache.putIfAbsent(sort, sort) ?: sort).uncheckedCast() + } + + fun mkArrayNSort(domain: List, range: R): KArrayNSort = + ensureContextActive { + ensureContextMatch(range) + ensureContextMatch(domain) + + val sort = KArrayNSort(this, domain, range) + (arrayNSortCache.putIfAbsent(sort, sort) ?: sort).uncheckedCast() } private val intSortCache by lazy { @@ -760,6 +810,9 @@ open class KContext( // array private val arrayStoreCache = mkAstInterner>() + private val array2StoreCache = mkAstInterner>() + private val array3StoreCache = mkAstInterner>() + private val arrayNStoreCache = mkAstInterner>() fun mkArrayStore( array: KExpr>, @@ -768,6 +821,30 @@ open class KContext( ): KExpr> = mkSimplified(array, index, value, KContext::simplifyArrayStore, ::mkArrayStoreNoSimplify) + fun mkArrayStore( + array: KExpr>, + index0: KExpr, + index1: KExpr, + value: KExpr + ): KExpr> = + mkSimplified(array, index0, index1, value, KContext::simplifyArrayStore, ::mkArrayStoreNoSimplify) + + fun mkArrayStore( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr, + value: KExpr + ): KExpr> = + mkSimplified(array, index0, index1, index2, value, KContext::simplifyArrayStore, ::mkArrayStoreNoSimplify) + + fun mkArrayNStore( + array: KExpr>, + indices: List>, + value: KExpr + ): KExpr> = + mkSimplified(array, indices, value, KContext::simplifyArrayNStore, ::mkArrayNStoreNoSimplify) + fun mkArrayStoreNoSimplify( array: KExpr>, index: KExpr, @@ -777,13 +854,66 @@ open class KContext( KArrayStore(this, array, index, value) }.cast() + fun mkArrayStoreNoSimplify( + array: KExpr>, + index0: KExpr, + index1: KExpr, + value: KExpr + ): KArray2Store = array2StoreCache.createIfContextActive { + ensureContextMatch(array, index0, index1, value) + KArray2Store(this, array, index0, index1, value) + }.cast() + + fun mkArrayStoreNoSimplify( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr, + value: KExpr + ): KArray3Store = array3StoreCache.createIfContextActive { + ensureContextMatch(array, index0, index1, index2, value) + KArray3Store(this, array, index0, index1, index2, value) + }.cast() + + fun mkArrayNStoreNoSimplify( + array: KExpr>, + indices: List>, + value: KExpr + ): KArrayNStore = arrayNStoreCache.createIfContextActive { + ensureContextMatch(indices) + ensureContextMatch(array, value) + + KArrayNStore(this, array, indices.uncheckedCast(), value) + }.cast() + private val arraySelectCache = mkAstInterner>() + private val array2SelectCache = mkAstInterner>() + private val array3SelectCache = mkAstInterner>() + private val arrayNSelectCache = mkAstInterner>() fun mkArraySelect( array: KExpr>, index: KExpr ): KExpr = mkSimplified(array, index, KContext::simplifyArraySelect, ::mkArraySelectNoSimplify) + fun mkArraySelect( + array: KExpr>, + index0: KExpr, + index1: KExpr + ): KExpr = mkSimplified(array, index0, index1, KContext::simplifyArraySelect, ::mkArraySelectNoSimplify) + + fun mkArraySelect( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr + ): KExpr = mkSimplified(array, index0, index1, index2, KContext::simplifyArraySelect, ::mkArraySelectNoSimplify) + + fun mkArrayNSelect( + array: KExpr>, + indices: List> + ): KExpr = mkSimplified(array, indices, KContext::simplifyArrayNSelect, ::mkArrayNSelectNoSimplify) + fun mkArraySelectNoSimplify( array: KExpr>, index: KExpr @@ -792,37 +922,112 @@ open class KContext( KArraySelect(this, array, index) }.cast() - private val arrayConstCache = mkAstInterner>() + fun mkArraySelectNoSimplify( + array: KExpr>, + index0: KExpr, + index1: KExpr + ): KArray2Select = array2SelectCache.createIfContextActive { + ensureContextMatch(array, index0, index1) + KArray2Select(this, array, index0, index1) + }.cast() + + fun mkArraySelectNoSimplify( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr + ): KArray3Select = array3SelectCache.createIfContextActive { + ensureContextMatch(array, index0, index1, index2) + KArray3Select(this, array, index0, index1, index2) + }.cast() + + fun mkArrayNSelectNoSimplify( + array: KExpr>, + indices: List> + ): KArrayNSelect = arrayNSelectCache.createIfContextActive { + ensureContextMatch(array) + ensureContextMatch(indices) - fun mkArrayConst( - arraySort: KArraySort, + KArrayNSelect(this, array, indices.uncheckedCast()) + }.cast() + + private val arrayConstCache = mkAstInterner, out KSort>>() + + fun , R : KSort> mkArrayConst( + arraySort: A, value: KExpr - ): KArrayConst = arrayConstCache.createIfContextActive { + ): KArrayConst = arrayConstCache.createIfContextActive { ensureContextMatch(arraySort, value) KArrayConst(this, arraySort, value) }.cast() - private val functionAsArrayCache = mkAstInterner>() + private val functionAsArrayCache = mkAstInterner, out KSort>>() - fun mkFunctionAsArray(function: KFuncDecl): KFunctionAsArray = + fun , R : KSort> mkFunctionAsArray(sort: A, function: KFuncDecl): KFunctionAsArray = functionAsArrayCache.createIfContextActive { ensureContextMatch(function) - KFunctionAsArray(this, function) + KFunctionAsArray(this, sort, function) }.cast() private val arrayLambdaCache = mkAstInterner>() + private val array2LambdaCache = mkAstInterner>() + private val array3LambdaCache = mkAstInterner>() + private val arrayNLambdaCache = mkAstInterner>() + + fun mkArrayLambda( + indexVar: KDecl, body: KExpr + ): KArrayLambda = arrayLambdaCache.createIfContextActive { + ensureContextMatch(indexVar, body) + KArrayLambda(this, indexVar, body) + }.cast() - fun mkArrayLambda(indexVar: KDecl, body: KExpr): KArrayLambda = - arrayLambdaCache.createIfContextActive { - ensureContextMatch(indexVar, body) - KArrayLambda(this, indexVar, body) - }.cast() + fun mkArrayLambda( + indexVar0: KDecl, indexVar1: KDecl, body: KExpr + ): KArray2Lambda = array2LambdaCache.createIfContextActive { + ensureContextMatch(indexVar0, indexVar1, body) + KArray2Lambda(this, indexVar0, indexVar1, body) + }.cast() + + fun mkArrayLambda( + indexVar0: KDecl, indexVar1: KDecl, indexVar2: KDecl, body: KExpr + ): KArray3Lambda = array3LambdaCache.createIfContextActive { + ensureContextMatch(indexVar0, indexVar1, indexVar2, body) + KArray3Lambda(this, indexVar0, indexVar1, indexVar2, body) + }.cast() + + fun mkArrayNLambda( + indices: List>, body: KExpr + ): KArrayNLambda = arrayNLambdaCache.createIfContextActive { + ensureContextMatch(indices) + ensureContextMatch(body) + + KArrayNLambda(this, indices, body) + }.cast() fun KExpr>.store(index: KExpr, value: KExpr) = mkArrayStore(this, index, value) + fun KExpr>.store( + index0: KExpr, index1: KExpr, value: KExpr + ) = mkArrayStore(this, index0, index1, value) + + fun KExpr>.store( + index0: KExpr, index1: KExpr, index2: KExpr, value: KExpr + ) = mkArrayStore(this, index0, index1, index2, value) + fun KExpr>.select(index: KExpr) = mkArraySelect(this, index) + fun KExpr>.select( + index0: KExpr, + index1: KExpr + ) = mkArraySelect(this, index0, index1) + + fun KExpr>.select( + index0: KExpr, + index1: KExpr, + index2: KExpr + ) = mkArraySelect(this, index0, index1, index2) + // arith private val arithAddCache = mkAstInterner>() @@ -1764,6 +1969,7 @@ open class KContext( mkFp16WithoutNaNCheck(normalizedValue) } } + fun mkFp16NaN(): KFp16Value = mkFp16WithoutNaNCheck(Float.NaN) private fun mkFp16WithoutNaNCheck(value: Float): KFp16Value = fp16Cache.createIfContextActive { KFp16Value(this, value) } @@ -1791,9 +1997,9 @@ open class KContext( biasedExponent: KBitVecValue<*>, signBit: Boolean ): KFp128Value = fp128Cache.createIfContextActive { - ensureContextMatch(significand, biasedExponent) - KFp128Value(this, significand, biasedExponent, signBit) - } + ensureContextMatch(significand, biasedExponent) + KFp128Value(this, significand, biasedExponent, signBit) + } fun mkFp128(significand: KBitVecValue<*>, unbiasedExponent: KBitVecValue<*>, signBit: Boolean): KFp128Value = mkFp128Biased( @@ -1906,7 +2112,7 @@ open class KContext( significand: KBitVecValue<*>, biasedExponent: KBitVecValue<*>, signBit: Boolean - ): KFpValue { + ): KFpValue { val intSignBit = if (signBit) 1 else 0 return when (sort) { @@ -2708,10 +2914,32 @@ open class KContext( fun mkArraySelectDecl(array: KArraySort): KArraySelectDecl = KArraySelectDecl(this, array) + fun mkArraySelectDecl( + array: KArray2Sort + ): KArray2SelectDecl = KArray2SelectDecl(this, array) + + fun mkArraySelectDecl( + array: KArray3Sort + ): KArray3SelectDecl = KArray3SelectDecl(this, array) + + fun mkArrayNSelectDecl(array: KArrayNSort): KArrayNSelectDecl = + KArrayNSelectDecl(this, array) + fun mkArrayStoreDecl(array: KArraySort): KArrayStoreDecl = KArrayStoreDecl(this, array) - fun mkArrayConstDecl(array: KArraySort): KArrayConstDecl = + fun mkArrayStoreDecl( + array: KArray2Sort + ): KArray2StoreDecl = KArray2StoreDecl(this, array) + + fun mkArrayStoreDecl( + array: KArray3Sort + ): KArray3StoreDecl = KArray3StoreDecl(this, array) + + fun mkArrayNStoreDecl(array: KArrayNSort): KArrayNStoreDecl = + KArrayNStoreDecl(this, array) + + fun , R : KSort> mkArrayConstDecl(array: A): KArrayConstDecl = KArrayConstDecl(this, array) // arith @@ -3244,4 +3472,16 @@ open class KContext( NO_SIMPLIFY -> createNoSimplify(a0, a1, a2, a3) } } + + @Suppress("LongParameterList") + private inline fun mkSimplified( + a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, + simplifier: KContext.(A0, A1, A2, A3, A4) -> KExpr, + createNoSimplify: (A0, A1, A2, A3, A4) -> KExpr + ): KExpr = ensureContextActive { + when (simplificationMode) { + SIMPLIFY -> simplifier(a0, a1, a2, a3, a4) + NO_SIMPLIFY -> createNoSimplify(a0, a1, a2, a3, a4) + } + } } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/decl/Array.kt b/ksmt-core/src/main/kotlin/org/ksmt/decl/Array.kt index 71b00b76f..c62b5043d 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/decl/Array.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/decl/Array.kt @@ -3,51 +3,168 @@ package org.ksmt.decl import org.ksmt.KContext import org.ksmt.expr.KApp import org.ksmt.expr.KExpr +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KSort +import org.ksmt.utils.uncheckedCast -class KArrayStoreDecl internal constructor( +sealed class KArrayStoreDeclBase, R : KSort>( ctx: KContext, - sort: KArraySort -) : KFuncDecl3, KArraySort, D, R>( - ctx, + sort: A +) : KFuncDecl( + ctx = ctx, name = "store", resultSort = sort, - arg0Sort = sort, - arg1Sort = sort.domain, - arg2Sort = sort.range -) { - override fun KContext.apply( - arg0: KExpr>, - arg1: KExpr, arg2: KExpr - ): KApp, *> = mkArrayStoreNoSimplify(arg0, arg1, arg2) + argSorts = buildList { + add(sort) + addAll(sort.domainSorts) + add(sort.range) + } +) + +class KArrayStoreDecl internal constructor( + ctx: KContext, + sort: KArraySort +) : KArrayStoreDeclBase, R>(ctx, sort) { + override fun apply(args: List>): KApp, *> { + val (array, index, value) = args + return ctx.mkArrayStoreNoSimplify( + array = array.uncheckedCast(), + index = index.uncheckedCast(), + value = value.uncheckedCast() + ) + } + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +class KArray2StoreDecl internal constructor( + ctx: KContext, + sort: KArray2Sort +) : KArrayStoreDeclBase, R>(ctx, sort) { + override fun apply(args: List>): KApp, *> { + val (array, index0, index1, value) = args + return ctx.mkArrayStoreNoSimplify( + array = array.uncheckedCast(), + index0 = index0.uncheckedCast(), + index1 = index1.uncheckedCast(), + value = value.uncheckedCast() + ) + } override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) } +class KArray3StoreDecl internal constructor( + ctx: KContext, + sort: KArray3Sort +) : KArrayStoreDeclBase, R>(ctx, sort) { + override fun apply(args: List>): KApp, *> { + val (array, index0, index1, index2, value) = args + return ctx.mkArrayStoreNoSimplify( + array = array.uncheckedCast(), + index0 = index0.uncheckedCast(), + index1 = index1.uncheckedCast(), + index2 = index2.uncheckedCast(), + value = value.uncheckedCast() + ) + } + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +class KArrayNStoreDecl internal constructor( + ctx: KContext, + sort: KArrayNSort +) : KArrayStoreDeclBase, R>(ctx, sort) { + override fun apply(args: List>): KApp, *> = + ctx.mkArrayNStoreNoSimplify( + array = args.first().uncheckedCast(), + indices = args.subList(fromIndex = 1, toIndex = args.size - 1).uncheckedCast(), + value = args.last().uncheckedCast() + ) + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +sealed class KArraySelectDeclBase, R : KSort>( + ctx: KContext, + sort: A +) : KFuncDecl(ctx, "select", sort.range, listOf(sort) + sort.domainSorts) + class KArraySelectDecl internal constructor( ctx: KContext, sort: KArraySort -) : KFuncDecl2, D>( - ctx, - name = "select", - resultSort = sort.range, - arg0Sort = sort, - arg1Sort = sort.domain -) { - override fun KContext.apply( - arg0: KExpr>, - arg1: KExpr - ): KApp = mkArraySelectNoSimplify(arg0, arg1) +) : KArraySelectDeclBase, R>(ctx, sort) { + + override fun apply(args: List>): KApp { + val (array, index) = args + return ctx.mkArraySelectNoSimplify( + array = array.uncheckedCast(), + index = index.uncheckedCast<_, KExpr>() + ) + } override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) } -class KArrayConstDecl( +class KArray2SelectDecl internal constructor( ctx: KContext, - sort: KArraySort -) : KFuncDecl1, R>(ctx, "const", sort, sort.range) { - override fun KContext.apply(arg: KExpr): KApp, R> = mkArrayConst(sort, arg) + sort: KArray2Sort +) : KArraySelectDeclBase, R>(ctx, sort) { + + override fun apply(args: List>): KApp { + val (array, index0, index1) = args + return ctx.mkArraySelectNoSimplify( + array = array.uncheckedCast(), + index0 = index0.uncheckedCast<_, KExpr>(), + index1 = index1.uncheckedCast<_, KExpr>() + ) + } + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +class KArray3SelectDecl internal constructor( + ctx: KContext, + sort: KArray3Sort +) : KArraySelectDeclBase, R>(ctx, sort) { + + override fun apply(args: List>): KApp { + val (array, index0, index1, index2) = args + return ctx.mkArraySelectNoSimplify( + array = array.uncheckedCast(), + index0 = index0.uncheckedCast<_, KExpr>(), + index1 = index1.uncheckedCast<_, KExpr>(), + index2 = index2.uncheckedCast<_, KExpr>() + ) + } + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +class KArrayNSelectDecl internal constructor( + ctx: KContext, + sort: KArrayNSort +) : KArraySelectDeclBase, R>(ctx, sort) { + + override fun apply(args: List>): KApp = + ctx.mkArrayNSelectNoSimplify( + array = args.first().uncheckedCast(), + indices = args.drop(1) + ) + + override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) +} + +class KArrayConstDecl, R : KSort>( + ctx: KContext, + sort: A +) : KFuncDecl1(ctx, "const", sort, sort.range) { + override fun KContext.apply(arg: KExpr): KApp = mkArrayConst(sort, arg) override fun accept(visitor: KDeclVisitor): R = visitor.visit(this) } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/decl/KDeclVisitor.kt b/ksmt-core/src/main/kotlin/org/ksmt/decl/KDeclVisitor.kt index 99d10651d..f25449dda 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/decl/KDeclVisitor.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/decl/KDeclVisitor.kt @@ -1,7 +1,11 @@ package org.ksmt.decl import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort @@ -31,8 +35,27 @@ interface KDeclVisitor { fun visit(decl: KIteDecl): T = visit(decl as KFuncDecl) fun visit(decl: KArraySelectDecl): T = visit(decl as KFuncDecl) + fun visit(decl: KArray2SelectDecl): T = visit(decl as KFuncDecl) + + fun visit( + decl: KArray3SelectDecl + ): T = visit(decl as KFuncDecl) + + fun visit(decl: KArrayNSelectDecl): T = visit(decl as KFuncDecl) + fun visit(decl: KArrayStoreDecl): T = visit(decl as KFuncDecl>) - fun visit(decl: KArrayConstDecl): T = visit(decl as KFuncDecl>) + + fun visit( + decl: KArray2StoreDecl + ): T = visit(decl as KFuncDecl>) + + fun visit( + decl: KArray3StoreDecl + ): T = visit(decl as KFuncDecl>) + + fun visit(decl: KArrayNStoreDecl): T = visit(decl as KFuncDecl>) + + fun , R : KSort> visit(decl: KArrayConstDecl): T = visit(decl as KFuncDecl) fun visit(decl: KArithSubDecl): T = visit(decl as KFuncDecl) fun visit(decl: KArithMulDecl): T = visit(decl as KFuncDecl) diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/Array.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/Array.kt index b406e0adc..60af6cf2c 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/Array.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/Array.kt @@ -9,114 +9,277 @@ import org.ksmt.decl.KFuncDecl import org.ksmt.expr.printer.ExpressionPrinter import org.ksmt.expr.transformer.KTransformerBase import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArraySortBase +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KSort import org.ksmt.utils.uncheckedCast +sealed class KArrayStoreBase, R : KSort>( + ctx: KContext, + val array: KExpr, + val value: KExpr +) : KApp(ctx) { + override val sort: A = array.sort + + abstract val indices: List> + + override val args: List> + get() = buildList { + add(array) + addAll(indices) + add(value) + }.uncheckedCast() +} + class KArrayStore internal constructor( ctx: KContext, - val array: KExpr>, + array: KExpr>, val index: KExpr, - val value: KExpr -) : KApp, KSort>(ctx) { + value: KExpr +) : KArrayStoreBase, R>(ctx, array, value) { + override val indices: List> + get() = listOf(index).uncheckedCast() override val decl: KDecl> get() = ctx.mkArrayStoreDecl(array.sort) - override val args: List> - get() = listOf(array, index, value).uncheckedCast() - override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) - override val sort: KArraySort = array.sort - override fun internHashCode(): Int = hash(array, index, value) override fun internEquals(other: Any): Boolean = structurallyEqual(other, { array }, { index }, { value }) } +class KArray2Store internal constructor( + ctx: KContext, + array: KExpr>, + val index0: KExpr, + val index1: KExpr, + value: KExpr +) : KArrayStoreBase, R>(ctx, array, value) { + override val indices: List> + get() = listOf(index0, index1).uncheckedCast() + + override val decl: KDecl> + get() = ctx.mkArrayStoreDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr> = + transformer.transform(this) + + override fun internHashCode(): Int = + hash(array, index0, index1, value) + + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { array }, { index0 }, { index1 }, { value }) +} + +class KArray3Store internal constructor( + ctx: KContext, + array: KExpr>, + val index0: KExpr, + val index1: KExpr, + val index2: KExpr, + value: KExpr +) : KArrayStoreBase, R>(ctx, array, value) { + override val indices: List> + get() = listOf(index0, index1, index2).uncheckedCast() + + override val decl: KDecl> + get() = ctx.mkArrayStoreDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr> = + transformer.transform(this) + + override fun internHashCode(): Int = + hash(array, index0, index1, index2, value) + + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { array }, { index0 }, { index1 }, { index2 }, { value }) +} + +class KArrayNStore internal constructor( + ctx: KContext, + array: KExpr>, + override val indices: List>, + value: KExpr +) : KArrayStoreBase, R>(ctx, array, value) { + init { + require(indices.size == array.sort.domainSorts.size) { + "Array domain size mismatch: expected ${array.sort.domainSorts.size}, provided: ${indices.size}" + } + } + + override val decl: KDecl> + get() = ctx.mkArrayNStoreDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr> = + transformer.transform(this) + + override fun internHashCode(): Int = hash(array, indices, value) + override fun internEquals(other: Any): Boolean = structurallyEqual(other, { array }, { indices }, { value }) +} + +sealed class KArraySelectBase, R : KSort>( + ctx: KContext, + val array: KExpr +) : KApp(ctx) { + override val sort: R = array.sort.range + + abstract val indices: List> + + override val args: List> + get() = buildList { + add(array) + addAll(indices) + }.uncheckedCast() +} + class KArraySelect internal constructor( ctx: KContext, - val array: KExpr>, + array: KExpr>, val index: KExpr -) : KApp(ctx) { +) : KArraySelectBase, R>(ctx, array) { + override val indices: List> + get() = listOf(index).uncheckedCast() override val decl: KDecl get() = ctx.mkArraySelectDecl(array.sort) - override val args: List> - get() = listOf(array, index).uncheckedCast() - override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) - override val sort: R = array.sort.range - override fun internHashCode(): Int = hash(array, index) override fun internEquals(other: Any): Boolean = structurallyEqual(other, { array }, { index }) } -class KArrayConst internal constructor( +class KArray2Select internal constructor( ctx: KContext, - override val sort: KArraySort, + array: KExpr>, + val index0: KExpr, + val index1: KExpr +) : KArraySelectBase, R>(ctx, array) { + override val indices: List> + get() = listOf(index0, index1).uncheckedCast() + + override val decl: KDecl + get() = ctx.mkArraySelectDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) + + override fun internHashCode(): Int = hash(array, index0, index1) + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { array }, { index0 }, { index1 }) +} + +class KArray3Select internal constructor( + ctx: KContext, + array: KExpr>, + val index0: KExpr, + val index1: KExpr, + val index2: KExpr +) : KArraySelectBase, R>(ctx, array) { + override val indices: List> + get() = listOf(index0, index1, index2).uncheckedCast() + + override val decl: KDecl + get() = ctx.mkArraySelectDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) + + override fun internHashCode(): Int = hash(array, index0, index1, index2) + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { array }, { index0 }, { index1 }, { index2 }) +} + +class KArrayNSelect internal constructor( + ctx: KContext, + array: KExpr>, + override val indices: List> +) : KArraySelectBase, R>(ctx, array) { + init { + require(indices.size == array.sort.domainSorts.size) { + "Array domain size mismatch: expected ${array.sort.domainSorts.size}, provided: ${indices.size}" + } + } + + override val decl: KDecl + get() = ctx.mkArrayNSelectDecl(array.sort) + + override fun accept(transformer: KTransformerBase): KExpr = transformer.transform(this) + + override fun internHashCode(): Int = hash(array, indices) + override fun internEquals(other: Any): Boolean = structurallyEqual(other, { array }, { indices }) +} + +class KArrayConst, R : KSort> internal constructor( + ctx: KContext, + override val sort: A, val value: KExpr -) : KApp, R>(ctx) { +) : KApp(ctx) { - override val decl: KArrayConstDecl + override val decl: KArrayConstDecl get() = ctx.mkArrayConstDecl(sort) override val args: List> get() = listOf(value) - override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) + override fun accept(transformer: KTransformerBase): KExpr = + transformer.transform(this) override fun internHashCode(): Int = hash(sort, value) override fun internEquals(other: Any): Boolean = structurallyEqual(other, { sort }, { value }) } -class KFunctionAsArray internal constructor( +class KFunctionAsArray, R : KSort> internal constructor( ctx: KContext, + override val sort: A, val function: KFuncDecl -) : KExpr>(ctx) { - val domainSort: D +) : KExpr(ctx) { init { - check(function.argSorts.size == 1) { - "Function with single argument required" + check(function.argSorts == sort.domainSorts) { + "Function arguments sort mismatch" } - @Suppress("UNCHECKED_CAST") - domainSort = function.argSorts.single() as D } - override val sort: KArraySort = ctx.mkArraySort(domainSort, function.sort) - override fun print(printer: ExpressionPrinter): Unit = with(printer) { append("(asArray ") append(function.name) append(")") } - override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) + override fun accept(transformer: KTransformerBase): KExpr = + transformer.transform(this) override fun internHashCode(): Int = hash(function) override fun internEquals(other: Any): Boolean = structurallyEqual(other) { function } } -/** Array lambda binding. +/** + * Array lambda binding. * * [Defined as in the Z3 solver](https://microsoft.github.io/z3guide/docs/logic/Lambdas) * */ -class KArrayLambda internal constructor( +sealed class KArrayLambdaBase, R : KSort>( ctx: KContext, - val indexVarDecl: KDecl, + override val sort: A, val body: KExpr -) : KExpr>(ctx) { +) : KExpr(ctx) { + abstract val indexVarDeclarations: List> override fun print(printer: ExpressionPrinter) { val str = buildString { - append("(lambda ((") - append(indexVarDecl.name) - append(' ') + append("(lambda (") + + indexVarDeclarations.forEach { + append('(') + append(it.name) + append(' ') + it.sort.print(this) + append(')') + } - indexVarDecl.sort.print(this) - append(")) ") + append(") ") body.print(this) @@ -124,11 +287,80 @@ class KArrayLambda internal constructor( } printer.append(str) } +} - override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) +class KArrayLambda internal constructor( + ctx: KContext, + val indexVarDecl: KDecl, + body: KExpr +) : KArrayLambdaBase, R>( + ctx, + ctx.mkArraySort(indexVarDecl.sort, body.sort), + body +) { + override val indexVarDeclarations: List> + get() = listOf(indexVarDecl) - override val sort: KArraySort = ctx.mkArraySort(indexVarDecl.sort, body.sort) + override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) override fun internHashCode(): Int = hash(indexVarDecl, body) override fun internEquals(other: Any): Boolean = structurallyEqual(other, { indexVarDecl }, { body }) } + +class KArray2Lambda internal constructor( + ctx: KContext, + val indexVar0Decl: KDecl, + val indexVar1Decl: KDecl, + body: KExpr +) : KArrayLambdaBase, R>( + ctx, + ctx.mkArraySort(indexVar0Decl.sort, indexVar1Decl.sort, body.sort), + body +) { + override val indexVarDeclarations: List> + get() = listOf(indexVar0Decl, indexVar1Decl) + + override fun accept(transformer: KTransformerBase): KExpr> = + transformer.transform(this) + + override fun internHashCode(): Int = hash(indexVar0Decl, indexVar1Decl, body) + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { indexVar0Decl }, { indexVar1Decl }, { body }) +} + +class KArray3Lambda internal constructor( + ctx: KContext, + val indexVar0Decl: KDecl, + val indexVar1Decl: KDecl, + val indexVar2Decl: KDecl, + body: KExpr +) : KArrayLambdaBase, R>( + ctx, + ctx.mkArraySort(indexVar0Decl.sort, indexVar1Decl.sort, indexVar2Decl.sort, body.sort), + body +) { + override val indexVarDeclarations: List> + get() = listOf(indexVar0Decl, indexVar1Decl, indexVar2Decl) + + override fun accept(transformer: KTransformerBase): KExpr> = + transformer.transform(this) + + override fun internHashCode(): Int = hash(indexVar0Decl, indexVar1Decl, indexVar2Decl, body) + override fun internEquals(other: Any): Boolean = + structurallyEqual(other, { indexVar0Decl }, { indexVar1Decl }, { indexVar2Decl }, { body }) +} + +class KArrayNLambda internal constructor( + ctx: KContext, + override val indexVarDeclarations: List>, + body: KExpr +) : KArrayLambdaBase, R>( + ctx, + ctx.mkArrayNSort(indexVarDeclarations.map { it.sort }, body.sort), + body +) { + override fun accept(transformer: KTransformerBase): KExpr> = transformer.transform(this) + + override fun internHashCode(): Int = hash(indexVarDeclarations, body) + override fun internEquals(other: Any): Boolean = structurallyEqual(other, { indexVarDeclarations }, { body }) +} diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprSubstitutor.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprSubstitutor.kt index 5232277be..05bf357dd 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprSubstitutor.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprSubstitutor.kt @@ -4,14 +4,21 @@ import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.decl.KFuncDecl import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KExistentialQuantifier import org.ksmt.expr.KExpr import org.ksmt.expr.KFunctionAsArray import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.rewrite.KExprUninterpretedDeclCollector.Companion.collectUninterpretedDeclarations import org.ksmt.expr.transformer.KNonRecursiveTransformer +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KSort import org.ksmt.utils.uncheckedCast @@ -49,27 +56,60 @@ open class KExprSubstitutor(ctx: KContext) : KNonRecursiveTransformer(ctx) { return transformExpr(transformedApp) } - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { val declSubstitution = declDeclSubstitution[expr.function]?.uncheckedCast() ?: expr.function - return transformExpr(ctx.mkFunctionAsArray(declSubstitution)) + return transformExpr(ctx.mkFunctionAsArray(expr.sort, declSubstitution)) } - override fun transform(expr: KArrayLambda): KExpr> = - transformQuantifiedExpression(expr, setOf(expr.indexVarDecl), expr.body) { body, bounds -> - ctx.mkArrayLambda(bounds.single().uncheckedCast(), body) - } + override fun transform( + expr: KArrayLambda + ): KExpr> = transformQuantifiedExpression( + expr, listOf(expr.indexVarDecl), expr.body + ) { body, bounds -> + val boundVar: KDecl = bounds.single().uncheckedCast() + ctx.mkArrayLambda(boundVar, body) + } + + override fun transform( + expr: KArray2Lambda + ): KExpr> = transformQuantifiedExpression( + expr, listOf(expr.indexVar0Decl, expr.indexVar1Decl), expr.body + ) { body, (b0, b1) -> + val bound0: KDecl = b0.uncheckedCast() + val bound1: KDecl = b1.uncheckedCast() + ctx.mkArrayLambda(bound0, bound1, body) + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = transformQuantifiedExpression( + expr, listOf(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl), expr.body + ) { body, (b0, b1, b2) -> + val bound0: KDecl = b0.uncheckedCast() + val bound1: KDecl = b1.uncheckedCast() + val bound2: KDecl = b2.uncheckedCast() + ctx.mkArrayLambda(bound0, bound1, bound2, body) + } + + override fun transform( + expr: KArrayNLambda + ): KExpr> = transformQuantifiedExpression( + expr, expr.indexVarDeclarations, expr.body + ) { body, bounds -> + ctx.mkArrayNLambda(bounds, body) + } override fun transform(expr: KExistentialQuantifier): KExpr = - transformQuantifiedExpression(expr, expr.bounds.toSet(), expr.body) { body, bounds -> - ctx.mkExistentialQuantifier(body, bounds.toList()) + transformQuantifiedExpression(expr, expr.bounds, expr.body) { body, bounds -> + ctx.mkExistentialQuantifier(body, bounds) } override fun transform(expr: KUniversalQuantifier): KExpr = - transformQuantifiedExpression(expr, expr.bounds.toSet(), expr.body) { body, bounds -> - ctx.mkUniversalQuantifier(body, bounds.toList()) + transformQuantifiedExpression(expr, expr.bounds, expr.body) { body, bounds -> + ctx.mkUniversalQuantifier(body, bounds) } - private val unprocessedQuantifiers = hashMapOf, Pair>, KExpr<*>>>() + private val unprocessedQuantifiers = hashMapOf, Pair>, KExpr<*>>>() /** * Resolve quantifier bound vars shadowing. @@ -83,9 +123,9 @@ open class KExprSubstitutor(ctx: KContext) : KNonRecursiveTransformer(ctx) { * */ private inline fun transformQuantifiedExpression( quantifiedExpr: KExpr, - quantifiedVars: Set>, + quantifiedVars: List>, body: KExpr, - crossinline quantifierBuilder: (KExpr, Set>) -> KExpr + crossinline quantifierBuilder: (KExpr, List>) -> KExpr ): KExpr { val (unshadowedBounds, unshadowedBody) = unprocessedQuantifiers.getOrPut(quantifiedExpr) { resolveQuantifierShadowedVars(quantifiedVars, body) @@ -98,9 +138,9 @@ open class KExprSubstitutor(ctx: KContext) : KNonRecursiveTransformer(ctx) { } private fun resolveQuantifierShadowedVars( - quantifiedVars: Set>, + quantifiedVars: List>, body: KExpr, - ): Pair>, KExpr<*>> { + ): Pair>, KExpr<*>> { val usedDeclarationsList = exprExprSubstitution.flatMap { collectUninterpretedDeclarations(it.key) + collectUninterpretedDeclarations(it.value) } + declDeclSubstitution.keys + declDeclSubstitution.values @@ -113,7 +153,7 @@ open class KExprSubstitutor(ctx: KContext) : KNonRecursiveTransformer(ctx) { } val shadowedBoundsReplacement = shadowedDeclarations.associateWith { it.freshStub() } - val unshadowedQuantifiedVars = quantifiedVars.mapTo(hashSetOf()) { shadowedBoundsReplacement[it] ?: it } + val unshadowedQuantifiedVars = quantifiedVars.map { shadowedBoundsReplacement[it] ?: it } val shadowedBoundRemover = KExprSubstitutor(ctx).also { remover -> shadowedBoundsReplacement.forEach { @Suppress("UNCHECKED_CAST") diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprUninterpretedDeclCollector.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprUninterpretedDeclCollector.kt index c93580b83..e6193a14f 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprUninterpretedDeclCollector.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/KExprUninterpretedDeclCollector.kt @@ -2,7 +2,10 @@ package org.ksmt.expr.rewrite import org.ksmt.KContext import org.ksmt.decl.KDecl +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KConst import org.ksmt.expr.KExistentialQuantifier import org.ksmt.expr.KExpr @@ -10,7 +13,11 @@ import org.ksmt.expr.KFunctionApp import org.ksmt.expr.KFunctionAsArray import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.transformer.KNonRecursiveTransformer +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KSort @@ -30,7 +37,7 @@ open class KExprUninterpretedDeclCollector(ctx: KContext) : KNonRecursiveTransfo return super.transform(expr) } - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { declarations += expr.function return super.transform(expr) } @@ -40,6 +47,25 @@ open class KExprUninterpretedDeclCollector(ctx: KContext) : KNonRecursiveTransfo return expr } + override fun transform( + expr: KArray2Lambda + ): KExpr> { + transformQuantifier(setOf(expr.indexVar0Decl, expr.indexVar1Decl), expr.body) + return expr + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> { + transformQuantifier(setOf(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl), expr.body) + return expr + } + + override fun transform(expr: KArrayNLambda): KExpr> { + transformQuantifier(expr.indexVarDeclarations.toSet(), expr.body) + return expr + } + override fun transform(expr: KExistentialQuantifier): KExpr { transformQuantifier(expr.bounds.toSet(), expr.body) return expr diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/ArraySimplification.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/ArraySimplification.kt index 96afb2ac7..fef9b7727 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/ArraySimplification.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/ArraySimplification.kt @@ -1,48 +1,128 @@ package org.ksmt.expr.rewrite.simplify import org.ksmt.KContext +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayLambdaBase +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect +import org.ksmt.expr.KArraySelectBase import org.ksmt.expr.KArrayStore +import org.ksmt.expr.KArrayStoreBase import org.ksmt.expr.KExpr import org.ksmt.expr.KInterpretedValue import org.ksmt.expr.rewrite.KExprSubstitutor +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KSort +import org.ksmt.utils.uncheckedCast -fun KContext.simplifyArrayStore( - array: KExpr>, - index: KExpr, - value: KExpr -) : KExpr> { +private inline fun < + reified A : KArraySortBase, R : KSort, + reified S : KArraySelectBase +> simplifyArrayStore( + array: KExpr, + value: KExpr, + selectIndicesMatch: (S) -> Boolean, + default: () -> KExpr +): KExpr { // (store (const v) i v) ==> (const v) - if (array is KArrayConst && array.value == value) { + if (array is KArrayConst && array.value == value) { return array } // (store a i (select a i)) ==> a - if (value is KArraySelect<*, *> && array == value.array && index == value.index) { + if (value is S && array == value.array && selectIndicesMatch(value)) { return array } - return mkArrayStoreNoSimplify(array, index, value) + return default() } -fun KContext.simplifyArraySelect( +fun KContext.simplifyArrayStore( array: KExpr>, - index: KExpr + index: KExpr, + value: KExpr +): KExpr> = simplifyArrayStore( + array, + value, + selectIndicesMatch = { select: KArraySelect -> index == select.index }, + default = { mkArrayStoreNoSimplify(array, index, value) } +) + +fun KContext.simplifyArrayStore( + array: KExpr>, + index0: KExpr, + index1: KExpr, + value: KExpr +): KExpr> = simplifyArrayStore( + array, + value, + selectIndicesMatch = { select: KArray2Select -> index0 == select.index0 && index1 == select.index1 }, + default = { mkArrayStoreNoSimplify(array, index0, index1, value) } +) + +fun KContext.simplifyArrayStore( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr, + value: KExpr +): KExpr> = simplifyArrayStore( + array, + value, + selectIndicesMatch = { select: KArray3Select -> + index0 == select.index0 && index1 == select.index1 && index2 == select.index2 + }, + default = { mkArrayStoreNoSimplify(array, index0, index1, index2, value) } +) + +fun KContext.simplifyArrayNStore( + array: KExpr>, + indices: List>, + value: KExpr +): KExpr> = simplifyArrayStore( + array, + value, + selectIndicesMatch = { select: KArrayNSelect -> indices == select.indices }, + default = { mkArrayNStoreNoSimplify(array, indices, value) } +) + +@Suppress("LongParameterList") +private inline fun < + reified A : KArraySortBase, + R : KSort, + reified S : KArrayStoreBase, + reified L : KArrayLambdaBase +> KContext.simplifyArraySelect( + array: KExpr, + indicesAreValues: () -> Boolean, + storeIndicesMatch: (S) -> Boolean, + storeIndicesAreValues: (S) -> Boolean, + mkLambdaSubstitution: KExprSubstitutor.(L) -> Unit, + default: (KExpr) -> KExpr ): KExpr { var currentArray = array - while (currentArray is KArrayStore) { + while (currentArray is S) { // (select (store i v) i) ==> v - if (currentArray.index == index) { + if (storeIndicesMatch(currentArray)) { return currentArray.value } // (select (store a i v) j), i != j ==> (select a j) - if (index is KInterpretedValue && currentArray.index is KInterpretedValue) { + if (indicesAreValues() && storeIndicesAreValues(currentArray)) { currentArray = currentArray.array } else { // possibly equal index, we can't expand stores @@ -52,18 +132,91 @@ fun KContext.simplifyArraySelect( when (currentArray) { // (select (const v) i) ==> v - is KArrayConst -> { - return currentArray.value + is KArrayConst -> { + return currentArray.value.uncheckedCast() } // (select (lambda x body) i) ==> body[i/x] - is KArrayLambda -> { + is L -> { val resolvedBody = KExprSubstitutor(this).apply { - val indexVarExpr = mkConstApp(currentArray.indexVarDecl) - substitute(indexVarExpr, index) + mkLambdaSubstitution(currentArray) }.apply(currentArray.body) return resolvedBody } } - return mkArraySelectNoSimplify(currentArray, index) + return default(currentArray) } + +fun KContext.simplifyArraySelect( + array: KExpr>, + index: KExpr +): KExpr = simplifyArraySelect( + array = array, + indicesAreValues = { index is KInterpretedValue }, + storeIndicesMatch = { store: KArrayStore -> index == store.index }, + storeIndicesAreValues = { store: KArrayStore -> store.index is KInterpretedValue }, + mkLambdaSubstitution = { lambda: KArrayLambda -> + substitute(mkConstApp(lambda.indexVarDecl), index) + }, + default = { mkArraySelectNoSimplify(it, index) } +) + +fun KContext.simplifyArraySelect( + array: KExpr>, + index0: KExpr, + index1: KExpr +): KExpr = simplifyArraySelect( + array = array, + indicesAreValues = { index0 is KInterpretedValue && index1 is KInterpretedValue }, + storeIndicesMatch = { store: KArray2Store -> + index0 == store.index0 && index1 == store.index1 + }, + storeIndicesAreValues = { store: KArray2Store -> + store.index0 is KInterpretedValue && store.index1 is KInterpretedValue + }, + mkLambdaSubstitution = { lambda: KArray2Lambda -> + substitute(mkConstApp(lambda.indexVar0Decl), index0) + substitute(mkConstApp(lambda.indexVar1Decl), index1) + }, + default = { mkArraySelectNoSimplify(it, index0, index1) } +) + +fun KContext.simplifyArraySelect( + array: KExpr>, + index0: KExpr, + index1: KExpr, + index2: KExpr +): KExpr = simplifyArraySelect( + array = array, + indicesAreValues = { + index0 is KInterpretedValue && index1 is KInterpretedValue && index2 is KInterpretedValue + }, + storeIndicesMatch = { store: KArray3Store -> + index0 == store.index0 && index1 == store.index1 && index2 == store.index2 + }, + storeIndicesAreValues = { s: KArray3Store -> + s.index0 is KInterpretedValue && s.index1 is KInterpretedValue && s.index2 is KInterpretedValue + }, + mkLambdaSubstitution = { lambda: KArray3Lambda -> + substitute(mkConstApp(lambda.indexVar0Decl), index0) + substitute(mkConstApp(lambda.indexVar1Decl), index1) + substitute(mkConstApp(lambda.indexVar2Decl), index2) + }, + default = { mkArraySelectNoSimplify(it, index0, index1, index2) } +) + +fun KContext.simplifyArrayNSelect( + array: KExpr>, + indices: List> +): KExpr = simplifyArraySelect( + array = array, + indicesAreValues = { indices.all { it is KInterpretedValue<*> } }, + storeIndicesMatch = { store: KArrayNStore -> indices == store.indices }, + storeIndicesAreValues = { store: KArrayNStore -> store.indices.all { it is KInterpretedValue<*> } }, + mkLambdaSubstitution = { lambda: KArrayNLambda -> + lambda.indexVarDeclarations.zip(indices) { varDecl, index -> + substitute(mkConstApp(varDecl).uncheckedCast<_, KExpr>(), index.uncheckedCast()) + } + }, + default = { mkArrayNSelectNoSimplify(it, indices) } +) diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KArrayExprSimplifier.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KArrayExprSimplifier.kt index 88954806f..274762446 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KArrayExprSimplifier.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KArrayExprSimplifier.kt @@ -3,31 +3,50 @@ package org.ksmt.expr.rewrite.simplify import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.expr.KAndExpr -import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayLambdaBase +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect +import org.ksmt.expr.KArraySelectBase import org.ksmt.expr.KArrayStore +import org.ksmt.expr.KArrayStoreBase import org.ksmt.expr.KEqExpr import org.ksmt.expr.KExpr +import org.ksmt.expr.KFunctionApp +import org.ksmt.expr.KFunctionAsArray import org.ksmt.expr.KInterpretedValue +import org.ksmt.expr.printer.ExpressionPrinter import org.ksmt.expr.rewrite.KExprSubstitutor import org.ksmt.expr.transformer.KTransformerBase +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KSort import org.ksmt.utils.uncheckedCast +@Suppress("LargeClass") interface KArrayExprSimplifier : KExprSimplifierBase { - fun simplifyEqArray( - lhs: KExpr>, - rhs: KExpr> + fun , R : KSort> simplifyEqArray( + lhs: KExpr, + rhs: KExpr ): KExpr = with(ctx) { if (lhs == rhs) return trueExpr - val leftArray = flatStores(lhs) - val rightArray = flatStores(rhs) + val leftArray = flatStoresGeneric(lhs.sort, lhs) + val rightArray = flatStoresGeneric(rhs.sort, rhs) val lBase = leftArray.base val rBase = rightArray.base @@ -39,7 +58,7 @@ interface KArrayExprSimplifier : KExprSimplifierBase { * (= a b) * ) */ - if (lBase == rBase || lBase is KArrayConst && rBase is KArrayConst) { + if (lBase == rBase || lBase is KArrayConst<*, *> && rBase is KArrayConst<*, *>) { val simplifiedExpr = simplifyArrayStoreEq(lhs, leftArray, rhs, rightArray) return rewrite(simplifiedExpr) } @@ -55,16 +74,20 @@ interface KArrayExprSimplifier : KExprSimplifierBase { * (= a b) * ) */ - private fun simplifyArrayStoreEq( - lhs: KExpr>, - leftArray: SimplifierFlatArrayStoreExpr, - rhs: KExpr>, - rightArray: SimplifierFlatArrayStoreExpr, + private fun , R : KSort> simplifyArrayStoreEq( + lhs: KExpr, + leftArray: SimplifierFlatArrayStoreBaseExpr, + rhs: KExpr, + rightArray: SimplifierFlatArrayStoreBaseExpr, ): SimplifierAuxExpression = auxExpr { val checks = arrayListOf>() - if (leftArray.base is KArrayConst && rightArray.base is KArrayConst) { + if (leftArray.base is KArrayConst && rightArray.base is KArrayConst) { // (= (const a) (const b)) ==> (= a b) - checks += KEqExpr(ctx, leftArray.base.value, rightArray.base.value) + checks += KEqExpr( + ctx, + leftArray.base.value.uncheckedCast<_, KExpr>(), + rightArray.base.value.uncheckedCast() + ) } else { check(leftArray.base == rightArray.base) { "Base arrays expected to be equal or constant" @@ -74,7 +97,7 @@ interface KArrayExprSimplifier : KExprSimplifierBase { val leftArraySearchInfo = analyzeArrayStores(leftArray) val rightArraySearchInfo = analyzeArrayStores(rightArray) - val allIndices = leftArray.indices.toSet() + rightArray.indices.toSet() + val allIndices = leftArraySearchInfo.storeIndices.keys + rightArraySearchInfo.storeIndices.keys for (idx in allIndices) { val lValue = selectArrayValue(idx, leftArraySearchInfo, lhs, leftArray) val rValue = selectArrayValue(idx, rightArraySearchInfo, rhs, rightArray) @@ -86,46 +109,38 @@ interface KArrayExprSimplifier : KExprSimplifierBase { KAndExpr(ctx, checks) } - private class ArrayStoreSearchInfo( - val storeIndices: Map, Int>, + private class ArrayStoreSearchInfo, R : KSort>( + val storeIndices: Map>, Int>, val storeValues: List>, - val nonConstants: List>, + val nonConstants: List>>, val nonConstantsToCheck: IntArray ) - private fun selectArrayValue( - selectIndex: KExpr, - arraySearchInfo: ArrayStoreSearchInfo, - originalArrayExpr: KExpr>, - flatArray: SimplifierFlatArrayStoreExpr + private fun , R : KSort> selectArrayValue( + selectIndex: List>, + arraySearchInfo: ArrayStoreSearchInfo, + originalArrayExpr: KExpr, + flatArray: SimplifierFlatArrayStoreBaseExpr ): KExpr = findStoredArrayValue(arraySearchInfo, selectIndex) - ?: SimplifierFlatArraySelectExpr( - ctx, - originalArrayExpr, - flatArray.base, - flatArray.indices, - flatArray.values, - selectIndex - ) + ?: flatArray.selectValue(originalArrayExpr, selectIndex) /** * Preprocess flat array stores for faster index search. * @see [findStoredArrayValue] * */ - private fun analyzeArrayStores( - array: SimplifierFlatArrayStoreExpr - ): ArrayStoreSearchInfo { - val indices = array.indices - val indexId = hashMapOf, Int>() - val nonConstants = arrayListOf>() - val nonConstantsToCheck = IntArray(indices.size) - - for (i in indices.indices) { - val storeIndex = indices[i] + private fun , R : KSort> analyzeArrayStores( + array: SimplifierFlatArrayStoreBaseExpr + ): ArrayStoreSearchInfo { + val indexId = hashMapOf>, Int>() + val nonConstants = arrayListOf>>() + val nonConstantsToCheck = IntArray(array.numIndices) + + for (i in 0 until array.numIndices) { + val storeIndex = array.getStoreIndex(i) if (storeIndex !in indexId) { indexId[storeIndex] = i - if (!storeIndex.definitelyIsConstant) { + if (!storeIndex.all { it.definitelyIsConstant }) { nonConstants += storeIndex } } @@ -145,9 +160,9 @@ interface KArrayExprSimplifier : KExprSimplifierBase { * @return null if there is no such index in the array, * or it is impossible to establish equality of some of the stored indices. * */ - private fun findStoredArrayValue( - array: ArrayStoreSearchInfo, - selectIndex: KExpr, + private fun , R : KSort> findStoredArrayValue( + array: ArrayStoreSearchInfo, + selectIndex: List>, ): KExpr? { val storeIndex = array.storeIndices[selectIndex] ?: return null @@ -172,115 +187,312 @@ interface KArrayExprSimplifier : KExprSimplifierBase { preprocess = { flatStores(expr) } ) - override fun transform(expr: KArraySelect): KExpr = + override fun transform( + expr: KArray2Store + ): KExpr> = + simplifyExpr( + expr = expr, + preprocess = { flatStores2(expr) } + ) + + override fun transform( + expr: KArray3Store + ): KExpr> = simplifyExpr( expr = expr, - preprocess = { - val array = flatStores(expr.array) - SimplifierFlatArraySelectExpr( - ctx, - original = expr.array, - baseArray = array.base, - storedValues = array.values, - storedIndices = array.indices, - index = expr.index + preprocess = { flatStores3(expr) } + ) + + override fun transform(expr: KArrayNStore): KExpr> = + simplifyExpr( + expr = expr, + preprocess = { flatStoresN(expr) } + ) + + private fun transform(expr: SimplifierFlatArrayStoreExpr): KExpr> = + simplifyExpr(expr, expr.unwrap()) { args: List> -> + simplifyArrayStore(expr.wrap(args)) + } + + private fun transform( + expr: SimplifierFlatArray2StoreExpr + ): KExpr> = + simplifyExpr(expr, expr.unwrap()) { args: List> -> + simplifyArrayStore(expr.wrap(args)) + } + + private fun transform( + expr: SimplifierFlatArray3StoreExpr + ): KExpr> = + simplifyExpr(expr, expr.unwrap()) { args: List> -> + simplifyArrayStore(expr.wrap(args)) + } + + private fun transform(expr: SimplifierFlatArrayNStoreExpr): KExpr> = + simplifyExpr(expr, expr.unwrap()) { args: List> -> + simplifyArrayStore(expr.wrap(args)) + } + + private fun simplifyArrayStore( + expr: SimplifierFlatArrayStoreExpr + ): KExpr> { + val indices = expr.indices + val storedIndices = hashMapOf, Int>() + val simplifiedIndices = arrayListOf>() + + return simplifyArrayStore( + expr = expr, + findStoredIndexPosition = { i -> + storedIndices[indices[i]] + }, + saveStoredIndexPosition = { i, indexPosition -> + storedIndices[indices[i]] = indexPosition + }, + indexIsConstant = { i -> + indices[i].definitelyIsConstant + }, + addSimplifiedIndex = { i -> + simplifiedIndices.add(indices[i]) + }, + distinctWithSimplifiedIndex = { i, simplifiedIdx -> + areDefinitelyDistinct(indices[i], simplifiedIndices[simplifiedIdx]) + }, + selectIndicesMatch = { select: KArraySelect, i -> + indices[i] == select.index + }, + mkSimplifiedStore = { array, simplifiedIdx, value -> + ctx.mkArrayStoreNoSimplify(array, simplifiedIndices[simplifiedIdx], value) + } + ) + } + + private fun simplifyArrayStore( + expr: SimplifierFlatArray2StoreExpr + ): KExpr> { + val indices0 = expr.indices0 + val indices1 = expr.indices1 + + val storedIndices = hashMapOf, MutableMap, Int>>() + val simplifiedIndices0 = arrayListOf>() + val simplifiedIndices1 = arrayListOf>() + + return simplifyArrayStore( + expr = expr, + findStoredIndexPosition = { i -> + storedIndices[indices0[i]]?.get(indices1[i]) + }, + saveStoredIndexPosition = { i, indexPosition -> + val index1Map = storedIndices.getOrPut(indices0[i]) { hashMapOf() } + index1Map[indices1[i]] = indexPosition + }, + indexIsConstant = { i -> + indices0[i].definitelyIsConstant && indices1[i].definitelyIsConstant + }, + addSimplifiedIndex = { i -> + simplifiedIndices0.add(indices0[i]) + simplifiedIndices1.add(indices1[i]) + }, + distinctWithSimplifiedIndex = { i, simplifiedIdx -> + areDefinitelyDistinct(indices0[i], simplifiedIndices0[simplifiedIdx]) + && areDefinitelyDistinct(indices1[i], simplifiedIndices1[simplifiedIdx]) + }, + selectIndicesMatch = { select: KArray2Select, i -> + indices0[i] == select.index0 && indices1[i] == select.index1 + }, + mkSimplifiedStore = { array, simplifiedIdx, value -> + ctx.mkArrayStoreNoSimplify( + array, + simplifiedIndices0[simplifiedIdx], + simplifiedIndices1[simplifiedIdx], + value ) } ) + } - @Suppress("ComplexMethod", "LoopWithTooManyJumpStatements") - private fun transform(expr: SimplifierFlatArrayStoreExpr): KExpr> = - simplifyExpr(expr, expr.args) { transformedArgs -> - val base: KExpr> = transformedArgs.first().uncheckedCast() - val indices: List> = transformedArgs.subList( - fromIndex = 1, toIndex = 1 + expr.indices.size - ).uncheckedCast() - val values: List> = transformedArgs.subList( - fromIndex = 1 + expr.indices.size, toIndex = transformedArgs.size - ).uncheckedCast() + private fun simplifyArrayStore( + expr: SimplifierFlatArray3StoreExpr + ): KExpr> { + val indices0 = expr.indices0 + val indices1 = expr.indices1 + val indices2 = expr.indices2 + + val storedIndices = hashMapOf, MutableMap, MutableMap, Int>>>() + val simplifiedIndices0 = arrayListOf>() + val simplifiedIndices1 = arrayListOf>() + val simplifiedIndices2 = arrayListOf>() - if (indices.isEmpty()) { - return@simplifyExpr base + return simplifyArrayStore( + expr = expr, + findStoredIndexPosition = { i -> + storedIndices[indices0[i]]?.get(indices1[i])?.get(indices2[i]) + }, + saveStoredIndexPosition = { i, indexPosition -> + val index1Map = storedIndices.getOrPut(indices0[i]) { hashMapOf() } + val index2Map = index1Map.getOrPut(indices1[i]) { hashMapOf() } + index2Map[indices2[i]] = indexPosition + }, + indexIsConstant = { i -> + indices0[i].definitelyIsConstant + && indices1[i].definitelyIsConstant + && indices2[i].definitelyIsConstant + }, + addSimplifiedIndex = { i -> + simplifiedIndices0.add(indices0[i]) + simplifiedIndices1.add(indices1[i]) + simplifiedIndices2.add(indices2[i]) + }, + distinctWithSimplifiedIndex = { i, simplifiedIdx -> + areDefinitelyDistinct(indices0[i], simplifiedIndices0[simplifiedIdx]) + && areDefinitelyDistinct(indices1[i], simplifiedIndices1[simplifiedIdx]) + && areDefinitelyDistinct(indices2[i], simplifiedIndices2[simplifiedIdx]) + }, + selectIndicesMatch = { select: KArray3Select, i -> + indices0[i] == select.index0 + && indices1[i] == select.index1 + && indices2[i] == select.index2 + }, + mkSimplifiedStore = { array, simplifiedIdx, value -> + ctx.mkArrayStoreNoSimplify( + array, + simplifiedIndices0[simplifiedIdx], + simplifiedIndices1[simplifiedIdx], + simplifiedIndices2[simplifiedIdx], + value + ) } + ) + } - val storedIndices = hashMapOf, Int>() - val simplifiedIndices = arrayListOf>() - val simplifiedValues = arrayListOf>() - var lastNonConstantIndex = -1 - - for (i in indices.indices.reversed()) { - val index = indices[i] - val value = values[i] - - val storedIndexPosition = storedIndices[index] - if (storedIndexPosition != null) { - - /** Try push store. - * (store (store a i x) j y), i != j ==> (store (store a j y) i x) - * - * Store can be pushed only if all parent indices are definitely not equal. - * */ - val allParentIndicesAreDistinct = allParentSoreIndicesAreDistinct( - index = index, - storedIndexPosition = storedIndexPosition, - lastNonConstantIndex = lastNonConstantIndex, - simplifiedIndices = simplifiedIndices - ) - - if (allParentIndicesAreDistinct) { - simplifiedValues[storedIndexPosition] = value - if (!index.definitelyIsConstant) { - lastNonConstantIndex = maxOf(lastNonConstantIndex, storedIndexPosition) - } - continue - } - } + private fun simplifyArrayStore( + expr: SimplifierFlatArrayNStoreExpr + ): KExpr> { + val indices = expr.indices + val storedIndices = hashMapOf>, Int>() + val simplifiedIndices = arrayListOf>>() - // simplify direct store to array - if (storedIndices.isEmpty()) { - // (store (const v) i v) ==> (const v) - if (base is KArrayConst && base.value == value) { - continue - } + return simplifyArrayStore( + expr = expr, + findStoredIndexPosition = { i -> + storedIndices[indices[i]] + }, + saveStoredIndexPosition = { i, indexPosition -> + storedIndices[indices[i]] = indexPosition + }, + indexIsConstant = { i -> + indices[i].all { it.definitelyIsConstant } + }, + addSimplifiedIndex = { i -> + simplifiedIndices.add(indices[i]) + }, + distinctWithSimplifiedIndex = { i, simplifiedIdx -> + areDefinitelyDistinct(indices[i], simplifiedIndices[simplifiedIdx]) + }, + selectIndicesMatch = { select: KArrayNSelect, i -> + indices[i] == select.indices + }, + mkSimplifiedStore = { array, simplifiedIdx, value -> + ctx.mkArrayNStoreNoSimplify(array, simplifiedIndices[simplifiedIdx], value) + } + ) + } + + @Suppress("LongParameterList", "NestedBlockDepth", "ComplexMethod", "LoopWithTooManyJumpStatements") + private inline fun , R : KSort, reified S : KArraySelectBase> simplifyArrayStore( + expr: SimplifierFlatArrayStoreBaseExpr, + findStoredIndexPosition: (Int) -> Int?, + saveStoredIndexPosition: (Int, Int) -> Unit, + indexIsConstant: (Int) -> Boolean, + addSimplifiedIndex: (Int) -> Unit, + distinctWithSimplifiedIndex: (Int, Int) -> Boolean, + selectIndicesMatch: (S, Int) -> Boolean, + mkSimplifiedStore: (KExpr, Int, KExpr) -> KExpr + ): KExpr { + if (expr.numIndices == 0) { + return expr.base + } + + val simplifiedValues = arrayListOf>() + var numSimplifiedIndices = 0 + var lastNonConstantIndex = -1 + + for (i in expr.numIndices - 1 downTo 0) { + val value = expr.values[i] + + val storedIndexPosition = findStoredIndexPosition(i) + if (storedIndexPosition != null) { + + /** Try push store. + * (store (store a i x) j y), i != j ==> (store (store a j y) i x) + * + * Store can be pushed only if all parent indices are definitely not equal. + * */ + val allParentIndicesAreDistinct = allParentSoreIndicesAreDistinct( + index = i, + storedIndexPosition = storedIndexPosition, + lastNonConstantIndex = lastNonConstantIndex, + numSimplifiedIndices = numSimplifiedIndices, + indexIsConstant = { x -> indexIsConstant(x) }, + distinctWithSimplifiedIndex = { x, y -> distinctWithSimplifiedIndex(x, y) } + ) - // (store a i (select a i)) ==> a - if (value is KArraySelect<*, *> && base == value.array && index == value.index) { - continue + if (allParentIndicesAreDistinct) { + simplifiedValues[storedIndexPosition] = value + if (!indexIsConstant(i)) { + lastNonConstantIndex = maxOf(lastNonConstantIndex, storedIndexPosition) } + continue } + } - // store - simplifiedIndices.add(index) - simplifiedValues.add(value) - val indexPosition = simplifiedIndices.lastIndex - storedIndices[index] = indexPosition - if (!index.definitelyIsConstant) { - lastNonConstantIndex = maxOf(lastNonConstantIndex, indexPosition) + // simplify direct store to array + if (numSimplifiedIndices == 0) { + // (store (const v) i v) ==> (const v) + if (expr.base is KArrayConst && expr.base.value == expr.values[i]) { + continue + } + + // (store a i (select a i)) ==> a + if (value is S && expr.base == value.array && selectIndicesMatch(value, i)) { + continue } } - var result = base - for (i in simplifiedIndices.indices) { - val index = simplifiedIndices[i] - val value = simplifiedValues[i] - result = mkArrayStoreNoSimplify(result, index, value) + // store + simplifiedValues.add(value) + + val indexPosition = numSimplifiedIndices++ + addSimplifiedIndex(i) + saveStoredIndexPosition(i, indexPosition) + + if (!indexIsConstant(i)) { + lastNonConstantIndex = maxOf(lastNonConstantIndex, indexPosition) } + } - return@simplifyExpr result + var result = expr.base + for (i in 0 until numSimplifiedIndices) { + result = mkSimplifiedStore(result, i, simplifiedValues[i]) } - private fun allParentSoreIndicesAreDistinct( - index: KExpr, + return result + } + + @Suppress("LongParameterList") + private inline fun allParentSoreIndicesAreDistinct( + index: Int, storedIndexPosition: Int, lastNonConstantIndex: Int, - simplifiedIndices: List> + numSimplifiedIndices: Int, + indexIsConstant: (Int) -> Boolean, + distinctWithSimplifiedIndex: (Int, Int) -> Boolean ): Boolean { /** * Since all constants are trivially comparable we can guarantee, that * all parents are distinct. * Otherwise, we need to perform full check of parent indices. * */ - if (index.definitelyIsConstant && storedIndexPosition >= lastNonConstantIndex) return true + if (indexIsConstant(index) && storedIndexPosition >= lastNonConstantIndex) return true /** * If non-constant index is among the indices we need to check, @@ -288,13 +500,13 @@ interface KArrayExprSimplifier : KExprSimplifierBase { * not definitely distinct, we will not check all other indices. * */ if (lastNonConstantIndex > storedIndexPosition - && !areDefinitelyDistinct(index, simplifiedIndices[lastNonConstantIndex]) + && !distinctWithSimplifiedIndex(index, lastNonConstantIndex) ) { return false } - for (checkIdx in (storedIndexPosition + 1) until simplifiedIndices.size) { - if (!areDefinitelyDistinct(index, simplifiedIndices[checkIdx])) { + for (checkIdx in (storedIndexPosition + 1) until numSimplifiedIndices) { + if (!distinctWithSimplifiedIndex(index, checkIdx)) { // possibly equal index, we can't squash stores return false } @@ -303,77 +515,276 @@ interface KArrayExprSimplifier : KExprSimplifierBase { return true } - /** - * Try to simplify only indices first. - * Usually this will be enough and we will not produce many irrelevant array store expressions. - * */ - private fun transform(expr: SimplifierFlatArraySelectExpr): KExpr = - simplifyExpr(expr, expr.args) { allIndices -> - val index = allIndices.first() - val arrayIndices = allIndices.subList(fromIndex = 1, toIndex = allIndices.size) - - var arrayStoreIdx = 0 - while (arrayStoreIdx < arrayIndices.size) { - val storeIdx = arrayIndices[arrayStoreIdx] - - // (select (store i v) i) ==> v - if (storeIdx == index) { - return@simplifyExpr rewrite(expr.storedValues[arrayStoreIdx]) - } + override fun transform(expr: KArraySelect): KExpr = + simplifyExpr(expr, expr.index) { index -> + transformSelect(expr.array, index) + } - if (!areDefinitelyDistinct(index, storeIdx)) { - break - } + override fun transform(expr: KArray2Select): KExpr = + simplifyExpr(expr, expr.index0, expr.index1) { index0, index1 -> + transformSelect(expr.array, index0, index1) + } - // (select (store a i v) j), i != j ==> (select a j) - arrayStoreIdx++ - } + override fun transform( + expr: KArray3Select + ): KExpr = simplifyExpr(expr, expr.index0, expr.index1, expr.index2) { index0, index1, index2 -> + transformSelect(expr.array, index0, index1, index2) + } - if (arrayStoreIdx == arrayIndices.size) { - return@simplifyExpr rewrite(SimplifierArraySelectExpr(ctx, expr.baseArray, index)) - } + override fun transform(expr: KArrayNSelect): KExpr = + simplifyExpr(expr, expr.indices) { indices -> + transformSelect(expr.array, indices) + } + + fun transformSelect( + array: KExpr>, index: KExpr + ): KExpr = transformSelect( + array, + selectFromStore = { store: KArrayStore -> SelectFromStoreExpr(ctx, store, index) }, + selectFromFunction = { f -> KFunctionApp(ctx, f, listOf(index).uncheckedCast()) }, + default = { SimplifierArraySelectExpr(ctx, array, index) } + ) + + fun transformSelect( + array: KExpr>, index0: KExpr, index1: KExpr + ): KExpr = transformSelect( + array, + selectFromStore = { store: KArray2Store -> Select2FromStoreExpr(ctx, store, index0, index1) }, + selectFromFunction = { f -> KFunctionApp(ctx, f, listOf(index0, index1).uncheckedCast()) }, + default = { SimplifierArray2SelectExpr(ctx, array, index0, index1) } + ) + + fun transformSelect( + array: KExpr>, + index0: KExpr, index1: KExpr, index2: KExpr + ): KExpr = transformSelect( + array, + selectFromStore = { store: KArray3Store -> + Select3FromStoreExpr(ctx, store, index0, index1, index2) + }, + selectFromFunction = { f -> KFunctionApp(ctx, f, listOf(index0, index1, index2).uncheckedCast()) }, + default = { SimplifierArray3SelectExpr(ctx, array, index0, index1, index2) } + ) - rewrite(SimplifierArraySelectExpr(ctx, expr.original, index)) + fun transformSelect( + array: KExpr>, indices: List> + ): KExpr = transformSelect( + array, + selectFromStore = { store: KArrayNStore -> SelectNFromStoreExpr(ctx, store, indices) }, + selectFromFunction = { f -> KFunctionApp(ctx, f, indices) }, + default = { SimplifierArrayNSelectExpr(ctx, array, indices) } + ) + + private inline fun , R : KSort, reified S : KArrayStoreBase> transformSelect( + array: KExpr, + selectFromStore: (S) -> KExpr, + selectFromFunction: (KDecl) -> KExpr, + default: () -> KExpr + ): KExpr = when (array) { + is S -> rewrite(selectFromStore(array)) + is KFunctionAsArray -> rewrite(selectFromFunction(array.function.uncheckedCast())) + else -> rewrite(default()) + } + + private fun transform(expr: SelectFromStoreExpr): KExpr = + simplifyExpr(expr, expr.array.index) { storeIndex -> + transformSelectFromStore( + expr = expr, + indexMatch = { expr.index == storeIndex }, + indexDistinct = { areDefinitelyDistinct(expr.index, storeIndex) }, + transformNested = { transformSelect(expr.array.array, expr.index) }, + default = { SimplifierArraySelectExpr(ctx, expr.array, expr.index) } + ) } + private fun transform(expr: Select2FromStoreExpr): KExpr = + simplifyExpr(expr, expr.array.index0, expr.array.index1) { storeIndex0, storeIndex1 -> + transformSelectFromStore( + expr = expr, + indexMatch = { expr.index0 == storeIndex0 && expr.index1 == storeIndex1 }, + indexDistinct = { + areDefinitelyDistinct(expr.index0, storeIndex0) + && areDefinitelyDistinct(expr.index1, storeIndex1) + }, + transformNested = { transformSelect(expr.array.array, expr.index0, expr.index1) }, + default = { SimplifierArray2SelectExpr(ctx, expr.array, expr.index0, expr.index1) } + ) + } + + private fun transform( + expr: Select3FromStoreExpr + ): KExpr = + simplifyExpr(expr, expr.array.index0, expr.array.index1, expr.array.index2) { si0, si1, si2 -> + transformSelectFromStore( + expr = expr, + indexMatch = { expr.index0 == si0 && expr.index1 == si1 && expr.index2 == si2 }, + indexDistinct = { + areDefinitelyDistinct(expr.index0, si0) + && areDefinitelyDistinct(expr.index1, si1) + && areDefinitelyDistinct(expr.index2, si2) + }, + transformNested = { transformSelect(expr.array.array, expr.index0, expr.index1, expr.index2) }, + default = { SimplifierArray3SelectExpr(ctx, expr.array, expr.index0, expr.index1, expr.index2) } + ) + } + + private fun transform(expr: SelectNFromStoreExpr): KExpr = + simplifyExpr(expr, expr.array.indices) { indices -> + transformSelectFromStore( + expr = expr, + indexMatch = { expr.indices == indices }, + indexDistinct = { areDefinitelyDistinct(expr.indices, indices) }, + transformNested = { transformSelect(expr.array.array, expr.indices) }, + default = { SimplifierArrayNSelectExpr(ctx, expr.array, expr.indices) } + ) + } + + private inline fun , R : KSort> transformSelectFromStore( + expr: SelectFromStoreExprBase, + indexMatch: () -> Boolean, + indexDistinct: () -> Boolean, + transformNested: () -> KExpr, + default: () -> KExpr + ): KExpr { + // (select (store i v) i) ==> v + if (indexMatch()) { + return rewrite(expr.storeValue) + } + + // (select (store a i v) j), i != j ==> (select a j) + if (indexDistinct()) { + return transformNested() + } + + return rewrite(default()) + } + private fun transform(expr: SimplifierArraySelectExpr): KExpr = - simplifyExpr(expr, expr.array, expr.index) { arrayArg, indexArg -> - var array: KExpr> = arrayArg.uncheckedCast() - val index: KExpr = indexArg.uncheckedCast() - - while (array is KArrayStore) { - // (select (store i v) i) ==> v - if (array.index == index) { - return@simplifyExpr array.value - } + simplifyExpr(expr, expr.array) { array -> + transformSelectFull( + expr = array, + storeIndexMatch = { store: KArrayStore -> expr.index == store.index }, + storeIndexDistinct = { store: KArrayStore -> areDefinitelyDistinct(expr.index, store.index) }, + mkLambdaSubstitution = { lambda: KArrayLambda -> + substitute(mkConstApp(lambda.indexVarDecl), expr.index) + }, + default = { mkArraySelectNoSimplify(it, expr.index) } + ) + } - // (select (store a i v) j), i != j ==> (select a j) - if (areDefinitelyDistinct(index, array.index)) { - array = array.array - } else { - // possibly equal index, we can't expand stores - break - } + private fun transform( + expr: SimplifierArray2SelectExpr + ): KExpr = simplifyExpr(expr, expr.array) { array -> + transformSelectFull( + expr = array, + storeIndexMatch = { store: KArray2Store -> + expr.index0 == store.index0 && expr.index1 == store.index1 + }, + storeIndexDistinct = { store: KArray2Store -> + areDefinitelyDistinct(expr.index0, store.index0) + && areDefinitelyDistinct(expr.index1, store.index1) + }, + mkLambdaSubstitution = { lambda: KArray2Lambda -> + substitute(mkConstApp(lambda.indexVar0Decl), expr.index0) + substitute(mkConstApp(lambda.indexVar1Decl), expr.index1) + }, + default = { mkArraySelectNoSimplify(it, expr.index0, expr.index1) } + ) + } + + private fun transform( + expr: SimplifierArray3SelectExpr + ): KExpr = simplifyExpr(expr, expr.array) { array -> + transformSelectFull( + expr = array, + storeIndexMatch = { store: KArray3Store -> + expr.index0 == store.index0 + && expr.index1 == store.index1 + && expr.index2 == store.index2 + }, + storeIndexDistinct = { store: KArray3Store -> + areDefinitelyDistinct(expr.index0, store.index0) + && areDefinitelyDistinct(expr.index1, store.index1) + && areDefinitelyDistinct(expr.index2, store.index2) + }, + mkLambdaSubstitution = { lambda: KArray3Lambda -> + substitute(mkConstApp(lambda.indexVar0Decl), expr.index0) + substitute(mkConstApp(lambda.indexVar1Decl), expr.index1) + substitute(mkConstApp(lambda.indexVar2Decl), expr.index2) + }, + default = { mkArraySelectNoSimplify(it, expr.index0, expr.index1, expr.index2) } + ) + } + + private fun transform(expr: SimplifierArrayNSelectExpr): KExpr = + simplifyExpr(expr, expr.array) { array -> + transformSelectFull( + expr = array, + storeIndexMatch = { store: KArrayNStore -> expr.indices == store.indices }, + storeIndexDistinct = { store: KArrayNStore -> areDefinitelyDistinct(expr.indices, store.indices) }, + mkLambdaSubstitution = { lambda: KArrayNLambda -> + lambda.indexVarDeclarations.zip(expr.indices) { varDecl, index -> + substitute(mkConstApp(varDecl).uncheckedCast<_, KExpr>(), index.uncheckedCast()) + } + }, + default = { mkArrayNSelectNoSimplify(it, expr.indices) } + ) + } + + private inline fun < + A : KArraySortBase, + R : KSort, + reified S : KArrayStoreBase, + reified L : KArrayLambdaBase + > transformSelectFull( + expr: KExpr, + storeIndexMatch: (S) -> Boolean, + storeIndexDistinct: (S) -> Boolean, + mkLambdaSubstitution: KExprSubstitutor.(L) -> Unit, + default: (KExpr) -> KExpr + ): KExpr { + var array: KExpr = expr + while (array is S) { + // (select (store i v) i) ==> v + if (storeIndexMatch(array)) { + return array.value } - when (array) { - // (select (const v) i) ==> v - is KArrayConst -> { - array.value - } - // (select (lambda x body) i) ==> body[i/x] - is KArrayLambda -> { - val resolvedBody = KExprSubstitutor(ctx).apply { - val indexVarExpr = mkConstApp(array.indexVarDecl) - substitute(indexVarExpr, index) - }.apply(array.body) - rewrite(resolvedBody) - } - else -> { - mkArraySelectNoSimplify(array, index) - } + // (select (store a i v) j), i != j ==> (select a j) + if (storeIndexDistinct(array)) { + array = array.array + } else { + // possibly equal index, we can't expand stores + break + } + } + + return when (array) { + // (select (const v) i) ==> v + is KArrayConst -> { + array.value.uncheckedCast() } + // (select (lambda x body) i) ==> body[i/x] + is L -> { + val resolvedBody = KExprSubstitutor(ctx).apply { + mkLambdaSubstitution(array) + }.apply(array.body) + rewrite(resolvedBody) + } + + else -> default(array) } + } + + @Suppress("USELESS_CAST") // Exhaustive when + private fun , R : KSort> flatStoresGeneric( + sort: A, expr: KExpr + ): SimplifierFlatArrayStoreBaseExpr = when (sort as KArraySortBase) { + is KArraySort<*, R> -> flatStores(expr.uncheckedCast()).uncheckedCast() + is KArray2Sort<*, *, R> -> flatStores2(expr.uncheckedCast()).uncheckedCast() + is KArray3Sort<*, *, *, R> -> flatStores3(expr.uncheckedCast()).uncheckedCast() + is KArrayNSort -> flatStoresN(expr.uncheckedCast()).uncheckedCast() + } private fun flatStores( expr: KExpr>, @@ -388,6 +799,76 @@ interface KArrayExprSimplifier : KExprSimplifierBase { } return SimplifierFlatArrayStoreExpr( ctx = ctx, + original = expr, + base = base, + indices = indices, + values = values + ) + } + + private fun flatStores2( + expr: KExpr>, + ): SimplifierFlatArray2StoreExpr { + val indices0 = arrayListOf>() + val indices1 = arrayListOf>() + val values = arrayListOf>() + var base = expr + while (base is KArray2Store) { + indices0.add(base.index0) + indices1.add(base.index1) + values.add(base.value) + base = base.array + } + return SimplifierFlatArray2StoreExpr( + ctx = ctx, + original = expr, + base = base, + indices0 = indices0, + indices1 = indices1, + values = values + ) + } + + private fun flatStores3( + expr: KExpr>, + ): SimplifierFlatArray3StoreExpr { + val indices0 = arrayListOf>() + val indices1 = arrayListOf>() + val indices2 = arrayListOf>() + val values = arrayListOf>() + var base = expr + while (base is KArray3Store) { + indices0.add(base.index0) + indices1.add(base.index1) + indices2.add(base.index2) + values.add(base.value) + base = base.array + } + return SimplifierFlatArray3StoreExpr( + ctx = ctx, + original = expr, + base = base, + indices0 = indices0, + indices1 = indices1, + indices2 = indices2, + values = values + ) + } + + private fun flatStoresN( + expr: KExpr>, + ): SimplifierFlatArrayNStoreExpr { + val indices = arrayListOf>>() + val values = arrayListOf>() + var base = expr + while (base is KArrayNStore) { + indices.add(base.indices) + values.add(base.value) + base = base.array + } + return SimplifierFlatArrayNStoreExpr( + ctx = ctx, + original = expr, base = base, indices = indices, values = values @@ -397,54 +878,276 @@ interface KArrayExprSimplifier : KExprSimplifierBase { private val KExpr<*>.definitelyIsConstant: Boolean get() = this is KInterpretedValue<*> + private fun areDefinitelyDistinct(left: List>, right: List>): Boolean { + for (i in left.indices) { + val lhs: KExpr = left[i].uncheckedCast() + val rhs: KExpr = right[i].uncheckedCast() + if (!areDefinitelyDistinct(lhs, rhs)) return false + } + return true + } + /** * Auxiliary expression to handle expanded array stores. * @see [SimplifierAuxExpression] * */ + private sealed class SimplifierFlatArrayStoreBaseExpr, R : KSort>( + ctx: KContext, + val numIndices: Int, + val original: KExpr, + val base: KExpr, + val values: List> + ) : KExpr(ctx) { + override val sort: A + get() = base.sort + + override fun internEquals(other: Any): Boolean = + error("Interning is not used for Aux expressions") + + override fun internHashCode(): Int = + error("Interning is not used for Aux expressions") + + override fun print(printer: ExpressionPrinter) { + original.print(printer) + } + + abstract fun getStoreIndex(idx: Int): List> + + abstract fun selectValue(originalArray: KExpr, index: List>): KExpr + } + private class SimplifierFlatArrayStoreExpr( ctx: KContext, - val base: KExpr>, + original: KExpr>, + base: KExpr>, val indices: List>, - val values: List>, - ) : KApp, KSort>(ctx) { + values: List>, + ) : SimplifierFlatArrayStoreBaseExpr, R>(ctx, indices.size, original, base, values) { + override fun accept(transformer: KTransformerBase): KExpr> { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } - override val args: List> = - (listOf(base) + indices + values).uncheckedCast() + fun unwrap(): List> = (listOf(base) + indices + values).uncheckedCast() + + fun wrap(args: List>) = SimplifierFlatArrayStoreExpr( + ctx, + original, + base = args.first().uncheckedCast(), + indices = args.subList( + fromIndex = 1, toIndex = 1 + numIndices + ).uncheckedCast(), + values = args.subList( + fromIndex = 1 + numIndices, toIndex = args.size + ).uncheckedCast() + ) - override val decl: KDecl> - get() = ctx.mkArrayStoreDecl(base.sort) + override fun getStoreIndex(idx: Int): List> = listOf(indices[idx]) - override val sort: KArraySort - get() = base.sort + override fun selectValue(originalArray: KExpr>, index: List>): KExpr = + KArraySelect(ctx, originalArray, index.single().uncheckedCast()) + } - override fun accept(transformer: KTransformerBase): KExpr> { + private class SimplifierFlatArray2StoreExpr( + ctx: KContext, + original: KExpr>, + base: KExpr>, + val indices0: List>, + val indices1: List>, + values: List>, + ) : SimplifierFlatArrayStoreBaseExpr, R>(ctx, indices0.size, original, base, values) { + override fun accept(transformer: KTransformerBase): KExpr> { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + + fun unwrap(): List> = buildList { + add(base) + addAll(indices0) + addAll(indices1) + addAll(values) + }.uncheckedCast() + + fun wrap(args: List>) = SimplifierFlatArray2StoreExpr( + ctx, + original, + base = args.first().uncheckedCast(), + indices0 = args.subList( + fromIndex = 1, toIndex = 1 + numIndices + ).uncheckedCast(), + indices1 = args.subList( + fromIndex = 1 + numIndices, toIndex = 1 + 2 * numIndices + ).uncheckedCast(), + values = args.subList( + fromIndex = 1 + 2 * numIndices, toIndex = args.size + ).uncheckedCast() + ) + + + override fun getStoreIndex(idx: Int): List> = listOf(indices0[idx], indices1[idx]) + + override fun selectValue(originalArray: KExpr>, index: List>): KExpr = + KArray2Select(ctx, originalArray, index.first().uncheckedCast(), index.last().uncheckedCast()) + } + + @Suppress("LongParameterList") + private class SimplifierFlatArray3StoreExpr( + ctx: KContext, + original: KExpr>, + base: KExpr>, + val indices0: List>, + val indices1: List>, + val indices2: List>, + values: List>, + ) : SimplifierFlatArrayStoreBaseExpr, R>(ctx, indices0.size, original, base, values) { + override fun accept(transformer: KTransformerBase): KExpr> { transformer as KArrayExprSimplifier return transformer.transform(this) } + + fun unwrap(): List> = buildList { + add(base) + addAll(indices0) + addAll(indices1) + addAll(indices2) + addAll(values) + }.uncheckedCast() + + @Suppress("MagicNumber") + fun wrap(args: List>) = SimplifierFlatArray3StoreExpr( + ctx, + original, + base = args.first().uncheckedCast(), + indices0 = args.subList( + fromIndex = 1, toIndex = 1 + numIndices + ).uncheckedCast(), + indices1 = args.subList( + fromIndex = 1 + numIndices, toIndex = 1 + 2 * numIndices + ).uncheckedCast(), + indices2 = args.subList( + fromIndex = 1 + 2 * numIndices, toIndex = 1 + 3 * numIndices + ).uncheckedCast(), + values = args.subList( + fromIndex = 1 + 3 * numIndices, toIndex = args.size + ).uncheckedCast() + ) + + override fun getStoreIndex(idx: Int): List> = listOf(indices0[idx], indices1[idx], indices2[idx]) + + override fun selectValue(originalArray: KExpr>, index: List>): KExpr { + val (idx0, idx1, idx2) = index + return KArray3Select( + ctx, + originalArray, + idx0.uncheckedCast(), + idx1.uncheckedCast(), + idx2.uncheckedCast() + ) + } + } + + private class SimplifierFlatArrayNStoreExpr( + ctx: KContext, + original: KExpr>, + base: KExpr>, + val indices: List>>, + values: List>, + ) : SimplifierFlatArrayStoreBaseExpr, R>(ctx, indices.size, original, base, values) { + override fun accept(transformer: KTransformerBase): KExpr> { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + + fun unwrap(): List> = buildList { + add(base) + addAll(values) + this@SimplifierFlatArrayNStoreExpr.indices.forEach { + addAll(it) + } + }.uncheckedCast() + + fun wrap(args: List>) = SimplifierFlatArrayNStoreExpr( + ctx, original, + base = args.first().uncheckedCast(), + values = args.subList( + fromIndex = 1, toIndex = 1 + numIndices + ).uncheckedCast(), + indices = args.subList( + fromIndex = 1 + numIndices, toIndex = args.size + ).chunked(numIndices) + ) + + override fun getStoreIndex(idx: Int): List> = indices[idx] + + override fun selectValue(originalArray: KExpr>, index: List>): KExpr = + KArrayNSelect(ctx, originalArray, index.uncheckedCast()) } /** - * Auxiliary expression to handle select with base array expanded. + * Auxiliary expression to handle array select. * @see [SimplifierAuxExpression] * */ - private class SimplifierFlatArraySelectExpr( + private sealed class SimplifierArraySelectBaseExpr, R : KSort>( ctx: KContext, - val original: KExpr>, - val baseArray: KExpr>, - val storedIndices: List>, - val storedValues: List>, - val index: KExpr, - ) : KApp(ctx) { + val array: KExpr + ) : KExpr(ctx) { + override val sort: R + get() = array.sort.range - override val args: List> = - listOf(index) + storedIndices + override fun internEquals(other: Any): Boolean = + error("Interning is not used for Aux expressions") - override val sort: R - get() = baseArray.sort.range + override fun internHashCode(): Int = + error("Interning is not used for Aux expressions") + + override fun print(printer: ExpressionPrinter) { + printer.append("(simplifierSelect ") + printer.append(array) + printer.append(")") + } + } + + private class SimplifierArraySelectExpr( + ctx: KContext, + array: KExpr>, + val index: KExpr, + ) : SimplifierArraySelectBaseExpr, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } - override val decl: KDecl - get() = ctx.mkArraySelectDecl(baseArray.sort) + private class SimplifierArray2SelectExpr( + ctx: KContext, + array: KExpr>, + val index0: KExpr, + val index1: KExpr + ) : SimplifierArraySelectBaseExpr, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } + + private class SimplifierArray3SelectExpr( + ctx: KContext, + array: KExpr>, + val index0: KExpr, + val index1: KExpr, + val index2: KExpr, + ) : SimplifierArraySelectBaseExpr, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } + private class SimplifierArrayNSelectExpr( + ctx: KContext, + array: KExpr>, + val indices: List>, + ) : SimplifierArraySelectBaseExpr, R>(ctx, array) { override fun accept(transformer: KTransformerBase): KExpr { transformer as KArrayExprSimplifier return transformer.transform(this) @@ -452,26 +1155,72 @@ interface KArrayExprSimplifier : KExprSimplifierBase { } /** - * Auxiliary expression to handle array select. + * Auxiliary expression to handle select from array store. * @see [SimplifierAuxExpression] * */ - private class SimplifierArraySelectExpr( + private sealed class SelectFromStoreExprBase, R : KSort>( ctx: KContext, - val array: KExpr>, - val index: KExpr, - ) : KApp(ctx) { - override val args: List> = listOf(array, index).uncheckedCast() + array: KArrayStoreBase + ) : KExpr(ctx) { + val storeValue: KExpr = array.value - override val sort: R - get() = array.sort.range + override val sort: R = storeValue.sort + + override fun print(printer: ExpressionPrinter) { + printer.append("(selectFromStore)") + } + + override fun internEquals(other: Any): Boolean = + error("Interning is not used for Aux expressions") + + override fun internHashCode(): Int = + error("Interning is not used for Aux expressions") + } + + private class SelectFromStoreExpr( + ctx: KContext, + val array: KArrayStore, + val index: KExpr, + ) : SelectFromStoreExprBase, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } - override val decl: KDecl - get() = ctx.mkArraySelectDecl(array.sort) + private class Select2FromStoreExpr( + ctx: KContext, + val array: KArray2Store, + val index0: KExpr, + val index1: KExpr + ) : SelectFromStoreExprBase, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } + private class Select3FromStoreExpr( + ctx: KContext, + val array: KArray3Store, + val index0: KExpr, + val index1: KExpr, + val index2: KExpr, + ) : SelectFromStoreExprBase, R>(ctx, array) { override fun accept(transformer: KTransformerBase): KExpr { transformer as KArrayExprSimplifier return transformer.transform(this) } } + private class SelectNFromStoreExpr( + ctx: KContext, + val array: KArrayNStore, + val indices: List>, + ) : SelectFromStoreExprBase, R>(ctx, array) { + override fun accept(transformer: KTransformerBase): KExpr { + transformer as KArrayExprSimplifier + return transformer.transform(this) + } + } } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KExprSimplifier.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KExprSimplifier.kt index 5c4be2383..1cee9163b 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KExprSimplifier.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/rewrite/simplify/KExprSimplifier.kt @@ -3,7 +3,10 @@ package org.ksmt.expr.rewrite.simplify import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KDistinctExpr import org.ksmt.expr.KEqExpr import org.ksmt.expr.KExistentialQuantifier @@ -12,7 +15,11 @@ import org.ksmt.expr.KInterpretedValue import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.rewrite.KExprUninterpretedDeclCollector import org.ksmt.expr.transformer.KNonRecursiveTransformerBase +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFpSort @@ -41,7 +48,7 @@ open class KExprSimplifier(override val ctx: KContext) : realSort -> simplifyEqReal(lhs.uncheckedCast(), rhs.uncheckedCast()) is KBvSort -> simplifyEqBv(lhs as KExpr, rhs.uncheckedCast()) is KFpSort -> simplifyEqFp(lhs as KExpr, rhs.uncheckedCast()) - is KArraySort<*, *> -> simplifyEqArray(lhs as KExpr>, rhs.uncheckedCast()) + is KArraySortBase<*> -> simplifyEqArray(lhs as KExpr>, rhs.uncheckedCast()) is KUninterpretedSort -> simplifyEqUninterpreted(lhs.uncheckedCast(), rhs.uncheckedCast()) else -> mkEqNoSimplify(lhs, rhs) } @@ -120,6 +127,24 @@ open class KExprSimplifier(override val ctx: KContext) : simplifyArrayLambda(expr.indexVarDecl, body) } + override fun transform( + expr: KArray2Lambda + ): KExpr> = simplifyExpr(expr, expr.body) { body -> + simplifyArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, body) + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = simplifyExpr(expr, expr.body) { body -> + simplifyArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl, body) + } + + override fun transform( + expr: KArrayNLambda + ): KExpr> = simplifyExpr(expr, expr.body) { body -> + simplifyArrayLambda(expr.indexVarDeclarations, body) + } + override fun transform(expr: KExistentialQuantifier): KExpr = simplifyExpr(expr, expr.body) { body -> simplifyExistentialQuantifier(expr.bounds, body) @@ -134,7 +159,7 @@ open class KExprSimplifier(override val ctx: KContext) : bound: KDecl, simplifiedBody: KExpr ): KExpr> = simplifyQuantifier( - bounds = listOf(element = bound), + bounds = listOf(bound), simplifiedBody = simplifiedBody, eliminateQuantifier = { body -> val sort = mkArraySort(bound.sort, body.sort) @@ -143,6 +168,45 @@ open class KExprSimplifier(override val ctx: KContext) : buildQuantifier = { _, body -> mkArrayLambda(bound, body) } ) + fun KContext.simplifyArrayLambda( + bound0: KDecl, bound1: KDecl, + simplifiedBody: KExpr + ): KExpr> = simplifyQuantifier( + bounds = listOf(bound0, bound1), + simplifiedBody = simplifiedBody, + eliminateQuantifier = { body -> + val sort = mkArraySort(bound0.sort, bound1.sort, body.sort) + mkArrayConst(sort, body) + }, + buildQuantifier = { _, body -> mkArrayLambda(bound0, bound1, body) } + ) + + fun KContext.simplifyArrayLambda( + bound0: KDecl, bound1: KDecl, bound2: KDecl, + simplifiedBody: KExpr + ): KExpr> = simplifyQuantifier( + bounds = listOf(bound0, bound1, bound2), + simplifiedBody = simplifiedBody, + eliminateQuantifier = { body -> + val sort = mkArraySort(bound0.sort, bound1.sort, bound2.sort, body.sort) + mkArrayConst(sort, body) + }, + buildQuantifier = { _, body -> mkArrayLambda(bound0, bound1, bound2, body) } + ) + + fun KContext.simplifyArrayLambda( + bounds: List>, + simplifiedBody: KExpr + ): KExpr> = simplifyQuantifier( + bounds = bounds, + simplifiedBody = simplifiedBody, + eliminateQuantifier = { body -> + val sort = mkArrayNSort(bounds.map { it.sort }, body.sort) + mkArrayConst(sort, body) + }, + buildQuantifier = { _, body -> mkArrayNLambda(bounds, body) } + ) + fun KContext.simplifyExistentialQuantifier( bounds: List>, simplifiedBody: KExpr diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformer.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformer.kt index 7af4ecf67..a352d638a 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformer.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformer.kt @@ -4,8 +4,17 @@ import org.ksmt.KContext import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBv2IntExpr @@ -114,7 +123,11 @@ import org.ksmt.expr.KUnaryMinusArithExpr import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv1Sort import org.ksmt.sort.KBvSort @@ -122,6 +135,7 @@ import org.ksmt.sort.KFpSort import org.ksmt.sort.KIntSort import org.ksmt.sort.KRealSort import org.ksmt.sort.KSort +import org.ksmt.utils.uncheckedCast /** * Apply specialized non-recursive transformations for all KSMT expressions. @@ -482,15 +496,99 @@ abstract class KNonRecursiveTransformer(override val ctx: KContext) : KNonRecurs ) { rm, value -> mkBvToFpExpr(expr.sort, rm, value, expr.signed) } // array transformers - override fun transform(expr: KArrayStore): KExpr> = - transformExprAfterTransformedDefault( - expr, expr.array, expr.index, expr.value, ::transformApp, KContext::mkArrayStore + override fun transform( + expr: KArrayStore + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.array, expr.index, expr.value, ::transformArrayStore, KContext::mkArrayStore + ) + + override fun transform( + expr: KArray2Store + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.array, expr.index0, expr.index1, expr.value, ::transformArrayStore, KContext::mkArrayStore + ) + + override fun transform( + expr: KArray3Store + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.array, expr.index0, expr.index1, expr.index2, expr.value, + ::transformArrayStore, KContext::mkArrayStore + ) + + override fun transform( + expr: KArrayNStore + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.args, ::transformArrayStore + ) { args -> + mkArrayNStore( + array = args.first().uncheckedCast(), + indices = args.subList(fromIndex = 1, toIndex = args.size - 1).uncheckedCast(), + value = args.last().uncheckedCast() ) + } + + override fun transform( + expr: KArraySelect + ): KExpr = transformExprAfterTransformedDefault( + expr, expr.array, expr.index, ::transformArraySelect, KContext::mkArraySelect + ) + + override fun transform( + expr: KArray2Select + ): KExpr = transformExprAfterTransformedDefault( + expr, expr.array, expr.index0, expr.index1, ::transformArraySelect, KContext::mkArraySelect + ) + + override fun transform( + expr: KArray3Select + ): KExpr = transformExprAfterTransformedDefault( + expr, expr.array, expr.index0, expr.index1, expr.index2, ::transformArraySelect, KContext::mkArraySelect + ) + + override fun transform( + expr: KArrayNSelect + ): KExpr = transformExprAfterTransformedDefault( + expr, expr.args, ::transformArraySelect + ) { args -> + mkArrayNSelect( + array = args.first().uncheckedCast(), + indices = args.drop(1) + ) + } + + override fun transform( + expr: KArrayLambda + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.body, ::transformArrayLambda + ) { body -> + mkArrayLambda(expr.indexVarDecl, body) + } - override fun transform(expr: KArraySelect): KExpr = - transformExprAfterTransformedDefault(expr, expr.array, expr.index, ::transformApp, KContext::mkArraySelect) + override fun transform( + expr: KArray2Lambda + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.body, ::transformArrayLambda + ) { body -> + mkArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, body) + } - override fun transform(expr: KArrayConst): KExpr> = + override fun transform( + expr: KArray3Lambda + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.body, ::transformArrayLambda + ) { body -> + mkArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl, body) + } + + override fun transform( + expr: KArrayNLambda + ): KExpr> = transformExprAfterTransformedDefault( + expr, expr.body, ::transformArrayLambda + ) { body -> + mkArrayNLambda(expr.indexVarDeclarations, body) + } + + override fun , R : KSort> transform(expr: KArrayConst): KExpr = transformExprAfterTransformedDefault(expr, expr.value, ::transformApp) { value -> mkArrayConst(expr.sort, value) } @@ -544,12 +642,6 @@ abstract class KNonRecursiveTransformer(override val ctx: KContext) : KNonRecurs transformExprAfterTransformedDefault(expr, expr.arg, ::transformApp, KContext::mkRealIsInt) // quantified expressions - override fun transform( - expr: KArrayLambda - ): KExpr> = transformExprAfterTransformedDefault(expr, expr.body, ::transformExpr) { body -> - mkArrayLambda(expr.indexVarDecl, body) - } - override fun transform(expr: KExistentialQuantifier): KExpr = transformExprAfterTransformedDefault(expr, expr.body, ::transformExpr) { body -> mkExistentialQuantifier(body, expr.bounds) @@ -649,7 +741,7 @@ abstract class KNonRecursiveTransformer(override val ctx: KContext) : KNonRecurs } /** - * Specialized version of [transformExprAfterTransformedDefault] for expression with 4 arguments. + * Specialized version of [transformExprAfterTransformedDefault] for expression with four arguments. * */ @Suppress("LongParameterList", "ComplexCondition") private inline fun , Out : KExpr, T : KSort, A0 : KSort, A1 : KSort, A2 : KSort, A3 : KSort> @@ -672,4 +764,33 @@ abstract class KNonRecursiveTransformer(override val ctx: KContext) : KNonRecurs return transformExprDefault(transformedExpr) } + /** + * Specialized version of [transformExprAfterTransformedDefault] for expression with five arguments. + * */ + @Suppress("LongParameterList", "ComplexCondition") + private inline fun < + In : KExpr, Out : KExpr, + T : KSort, A0 : KSort, A1 : KSort, A2 : KSort, A3 : KSort, A4 : KSort + > transformExprAfterTransformedDefault( + expr: In, + d0: KExpr, + d1: KExpr, + d2: KExpr, + d3: KExpr, + d4: KExpr, + ifNotTransformed: (In) -> KExpr, + transformer: KContext.(KExpr, KExpr, KExpr, KExpr, KExpr) -> Out + ): KExpr = + transformExprAfterTransformed( + expr, d0, d1, d2, d3, d4 + ) { td0, td1, td2, td3, td4 -> + if (td0 == d0 && td1 == d1 && td2 == d2 && td3 == d3 && td4 == d4) { + return ifNotTransformed(expr) + } + + val transformedExpr = ctx.transformer(td0, td1, td2, td3, td4) + + return transformExprDefault(transformedExpr) + } + } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformerBase.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformerBase.kt index 341c85757..c230c2861 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformerBase.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KNonRecursiveTransformerBase.kt @@ -1,10 +1,16 @@ package org.ksmt.expr.transformer import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KExistentialQuantifier import org.ksmt.expr.KExpr import org.ksmt.expr.KUniversalQuantifier +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort import org.ksmt.sort.KBoolSort import org.ksmt.sort.KSort @@ -60,6 +66,28 @@ abstract class KNonRecursiveTransformerBase: KTransformer { * */ abstract override fun transform(expr: KArrayLambda): KExpr> + /** + * Disable [KTransformer] transform KArray2Lambda implementation since it is incorrect + * for non-recursive usage. + * */ + abstract override fun transform( + expr: KArray2Lambda + ): KExpr> + + /** + * Disable [KTransformer] transform KArray3Lambda implementation since it is incorrect + * for non-recursive usage. + * */ + abstract override fun transform( + expr: KArray3Lambda + ): KExpr> + + /** + * Disable [KTransformer] transform KArrayNLambda implementation since it is incorrect + * for non-recursive usage. + * */ + abstract override fun transform(expr: KArrayNLambda): KExpr> + /** * Disable [KTransformer] transform KExistentialQuantifier implementation since it is incorrect * for non-recursive usage. @@ -270,4 +298,40 @@ abstract class KNonRecursiveTransformerBase: KTransformer { return transformer(td0, td1, td2, td3) } + + /** + * Specialized version of [transformExprAfterTransformed] for expression with five arguments. + * */ + @Suppress("LongParameterList", "ComplexCondition") + inline fun transformExprAfterTransformed( + expr: KExpr, + dependency0: KExpr, + dependency1: KExpr, + dependency2: KExpr, + dependency3: KExpr, + dependency4: KExpr, + transformer: (KExpr, KExpr, KExpr, KExpr, KExpr) -> KExpr + ): KExpr { + val td0 = transformedExpr(dependency0) + val td1 = transformedExpr(dependency1) + val td2 = transformedExpr(dependency2) + val td3 = transformedExpr(dependency3) + val td4 = transformedExpr(dependency4) + + if (td0 == null || td1 == null || td2 == null || td3 == null || td4 == null) { + retryExprTransformation(expr) + + transformExprDependencyIfNeeded(dependency0, td0) + transformExprDependencyIfNeeded(dependency1, td1) + transformExprDependencyIfNeeded(dependency2, td2) + transformExprDependencyIfNeeded(dependency3, td3) + transformExprDependencyIfNeeded(dependency4, td4) + + markExpressionAsNotTransformed() + + return expr + } + + return transformer(td0, td1, td2, td3, td4) + } } diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformer.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformer.kt index 2768abcfd..5829dd3ea 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformer.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformer.kt @@ -4,10 +4,22 @@ import org.ksmt.KContext import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr import org.ksmt.expr.KApp +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayLambdaBase +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect +import org.ksmt.expr.KArraySelectBase import org.ksmt.expr.KArrayStore +import org.ksmt.expr.KArrayStoreBase import org.ksmt.expr.KBitVec16Value import org.ksmt.expr.KBitVec1Value import org.ksmt.expr.KBitVec32Value @@ -136,7 +148,11 @@ import org.ksmt.expr.KUnaryMinusArithExpr import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort @@ -294,17 +310,73 @@ interface KTransformer : KTransformerBase { override fun transform(expr: KBvToFpExpr): KExpr = transformApp(expr) // array transformers - override fun transform(expr: KArrayStore): KExpr> = transformApp(expr) - override fun transform(expr: KArraySelect): KExpr = transformApp(expr) - override fun transform(expr: KArrayConst): KExpr> = transformApp(expr) + fun , R : KSort> transformArrayStore(expr: KArrayStoreBase): KExpr = + transformApp(expr) - override fun transform(expr: KFunctionAsArray): KExpr> = + override fun transform(expr: KArrayStore): KExpr> = + transformArrayStore(expr) + + override fun transform( + expr: KArray2Store + ): KExpr> = transformArrayStore(expr) + + override fun transform( + expr: KArray3Store + ): KExpr> = transformArrayStore(expr) + + override fun transform(expr: KArrayNStore): KExpr> = + transformArrayStore(expr) + + fun , R : KSort> transformArraySelect(expr: KArraySelectBase): KExpr = + transformApp(expr) + + override fun transform(expr: KArraySelect): KExpr = + transformArraySelect(expr) + + override fun transform(expr: KArray2Select): KExpr = + transformArraySelect(expr) + + override fun transform( + expr: KArray3Select + ): KExpr = transformArraySelect(expr) + + override fun transform(expr: KArrayNSelect): KExpr = + transformArraySelect(expr) + + override fun , R : KSort> transform(expr: KArrayConst): KExpr = transformApp(expr) + + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr = + transformExpr(expr) + + fun , R : KSort> transformArrayLambda(expr: KArrayLambdaBase): KExpr = transformExpr(expr) override fun transform(expr: KArrayLambda): KExpr> = with(ctx) { val body = expr.body.accept(this@KTransformer) - if (body == expr.body) return transformExpr(expr) - return transformExpr(mkArrayLambda(expr.indexVarDecl, body)) + if (body == expr.body) return transformArrayLambda(expr) + return transformArrayLambda(mkArrayLambda(expr.indexVarDecl, body)) + } + + override fun transform( + expr: KArray2Lambda + ): KExpr> = with(ctx) { + val body = expr.body.accept(this@KTransformer) + if (body == expr.body) return transformArrayLambda(expr) + return transformArrayLambda(mkArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, body)) + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = with(ctx) { + val body = expr.body.accept(this@KTransformer) + if (body == expr.body) return transformArrayLambda(expr) + return transformArrayLambda(mkArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl, body)) + } + + override fun transform(expr: KArrayNLambda): KExpr> = with(ctx) { + val body = expr.body.accept(this@KTransformer) + if (body == expr.body) return transformArrayLambda(expr) + return transformArrayLambda(mkArrayNLambda(expr.indexVarDeclarations, body)) } // arith transformers diff --git a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformerBase.kt b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformerBase.kt index 499d3e8a4..0cfa3e45a 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformerBase.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/expr/transformer/KTransformerBase.kt @@ -2,8 +2,17 @@ package org.ksmt.expr.transformer import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -130,7 +139,11 @@ import org.ksmt.expr.KUnaryMinusArithExpr import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort @@ -272,10 +285,30 @@ interface KTransformerBase { // array transformers fun transform(expr: KArrayStore): KExpr> + fun transform(expr: KArray2Store): KExpr> + + fun transform( + expr: KArray3Store + ): KExpr> + + fun transform(expr: KArrayNStore): KExpr> + fun transform(expr: KArraySelect): KExpr - fun transform(expr: KArrayConst): KExpr> - fun transform(expr: KFunctionAsArray): KExpr> + fun transform(expr: KArray2Select): KExpr + fun transform(expr: KArray3Select): KExpr + fun transform(expr: KArrayNSelect): KExpr + + fun , R : KSort> transform(expr: KArrayConst): KExpr + fun , R : KSort> transform(expr: KFunctionAsArray): KExpr + fun transform(expr: KArrayLambda): KExpr> + fun transform(expr: KArray2Lambda): KExpr> + + fun transform( + expr: KArray3Lambda + ): KExpr> + + fun transform(expr: KArrayNLambda): KExpr> // arith transformers fun transform(expr: KAddArithExpr): KExpr diff --git a/ksmt-core/src/main/kotlin/org/ksmt/solver/model/DefaultValueSampler.kt b/ksmt-core/src/main/kotlin/org/ksmt/solver/model/DefaultValueSampler.kt index 3cb03f0d9..e2ca1b119 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/solver/model/DefaultValueSampler.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/solver/model/DefaultValueSampler.kt @@ -3,7 +3,11 @@ package org.ksmt.solver.model import org.ksmt.KContext import org.ksmt.expr.KExpr import org.ksmt.expr.KFpRoundingMode +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFpRoundingModeSort @@ -44,9 +48,20 @@ open class DefaultValueSampler ( mkFpRoundingModeExpr(defaultRm).asExpr(this@DefaultValueSampler.sort) } - override fun visit(sort: KArraySort): KExpr = with(ctx) { - mkArrayConst(sort, sort.range.sampleValue()).asExpr(this@DefaultValueSampler.sort) - } + private fun , R: KSort> sampleArrayValue(sort: T): KExpr = + ctx.mkArrayConst(sort, sort.range.sampleValue()) + + override fun visit(sort: KArraySort): KExpr = + sampleArrayValue(sort).asExpr(this@DefaultValueSampler.sort) + + override fun visit(sort: KArray2Sort): KExpr = + sampleArrayValue(sort).asExpr(this@DefaultValueSampler.sort) + + override fun visit(sort: KArray3Sort): KExpr = + sampleArrayValue(sort).asExpr(this@DefaultValueSampler.sort) + + override fun visit(sort: KArrayNSort): KExpr = + sampleArrayValue(sort).asExpr(this@DefaultValueSampler.sort) override fun visit(sort: KUninterpretedSort): KExpr = ctx.uninterpretedSortDefaultValue(sort).asExpr(this@DefaultValueSampler.sort) diff --git a/ksmt-core/src/main/kotlin/org/ksmt/solver/model/KModelEvaluator.kt b/ksmt-core/src/main/kotlin/org/ksmt/solver/model/KModelEvaluator.kt index 5599a754b..d961aa321 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/solver/model/KModelEvaluator.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/solver/model/KModelEvaluator.kt @@ -2,9 +2,10 @@ package org.ksmt.solver.model import org.ksmt.KContext import org.ksmt.decl.KDecl +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray3Lambda import org.ksmt.expr.KArrayLambda -import org.ksmt.expr.KArraySelect -import org.ksmt.expr.KArrayStore +import org.ksmt.expr.KArrayNLambda import org.ksmt.expr.KConst import org.ksmt.expr.KExistentialQuantifier import org.ksmt.expr.KExpr @@ -18,7 +19,11 @@ import org.ksmt.expr.rewrite.simplify.KExprSimplifier import org.ksmt.expr.rewrite.simplify.simplifyExpr import org.ksmt.solver.KModel import org.ksmt.solver.model.DefaultValueSampler.Companion.sampleValue +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KSort import org.ksmt.sort.KUninterpretedSort @@ -47,20 +52,29 @@ open class KModelEvaluator( evalFunction(expr.decl, args).also { rewrite(it) } } - private val rewrittenArraySelects = hashMapOf, KArraySelect<*, *>?>() - override fun transform(expr: KArraySelect): KExpr { - val rewrittenSelect: KArraySelect? = rewrittenArraySelects.getOrPut(expr) { - ctx.tryEliminateFunctionAsArray(expr) - }?.uncheckedCast() + override fun transformSelect(array: KExpr>, index: KExpr): KExpr = + super.transformSelect(tryEvalArrayConst(array), index) - return if (rewrittenSelect == null) { - super.transform(expr) - } else { - simplifyExpr(expr, preprocess = { rewrittenSelect }) - } + override fun transformSelect( + array: KExpr>, index0: KExpr, index1: KExpr + ): KExpr = super.transformSelect(tryEvalArrayConst(array), index0, index1) + + override fun transformSelect( + array: KExpr>, index0: KExpr, index1: KExpr, index2: KExpr + ): KExpr = super.transformSelect(tryEvalArrayConst(array), index0, index1, index2) + + override fun transformSelect(array: KExpr>, indices: List>): KExpr = + super.transformSelect(tryEvalArrayConst(array), indices) + + // If base array is uninterpreted, try to replace it with model value + private fun , R : KSort> tryEvalArrayConst(array: KExpr): KExpr { + if (array !is KConst) return array + val interpretation = model.interpretation(array.decl) ?: return array + if (interpretation.entries.isNotEmpty()) return array + return interpretation.default ?: array } - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { // No way to evaluate f when it is quantified in (as-array f) if (expr.function in quantifiedVars) { return expr @@ -79,15 +93,38 @@ open class KModelEvaluator( return@getOrPut completeModelValue(expr.sort) } - val idxDecl = interpretation.vars.singleOrNull() - ?: error("Function ${expr.function} has ${interpretation.vars} vars but used in as-array") + val usedDeclarations = interpretation.usedDeclarations() + + // argument value is unused in function interpretation. + if (interpretation.vars.all { it !in usedDeclarations }) { + return evalArrayInterpretation(expr.sort, interpretation) + } + + val evaluated = evalFunction(expr.function, interpretation.vars.map { ctx.mkConstApp(it) }) + + @Suppress("USELESS_CAST") // Exhaustive when + return when (expr.sort as KArraySortBase) { + is KArraySort<*, *> -> ctx.mkArrayLambda( + interpretation.vars.single(), + evaluated + ).uncheckedCast() + + is KArray2Sort<*, *, *> -> ctx.mkArrayLambda( + interpretation.vars.first(), + interpretation.vars.last(), + evaluated + ).uncheckedCast() - evalArrayFunction( - expr.sort, - expr.function, - idxDecl.uncheckedCast(), - interpretation - ) + is KArray3Sort<*, *, *, *> -> { + val (v0, v1, v2) = interpretation.vars + ctx.mkArrayLambda(v0, v1, v2, evaluated).uncheckedCast() + } + + is KArrayNSort<*> -> ctx.mkArrayNLambda( + interpretation.vars, + evaluated + ).uncheckedCast() + } } return evaluatedArray.asExpr(expr.sort).also { rewrite(it) } } @@ -97,6 +134,28 @@ open class KModelEvaluator( ctx.simplifyArrayLambda(expr.indexVarDecl, body) } + override fun transform( + expr: KArray2Lambda + ): KExpr> = + transformQuantifiedExpression(setOf(expr.indexVar0Decl, expr.indexVar1Decl), expr.body) { body -> + ctx.simplifyArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, body) + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = + transformQuantifiedExpression( + setOf(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl), + expr.body + ) { body -> + ctx.simplifyArrayLambda(expr.indexVar0Decl, expr.indexVar1Decl, expr.indexVar2Decl, body) + } + + override fun transform(expr: KArrayNLambda): KExpr> = + transformQuantifiedExpression(expr.indexVarDeclarations.toSet(), expr.body) { body -> + ctx.simplifyArrayLambda(expr.indexVarDeclarations, body) + } + override fun transform(expr: KExistentialQuantifier): KExpr = transformQuantifiedExpression(expr.bounds.toSet(), expr.body) { body -> ctx.simplifyExistentialQuantifier(expr.bounds, body) @@ -107,7 +166,7 @@ open class KModelEvaluator( ctx.simplifyUniversalQuantifier(expr.bounds, body) } - private inline fun transformQuantifiedExpression( + private inline fun transformQuantifiedExpression( quantifiedVars: Set>, body: KExpr, crossinline quantifierBuilder: (KExpr) -> KExpr @@ -143,34 +202,45 @@ open class KModelEvaluator( return expr in sortUniverse } - private fun evalArrayFunction( - sort: KArraySort, - function: KDecl, - indexVar: KDecl, + @Suppress("USELESS_CAST") // Exhaustive when + private fun , R : KSort> evalArrayInterpretation( + sort: A, interpretation: KModel.KFuncInterp - ): KExpr> { - val usedDeclarations = interpretation.usedDeclarations() + ): KExpr = when (sort as KArraySortBase) { + is KArraySort<*, R> -> sort.evalArrayInterpretation( + interpretation + ) { array: KExpr>, args, value -> + mkArrayStore(array, args.single(), value) + } + + is KArray2Sort<*, *, *> -> sort.evalArrayInterpretation( + interpretation + ) { array: KExpr>, (idx0, idx1), value -> + mkArrayStore(array, idx0, idx1, value) + } - // argument value is unused in function interpretation. - if (indexVar !in usedDeclarations) { - return evalArrayInterpretation(sort, interpretation) + is KArray3Sort<*, *, *, *> -> sort.evalArrayInterpretation( + interpretation + ) { array: KExpr>, (idx0, idx1, idx2), value -> + mkArrayStore(array, idx0, idx1, idx2, value) } - val index = ctx.mkConstApp(indexVar) - val evaluated = evalFunction(function, listOf(index)) - return ctx.mkArrayLambda(index.decl, evaluated) + is KArrayNSort<*> -> sort.evalArrayInterpretation( + interpretation + ) { array: KExpr>, args, value -> + mkArrayNStore(array, args, value) + } } - private fun evalArrayInterpretation( - sort: KArraySort, - interpretation: KModel.KFuncInterp - ): KExpr> = with(ctx) { - val defaultValue = interpretation.default ?: completeModelValue(sort.range) - val defaultArray: KExpr> = mkArrayConst(sort, defaultValue) + private inline fun , R : KSort, reified S : KArraySortBase> A.evalArrayInterpretation( + interpretation: KModel.KFuncInterp, + mkEntryStore: KContext.(KExpr, List>, KExpr) -> KExpr + ): KExpr = with(ctx) { + val defaultValue = interpretation.default ?: completeModelValue(range) + val defaultArray: KExpr = mkArrayConst(this@evalArrayInterpretation, defaultValue) interpretation.entries.foldRight(defaultArray) { entry, acc -> - val idx = entry.args.single().asExpr(sort.domain) - acc.store(idx, entry.value) + mkEntryStore(acc.uncheckedCast(), entry.args.uncheckedCast(), entry.value).uncheckedCast() } } @@ -290,47 +360,6 @@ open class KModelEvaluator( ) } - /** - * Usually, [KFunctionAsArray] will be expanded into large array store chain. - * In case of array select, we can avoid such expansion and replace - * (select (as-array f) i) with (f i). - * */ - private fun KContext.tryEliminateFunctionAsArray( - expr: KArraySelect - ): KArraySelect? { - // Unroll stores until we find some base array - val parentStores = arrayListOf>() - var base = expr.array - while (base is KArrayStore) { - parentStores += base - base = base.array - } - - // If base array in uninterpreted, try to replace it with model value - if (base is KConst>) { - val interpretation = model.interpretation(base.decl) ?: return null - if (interpretation.entries.isNotEmpty()) return null - base = interpretation.default ?: return null - } - - if (base !is KFunctionAsArray) return null - - /** - * Replace as-array with (const (f i)) since: - * 1. we may have parent stores here and we need an array expression - * 2. (select (const (f i)) i) ==> (f i) - * */ - val defaultSelectValue = base.function.apply(listOf(expr.index)) - var newArrayBase: KExpr> = mkArrayConst(base.sort, defaultSelectValue) - - // Rebuild array - for (store in parentStores.asReversed()) { - newArrayBase = newArrayBase.store(store.index, store.value) - } - - return mkArraySelectNoSimplify(newArrayBase, expr.index) - } - private fun completeModelValue(sort: T): KExpr { val value = when (sort) { is KUninterpretedSort -> @@ -338,9 +367,9 @@ open class KModelEvaluator( ?.randomOrNull() ?: sort.sampleValue() - is KArraySort<*, *> -> { + is KArraySortBase<*> -> { val arrayValue = completeModelValue(sort.range) - ctx.mkArrayConst(sort, arrayValue) + ctx.mkArrayConst(sort.uncheckedCast(), arrayValue) } else -> sort.sampleValue() diff --git a/ksmt-core/src/main/kotlin/org/ksmt/sort/KSort.kt b/ksmt-core/src/main/kotlin/org/ksmt/sort/KSort.kt index 511e90740..e336dd2b9 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/sort/KSort.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/sort/KSort.kt @@ -46,23 +46,102 @@ class KRealSort internal constructor(ctx: KContext) : KArithSort(ctx) { override fun equals(other: Any?): Boolean = this === other || other is KRealSort } -class KArraySort internal constructor( - ctx: KContext, val domain: D, val range: R -) : KSort(ctx) { - override fun accept(visitor: KSortVisitor): T = visitor.visit(this) +sealed class KArraySortBase(ctx: KContext) : KSort(ctx) { + abstract val domainSorts: List + abstract val range: R override fun print(builder: StringBuilder): Unit = with(builder) { append("(Array ") - domain.print(this) - append(' ') + domainSorts.forEach { + append(it) + append(' ') + } range.print(this) append(')') } +} + +class KArraySort internal constructor( + ctx: KContext, val domain: D, override val range: R +) : KArraySortBase(ctx) { + + override val domainSorts: List + get() = listOf(domain) + + override fun accept(visitor: KSortVisitor): T = visitor.visit(this) override fun hashCode(): Int = hash(javaClass, domain, range) override fun equals(other: Any?): Boolean = this === other || (other is KArraySort<*, *> && domain == other.domain && range == other.range) + + companion object { + const val DOMAIN_SIZE = 1 + } +} + +class KArray2Sort internal constructor( + ctx: KContext, val domain0: D0, val domain1: D1, override val range: R +) : KArraySortBase(ctx) { + + override val domainSorts: List + get() = listOf(domain0, domain1) + + override fun accept(visitor: KSortVisitor): T = visitor.visit(this) + + override fun hashCode(): Int = hash(javaClass, domain0, domain1, range) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is KArray2Sort<*, *, *>) return false + return domain0 == other.domain0 && domain1 == other.domain1 && range == other.range + } + + companion object { + const val DOMAIN_SIZE = 2 + } +} + +class KArray3Sort internal constructor( + ctx: KContext, val domain0: D0, val domain1: D1, val domain2: D2, override val range: R +) : KArraySortBase(ctx) { + + override val domainSorts: List + get() = listOf(domain0, domain1, domain2) + + override fun accept(visitor: KSortVisitor): T = visitor.visit(this) + + override fun hashCode(): Int = hash(javaClass, domain0, domain1, domain2, range) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is KArray3Sort<*, *, *, *>) return false + return domain0 == other.domain0 && domain1 == other.domain1 && domain2 == other.domain2 && range == other.range + } + + companion object { + const val DOMAIN_SIZE = 3 + } +} + +class KArrayNSort internal constructor( + ctx: KContext, override val domainSorts: List, override val range: R +) : KArraySortBase(ctx) { + init { + require(domainSorts.size > KArray3Sort.DOMAIN_SIZE) { + "Use specialized Array with domain size ${domainSorts.size}" + } + } + + override fun accept(visitor: KSortVisitor): T = visitor.visit(this) + + override fun hashCode(): Int = hash(javaClass, domainSorts, range) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is KArrayNSort<*>) return false + return domainSorts == other.domainSorts && range == other.range + } } abstract class KBvSort(ctx: KContext) : KSort(ctx) { diff --git a/ksmt-core/src/main/kotlin/org/ksmt/sort/KSortVisitor.kt b/ksmt-core/src/main/kotlin/org/ksmt/sort/KSortVisitor.kt index f1cec4587..5639148d8 100644 --- a/ksmt-core/src/main/kotlin/org/ksmt/sort/KSortVisitor.kt +++ b/ksmt-core/src/main/kotlin/org/ksmt/sort/KSortVisitor.kt @@ -8,6 +8,9 @@ interface KSortVisitor { fun visit(sort: S): T fun visit(sort: S): T fun visit(sort: KArraySort): T + fun visit(sort: KArray2Sort): T + fun visit(sort: KArray3Sort): T + fun visit(sort: KArrayNSort): T fun visit(sort: KFpRoundingModeSort): T fun visit(sort: KUninterpretedSort): T } diff --git a/ksmt-core/src/test/kotlin/org/ksmt/ModelEvaluationTest.kt b/ksmt-core/src/test/kotlin/org/ksmt/ModelEvaluationTest.kt index 2c9cfe686..457ccd6bb 100644 --- a/ksmt-core/src/test/kotlin/org/ksmt/ModelEvaluationTest.kt +++ b/ksmt-core/src/test/kotlin/org/ksmt/ModelEvaluationTest.kt @@ -3,7 +3,6 @@ package org.ksmt import org.ksmt.solver.KModel import org.ksmt.solver.model.DefaultValueSampler.Companion.sampleValue import org.ksmt.solver.model.KModelImpl -import org.ksmt.sort.KBv32Sort import org.ksmt.utils.getValue import kotlin.test.Test import kotlin.test.assertEquals @@ -62,7 +61,7 @@ class ModelEvaluationTest { default = null ) - val arrayInterp = mkFunctionAsArray(tmpDecl) + val arrayInterp = mkFunctionAsArray(arraySort, tmpDecl) val model = KModelImpl( @@ -121,8 +120,8 @@ class ModelEvaluationTest { default = null ) - val array1Interp = mkFunctionAsArray(tmpDecl1) - val array2Interp = mkFunctionAsArray(tmpDecl2) + val array1Interp = mkFunctionAsArray(arraySort, tmpDecl1) + val array2Interp = mkFunctionAsArray(arraySort, tmpDecl2) val model = KModelImpl( diff --git a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstDeserializer.kt b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstDeserializer.kt index 14477943d..dbff73864 100644 --- a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstDeserializer.kt +++ b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstDeserializer.kt @@ -9,10 +9,12 @@ import org.ksmt.expr.KExpr import org.ksmt.expr.KIntNumExpr import org.ksmt.sort.KArithSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KFpSort import org.ksmt.sort.KIntSort import org.ksmt.sort.KSort +import org.ksmt.utils.uncheckedCast @OptIn(ExperimentalUnsignedTypes::class) class AstDeserializer( @@ -86,6 +88,9 @@ class AstDeserializer( SortKind.Bv -> mkBvSort(readUInt()) SortKind.Fp -> mkFpSort(readUInt(), readUInt()) SortKind.Array -> mkArraySort(readSort(), readSort()) + SortKind.Array2 -> mkArraySort(readSort(), readSort(), readSort()) + SortKind.Array3 -> mkArraySort(readSort(), readSort(), readSort(), readSort()) + SortKind.ArrayN -> mkArraySort(readAstArray().uncheckedCast(), readSort()) SortKind.Uninterpreted -> mkUninterpretedSort(readString()) } } @@ -201,11 +206,47 @@ class AstDeserializer( ExprKind.FpToFpExpr -> mkFpToFpExprNoSimplify(readSort() as KFpSort, readExpr(), readExpr()) ExprKind.RealToFpExpr -> mkRealToFpExprNoSimplify(readSort() as KFpSort, readExpr(), readExpr()) ExprKind.BvToFpExpr -> mkBvToFpExprNoSimplify(readSort() as KFpSort, readExpr(), readExpr(), readBoolean()) - ExprKind.ArrayStore -> deserialize(::mkArrayStoreNoSimplify) - ExprKind.ArraySelect -> deserialize(::mkArraySelectNoSimplify) + ExprKind.ArrayStore -> mkArrayStoreNoSimplify( + readExpr(), readExpr(), readExpr() + ) + ExprKind.Array2Store -> mkArrayStoreNoSimplify( + readExpr(), readExpr(), readExpr(), readExpr() + ) + ExprKind.Array3Store -> mkArrayStoreNoSimplify( + readExpr(), readExpr(), readExpr(), readExpr(), readExpr() + ) + ExprKind.ArrayNStore -> mkArrayNStoreNoSimplify( + readExpr(), readAstArray() as List>, readExpr() + ) + ExprKind.ArraySelect -> mkArraySelectNoSimplify( + readExpr(), readExpr() + ) + ExprKind.Array2Select -> mkArraySelectNoSimplify( + readExpr(), readExpr(), readExpr() + ) + ExprKind.Array3Select -> mkArraySelectNoSimplify( + readExpr(), readExpr(), readExpr(), readExpr() + ) + ExprKind.ArrayNSelect -> mkArrayNSelectNoSimplify( + readExpr(), readAstArray() as List> + ) ExprKind.ArrayConst -> mkArrayConst(readSort() as KArraySort, readExpr()) - ExprKind.FunctionAsArray -> mkFunctionAsArray(readDecl() as KFuncDecl) - ExprKind.ArrayLambda -> mkArrayLambda(readDecl(), readExpr()) + ExprKind.FunctionAsArray -> mkFunctionAsArray( + readSort() as KArraySortBase, + readDecl() as KFuncDecl + ) + ExprKind.ArrayLambda -> mkArrayLambda( + readDecl(), readExpr() + ) + ExprKind.Array2Lambda -> mkArrayLambda( + readDecl(), readDecl(), readExpr() + ) + ExprKind.Array3Lambda -> mkArrayLambda( + readDecl(), readDecl(), readDecl(), readExpr() + ) + ExprKind.ArrayNLambda -> mkArrayNLambda( + readAstArray() as List>, readExpr() + ) ExprKind.AddArithExpr -> mkArithAddNoSimplify(readAstArray() as List>) ExprKind.MulArithExpr -> mkArithMulNoSimplify(readAstArray() as List>) ExprKind.SubArithExpr -> mkArithSubNoSimplify(readAstArray() as List>) diff --git a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstSerializer.kt b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstSerializer.kt index 2c651994e..889b96889 100644 --- a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstSerializer.kt +++ b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/AstSerializer.kt @@ -8,8 +8,17 @@ import org.ksmt.decl.KDeclVisitor import org.ksmt.decl.KFuncDecl import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -137,7 +146,11 @@ import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.solver.util.KExprInternalizerBase import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFp128Sort @@ -152,7 +165,7 @@ import org.ksmt.sort.KSort import org.ksmt.sort.KSortVisitor import org.ksmt.sort.KUninterpretedSort -@Suppress("ArrayPrimitive") +@Suppress("ArrayPrimitive", "LargeClass") @OptIn(ExperimentalUnsignedTypes::class) class AstSerializer( private val serializationCtx: AstSerializationCtx, @@ -223,6 +236,39 @@ class AstSerializer( } } + override fun visit(sort: KArray2Sort): Int { + val domain0 = sort.domain0.serializeSort() + val domain1 = sort.domain1.serializeSort() + val range = sort.range.serializeSort() + return serializeSort(sort, SortKind.Array2) { + writeAst(domain0) + writeAst(domain1) + writeAst(range) + } + } + + override fun visit(sort: KArray3Sort): Int { + val domain0 = sort.domain0.serializeSort() + val domain1 = sort.domain1.serializeSort() + val domain2 = sort.domain2.serializeSort() + val range = sort.range.serializeSort() + return serializeSort(sort, SortKind.Array3) { + writeAst(domain0) + writeAst(domain1) + writeAst(domain2) + writeAst(range) + } + } + + override fun visit(sort: KArrayNSort): Int { + val domain = sort.domainSorts.map { it.serializeSort() } + val range = sort.range.serializeSort() + return serializeSort(sort, SortKind.ArrayN) { + writeAstArray(domain) + writeAst(range) + } + } + override fun visit(sort: KFpRoundingModeSort): Int = serializeSort(sort, SortKind.FpRM) {} override fun visit(sort: KUninterpretedSort): Int = serializeSort(sort, SortKind.Uninterpreted) { @@ -810,11 +856,82 @@ class AstSerializer( serialize(array, index, value) } + override fun transform( + expr: KArray2Store + ): KExpr> = with(expr) { + transform(array, index0, index1, value) { a: Int, i0: Int, i1: Int, v: Int -> + writeExpr { + writeAst(a) + writeAst(i0) + writeAst(i1) + writeAst(v) + } + } + } + + override fun transform( + expr: KArray3Store + ): KExpr> = with(expr) { + transformList(listOf(array, index0, index1, index2, value)) { args: Array -> + val (a: Int, i0: Int, i1: Int, i2: Int, v: Int) = args + writeExpr { + writeAst(a) + writeAst(i0) + writeAst(i1) + writeAst(i2) + writeAst(v) + } + } + } + + override fun transform(expr: KArrayNStore): KExpr> = with(expr) { + transformList(indices + listOf(array, value)) { args: Array -> + val array = args[args.lastIndex - 1] + val value = args[args.lastIndex] + val indices = args.dropLast(2) + writeExpr { + writeAst(array) + writeAstArray(indices) + writeAst(value) + } + } + } + override fun transform(expr: KArraySelect) = with(expr) { serialize(array, index) } - override fun transform(expr: KArrayConst) = with(expr) { + override fun transform( + expr: KArray2Select + ): KExpr = with(expr) { + serialize(array, index0, index1) + } + + override fun transform( + expr: KArray3Select + ): KExpr = with(expr) { + transform(array, index0, index1, index2) { a: Int, i0: Int, i1: Int, i2: Int -> + writeExpr { + writeAst(a) + writeAst(i0) + writeAst(i1) + writeAst(i2) + } + } + } + + override fun transform(expr: KArrayNSelect): KExpr = with(expr) { + transformList(indices + array) { args: Array -> + val array = args.last() + val indices = args.dropLast(1) + writeExpr { + writeAst(array) + writeAstArray(indices) + } + } + } + + override fun , R : KSort> transform(expr: KArrayConst) = with(expr) { transform(value) { value: Int -> val sortIdx = sort.serializeSort() writeExpr { @@ -826,9 +943,49 @@ class AstSerializer( override fun transform(expr: KArrayLambda) = with(expr) { transform(body) { body: Int -> - val internalizedIndex = indexVarDecl.serializeDecl() + val serializedIndex = indexVarDecl.serializeDecl() + writeExpr { + writeAst(serializedIndex) + writeAst(body) + } + } + } + + override fun transform( + expr: KArray2Lambda + ): KExpr> = with(expr) { + transform(body) { body: Int -> + val serializedIndex0 = indexVar0Decl.serializeDecl() + val serializedIndex1 = indexVar1Decl.serializeDecl() writeExpr { - writeAst(internalizedIndex) + writeAst(serializedIndex0) + writeAst(serializedIndex1) + writeAst(body) + } + } + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = with(expr) { + transform(body) { body: Int -> + val serializedIndex0 = indexVar0Decl.serializeDecl() + val serializedIndex1 = indexVar1Decl.serializeDecl() + val serializedIndex2 = indexVar2Decl.serializeDecl() + writeExpr { + writeAst(serializedIndex0) + writeAst(serializedIndex1) + writeAst(serializedIndex2) + writeAst(body) + } + } + } + + override fun transform(expr: KArrayNLambda): KExpr> = with(expr) { + transform(body) { body: Int -> + val serializedIndices = indexVarDeclarations.map { it.serializeDecl() } + writeExpr { + writeAstArray(serializedIndices) writeAst(body) } } @@ -920,10 +1077,12 @@ class AstSerializer( } } - override fun transform(expr: KFunctionAsArray) = with(expr) { + override fun , R : KSort> transform(expr: KFunctionAsArray) = with(expr) { transform { + val sortIdx = sort.serializeSort() val funcIdx = function.serializeDecl() writeExpr { + writeAst(sortIdx) writeAst(funcIdx) } } diff --git a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKind.kt b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKind.kt index 0e7981f25..b1b7764a8 100644 --- a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKind.kt +++ b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKind.kt @@ -104,10 +104,19 @@ enum class ExprKind { RealToFpExpr, BvToFpExpr, ArrayStore, + Array2Store, + Array3Store, + ArrayNStore, ArraySelect, + Array2Select, + Array3Select, + ArrayNSelect, ArrayConst, FunctionAsArray, ArrayLambda, + Array2Lambda, + Array3Lambda, + ArrayNLambda, AddArithExpr, MulArithExpr, SubArithExpr, diff --git a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKindMapper.kt b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKindMapper.kt index b1e7a6e4c..e1bb844be 100644 --- a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKindMapper.kt +++ b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/ExprKindMapper.kt @@ -2,8 +2,17 @@ package org.ksmt.runner.serializer import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -131,7 +140,11 @@ import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.expr.transformer.KTransformerBase import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort @@ -502,21 +515,49 @@ class ExprKindMapper: KTransformerBase { override fun transform(expr: KArrayStore): KExpr> = expr.kind(ExprKind.ArrayStore) + override fun transform( + expr: KArray2Store + ): KExpr> = expr.kind(ExprKind.Array2Store) + + override fun transform( + expr: KArray3Store + ): KExpr> = expr.kind(ExprKind.Array3Store) + + override fun transform(expr: KArrayNStore): KExpr> = + expr.kind(ExprKind.ArrayNStore) override fun transform(expr: KArraySelect): KExpr = expr.kind(ExprKind.ArraySelect) + override fun transform( + expr: KArray2Select + ): KExpr = expr.kind(ExprKind.Array2Select) - override fun transform(expr: KArrayConst): KExpr> = - expr.kind(ExprKind.ArrayConst) + override fun transform( + expr: KArray3Select + ): KExpr = expr.kind(ExprKind.Array3Select) + + override fun transform(expr: KArrayNSelect): KExpr = expr.kind(ExprKind.ArrayNSelect) + override fun , R : KSort> transform(expr: KArrayConst): KExpr = + expr.kind(ExprKind.ArrayConst) - override fun transform(expr: KFunctionAsArray): KExpr> = + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr = expr.kind(ExprKind.FunctionAsArray) override fun transform(expr: KArrayLambda): KExpr> = expr.kind(ExprKind.ArrayLambda) + override fun transform( + expr: KArray2Lambda + ): KExpr> = expr.kind(ExprKind.Array2Lambda) + + override fun transform( + expr: KArray3Lambda + ): KExpr> = expr.kind(ExprKind.Array3Lambda) + + override fun transform(expr: KArrayNLambda): KExpr> = + expr.kind(ExprKind.ArrayLambda) override fun transform(expr: KAddArithExpr): KExpr = expr.kind(ExprKind.AddArithExpr) diff --git a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/SortKind.kt b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/SortKind.kt index b4bb1c7de..1ab86960c 100644 --- a/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/SortKind.kt +++ b/ksmt-runner/src/main/kotlin/org/ksmt/runner/serializer/SortKind.kt @@ -1,5 +1,5 @@ package org.ksmt.runner.serializer enum class SortKind { - Bool, Int, Real, Bv, Fp, Array, FpRM, Uninterpreted + Bool, Int, Real, Bv, Fp, Array, Array2, Array3, ArrayN, FpRM, Uninterpreted } diff --git a/ksmt-test/src/main/kotlin/org/ksmt/test/RandomExpressionGenerator.kt b/ksmt-test/src/main/kotlin/org/ksmt/test/RandomExpressionGenerator.kt index 0a92893e3..8e5ed54ed 100644 --- a/ksmt-test/src/main/kotlin/org/ksmt/test/RandomExpressionGenerator.kt +++ b/ksmt-test/src/main/kotlin/org/ksmt/test/RandomExpressionGenerator.kt @@ -5,7 +5,11 @@ import org.ksmt.KContext import org.ksmt.expr.KExpr import org.ksmt.expr.KFpRoundingMode import org.ksmt.expr.KInterpretedValue +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFpRoundingModeSort @@ -401,7 +405,7 @@ class RandomExpressionGenerator { args + sortArgument } - override fun visit(sort: KArraySort): AstGenerator { + 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) @@ -409,6 +413,20 @@ class RandomExpressionGenerator { 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 { diff --git a/ksmt-test/src/test/kotlin/org/ksmt/test/BenchmarksBasedTest.kt b/ksmt-test/src/test/kotlin/org/ksmt/test/BenchmarksBasedTest.kt index 4c1941186..b920c1a31 100644 --- a/ksmt-test/src/test/kotlin/org/ksmt/test/BenchmarksBasedTest.kt +++ b/ksmt-test/src/test/kotlin/org/ksmt/test/BenchmarksBasedTest.kt @@ -37,7 +37,7 @@ import org.ksmt.solver.KSolverUnsupportedFeatureException import org.ksmt.solver.async.KAsyncSolver import org.ksmt.solver.runner.KSolverRunnerManager import org.ksmt.sort.KArithSort -import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFpSort @@ -422,7 +422,7 @@ abstract class BenchmarksBasedTest { } class AsArrayDeclChecker(override val ctx: KContext, val model: KModel) : KTransformer { - override fun transform(expr: KFunctionAsArray): KExpr> { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr { assertNotNull(model.interpretation(expr.function), "no interpretation for as-array: $expr") return expr } diff --git a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprConverter.kt b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprConverter.kt index 462f68c3f..55c57ae7c 100644 --- a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprConverter.kt +++ b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprConverter.kt @@ -82,13 +82,13 @@ open class KYicesExprConverter( && argType.range is KIntSort && exprSort is KArraySort<*, *> && exprSort.range is KRealSort -> - castArrayRangeToInt(convertedExpr.uncheckedCast<_, KExpr>>()) + castArrayRangeToInt(convertedExpr.uncheckedCast<_, KExpr>>()) argType is KArraySort<*, *> && argType.range is KRealSort && exprSort is KArraySort<*, *> && exprSort.range is KIntSort -> - castArrayRangeToReal(convertedExpr.uncheckedCast<_, KExpr>>()) + castArrayRangeToReal(convertedExpr.uncheckedCast<_, KExpr>>()) else -> error("Invalid state") } @@ -345,7 +345,9 @@ open class KYicesExprConverter( if (convertedExprSort is KArraySort<*, *>) { argTypes.addAll(listOf(convertedExprSort, convertedExprSort.domain)) - return expr.convert(yicesArgs, ::mkArraySelect) + return expr.convert(yicesArgs) { array: KExpr>, index: KExpr -> + mkArraySelect(array, index) + } } if (!Terms.isAtomic(yicesArgs.first())) { @@ -376,7 +378,11 @@ open class KYicesExprConverter( val rangeType = arrayType.range argTypes.addAll(listOf(arrayType, indexType, rangeType)) - expr.convert(yicesArgs, ::mkArrayStore) + expr.convert( + yicesArgs + ) { array: KExpr>, index: KExpr, value: KExpr -> + mkArrayStore(array, index, value) + } } Constructor.EQ_TERM -> { if (Terms.isArithmetic(yicesArgs.first())) { @@ -413,7 +419,7 @@ open class KYicesExprConverter( if (yicesCtx.findConvertedDecl(yicesArgs.first()) == null) { mkArrayConst(mkArraySort(convertSort(Terms.typeOf(yicesArgs.first())), body.sort), body) } else { - val index = convertDecl(yicesArgs.first()) + val index: KDecl = convertDecl(yicesArgs.first()).uncheckedCast() mkArrayLambda(index, body) } } diff --git a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprInternalizer.kt b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprInternalizer.kt index bb8cfa58a..5ab206dbc 100644 --- a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprInternalizer.kt +++ b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesExprInternalizer.kt @@ -4,8 +4,17 @@ import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -147,6 +156,10 @@ import org.ksmt.sort.KFpSort import org.ksmt.sort.KRealSort import org.ksmt.sort.KSort import org.ksmt.solver.KSolverUnsupportedFeatureException +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBv16Sort import org.ksmt.sort.KBv1Sort import org.ksmt.sort.KBv32Sort @@ -639,19 +652,49 @@ open class KYicesExprInternalizer( transform(array, index, value, yicesCtx::functionUpdate1) } + override fun transform( + expr: KArray2Store + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Store + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform(expr: KArrayNStore): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + override fun transform(expr: KArraySelect): KExpr = with(expr) { transform(array, index) { array: YicesTerm, index: YicesTerm -> yicesCtx.funApplication(array, index) } } - override fun transform(expr: KArrayConst): KExpr> = with(expr) { + override fun transform(expr: KArray2Select): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Select + ): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform(expr: KArrayNSelect): KExpr { + TODO("Multi-indexed arrays are not supported") + } + + override fun , R : KSort> transform(expr: KArrayConst): KExpr = with(expr) { transform(value) { value: YicesTerm -> yicesCtx.lambda(listOf(yicesCtx.newVariable(sort.internalizeSort())), value) } } - override fun transform(expr: KFunctionAsArray): KExpr> = with(expr) { + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr = with(expr) { transform { function.internalizeDecl() } } @@ -675,6 +718,22 @@ open class KYicesExprInternalizer( } } + override fun transform( + expr: KArray2Lambda + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + + override fun transform(expr: KArrayNLambda): KExpr> { + TODO("Multi-indexed arrays are not supported") + } + override fun transform(expr: KAddArithExpr): KExpr = with(expr) { transformList(args) { args: Array -> yicesCtx.add(args.toList()) } } diff --git a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesModel.kt b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesModel.kt index 34f750ab6..0aa577f8b 100644 --- a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesModel.kt +++ b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesModel.kt @@ -70,7 +70,7 @@ class KYicesModel( funcInterpretationsToDo.add(Pair(yval, funcDecl)) - mkFunctionAsArray(funcDecl).uncheckedCast() + mkFunctionAsArray(sort.uncheckedCast(), funcDecl).uncheckedCast() } else -> error("Unsupported sort $sort") } diff --git a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesSortInternalizer.kt b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesSortInternalizer.kt index a5e55ab7a..3190010c3 100644 --- a/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesSortInternalizer.kt +++ b/ksmt-yices/src/main/kotlin/org/ksmt/solver/yices/KYicesSortInternalizer.kt @@ -1,6 +1,9 @@ package org.ksmt.solver.yices import org.ksmt.solver.KSolverUnsupportedFeatureException +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort @@ -36,6 +39,18 @@ open class KYicesSortInternalizer( yicesCtx.functionType(sort.domain.internalizeYicesSort(), sort.range.internalizeYicesSort()) } + override fun visit(sort: KArray2Sort): YicesSort { + TODO("Multi-indexed arrays are not supported") + } + + override fun visit(sort: KArray3Sort): YicesSort { + TODO("Multi-indexed arrays are not supported") + } + + override fun visit(sort: KArrayNSort): YicesSort { + TODO("Multi-indexed arrays are not supported") + } + override fun visit(sort: KUninterpretedSort): YicesSort = yicesCtx.internalizeSort(sort) { yicesCtx.newUninterpretedType(sort.name) } diff --git a/ksmt-z3/src/main/kotlin/com/microsoft/z3/Util.kt b/ksmt-z3/src/main/kotlin/com/microsoft/z3/Util.kt index 614af8c05..0e92676ce 100644 --- a/ksmt-z3/src/main/kotlin/com/microsoft/z3/Util.kt +++ b/ksmt-z3/src/main/kotlin/com/microsoft/z3/Util.kt @@ -1,5 +1,7 @@ package com.microsoft.z3 +import com.microsoft.z3.enumerations.Z3_error_code + fun intOrNull(ctx: Long, expr: Long): Int? { val result = Native.IntPtr() if (!Native.getNumeralInt(ctx, expr, result)) return null @@ -46,3 +48,28 @@ fun getAppArgs(ctx: Long, expr: Long): LongArray { val size = Native.getAppNumArgs(ctx, expr) return LongArray(size) { idx -> Native.getAppArg(ctx, expr, idx) } } + +/** + * We have no way to obtain array sort domain size. + * To overcome this we iterate over domain until index is out of bounds. + * */ +@Suppress("LoopWithTooManyJumpStatements") +fun getArraySortDomain(ctx: Long, sort: Long): List { + val domain = arrayListOf() + while (true) { + val domainSortI = Native.INTERNALgetArraySortDomainN(ctx, sort, domain.size) + when (val errorCode = Z3_error_code.fromInt(Native.INTERNALgetErrorCode(ctx))) { + Z3_error_code.Z3_OK -> { + domain.add(domainSortI) + continue + } + + /** + * Z3 set [Z3_error_code.Z3_INVALID_ARG] error code when sort domain index is out of bounds. + * */ + Z3_error_code.Z3_INVALID_ARG -> break + else -> throw Z3Exception(Native.INTERNALgetErrorMsg(ctx, errorCode.toInt())) + } + } + return domain +} diff --git a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprConverter.kt b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprConverter.kt index 6da2f71c2..2055f002f 100644 --- a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprConverter.kt +++ b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprConverter.kt @@ -10,6 +10,7 @@ import com.microsoft.z3.enumerations.Z3_sort_kind import com.microsoft.z3.enumerations.Z3_symbol_kind import com.microsoft.z3.fpSignOrNull import com.microsoft.z3.getAppArgs +import com.microsoft.z3.getArraySortDomain import com.microsoft.z3.intOrNull import com.microsoft.z3.longOrNull import org.ksmt.KContext @@ -23,7 +24,11 @@ import org.ksmt.expr.KIntNumExpr import org.ksmt.expr.KRealNumExpr import org.ksmt.solver.util.KExprConverterBase import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBv1Sort import org.ksmt.sort.KBvSort @@ -31,6 +36,7 @@ import org.ksmt.sort.KFpRoundingModeSort import org.ksmt.sort.KFpSort import org.ksmt.sort.KRealSort import org.ksmt.sort.KSort +import org.ksmt.utils.uncheckedCast open class KZ3ExprConverter( private val ctx: KContext, @@ -91,11 +97,7 @@ open class KZ3ExprConverter( Z3_sort_kind.Z3_BOOL_SORT -> boolSort Z3_sort_kind.Z3_INT_SORT -> intSort Z3_sort_kind.Z3_REAL_SORT -> realSort - Z3_sort_kind.Z3_ARRAY_SORT -> { - val domain = Native.getArraySortDomain(nCtx, sort) - val range = Native.getArraySortRange(nCtx, sort) - mkArraySort(domain.convertSort(), range.convertSort()) - } + Z3_sort_kind.Z3_ARRAY_SORT -> convertNativeArraySort(sort) Z3_sort_kind.Z3_BV_SORT -> mkBvSort(Native.getBvSortSize(nCtx, sort).toUInt()) Z3_sort_kind.Z3_FLOATING_POINT_SORT -> mkFpSort(Native.fpaGetEbits(nCtx, sort).toUInt(), Native.fpaGetSbits(nCtx, sort).toUInt()) @@ -115,6 +117,22 @@ open class KZ3ExprConverter( } } + open fun KContext.convertNativeArraySort(sort: Long): KSort { + val domain = getArraySortDomain(nCtx, sort).map { it.convertSort() } + val range = Native.getArraySortRange(nCtx, sort).convertSort() + + return when (domain.size) { + KArraySort.DOMAIN_SIZE -> mkArraySort(domain.single(), range) + KArray2Sort.DOMAIN_SIZE -> mkArraySort(domain.first(), domain.last(), range) + KArray3Sort.DOMAIN_SIZE -> { + val (d0, d1, d2) = domain + mkArraySort(d0, d1, d2, range) + } + + else -> mkArrayNSort(domain, range) + } + } + /** * Convert expression non-recursively. * 1. Ensure all expression arguments are already converted and available in [z3Ctx]. @@ -177,11 +195,11 @@ open class KZ3ExprConverter( Z3_decl_kind.Z3_OP_TO_REAL -> expr.convert(::mkIntToReal) Z3_decl_kind.Z3_OP_TO_INT -> expr.convert(::mkRealToInt) Z3_decl_kind.Z3_OP_IS_INT -> expr.convert(::mkRealIsInt) - Z3_decl_kind.Z3_OP_STORE -> expr.convert(::mkArrayStore) - Z3_decl_kind.Z3_OP_SELECT -> expr.convert(::mkArraySelect) + Z3_decl_kind.Z3_OP_STORE -> convertArrayStore(expr) + Z3_decl_kind.Z3_OP_SELECT -> convertArraySelect(expr) Z3_decl_kind.Z3_OP_CONST_ARRAY -> expr.convert { arg: KExpr -> - val range = Native.getRange(nCtx, decl).convertSort>() - mkArrayConst(range, arg) + val sort = Native.getRange(nCtx, decl).convertSort>() + mkArrayConst(sort, arg) } Z3_decl_kind.Z3_OP_BNUM, Z3_decl_kind.Z3_OP_BIT1, @@ -277,9 +295,10 @@ open class KZ3ExprConverter( Z3_decl_kind.Z3_OP_BSMUL_NO_UDFL -> expr.convert(::mkBvMulNoUnderflowExpr) Z3_decl_kind.Z3_OP_AS_ARRAY -> convert { + val sort = Native.getRange(nCtx, decl).convertSort>() val z3Decl = Native.getDeclFuncDeclParameter(nCtx, decl, 0).convertDecl() val funDecl = z3Decl as? KFuncDecl ?: error("unexpected as-array decl $z3Decl") - mkFunctionAsArray(funDecl) + mkFunctionAsArray(sort, funDecl) } Z3_decl_kind.Z3_OP_FPA_NEG -> expr.convert(::mkFpNegationExpr) @@ -495,6 +514,73 @@ open class KZ3ExprConverter( mkFpRoundingModeExpr(roundingMode) } + private fun convertArrayStore(expr: Long): ExprConversionResult = + when (Native.getAppNumArgs(nCtx, expr)) { + KArraySort.DOMAIN_SIZE + 2 -> expr.convert(::mkArray1Store) + KArray2Sort.DOMAIN_SIZE + 2 -> expr.convert(::mkArray2Store) + KArray3Sort.DOMAIN_SIZE + 2 -> expr.convertList { args: List> -> + val (i0, i1, i2) = args.subList(1, args.lastIndex) + mkArray3Store(args.first().uncheckedCast(), i0, i1, i2, args.last()) + } + else -> expr.convertList { args: List> -> + mkArrayNStore(args.first().uncheckedCast(), args.subList(1, args.lastIndex), args.last()) + } + } + + private fun convertArraySelect(expr: Long): ExprConversionResult = + when (Native.getAppNumArgs(nCtx, expr)) { + KArraySort.DOMAIN_SIZE + 1 -> expr.convert(::mkArray1Select) + KArray2Sort.DOMAIN_SIZE + 1 -> expr.convert(::mkArray2Select) + KArray3Sort.DOMAIN_SIZE + 1 -> expr.convert(::mkArray3Select) + else -> expr.convertList { args: List> -> + mkArrayNSelect(args.first().uncheckedCast(), args.drop(1)) + } + } + + private fun mkArray1Select( + array: KExpr>, + index: KExpr + ) = ctx.mkArraySelect(array, index) + + private fun mkArray2Select( + array: KExpr>, + index0: KExpr, index1: KExpr + ) = ctx.mkArraySelect(array, index0, index1) + + private fun mkArray3Select( + array: KExpr>, + index0: KExpr, index1: KExpr, index2: KExpr + ) = ctx.mkArraySelect(array, index0, index1, index2) + + private fun mkArrayNSelect( + array: KExpr>, + indices: List> + ) = ctx.mkArrayNSelect(array, indices) + + private fun mkArray1Store( + array: KExpr>, + index: KExpr, + value: KExpr + ) = ctx.mkArrayStore(array, index, value) + + private fun mkArray2Store( + array: KExpr>, + index0: KExpr, index1: KExpr, + value: KExpr + ) = ctx.mkArrayStore(array, index0, index1, value) + + private fun mkArray3Store( + array: KExpr>, + index0: KExpr, index1: KExpr, index2: KExpr, + value: KExpr + ) = ctx.mkArrayStore(array, index0, index1, index2, value) + + private fun mkArrayNStore( + array: KExpr>, + indices: List>, + value: KExpr + ) = ctx.mkArrayNStore(array, indices, value) + open fun convertQuantifier(expr: Long): ExprConversionResult = with(ctx) { val numBound = Native.getQuantifierNumBound(nCtx, expr) val boundSorts = List(numBound) { idx -> Native.getQuantifierBoundSort(nCtx, expr, idx) } @@ -530,8 +616,15 @@ open class KZ3ExprConverter( Native.isQuantifierForall(nCtx, expr) -> mkUniversalQuantifier(body, bounds) Native.isQuantifierExists(nCtx, expr) -> mkExistentialQuantifier(body, bounds) Native.isLambda(nCtx, expr) -> { - val boundVar = bounds.singleOrNull() ?: TODO("Array lambda with multiple indices") - mkArrayLambda(boundVar, body) + when (bounds.size) { + KArraySort.DOMAIN_SIZE -> mkArrayLambda(bounds.single(), body) + KArray2Sort.DOMAIN_SIZE -> mkArrayLambda(bounds.first(), bounds.last(), body) + KArray3Sort.DOMAIN_SIZE -> { + val (b0, b1, b2) = bounds + mkArrayLambda(b0, b1, b2, body) + } + else -> mkArrayNLambda(bounds, body) + } } else -> TODO("unexpected quantifier: ${Native.astToString(nCtx, expr)}") } diff --git a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprInternalizer.kt b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprInternalizer.kt index cb08dbeb6..8cc7f8fef 100644 --- a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprInternalizer.kt +++ b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3ExprInternalizer.kt @@ -9,8 +9,17 @@ import org.ksmt.KContext import org.ksmt.decl.KDecl import org.ksmt.expr.KAddArithExpr import org.ksmt.expr.KAndExpr +import org.ksmt.expr.KArray2Lambda +import org.ksmt.expr.KArray2Select +import org.ksmt.expr.KArray2Store +import org.ksmt.expr.KArray3Lambda +import org.ksmt.expr.KArray3Select +import org.ksmt.expr.KArray3Store import org.ksmt.expr.KArrayConst import org.ksmt.expr.KArrayLambda +import org.ksmt.expr.KArrayNLambda +import org.ksmt.expr.KArrayNSelect +import org.ksmt.expr.KArrayNStore import org.ksmt.expr.KArraySelect import org.ksmt.expr.KArrayStore import org.ksmt.expr.KBitVec16Value @@ -142,7 +151,11 @@ import org.ksmt.expr.KUniversalQuantifier import org.ksmt.expr.KXorExpr import org.ksmt.solver.util.KExprInternalizerBase import org.ksmt.sort.KArithSort +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort +import org.ksmt.sort.KArraySortBase import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort import org.ksmt.sort.KFp128Sort @@ -605,19 +618,113 @@ open class KZ3ExprInternalizer( transform(array, index, value, Native::mkStore) } + override fun transform( + expr: KArray2Store + ): KExpr> = with(expr) { + transform(array, index0, index1, value) { array: Long, i0: Long, i1: Long, value: Long -> + val indices = longArrayOf(i0, i1) + Native.mkStoreN(nCtx, array, indices.size, indices, value) + } + } + + override fun transform( + expr: KArray3Store + ): KExpr> = with(expr) { + transformArray(listOf(array, index0, index1, index2, value)) { args -> + val (array, i0, i1, i2, value) = args + val indices = longArrayOf(i0, i1, i2) + Native.mkStoreN(nCtx, array, indices.size, indices, value) + } + } + + override fun transform(expr: KArrayNStore): KExpr> = with(expr) { + transformArray(indices + listOf(array, value)) { args -> + val value = args[args.lastIndex] + val array = args[args.lastIndex - 1] + val indices = args.copyOf(args.size - 2) + Native.mkStoreN(nCtx, array, indices.size, indices, value) + } + } + override fun transform(expr: KArraySelect) = with(expr) { transform(array, index, Native::mkSelect) } - override fun transform(expr: KArrayConst) = with(expr) { - transform(value) { value: Long -> Native.mkConstArray(nCtx, sort.domain.internalizeSort(), value) } + override fun transform( + expr: KArray2Select + ): KExpr = with(expr) { + transform(array, index0, index1) { array: Long, i0: Long, i1: Long -> + val indices = longArrayOf(i0, i1) + Native.mkSelectN(nCtx, array, indices.size, indices) + } + } + + override fun transform( + expr: KArray3Select + ): KExpr = with(expr) { + transform(array, index0, index1, index2) { array: Long, i0: Long, i1: Long, i2: Long -> + val indices = longArrayOf(i0, i1, i2) + Native.mkSelectN(nCtx, array, indices.size, indices) + } + } + + override fun transform(expr: KArrayNSelect): KExpr = with(expr) { + transformArray(indices + array) { args -> + val array = args.last() + val indices = args.copyOf(args.size - 1) + Native.mkSelectN(nCtx, array, indices.size, indices) + } } + override fun , R : KSort> transform(expr: KArrayConst) = with(expr) { + transform(value) { value: Long -> + mkConstArray(sort, value) + } + } + + private fun mkConstArray(sort: KArraySortBase<*>, value: Long): Long = + if (sort is KArraySort<*, *>) { + Native.mkConstArray(nCtx, sort.domain.internalizeSort(), value) + } else { + val domain = sort.domainSorts.map { it.internalizeSort() }.toLongArray() + val domainNames = LongArray(sort.domainSorts.size) { Native.mkIntSymbol(nCtx, it) } + Native.mkLambda(nCtx, domain.size, domain, domainNames, value) + } + override fun transform(expr: KArrayLambda) = with(expr) { - transform(body) { body: Long -> - val indexSort = indexVarDecl.sort.internalizeSort() - val indexName = Native.mkStringSymbol(nCtx, indexVarDecl.name) - Native.mkLambda(nCtx, 1, longArrayOf(indexSort), longArrayOf(indexName), body) + transform(body, ctx.mkConstApp(indexVarDecl)) { body: Long, index: Long -> + val indices = longArrayOf(index) + Native.mkLambdaConst(nCtx, indices.size, indices, body) + } + } + + override fun transform( + expr: KArray2Lambda + ): KExpr> = with(expr) { + transform( + body, ctx.mkConstApp(indexVar0Decl), ctx.mkConstApp(indexVar1Decl) + ) { body: Long, index0: Long, index1: Long -> + val indices = longArrayOf(index0, index1) + Native.mkLambdaConst(nCtx, indices.size, indices, body) + } + } + + override fun transform( + expr: KArray3Lambda + ): KExpr> = with(expr) { + transform( + body, ctx.mkConstApp(indexVar0Decl), ctx.mkConstApp(indexVar1Decl), ctx.mkConstApp(indexVar2Decl) + ) { body: Long, index0: Long, index1: Long, index2: Long -> + val indices = longArrayOf(index0, index1, index2) + Native.mkLambdaConst(nCtx, indices.size, indices, body) + } + } + + override fun transform(expr: KArrayNLambda): KExpr> = with(expr) { + transformArray(indexVarDeclarations.map { ctx.mkConstApp(it) } + body) { args -> + val body = args.last() + val indices = args.copyOf(args.size - 1) + Native.mkLambdaConst(nCtx, indices.size, indices, body) } } @@ -706,8 +813,8 @@ open class KZ3ExprInternalizer( override fun transform(expr: KUniversalQuantifier) = transformQuantifier(expr, isUniversal = true) - override fun transform(expr: KFunctionAsArray): KExpr> { - TODO("KFunctionAsArray internalization is not implemented in z3") + override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr = with(expr) { + transform { Native.mkAsArray(nCtx, function.internalizeDecl()) } } inline fun > S.transform( diff --git a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3SortInternalizer.kt b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3SortInternalizer.kt index c449d2dea..2fc609a3d 100644 --- a/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3SortInternalizer.kt +++ b/ksmt-z3/src/main/kotlin/org/ksmt/solver/z3/KZ3SortInternalizer.kt @@ -1,6 +1,9 @@ package org.ksmt.solver.z3 import com.microsoft.z3.Native +import org.ksmt.sort.KArray2Sort +import org.ksmt.sort.KArray3Sort +import org.ksmt.sort.KArrayNSort import org.ksmt.sort.KArraySort import org.ksmt.sort.KBoolSort import org.ksmt.sort.KBvSort @@ -42,6 +45,34 @@ open class KZ3SortInternalizer( Native.mkArraySort(nCtx, domain, range) } + override fun visit(sort: KArray2Sort): Long = + z3Ctx.internalizeSort(sort) { + val domain = longArrayOf( + sort.domain0.internalizeZ3Sort(), + sort.domain1.internalizeZ3Sort() + ) + val range = sort.range.internalizeZ3Sort() + Native.mkArraySortN(nCtx, domain.size, domain, range) + } + + override fun visit(sort: KArray3Sort): Long = + z3Ctx.internalizeSort(sort) { + val domain = longArrayOf( + sort.domain0.internalizeZ3Sort(), + sort.domain1.internalizeZ3Sort(), + sort.domain2.internalizeZ3Sort() + ) + val range = sort.range.internalizeZ3Sort() + Native.mkArraySortN(nCtx, domain.size, domain, range) + } + + override fun visit(sort: KArrayNSort): Long = + z3Ctx.internalizeSort(sort) { + val domain = sort.domainSorts.map { it.internalizeZ3Sort() }.toLongArray() + val range = sort.range.internalizeZ3Sort() + Native.mkArraySortN(nCtx, domain.size, domain, range) + } + override fun visit(sort: KFpRoundingModeSort): Long = z3Ctx.internalizeSort(sort) { Native.mkFpaRoundingModeSort(nCtx) } diff --git a/ksmt-z3/src/test/kotlin/org/ksmt/solver/z3/ArrayTest.kt b/ksmt-z3/src/test/kotlin/org/ksmt/solver/z3/ArrayTest.kt new file mode 100644 index 000000000..a7249bd81 --- /dev/null +++ b/ksmt-z3/src/test/kotlin/org/ksmt/solver/z3/ArrayTest.kt @@ -0,0 +1,141 @@ +package org.ksmt.solver.z3 + +import com.microsoft.z3.Context +import org.ksmt.KContext +import org.ksmt.expr.KExpr +import org.ksmt.sort.KSort +import org.ksmt.utils.getValue +import kotlin.test.Test +import kotlin.test.assertEquals + +class ArrayTest { + + @Test + fun testArrayConversion(): Unit = with(KContext()) { + val sort = mkArraySort(bv32Sort, bv64Sort) + + val array by sort + val index by bv32Sort + val value by bv64Sort + val decl = mkFuncDecl("F", bv64Sort, listOf(bv32Sort)) + + testArrayConvert( + mkConst = { mkArrayConst(sort, value) }, + mkSelect = { mkArraySelect(array, index) }, + mkStore = { mkArrayStore(array, index, value) }, + mkLambda = { mkArrayLambda(index.decl, mkArraySelect(array, index)) }, + mkAsArray = { mkFunctionAsArray(sort, decl) } + ) + } + + @Test + fun testArray2Conversion(): Unit = with(KContext()) { + val sort = mkArraySort(bv32Sort, bv8Sort, bv64Sort) + + val array by sort + val index0 by bv32Sort + val index1 by bv8Sort + val value by bv64Sort + val decl = mkFuncDecl("F", bv64Sort, listOf(bv32Sort, bv8Sort)) + + testArrayConvert( + mkConst = { mkArrayConst(sort, value) }, + mkSelect = { mkArraySelect(array, index0, index1) }, + mkStore = { mkArrayStore(array, index0, index1, value) }, + mkLambda = { mkArrayLambda(index0.decl, index1.decl, mkArraySelect(array, index0, index1)) }, + mkAsArray = { mkFunctionAsArray(sort, decl) } + ) + } + + @Test + fun testArray3Conversion(): Unit = with(KContext()) { + val sort = mkArraySort(bv32Sort, bv8Sort, bv16Sort, bv64Sort) + + val array by sort + val index0 by bv32Sort + val index1 by bv8Sort + val index2 by bv16Sort + val value by bv64Sort + val decl = mkFuncDecl("F", bv64Sort, listOf(bv32Sort, bv8Sort, bv16Sort)) + + testArrayConvert( + mkConst = { mkArrayConst(sort, value) }, + mkSelect = { mkArraySelect(array, index0, index1, index2) }, + mkStore = { mkArrayStore(array, index0, index1, index2, value) }, + mkLambda = { + mkArrayLambda( + index0.decl, index1.decl, index2.decl, + mkArraySelect(array, index0, index1, index2) + ) + }, + mkAsArray = { mkFunctionAsArray(sort, decl) } + ) + } + + @Test + fun testArrayNConversion(): Unit = with(KContext()) { + val domain = listOf(bv32Sort, bv8Sort, bv32Sort, bv8Sort, bv32Sort) + val sort = mkArrayNSort(domain, bv64Sort) + + val array by sort + val indices = domain.mapIndexed { i, s -> mkConst("x$i", s) } + val value by bv64Sort + val decl = mkFuncDecl("F", bv64Sort, domain) + + testArrayConvert( + mkConst = { mkArrayConst(sort, value) }, + mkSelect = { mkArrayNSelect(array, indices) }, + mkStore = { mkArrayNStore(array, indices, value) }, + mkLambda = { mkArrayNLambda(indices.map { it.decl }, mkArrayNSelect(array, indices)) }, + mkAsArray = { mkFunctionAsArray(sort, decl) } + ) + } + + private inline fun KContext.testArrayConvert( + mkConst: KContext.() -> KExpr<*>, + mkSelect: KContext.() -> KExpr<*>, + mkStore: KContext.() -> KExpr<*>, + mkLambda: KContext.() -> KExpr<*>, + mkAsArray: KContext.() -> KExpr<*> + ) { + val const = mkConst() + val select = mkSelect() + val store = mkStore() + val lambda = mkLambda() + val asArray = mkAsArray() + + val ctx = this + Context().use { z3Native -> + val z3InternCtx = KZ3Context(z3Native) + val z3ConvertCtx = KZ3Context(z3Native) + + with(KZ3ExprInternalizer(ctx, z3InternCtx)) { + val iConst = const.internalizeExprWrapped().simplify() + val iSelect = select.internalizeExprWrapped().simplify() + val iStore = store.internalizeExprWrapped().simplify() + val iLambda = lambda.internalizeExprWrapped().simplify() + val iAsArray = asArray.internalizeExprWrapped().simplify() + + with(KZ3ExprConverter(ctx, z3ConvertCtx)) { + val cConst = iConst.convertExprWrapped() + val cSelect = iSelect.convertExprWrapped() + val cStore = iStore.convertExprWrapped() + val cLambda = iLambda.convertExprWrapped() + val cAsArray = iAsArray.convertExprWrapped() + + assertEquals(const, cConst) + assertEquals(select, cSelect) + assertEquals(store, cStore) + assertEquals(lambda, cLambda) + assertEquals(asArray, cAsArray) + } + } + } + } + + companion object { + init { + KZ3Solver(KContext()).close() + } + } +}