All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.io.ksmt.solver.yices.KYicesContext.kt Maven / Gradle / Ivy

There is a newer version: 0.5.26
Show newest version
package io.ksmt.solver.yices

import com.sri.yices.Terms
import com.sri.yices.Types
import com.sri.yices.Yices
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
import io.ksmt.decl.KDecl
import io.ksmt.expr.KConst
import io.ksmt.expr.KExpr
import io.ksmt.expr.KInterpretedValue
import io.ksmt.solver.KSolverUnsupportedFeatureException
import io.ksmt.solver.util.KExprIntInternalizerBase.Companion.NOT_INTERNALIZED
import io.ksmt.solver.yices.TermUtils.addTerm
import io.ksmt.solver.yices.TermUtils.andTerm
import io.ksmt.solver.yices.TermUtils.distinctTerm
import io.ksmt.solver.yices.TermUtils.funApplicationTerm
import io.ksmt.solver.yices.TermUtils.mulTerm
import io.ksmt.solver.yices.TermUtils.orTerm
import io.ksmt.sort.KSort
import io.ksmt.utils.library.NativeLibraryLoaderUtils
import java.math.BigInteger
import java.util.concurrent.atomic.AtomicInteger

open class KYicesContext : AutoCloseable {
    private var isClosed = false

    private val expressions = mkTermCache>()
    private val yicesExpressions = mkTermReverseCache>()

    private val sorts = mkSortCache()
    private val yicesSorts = mkSortReverseCache()

    private val decls = mkTermCache>()
    private val yicesDecls = mkTermReverseCache>()

    private val vars = mkTermCache>()
    private val yicesVars = mkTermReverseCache>()

    private val yicesTypes = mkSortSet()
    private val yicesTerms = mkTermSet()

    val isActive: Boolean
        get() = !isClosed

    fun findInternalizedExpr(expr: KExpr<*>): YicesTerm = expressions.getInt(expr)
    fun saveInternalizedExpr(expr: KExpr<*>, internalized: YicesTerm) {
        if (expressions.putIfAbsent(expr, internalized) == NOT_INTERNALIZED) {
            if (expr is KInterpretedValue<*> || expr is KConst<*>) {
                yicesExpressions.put(internalized, expr)
            }
        }
    }

    fun findInternalizedSort(sort: KSort): YicesSort = sorts.getInt(sort)
    fun saveInternalizedSort(sort: KSort, internalized: YicesSort) {
        saveWithReverseCache(sorts, yicesSorts, sort, internalized)
    }

    fun findInternalizedDecl(decl: KDecl<*>): YicesTerm = decls.getInt(decl)
    fun saveInternalizedDecl(decl: KDecl<*>, internalized: YicesTerm) {
        saveWithReverseCache(decls, yicesDecls, decl, internalized)
    }

    fun findInternalizedVar(decl: KDecl<*>): YicesTerm = vars.getInt(decl)
    fun saveInternalizedVar(decl: KDecl<*>, internalized: YicesTerm) {
        saveWithReverseCache(vars, yicesVars, decl, internalized)
    }

    fun findConvertedExpr(expr: YicesTerm): KExpr<*>? = yicesExpressions[expr]
    fun saveConvertedExpr(expr: YicesTerm, converted: KExpr<*>) {
        saveWithReverseCache(yicesExpressions, expressions, expr, converted)
    }

    fun findConvertedSort(sort: YicesSort): KSort? = yicesSorts[sort]
    fun saveConvertedSort(sort: YicesSort, converted: KSort) {
        saveWithReverseCache(yicesSorts, sorts, sort, converted)
    }

    fun findConvertedDecl(decl: YicesTerm): KDecl<*>? = yicesDecls[decl]
    fun saveConvertedDecl(decl: YicesTerm, converted: KDecl<*>) {
        saveWithReverseCache(yicesDecls, decls, decl, converted)
    }

