fix: Fixes

This commit is contained in:
Tibo De Peuter 2025-05-18 14:56:16 +02:00
parent a1187238c3
commit 83f65bd683
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
8 changed files with 65 additions and 29 deletions

View file

@ -3,8 +3,9 @@ package prolog.ast.arithmetic
import prolog.Answers
import prolog.Substitutions
import prolog.ast.logic.LogicOperand
import prolog.ast.terms.Body
data class Integer(override val value: Int) : Number, LogicOperand() {
data class Integer(override val value: Int) : Number, Body, LogicOperand() {
// Integers are already evaluated
override fun simplify(subs: Substitutions): Simplification = Simplification(this, this)

View file

@ -6,6 +6,7 @@ import prolog.ast.Database.Program
import prolog.ast.terms.*
import prolog.builtins.True
import prolog.flags.AppliedCut
import prolog.flags.AppliedShift
import prolog.logic.applySubstitution
import prolog.logic.numbervars
import prolog.logic.occurs
@ -27,18 +28,23 @@ abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
// Only if the body can be proven, the substitutions should be returned.
// Do this in a lazy way.
val simplifiedGoal = applySubstitution(goal, subs)
// Since we are only interested in substitutions in the goal (as opposed to the head of this clause),
// we can use variable renaming and filter out the substitutions that are not in the goal.
val (headEnd, headRenaming) = numbervars(head, Program.variableRenamingStart, emptyMap())
Program.variableRenamingStart = headEnd
val (goalEnd, goalRenaming) = numbervars(simplifiedGoal, headEnd, emptyMap())
Program.variableRenamingStart = goalEnd
val reverseGoalRenaming = goalRenaming.entries.associate { (key, value) -> value to key }
val renamedHead = applySubstitution(head, headRenaming)
val simplifiedGoal = applySubstitution(goal, subs)
unifyLazy(simplifiedGoal, renamedHead, emptyMap()).forEach { headAnswer ->
val renamedGoal = applySubstitution(simplifiedGoal, goalRenaming)
unifyLazy(renamedGoal, renamedHead, emptyMap()).forEach { headAnswer ->
headAnswer.map { headAndGoalSubs ->
// Filter the substitutions to only include those that map from head to goal
val headToGoalSubs = headAndGoalSubs.filterKeys { it in headRenaming.values }
val goalToHeadSubs = headAndGoalSubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
val goalToHeadSubs = headAndGoalSubs.filterKeys { occurs(it as Variable, renamedGoal) }
val headResult = headToGoalSubs.filterKeys { key ->
goalToHeadSubs.any { occurs(key as Variable, it.value) }
}
@ -47,24 +53,34 @@ abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
bodyAnswer.fold(
onSuccess = { bodySubs ->
// If the body can be proven, yield the (combined) substitutions
val goalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
val bodyResult = bodySubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
val renamedGoalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
val goalResult =
renamedGoalResult.mapKeys { applySubstitution(it.key, reverseGoalRenaming) }
val renamedBodyResult = bodySubs.filterKeys { occurs(it as Variable, renamedGoal) }
val bodyResult = renamedBodyResult.mapKeys { applySubstitution(it.key, reverseGoalRenaming) }
yield(Result.success(goalResult + headResult + bodyResult))
},
onFailure = { error ->
if (error is AppliedCut) {
// Find single solution and return immediately
if (error.subs != null) {
val bodySubs = error.subs
val goalResult = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
val bodyResult = bodySubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
yield(Result.failure(AppliedCut(goalResult + headResult + bodyResult)))
} else {
yield(Result.failure(AppliedCut()))
when (error) {
is AppliedCut -> {
// Find single solution and return immediately
if (error.subs != null) {
val bodySubs = error.subs
val goalResult =
goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
val bodyResult = bodySubs.filterKeys { occurs(it as Variable, renamedGoal) }
yield(Result.failure(AppliedCut(goalResult + headResult + bodyResult)))
} else {
yield(Result.failure(AppliedCut()))
}
return@sequence
}
return@sequence
} else {
yield(Result.failure(error))
is AppliedShift -> {
yield(Result.failure(error))
}
else -> yield(Result.failure(error))
}
}
)

View file

@ -1,5 +1,8 @@
package prolog.ast.terms
import prolog.Substitutions
import prolog.ast.logic.Satisfiable
interface Body : Term, Satisfiable
interface Body : Term, Satisfiable {
override fun applySubstitution(subs: Substitutions): Body
}

View file

@ -55,7 +55,7 @@ open class Conjunction(val left: LogicOperand, private val right: LogicOperand)
right.fold(
// If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs))
yield(Result.success(rightSubs + leftSubs))
},
onFailure = { exception ->
// If the right part fails, check if it's a cut
@ -74,8 +74,8 @@ open class Conjunction(val left: LogicOperand, private val right: LogicOperand)
}
fun findNextCutSolution(appliedCut: AppliedCut): Answers = sequence {
val leftSubs = appliedCut.subs
right.satisfy(subs + (appliedCut.subs!!)).firstOrNull()?.map { rightSubs ->
val leftSubs = appliedCut.subs ?: emptyMap()
right.satisfy(subs + leftSubs).firstOrNull()?.map { rightSubs ->
// If the right part succeeds, yield the result with the left substitutions
yield(Result.success(leftSubs + rightSubs))
return@sequence

View file

@ -39,7 +39,7 @@ class Reset(private val goal: Goal, private val ball: Term, private val cont: Te
if (failure is AppliedShift) {
require(failure.cont != null) { "Shift must have a continuation" }
// Reset/3 succeeds immediately, binding Term1 to Term2 and Cont to the remainder of Goal.
val shiftSubs = failure.subs
val shiftSubs = failure.subs + subs
val appliedBall = applySubstitution(failure.ball, shiftSubs)
val appliedCont = applySubstitution(failure.cont, shiftSubs)
Conjunction(Unify(ball, appliedBall), Unify(appliedCont, cont))