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

commonMain.it.unibo.tuprolog.solve.TestingClauseTheories.kt Maven / Gradle / Ivy

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

import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.Var
import it.unibo.tuprolog.dsl.theory.logicProgramming
import it.unibo.tuprolog.solve.PrologStandardExampleTheories.allPrologStandardTestingTheoryToRespectiveGoalsAndSolutions
import it.unibo.tuprolog.solve.exception.HaltException
import it.unibo.tuprolog.solve.exception.TimeOutException
import it.unibo.tuprolog.solve.exception.error.InstantiationError
import it.unibo.tuprolog.solve.exception.error.SystemError
import it.unibo.tuprolog.solve.exception.error.TypeError
import it.unibo.tuprolog.theory.Theory

/**
 * An object containing a collection of notable databases to be used when testing Solver functionality
 *
 * @author Enrico
 */
object TestingClauseTheories {
    internal val aContext = DummyInstances.executionContext

    internal val haltException = HaltException(context = aContext)

    internal fun instantiationError(
        functor: String,
        arity: Int,
        culprit: Var,
        index: Int? = null,
    ) = instantiationError(Signature(functor, arity), culprit, index)

    internal fun instantiationError(
        signature: Signature,
        culprit: Var,
        index: Int? = null,
    ) = if (index != null) {
        InstantiationError.forArgument(aContext, signature, culprit, index)
    } else {
        InstantiationError.forGoal(aContext, signature, culprit)
    }

    internal fun typeError(
        functor: String,
        arity: Int,
        actualValue: Term,
        index: Int? = null,
    ) = typeError(Signature(functor, arity), actualValue, index)

    internal fun typeError(
        signature: Signature,
        actualValue: Term,
        index: Int? = null,
    ) = if (index != null) {
        TypeError.forArgument(aContext, signature, TypeError.Expected.CALLABLE, actualValue, index)
    } else {
        TypeError.forGoal(aContext, signature, TypeError.Expected.CALLABLE, actualValue)
    }

    internal fun systemError(uncaught: Term) = SystemError.forUncaughtException(aContext, uncaught)

    internal val timeOutException = TimeOutException(context = aContext, exceededDuration = 1)

    /**
     * A database containing the following facts:
     * ```prolog
     * f(a).
     * g(a).
     * g(b).
     * h(a).
     * h(b).
     * h(c).
     * ```
     */
    val simpleFactTheory by lazy {
        logicProgramming {
            theory(
                { "f"("a") },
                { "g"("a") },
                { "g"("b") },
                { "h"("a") },
                { "h"("b") },
                { "h"("c") },
            )
        }
    }

    /**
     * a(A) :- b(A), d(Z).
     * b(B) :- c(B), d(W).
     * d(_).
     * c(1).
     */
    val callsWithVariablesTheory by lazy {
        logicProgramming {
            theoryOf(
                rule { "a"(A) `if` ("b"(A) and "d"(Z)) },
                rule { "b"(B) `if` ("c"(B) and "d"(W)) },
                fact { "d"(`_`) },
                fact { "c"(1) },
            )
        }
    }

    /**
     * ```
     * _one(X) :- ([X])
     * p(A, B, C) :-
     *   ([A, B, C]),
     *   _one(D),
     *   _one(E),
     *   ([f(A), f(B), f(C), f(D), f(E)]).
     * ```
     */
    fun callsWithVariablesAndInspectorTheory(
        predicate: String,
        observe: String,
    ) = logicProgramming {
        val observeOne = "${observe}_one"
        theoryOf(
            rule { observeOne(X) `if` observe(logicListOf(X)) },
            rule {
                predicate(A, B, C).`if`(
                    observe(logicListOf(A, B, C)),
                    observeOne(D),
                    observeOne(E),
                    observe(logicListOf("f"(A), "f"(B), "f"(C), "f"(D), "f"(E))),
                )
            },
        )
    }