    fun findConvertedVar(variable: YicesTerm): KDecl<*>? = yicesVars[variable]
    fun saveConvertedVar(variable: YicesTerm, converted: KDecl<*>) {
        saveWithReverseCache(yicesVars, vars, variable, converted)
    }

    inline fun internalizeSort(sort: KSort, internalizer: (KSort) -> YicesSort): YicesSort =
        findOrSave(::findInternalizedSort, ::saveInternalizedSort, sort) { internalizer(sort) }

    inline fun internalizeDecl(decl: KDecl<*>, internalizer: (KDecl<*>) -> YicesTerm): YicesTerm =
        findOrSave(::findInternalizedDecl, ::saveInternalizedDecl, decl) { internalizer(decl) }

    inline fun internalizeVar(decl: KDecl<*>, internalizer: (KDecl<*>) -> YicesTerm): YicesTerm =
        findOrSave(::findInternalizedVar, ::saveInternalizedVar, decl) { internalizer(decl) }

    inline fun convertSort(sort: YicesSort, converter: (YicesSort) -> KSort): KSort =
        findOrSave(::findConvertedSort, ::saveConvertedSort, sort) { converter(sort) }

    inline fun convertDecl(decl: YicesTerm, converter: (YicesTerm) -> KDecl<*>): KDecl<*> =
        findOrSave(::findConvertedDecl, ::saveConvertedDecl, decl) { converter(decl) }

    inline fun convertVar(variable: YicesTerm, converter: (YicesTerm) -> KDecl<*>): KDecl<*> =
        findOrSave(::findConvertedVar, ::saveConvertedVar, variable) { converter(variable) }

    private fun  saveWithReverseCache(
        cache: Int2ObjectOpenHashMap,
        reverseCache: Object2IntOpenHashMap,
        key: Int,
        value: V
    ) {
        if (cache.putIfAbsent(key, value) == null) {
            reverseCache.putIfAbsent(value, key)
        }
    }

    private fun  saveWithReverseCache(
        cache: Object2IntOpenHashMap,
        reverseCache: Int2ObjectOpenHashMap,
        key: K,
        value: Int
    ) {
        if (cache.putIfAbsent(key, value) == NOT_INTERNALIZED) {
            reverseCache.putIfAbsent(value, key)
        }
    }

    inline fun  findOrSave(
        find: (Int) -> V?,
        save: (Int, V) -> Unit,
        key: Int,
        computeValue: () -> V
    ): V {
        val currentValue = find(key)
        if (currentValue != null) return currentValue

        val value = computeValue()
        save(key, value)
        return value
    }

    inline fun  findOrSave(
        find: (K) -> Int,
        save: (K, Int) -> Unit,
        key: K,
        computeValue: () -> Int
    ): Int {
        val currentValue = find(key)
        if (currentValue != NOT_INTERNALIZED) return currentValue

        val value = computeValue()
        save(key, value)
        return value
    }

    val bool = Types.BOOL
    val int = Types.INT
    val real = Types.REAL

    private inline fun mkType(mk: () -> YicesSort): YicesSort = withGcGuard {
        val type = mk()

        if (yicesTypes.add(type)) {
            Yices.yicesIncrefType(type)
        }

        return type
    }

    fun bvType(sizeBits: UInt) = mkType { Types.bvType(sizeBits.toInt()) }
    fun functionType(domain: YicesSort, range: YicesSort) = mkType { Types.functionType(domain, range) }
    fun functionType(domain: YicesSortArray, range: YicesSort) = mkType { Types.functionType(domain, range) }
    fun newUninterpretedType(name: String) = mkType { Types.newUninterpretedType(name) }

    val zero = mkTerm { Terms.intConst(0L) }
    val one = mkTerm { Terms.intConst(1L) }
    val minusOne = mkTerm { Terms.intConst(-1L) }

    private inline fun mkTerm(mk: () -> YicesTerm): YicesTerm = withGcGuard {
        val term = mk()

        if (yicesTerms.add(term)) {
            Yices.yicesIncrefTerm(term)
        }

        return term
    }

    fun newUninterpretedTerm(name: String, type: YicesSort) = mkTerm {
        Terms.newUninterpretedTerm(name, type)
    }

    fun newVariable(type: YicesSort) = mkTerm { Terms.newVariable(type) }
    fun newVariable(name: String, type: YicesSort) = mkTerm { Terms.newVariable(name, type) }

    fun and(args: YicesTermArray) = mkTerm { andTerm(args) }
    fun or(args: YicesTermArray) = mkTerm { orTerm(args) }
    fun not(term: YicesTerm) = mkTerm { Terms.not(term) }
    fun implies(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.implies(arg0, arg1) }
    fun xor(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.xor(arg0, arg1) }
    fun mkTrue() = mkTerm(Terms::mkTrue)
    fun mkFalse() = mkTerm(Terms::mkFalse)
    fun eq(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.eq(arg0, arg1) }
    fun distinct(args: YicesTermArray) = mkTerm { distinctTerm(args) }
    fun ifThenElse(condition: YicesTerm, trueBranch: YicesTerm, falseBranch: YicesTerm) = mkTerm {
        Terms.ifThenElse(condition, trueBranch, falseBranch)
    }

    fun bvConst(sizeBits: UInt, value: Long) = mkTerm { Terms.bvConst(sizeBits.toInt(), value) }
    fun parseBvBin(value: String) = mkTerm { Terms.parseBvBin(value) }
    fun bvNot(arg: YicesTerm) = mkTerm { Terms.bvNot(arg) }
    fun bvRedAnd(arg: YicesTerm) = mkTerm { Terms.bvRedAnd(arg) }
    fun bvRedOr(arg: YicesTerm) = mkTerm { Terms.bvRedOr(arg) }
    fun bvAnd(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvAnd(arg0, arg1) }
    fun bvOr(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvOr(arg0, arg1) }
    fun bvXor(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvXor(arg0, arg1) }
    fun bvNand(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvNand(arg0, arg1) }
    fun bvNor(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvNor(arg0, arg1) }
    fun bvXNor(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvXNor(arg0, arg1) }
    fun bvNeg(arg: YicesTerm) = mkTerm { Terms.bvNeg(arg) }
    fun bvAdd(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvAdd(arg0, arg1) }
    fun bvSub(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSub(arg0, arg1) }
    fun bvMul(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvMul(arg0, arg1) }
    fun bvDiv(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvDiv(arg0, arg1) }
    fun bvSDiv(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSDiv(arg0, arg1) }
    fun bvRem(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvRem(arg0, arg1) }
    fun bvSRem(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSRem(arg0, arg1) }
    fun bvSMod(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSMod(arg0, arg1) }
    fun bvLt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvLt(arg0, arg1) }
    fun bvSLt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSLt(arg0, arg1) }
    fun bvLe(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvLe(arg0, arg1) }
    fun bvSLe(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSLe(arg0, arg1) }
    fun bvGe(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvGe(arg0, arg1) }
    fun bvSGe(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSGe(arg0, arg1) }
    fun bvGt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvGt(arg0, arg1) }
    fun bvSGt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvSGt(arg0, arg1) }
    fun bvConcat(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.bvConcat(arg0, arg1) }
    fun bvExtract(arg: YicesTerm, low: Int, high: Int) = mkTerm { Terms.bvExtract(arg, low, high) }
    fun bvExtractBit(arg: YicesTerm, index: Int) = mkTerm { Terms.bvExtractBit(arg, index) }
    fun bvSignExtend(arg: YicesTerm, extensionSize: Int) = mkTerm {
        Terms.bvSignExtend(arg, extensionSize)
    }

    fun bvZeroExtend(arg: YicesTerm, extensionSize: Int) = mkTerm {
        Terms.bvZeroExtend(arg, extensionSize)
    }

    fun bvRepeat(arg: YicesTerm, repeatNumber: Int) = mkTerm { Terms.bvRepeat(arg, repeatNumber) }
    fun bvShl(arg: YicesTerm, shift: Int) = mkTerm { Terms.bvShl(arg, shift) }
    fun bvLshr(arg: YicesTerm, shift: Int) = mkTerm { Terms.bvLshr(arg, shift) }
    fun bvAshr(arg: YicesTerm, shift: Int) = mkTerm { Terms.bvAshr(arg, shift) }
    fun bvRotateLeft(arg: YicesTerm, rotationNumber: Int) = mkTerm {
        Terms.bvRotateLeft(arg, rotationNumber)
    }

    fun bvRotateRight(arg: YicesTerm, rotationNumber: Int) = mkTerm {
        Terms.bvRotateRight(arg, rotationNumber)
    }

    fun funApplication(func: YicesTerm, index: YicesTerm) = mkTerm { Terms.funApplication(func, index) }
    fun funApplication(func: YicesTerm, args: YicesTermArray) = mkTerm { funApplicationTerm(func, args) }

    fun functionUpdate(func: YicesTerm, args: YicesTermArray, value: YicesTerm) = mkTerm {
        Terms.functionUpdate(func, args, value)
    }

    fun lambda(bounds: YicesTermArray, body: YicesTerm) = mkTerm { Terms.lambda(bounds, body) }

    fun add(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.add(arg0, arg1) }
    fun add(args: YicesTermArray) = mkTerm { addTerm(args) }
    fun mul(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.mul(arg0, arg1) }
    fun mul(args: YicesTermArray) = mkTerm { mulTerm(args) }
    fun sub(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.sub(arg0, arg1) }
    fun neg(arg: YicesTerm) = mkTerm { Terms.neg(arg) }
    fun div(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.div(arg0, arg1) }
    fun power(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.power(arg0, arg1) }
    fun arithLt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.arithLt(arg0, arg1) }
    fun arithLeq(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.arithLeq(arg0, arg1) }
    fun arithLeq0(arg: YicesTerm) = mkTerm { Terms.arithLeq0(arg) }
    fun arithGt(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.arithGt(arg0, arg1) }
    fun arithGeq(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.arithGeq(arg0, arg1) }
    fun idiv(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.idiv(arg0, arg1) }
    fun imod(arg0: YicesTerm, arg1: YicesTerm) = mkTerm { Terms.imod(arg0, arg1) }
    fun intConst(value: Long) = mkTerm { Terms.intConst(value) }
    fun intConst(value: BigInteger) = mkTerm { Terms.intConst(value) }
    fun floor(arg: YicesTerm) = mkTerm { Terms.floor(arg) }
    fun isInt(arg: YicesTerm) = mkTerm { Terms.isInt(arg) }

    fun exists(bounds: YicesTermArray, body: YicesTerm) = mkTerm { Terms.exists(bounds, body) }
    fun forall(bounds: YicesTermArray, body: YicesTerm) = mkTerm { Terms.forall(bounds, body) }

    fun uninterpretedSortConst(sort: YicesSort, idx: Int) = mkTerm { Terms.mkConst(sort, idx) }

    private var maxValueIndex = 0

    /**
     * Yices can produce different values with the same index.
     * This situation happens if we create a value with index I (e.g. 2)
     * and Yices generates a value in the model with index I.
     * To overcome this, we shift our indices by some very huge value.
     * Since Yices generates values with a small indices (e.g. 0, 1, 2, ...)
     * this trick solves the issue.
     * */
    fun uninterpretedSortValueIndex(idx: Int): Int {
        if (idx !in UNINTERPRETED_SORT_VALUE_INDEX_RANGE) {
            throw KSolverUnsupportedFeatureException(
                "Yices solver requires value index to be in range: $UNINTERPRETED_SORT_VALUE_INDEX_RANGE"
            )
        }

        maxValueIndex = maxOf(maxValueIndex, idx)
        return idx + UNINTERPRETED_SORT_VALUE_SHIFT
    }

    fun convertUninterpretedSortValueIndex(internalIndex: Int): Int {
        // User provided value index
        if (internalIndex >= UNINTERPRETED_SORT_MIN_SHIFTED_VALUE) {
            return internalIndex - UNINTERPRETED_SORT_VALUE_SHIFT
        }

        // Create a new index that doesn't overlap with any other indices
        return ++maxValueIndex
    }

    fun substitute(term: YicesTerm, substituteFrom: YicesTermArray, substituteTo: YicesTermArray): YicesTerm =
        mkTerm { Terms.subst(term, substituteFrom, substituteTo) }

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

        yicesTerms.forEach { Yices.yicesDecrefTerm(it) }
        yicesTypes.forEach { Yices.yicesDecrefType(it) }

        performGc()
    }

    companion object {
        init {
            if (!Yices.isReady()) {
                NativeLibraryLoaderUtils.load()
                Yices.init()
                Yices.setReadyFlag(true)
            }
        }

        private const val UNINTERPRETED_SORT_VALUE_SHIFT = 1 shl 30
        private const val UNINTERPRETED_SORT_MAX_ALLOWED_VALUE = UNINTERPRETED_SORT_VALUE_SHIFT / 2
        private const val UNINTERPRETED_SORT_MIN_ALLOWED_VALUE = -UNINTERPRETED_SORT_MAX_ALLOWED_VALUE
        private const val UNINTERPRETED_SORT_MIN_SHIFTED_VALUE = UNINTERPRETED_SORT_MAX_ALLOWED_VALUE

        internal val UNINTERPRETED_SORT_VALUE_INDEX_RANGE =
            UNINTERPRETED_SORT_MIN_ALLOWED_VALUE..UNINTERPRETED_SORT_MAX_ALLOWED_VALUE

        internal fun  mkTermCache() = Object2IntOpenHashMap().apply {
            defaultReturnValue(NOT_INTERNALIZED)
        }

        internal fun  mkTermReverseCache() = Int2ObjectOpenHashMap()

        internal fun  mkSortCache() = Object2IntOpenHashMap().apply {
            defaultReturnValue(NOT_INTERNALIZED)
        }

        internal fun  mkSortReverseCache() = Int2ObjectOpenHashMap()

        internal fun mkTermSet() = IntOpenHashSet()

        internal fun mkSortSet() = IntOpenHashSet()

        private const val FREE = 0
        private const val ON_GC = -1000000

        @JvmStatic
        private val gcGuard = AtomicInteger(FREE)

        /**
         * Since Yices manages terms globally we must ensure that
         * there are no Yices GC operations between
         * the initiation of the term creation process
         * and the execution of `incRef` on the newly created term.
         * Otherwise, there is a scenario when the term is deleted before the `incRef`
         * and therefore remains invalid.
         *
         * According to the [gcGuard] possible values we have the following situations:
         * 1. [gcGuard] == [FREE] -- no currently performing operations
         * 2. [gcGuard] > [FREE] -- some term creation operations are performed
         * 3. [gcGuard] < [FREE] -- GC is performed
         *
         * All term operations are performed according to the following rules:
         * 1. We can create term only if [gcGuard] >= [FREE]
         * (no operations or other term creation operation. No GC operations).
         * 2. If we are on GC and we want to create a term we spin wait until
         * [gcGuard] >= [FREE].
         * 3. If we want to perform GC we spin wait until [gcGuard] == [FREE] (no operations).
         *
         * See also [performGc]
         * */
        private inline fun  withGcGuard(body: () -> T): T {
            // spin wait until [gcGuard] >= [FREE]
            while (true) {
                val status = gcGuard.getAndIncrement()
                if (status >= FREE) break
                gcGuard.getAndDecrement()
            }

            return try {
                body()
            } finally {
                gcGuard.getAndDecrement()
            }
        }

        private fun performGc() {
            // spin wait until [gcGuard] == [FREE]
            while (true) {
                if (gcGuard.compareAndSet(FREE, ON_GC)) {
                    break
                }
            }

            Yices.yicesGarbageCollect()

            gcGuard.getAndAdd(-ON_GC)
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy