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

main.io.ksmt.solver.cvc5.KCvc5Solver.kt Maven / Gradle / Ivy

The newest version!
package io.ksmt.solver.cvc5

import io.github.cvc5.CVC5ApiException
import io.github.cvc5.Result
import io.github.cvc5.Solver
import io.ksmt.KContext
import io.ksmt.expr.KExpr
import io.ksmt.solver.KModel
import io.ksmt.solver.KSolver
import io.ksmt.solver.KSolverException
import io.ksmt.solver.KSolverStatus
import io.ksmt.solver.model.KNativeSolverModel
import io.ksmt.sort.KBoolSort
import io.ksmt.utils.library.NativeLibraryLoaderUtils
import kotlin.time.Duration
import kotlin.time.DurationUnit

open class KCvc5Solver(private val ctx: KContext) : KSolver {
    private val termManager = KCvc5TermManager()
    private val cvc5Ctx = KCvc5Context(termManager, ctx)

    private var solverLazyConfiguration: KCvc5SolverLazyConfiguration? = null
    private var solverInitialized: Boolean = false
    private val solver by lazy { initSolver() }
    fun nativeSolver(): Solver = solver

    private val exprInternalizer by lazy { createExprInternalizer(cvc5Ctx) }

    private val currentScope: UInt
        get() = cvc5TrackedAssertions.lastIndex.toUInt()

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

    private var cvc5LastAssumptions: KCvc5TermMap>? = null

    private var cvc5CurrentLevelTrackedAssertions = KCvc5TermMap>()
    private val cvc5TrackedAssertions = mutableListOf(cvc5CurrentLevelTrackedAssertions)

    private fun initSolver(): Solver = cvc5Try {
        val solver = termManager.builder { Solver(this) }

        solverInitialized = true
        solverLazyConfiguration?.configure(solver)
        solverLazyConfiguration = null

        solver.setOption("produce-models", "true")
        solver.setOption("produce-unsat-cores", "true")

        /**
         * Allow floating-point sorts of all sizes, rather than
         * only Float32 (8/24) or Float64 (11/53) (experimental in cvc5 1.0.2)
         */
        solver.setOption("fp-exp", "true")

        return solver
    }

    open fun createExprInternalizer(cvc5Ctx: KCvc5Context): KCvc5ExprInternalizer =
        KCvc5ExprInternalizer(cvc5Ctx, solver)

    override fun configure(configurator: KCvc5SolverConfiguration.() -> Unit) {
        if (!solverInitialized) {
            val config = solverLazyConfiguration
                ?: KCvc5SolverLazyConfiguration().also { solverLazyConfiguration = it }

            config.configurator()
        } else {
            KCvc5SolverOptionsConfiguration(solver).configurator()
        }
    }

    override fun assert(expr: KExpr) = cvc5Try {
        ctx.ensureContextMatch(expr)

        val cvc5Expr = with(exprInternalizer) { expr.internalizeExpr() }
        solver.assertFormula(cvc5Expr)

        cvc5Ctx.assertPendingAxioms(solver)
    }

    override fun assertAndTrack(expr: KExpr) {
        ctx.ensureContextMatch(expr)

        val trackVarApp = ctx.mkFreshConst("track", ctx.boolSort)
        val cvc5TrackVar = with(exprInternalizer) { trackVarApp.internalizeExpr() }
        val trackedExpr = with(ctx) { trackVarApp implies expr }

        cvc5CurrentLevelTrackedAssertions[cvc5TrackVar] = expr

        assert(trackedExpr)
        solver.assertFormula(cvc5TrackVar)
    }

    override fun push() = solver.push().also {
        cvc5CurrentLevelTrackedAssertions = KCvc5TermMap()
        cvc5TrackedAssertions.add(cvc5CurrentLevelTrackedAssertions)

        cvc5Ctx.push()
    }

    override fun pop(n: UInt) {
        require(n <= currentScope) {
            "Can not pop $n scope levels because current scope level is $currentScope"
        }

        if (n == 0u) return

        repeat(n.toInt()) {
            cvc5TrackedAssertions.removeLast()
        }
        cvc5CurrentLevelTrackedAssertions = cvc5TrackedAssertions.last()

        cvc5Ctx.pop(n)
        solver.pop(n.toInt())
    }