    /**
     * Notable [simpleFactTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A).
     * ?- g(A).
     * ?- h(A).
     * ```
     */
    val simpleFactTheoryNotableGoalToSolutions by lazy {
        logicProgramming {
            listOf(
                "f"("A").hasSolutions(
                    { yes("A" to "a") },
                ),
                "g"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "b") },
                ),
                "h"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "b") },
                    { yes("A" to "c") },
                ),
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * f(only) :- !.
     * f(a).
     *
     * g(a).
     * g(only) :- !.
     * g(b).
     *
     * h(A) :- e(A).
     * h(A) :- d(A).
     * e(a) :- !.
     * e(b).
     * d(c).
     * d(d).
     * ```
     */
    val simpleCutTheory =
        logicProgramming {
            theory(
                { "f"("only") `if` "!" },
                { "f"("a") },
                { "g"("a") },
                { "g"("only") `if` "!" },
                { "g"("b") },
                { "h"("A") `if` "e"("A") },
                { "h"("A") `if` "d"("A") },
                { "e"("a") `if` "!" },
                { "e"("b") },
                { "d"("c") },
                { "d"("d") },
            )
        }

    /**
     * Notable [simpleCutTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A).
     * ?- g(A).
     * ?- h(A).
     * ```
     */
    val simpleCutTheoryNotableGoalToSolutions by lazy {
        logicProgramming {
            listOf(
                "f"("A").hasSolutions(
                    { yes("A" to "only") },
                ),
                "g"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "only") },
                ),
                "h"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "c") },
                    { yes("A" to "d") },
                ),
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * f(X, Y) :- q(X), !, r(Y).
     * f(X, Y) :- r(X).
     * q(a).
     * q(b).
     * r(a1).
     * r(b1).
     * ```
     */
    val simpleCutAndConjunctionTheory =
        logicProgramming {
            theory(
                { "f"("X", "Y") `if` ("q"("X") and "!" and "r"("Y")) },
                { "f"("X", "Y") `if` "r"("X") },
                { "q"("a") },
                { "q"("b") },
                { "r"("a1") },
                { "r"("b1") },
            )
        }

    /**
     * Notable [simpleCutAndConjunctionTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A, B).
     * ```
     */
    val simpleCutAndConjunctionTheoryNotableGoalToSolutions by lazy {
        logicProgramming {
            listOf(
                "f"("A", "B").hasSolutions(
                    { yes("A" to "a", "B" to "a1") },
                    { yes("A" to "a", "B" to "b1") },
                ),
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * a(X) :- b(X).
     * a(6).
     * b(X) :- c(X), d(X).
     * b(4) :- !.
     * b(5).
     * c(1).
     * c(2) :- !.
     * c(3).
     * d(2).
     * d(3).
     * ```
     */
    val cutConjunctionAndBacktrackingTheory =
        logicProgramming {
            theory(
                { "a"("X") `if` "b"("X") },
                { "a"(6) },
                { "b"("X") `if` ("c"("X") and "d"("X")) },
                { "b"(4) `if` "!" },
                { "b"(5) },
                { "c"(1) },
                { "c"(2) `if` "!" },
                { "c"(3) },
                { "d"(2) },
                { "d"(3) },
            )
        }

    /**
     * Notable [cutConjunctionAndBacktrackingTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- a(X).
     * ```
     */
    val cutConjunctionAndBacktrackingTheoryNotableGoalToSolutions by lazy {
        logicProgramming {
            listOf(
                "a"("X").hasSolutions(
                    { yes("X" to 2) },
                    { yes("X" to 4) },
                    { yes("X" to 6) },
                ),
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * a :- b.
     * b :- a.
     * ```
     */
    val infiniteComputationTheory =
        logicProgramming {
            theory(
                { "a" `if` "b" },
                { "b" `if` "a" },
            )
        }

    /**
     * Notable [infiniteComputationTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- a(X).
     * ```
     */
    val infiniteComputationTheoryNotableGoalToSolution by lazy {
        logicProgramming {
            listOf(
                atomOf("a").hasSolutions({ halt(timeOutException) }),
            )
        }
    }

    /**
     * A database that implements custom "reverse" over lists; it should test backtracking functionality
     *
     * ```prolog
     * my_reverse(L1, L2) :- my_rev(L1, L2, []).
     *
     * my_rev([], L2, L2) :- !.
     * my_rev([X | Xs], L2, Acc) :- my_rev(Xs, L2, [X | Acc]).
     * ```
     */
    val customReverseListTheory by lazy {
        logicProgramming {
            theory(
                { "my_reverse"("L1", "L2") `if` "my_rev"("L1", "L2", emptyLogicList) },
                { "my_rev"(emptyLogicList, "L2", "L2") `if` "!" },
                {
                    "my_rev"(consOf("X", "Xs"), "L2", "Acc") `if`
                        "my_rev"("Xs", "L2", consOf("X", "Acc"))
                },
            )
        }
    }

    /**
     * Notable [customReverseListTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- my_reverse([1, 2, 3, 4], L).
     * ```
     */
    val customReverseListTheoryNotableGoalToSolution by lazy {
        logicProgramming {
            listOf(
                "my_reverse"(logicListOf(1, 2, 3, 4), "L").hasSolutions(
                    { yes("L" to logicListOf(4, 3, 2, 1)) },
                ),
            )
        }
    }

    /**
     * A database that implements custom "range" to generate lists; it should test backtracking functionality along with arithmetic
     *
     * ```prolog
     * range(N, N, [N]) :- !.
     * range(M, N, [M | Ns]) :-
     *       M < N,
     *       M1 is M + 1,
     *       range(M1, N, Ns).
     * ```
     */
    val customRangeListGeneratorTheory by lazy {
        logicProgramming {
            theory(
                { "range"("N", "N", logicListOf("N")) `if` "!" },
                {
                    "range"("M", "N", consOf("M", "Ns")) `if` (
                        "<"("M", "N") and
                            ("M1" `is` (varOf("M") + 1)) and
                            "range"("M1", "N", "Ns")
                    )
                },
            )
        }
    }

    /**
     * Notable [customRangeListGeneratorTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- range(1, 4, [1, 2, 3, 4]).
     * ?- range(1, 4, [1, 2, 3, 4, 5]).
     * ?- range(1, 4, L).
     * ?- range(1, 1, L).
     * ?- range(2, 1, L).
     * ?- range(A, 4, [2, 3, 4]).
     * ?- range(2, A, [2, 3, 4]).
     * ```
     */
    @Suppress("LocalVariableName", "ktlint:standard:property-naming")
    val customRangeListGeneratorTheoryNotableGoalToSolution by lazy {
        val N = customRangeListGeneratorTheory.last().head!![1] as Var
        logicProgramming {
            listOf(
                "range"(1, 4, logicListOf(1, 2, 3, 4)).hasSolutions({ yes() }),
                "range"(1, 4, logicListOf(1, 2, 3, 4, 5)).hasSolutions({ no() }),
                "range"(1, 4, "L").hasSolutions({ yes("L" to logicListOf(1, 2, 3, 4)) }),
                "range"(1, 1, "L").hasSolutions({ yes("L" to logicListOf(1)) }),
                "range"(2, 1, "L").hasSolutions({ no() }),
                "range"("A", 4, logicListOf(2, 3, 4)).hasSolutions({ yes("A" to 2) }),
                "range"(2, "A", logicListOf(2, 3, 4)).hasSolutions(
                    { halt(instantiationError("<", 2, N, 1)) },
                ),
            )
        }
    }

    /**
     * Call primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- call(true).
     * ?- call(fail).
     * ?- call(halt).
     * ?- call((true, true)).
     * ?- call('!').
     * ?- call(X).
     * ?- call((true, 1)).
     * ```
     */
    fun callTestingGoalsToSolutions(errorSignature: Signature) =
        logicProgramming {
            listOf(
                call(true).hasSolutions({ yes() }),
                call(false).hasSolutions({ no() }),
                call(halt).hasSolutions({ halt(haltException) }),
                call("true" and "true").hasSolutions({ yes() }),
                call(cut).hasSolutions({ yes() }),
                call("X").hasSolutions({ halt(instantiationError(errorSignature, varOf("X"))) }),
                call("true" and 1).hasSolutions({ halt(typeError(errorSignature, ("true" and 1))) }),
            )
        }

    /**
     * Catch primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- catch(true, _, fail).
     * ?- catch(catch(throw(external(deepBall)), internal(I), fail), external(E), true).
     * ?- catch(throw(first), X, throw(second)).
     * ?- catch(throw(hello), X, true).
     * ?- catch((throw(hello), fail), X, true).
     * ```
     */
    val catchTestingGoalsToSolutions by lazy {
        logicProgramming {
            listOf(
                catch(true, `_`, false).hasSolutions({ yes() }),
                catch(catch(`throw`("external"("deepBall")), "internal"("I"), false), "external"("E"), true)
                    .hasSolutions({ yes("E" to "deepBall") }),
                catch(`throw`("first"), "X", `throw`("second")).hasSolutions(
                    { halt(systemError(atomOf("second"))) },
                ),
                catch(`throw`("hello"), "X", true).hasSolutions({ yes("X" to "hello") }),
                catch(`throw`("hello") and false, "X", true).hasSolutions({ yes("X" to "hello") }),
            )
        }
    }

    /**
     * Halt primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- halt.
     * ?- catch(halt, _, true).
     * ?- catch(catch(throw(something), _, halt), _, true).
     * ```
     */
    val haltTestingGoalsToSolutions by lazy {
        logicProgramming {
            listOf(
                halt.hasSolutions({ halt(haltException) }),
                catch(halt, `_`, true).hasSolutions({ halt(haltException) }),
                catch(catch(`throw`("something"), `_`, halt), `_`, true).hasSolutions(
                    { halt(haltException) },
                ),
            )
        }
    }

    /** Collection of all Prolog example (custom created and from Prolog Standard) databases and their respective callable goals with expected solutions */
    fun allPrologTestingTheoriesToRespectiveGoalsAndSolutions(
        callErrorSignature: Signature,
        nafErrorSignature: Signature,
        notErrorSignature: Signature,
    ) = mapOf(
        simpleFactTheory to simpleFactTheoryNotableGoalToSolutions,
        simpleCutTheory to simpleCutTheoryNotableGoalToSolutions,
        simpleCutAndConjunctionTheory to simpleCutAndConjunctionTheoryNotableGoalToSolutions,
        cutConjunctionAndBacktrackingTheory to cutConjunctionAndBacktrackingTheoryNotableGoalToSolutions,
        customReverseListTheory to customReverseListTheoryNotableGoalToSolution,
        Theory.empty() to callTestingGoalsToSolutions(callErrorSignature),
        Theory.empty() to catchTestingGoalsToSolutions,
        Theory.empty() to haltTestingGoalsToSolutions,
    ) +
        allPrologStandardTestingTheoryToRespectiveGoalsAndSolutions(
            callErrorSignature,
            nafErrorSignature,
            notErrorSignature,
        )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy