fix: Satisfy clause

This commit is contained in:
Tibo De Peuter 2025-05-06 14:13:03 +02:00
parent 256a189125
commit 4810b91628
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
3 changed files with 18 additions and 17 deletions

View file

@ -4,6 +4,6 @@ leq(s(X), s(Y)) :- leq(X, Y).
:- initialization(main). :- initialization(main).
main :- main :-
leq(X, s(s(s(0)))), leq(A, s(s(s(0)))),
write(X), nl, write(A), nl,
fail. fail.

View file

@ -27,30 +27,31 @@ abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
// Only if the body can be proven, the substitutions should be returned. // Only if the body can be proven, the substitutions should be returned.
// Do this in a lazy way. // Do this in a lazy way.
val (headEnd, headRenaming) = numbervars(head, Program.variableRenamingStart, 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 Program.variableRenamingStart = headEnd
val renamedHead = applySubstitution(head, subs + headRenaming) val renamedHead = applySubstitution(head, headRenaming)
val renamedBody = applySubstitution(body, subs + headRenaming) as Body val simplifiedGoal = applySubstitution(goal, subs)
unifyLazy(simplifiedGoal, renamedHead, emptyMap()).forEach { headAnswer ->
unifyLazy(goal, renamedHead, subs).forEach { headAnswer -> headAnswer.map { headAndGoalSubs ->
headAnswer.map { headSubs -> // Filter the substitutions to only include those that map from head to goal
val subsNotInGoal = headSubs.filterNot { occurs(it.key as Variable, goal, emptyMap()) } val headToGoalSubs = headAndGoalSubs.filterKeys { it in headRenaming.values }
renamedBody.satisfy(subsNotInGoal).forEach { bodyAnswer -> val goalToHeadSubs = headAndGoalSubs.filterKeys { occurs(it as Variable, simplifiedGoal) }
val newSubs = headRenaming + headToGoalSubs
body.satisfy(newSubs).forEach { bodyAnswer ->
bodyAnswer.fold( bodyAnswer.fold(
// If the body can be proven, yield the (combined) substitutions
onSuccess = { bodySubs -> onSuccess = { bodySubs ->
var result = headSubs + bodySubs // If the body can be proven, yield the (combined) substitutions
result = result var result = goalToHeadSubs.mapValues { applySubstitution(it.value, bodySubs) }
.mapValues { applySubstitution(it.value, result) }
.filterKeys { it !is AnonymousVariable && occurs(it as Variable, goal, emptyMap()) }
yield(Result.success(result)) yield(Result.success(result))
}, },
onFailure = { error -> onFailure = { error ->
if (error is AppliedCut) { if (error is AppliedCut) {
// Find single solution and return immediately // Find single solution and return immediately
if (error.subs != null) { if (error.subs != null) {
yield(Result.failure(AppliedCut(headSubs + error.subs))) yield(Result.failure(AppliedCut(headAndGoalSubs + error.subs)))
} else { } else {
yield(Result.failure(AppliedCut())) yield(Result.failure(AppliedCut()))
} }

View file

@ -41,7 +41,7 @@ fun applySubstitution(expr: Expression, subs: Substitutions): Expression = when
} }
// Check if a variable occurs in a term // Check if a variable occurs in a term
fun occurs(variable: Variable, term: Term, subs: Substitutions): Boolean = when { fun occurs(variable: Variable, term: Term, subs: Substitutions = emptyMap()): Boolean = when {
variable(term, subs) -> term == variable variable(term, subs) -> term == variable
atomic(term, subs) -> false atomic(term, subs) -> false
compound(term, subs) -> { compound(term, subs) -> {