    override fun check(timeout: Duration): KSolverStatus = cvc5TryCheck {
        solver.updateTimeout(timeout)
        solver.checkSat().processCheckResult()
    }

    override fun checkWithAssumptions(
        assumptions: List>,
        timeout: Duration
    ): KSolverStatus = cvc5TryCheck {
        ctx.ensureContextMatch(assumptions)

        val lastAssumptions = KCvc5TermMap>().also { cvc5LastAssumptions = it }
        val cvc5Assumptions = Array(assumptions.size) { idx ->
            val assumedExpr = assumptions[idx]
            with(exprInternalizer) {
                assumedExpr.internalizeExpr().also {
                    lastAssumptions[it] = assumedExpr
                }
            }
        }

        solver.updateTimeout(timeout)
        solver.checkSatAssuming(cvc5Assumptions).processCheckResult()
    }

    override fun reasonOfUnknown(): String = cvc5Try {
        require(lastCheckStatus == KSolverStatus.UNKNOWN) {
            "Unknown reason is only available after UNKNOWN checks"
        }
        lastReasonOfUnknown ?: "no explanation"
    }

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

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

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

    override fun unsatCore(): List> = cvc5Try {
        require(lastCheckStatus == KSolverStatus.UNSAT) { "Unsat cores are only available after UNSAT checks" }

        val cvc5FullCore = solver.unsatCore.onEach { termManager.registerPointer(it) }

        val trackedTerms = KCvc5TermMap>()
        cvc5TrackedAssertions.forEach { frame ->
            trackedTerms.putAll(frame)
        }
        cvc5LastAssumptions?.also { trackedTerms.putAll(it) }

        cvc5FullCore.mapNotNull { trackedTerms[it] }
    }

    override fun close() {
        cvc5CurrentLevelTrackedAssertions.clear()
        cvc5TrackedAssertions.clear()

        cvc5Ctx.close()
        termManager.close()
    }

    /*
     there are no methods to interrupt cvc5,
     but maybe CVC5ApiRecoverableException can be thrown in someway
    */
    override fun interrupt() = Unit

    private fun Result.processCheckResult() = when {
        isSat -> KSolverStatus.SAT
        isUnsat -> KSolverStatus.UNSAT
        isUnknown || isNull -> KSolverStatus.UNKNOWN
        else -> KSolverStatus.UNKNOWN
    }.also {
        lastCheckStatus = it
        if (it == KSolverStatus.UNKNOWN) {
            lastReasonOfUnknown = this.unknownExplanation?.name
        }
    }

    private fun Solver.updateTimeout(timeout: Duration) {
        val cvc5Timeout = if (timeout == Duration.INFINITE) 0 else timeout.toInt(DurationUnit.MILLISECONDS)
        setOption("tlimit-per", cvc5Timeout.toString())
    }

    private inline fun  cvc5Try(body: () -> T): T = try {
        body()
    } catch (ex: CVC5ApiException) {
        throw KSolverException(ex)
    }

    private inline fun cvc5TryCheck(body: () -> KSolverStatus): KSolverStatus = try {
        invalidateSolverState()
        body()
    } catch (ex: CVC5ApiException) {
        lastReasonOfUnknown = ex.message
        KSolverStatus.UNKNOWN.also { lastCheckStatus = it }
    }

    private fun invalidateSolverState() {
        /**
         * Cvc5 model is only valid until the next check-sat call.
         * */
        lastCvcModel?.markInvalid()
        lastCvcModel = null
        lastModel = null

        lastCheckStatus = KSolverStatus.UNKNOWN
        lastReasonOfUnknown = null

        cvc5LastAssumptions = null
    }

    companion object {
        init {
            if (System.getProperty("cvc5.skipLibraryLoad") != "true") {
                NativeLibraryLoaderUtils.load()
                System.setProperty("cvc5.skipLibraryLoad", "true")
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy