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

commonMain.it.unibo.tuprolog.solve.solver.fsm.impl.StateRuleSelection.kt Maven / Gradle / Ivy

Go to download

Experimental, functional-programming-based implementation of Prolog's SLDNF resolution principle

There is a newer version: 1.0.4
Show newest version
package it.unibo.tuprolog.solve.solver.fsm.impl

import it.unibo.tuprolog.core.Rule
import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.Var
import it.unibo.tuprolog.core.prepareForExecution
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.Solution
import it.unibo.tuprolog.solve.currentTimeInstant
import it.unibo.tuprolog.solve.forEachWithLookahead
import it.unibo.tuprolog.solve.primitive.Solve
import it.unibo.tuprolog.solve.solver.*
import it.unibo.tuprolog.solve.solver.fsm.*
import it.unibo.tuprolog.unify.Unificator.Companion.mguWith

/**
 * State responsible of selecting a rule to be solved to demonstrate a goal
 *
 * @author Enrico
 */
internal class StateRuleSelection(
    override val solve: Solve.Request
) : AbstractTimedState(solve) {

    /** The execute function to be used when a [State] needs, internally, to execute sub-[State]s behaviour */
    private val subStateExecute: (State) -> Sequence = StateMachineExecutor::executeWrapping

    override fun behaveTimed(): Sequence = sequence {
        val currentGoal = solve.query
        val matchingRules = solve.context.retrieveRulesMatching(currentGoal)
        val isChoicePoint = moreThanOne(matchingRules)

        when {
            matchingRules.none() -> yield(stateEndFalse())

            else -> with(solve.context) {
                matchingRules.orderWithStrategy(this, solverStrategies::clauseChoiceStrategy)
            }.map { it.prepareForExecution().freshCopy() as Rule }
                .forEachWithLookahead { refreshedRule, hasAlternatives ->
                    val unifyingSubstitution = currentGoal mguWith refreshedRule.head

                    val wellFormedRuleBody = refreshedRule.body.apply(unifyingSubstitution) as Struct

                    val subSolveRequest =
                        solve.newSolveRequest(
                            wellFormedRuleBody,
                            unifyingSubstitution,
                            isChoicePointChild = isChoicePoint,
                            requestIssuingInstant = currentTimeInstant()
                        )

                    val subInitialState = StateInit(subSolveRequest.initializeForSubRuleScope())
                        .also { yield(it.asAlreadyExecuted()) }

                    var cutNextSiblings = false

                    // execute internally the sub-request in a sub-state-machine, to see what it will respond
                    subStateExecute(subInitialState).forEach {
                        val subState = it.wrappedState

                        // find in sub-goal state sequence, the final state responding to current solveRequest
                        if (subState is FinalState && subState.solve.solution.query == subSolveRequest.query) {

                            if (subState.solve.sideEffectManager.shouldCutExecuteInRuleSelection())
                                cutNextSiblings = true

                            // yield only non-false states or false states when there are no open alternatives (because no more or cut)
                            if (subState !is StateEnd.False || !hasAlternatives || cutNextSiblings) {
                                val extendedScopeSideEffectManager = subState.solve.sideEffectManager
                                    .extendParentScopeWith(solve.context.sideEffectManager)

                                yield(
                                    stateEnd(
                                        subState.solve.copy(
                                            solution = subState.solve.solution.removeSubstitutionFor(refreshedRule.variables),
                                            sideEffectManager = extendedScopeSideEffectManager
                                        )
                                    )
                                )
                            }

                            if (subState is StateEnd.Halt) return@sequence // if halt reached, overall computation should stop

                        } else yield(it) // return wrapped subState as is, only if not interested in it
                    }
                    if (cutNextSiblings) return@sequence // cut here other matching rules trial
                }
        }
    }

    private companion object {

        /**
         * Retrieves from receiver [ExecutionContext] those rules whose head matches [currentGoal]
         *
         * 1) It searches for matches inside libraries, if nothing found
         * 2) it looks inside both staticKb and dynamicKb
         */
        private fun ExecutionContext.retrieveRulesMatching(currentGoal: Struct): Sequence =
            currentGoal.freshCopy().let { refreshedGoal ->
                libraries.theory[refreshedGoal].takeIf { it.any() }
                    ?: sequenceOf(staticKb, dynamicKb).flatMap { it[refreshedGoal] }
            }

        /** Prepares provided solveRequest "side effects manager" to enter this "rule body sub-scope" */
        private fun Solve.Request.initializeForSubRuleScope() =
            copy(context = with(context) { copy(sideEffectManager = sideEffectManager.enterRuleSubScope()) })

        /**
         * Utility function to eliminate from solution substitution non meaningful variables
         * for the "upper scope" query, (i.e. variables introduced only for solving the "current" query)
         */
        private fun Solution.removeSubstitutionFor(unusedVariables: Sequence) = when (this) {
            is Solution.Yes -> copy(substitution = substitution - unusedVariables.asIterable())
            else -> this
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy