This repository has been archived on 2025-09-23. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2025LogProg-project-GhentPr.../tests/prolog/EvaluationTests.kt
2025-05-08 17:47:13 +02:00

501 lines
19 KiB
Kotlin

package prolog
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.builtins.Conjunction
import prolog.builtins.Disjunction
import prolog.builtins.Query
import prolog.logic.equivalent
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.ast.Database.Program
import prolog.ast.arithmetic.Integer
import prolog.ast.lists.List.Empty
import prolog.ast.lists.List.Cons
import prolog.ast.terms.AnonymousVariable
import prolog.builtins.Unify
class EvaluationTests {
@BeforeEach
fun setUp() {
Program.reset()
}
@Test
fun successful_single_fact_query() {
val fact = Fact(Atom("a"))
Program.load(listOf(fact))
val result = Program.query(Atom("a"))
assertTrue(result.any())
}
@Test
fun failing_single_fact_query() {
val fact = Fact(Atom("a"))
Program.load(listOf(fact))
val result = Program.query(Atom("b"))
assertFalse(result.any())
}
@Test
fun multiple_fact_query() {
val fact1 = Fact(Atom("a"))
val fact2 = Fact(Atom("b"))
Program.load(listOf(fact1, fact2))
assertTrue(Program.query(Atom("a")).any())
assertTrue(Program.query(Atom("b")).any())
assertFalse(Program.query(Atom("c")).any())
}
@Test
fun single_compound_query() {
val structure = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
Program.load(listOf(Fact(structure)))
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))).any())
}
@Test
fun multiple_compound_query() {
val structure1 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
val structure2 = Structure(Atom("g"), listOf(Atom("c"), Atom("d")))
Program.load(listOf(Fact(structure1), Fact(structure2)))
assertTrue(Program.query(Structure(Atom("f"), listOf(Atom("a"), Atom("b")))).any())
assertTrue(Program.query(Structure(Atom("g"), listOf(Atom("c"), Atom("d")))).any())
assertFalse(Program.query(Structure(Atom("h"), listOf(Atom("e")))).any())
}
/**
* John and Jane are Jimmy's parents.
*/
@Test
fun parents_prolog_way() {
val father = Fact(Structure(Atom("father"), listOf(Atom("john"), Atom("jimmy"))))
val mother = Fact(Structure(Atom("mother"), listOf(Atom("jane"), Atom("jimmy"))))
val parent1 = Rule(
Structure(Atom("parent"), listOf(Variable("X"), Variable("Y"))),
/* :- */
Structure(Atom("father"), listOf(Variable("X"), Variable("Y")))
)
val parent2 = Rule(
Structure(Atom("parent"), listOf(Variable("X"), Variable("Y"))),
/* :- */
Structure(Atom("mother"), listOf(Variable("X"), Variable("Y")))
)
Program.load(listOf(father, mother, parent1, parent2))
assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy")))).any())
assertTrue(Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy")))).any())
}
/**
* John and Jane are Jimmy's parents.
*/
@Test
fun parents_disjunction_way() {
val father = Fact(Structure(Atom("father"), listOf(Atom("john"), Atom("jimmy"))))
val mother = Fact(Structure(Atom("mother"), listOf(Atom("jane"), Atom("jimmy"))))
val variable1 = Variable("X")
val variable2 = Variable("Y")
val parent = Rule(
Structure(Atom("parent"), listOf(variable1, variable2)), /* :- */
Disjunction(
Structure(Atom("father"), listOf(variable1, variable2)),
/* ; */
Structure(Atom("mother"), listOf(variable1, variable2))
)
)
Program.load(listOf(father, mother, parent))
val result1 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy")))).toList()
assertEquals(1, result1.size, "Expected 1 result")
assertTrue(result1[0].isSuccess, "Expected success")
assertTrue(result1[0].getOrNull()!!.isEmpty(), "Expected no substitutions")
val result2 = Program.query(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy")))).toList()
assertEquals(1, result2.size, "Expected 1 result")
assertTrue(result2[0].isSuccess, "Expected success")
assertTrue(result2[0].getOrNull()!!.isEmpty(), "Expected no substitutions")
val result3 = Program.query(Structure(Atom("parent"), listOf(Atom("john"), Atom("jane"))))
assertFalse(result3.any())
val result4 = Program.query(Structure(Atom("father"), listOf(Atom("john"), Atom("jane"))))
assertFalse(result4.any())
}
/**
* John is Jimmy's father.
* Jane is Jimmy's mother.
*/
@Test
fun father_or_mother() {
val male = Fact(Structure(Atom("male"), listOf(Atom("john"))))
val female = Fact(Structure(Atom("female"), listOf(Atom("jane"))))
val parent1 = Fact(Structure(Atom("parent"), listOf(Atom("john"), Atom("jimmy"))))
val parent2 = Fact(Structure(Atom("parent"), listOf(Atom("jane"), Atom("jimmy"))))
val variable1 = Variable("X")
val variable2 = Variable("Y")
val isFather = Rule(
Structure(Atom("isFather"), listOf(variable1, variable2)),
Conjunction(
Structure(Atom("parent"), listOf(variable1, variable2)),
Structure(Atom("male"), listOf(variable1))
)
)
val variable3 = Variable("X")
val variable4 = Variable("Y")
val isMother = Rule(
Structure(Atom("isMother"), listOf(variable3, variable4)),
Conjunction(
Structure(Atom("parent"), listOf(variable3, variable4)),
Structure(Atom("female"), listOf(variable3))
)
)
Program.load(listOf(male, female, parent1, parent2, isFather, isMother))
val result1 = Program.query(Structure(Atom("isFather"), listOf(Atom("john"), Atom("jimmy"))))
assertTrue(result1.any())
val result2 = Program.query(Structure(Atom("isMother"), listOf(Atom("jane"), Atom("jimmy"))))
assertTrue(result2.any())
val result3 = Program.query(Structure(Atom("isFather"), listOf(Atom("jane"), Atom("jimmy"))))
assertFalse(result3.any())
val result4 = Program.query(Structure(Atom("isMother"), listOf(Atom("john"), Atom("jimmy"))))
assertFalse(result4.any())
val result5 = Program.query(Structure(Atom("parent"), listOf(Atom("trudy"), Atom("jimmy"))))
assertFalse(result5.any())
}
@Test
fun requires_backtracking() {
val fact1 = Fact(Structure(Atom("a"), listOf(Atom("b"))))
val fact2 = Fact(Structure(Atom("a"), listOf(Atom("c"))))
val fact3 = Fact(Structure(Atom("a"), listOf(Atom("d"))))
Program.load(listOf(fact1, fact2, fact3))
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("b")))).any())
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("c")))).any())
assertTrue(Program.query(Structure(Atom("a"), listOf(Atom("d")))).any())
}
@Test
fun multiple_choices() {
val fact1 = Fact(Structure(Atom("a"), listOf(Atom("b"))))
val fact2 = Fact(Structure(Atom("a"), listOf(Atom("c"))))
val fact3 = Fact(Structure(Atom("a"), listOf(Atom("d"))))
Program.load(listOf(fact1, fact2, fact3))
val results = Query(Structure(Atom("a"), listOf(Variable("X")))).satisfy(emptyMap())
val expectedResults = listOf(
mapOf(Variable("X") to Atom("b")),
mapOf(Variable("X") to Atom("c")),
mapOf(Variable("X") to Atom("d"))
)
val actualResults = results.toList()
assertEquals(expectedResults.size, actualResults.size, "Number of results should match")
for (i in expectedResults.indices) {
assertEquals(expectedResults[i].size, actualResults[i].getOrNull()!!.size, "Substitution size should match")
assertTrue(expectedResults[i].all {
actualResults[i].getOrNull()!![it.key]?.let { it1 ->
equivalent(
it.value,
it1,
emptyMap()
)
} ?: false
}, "Substitution values should match")
}
}
@Test
fun `likes(alice, pizza)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val goal = Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza")))
Program.load(listOf(fact))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Test
fun `likes(Person, pizza)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val goal = Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
Program.load(listOf(fact))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(Atom("alice"), subs[Variable("Person")], "Expected Person to be alice")
}
@Test
fun `likes_food(alice)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val rule = Rule(
Structure(Atom("likes_food"), listOf(Variable("Person"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
)
val goal = Structure(Atom("likes_food"), listOf(Atom("alice")))
Program.load(listOf(fact, rule))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Test
fun `likes_food(Person)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val rule = Rule(
Structure(Atom("likes_food"), listOf(Variable("Person"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
)
val goal = Structure(Atom("likes"), listOf(Variable("X"), Atom("pizza")))
Program.load(listOf(fact, rule))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(Atom("alice"), subs[Variable("X")], "Expected Person to be alice")
}
@Test
fun `requires querying exact`() {
val fact1 = Fact(Atom("a"))
val fact2 = Fact(Atom("b"))
val rule1 = Rule(
Atom("c"),
Conjunction(
Atom("a"),
Atom("b")
)
)
Program.load(listOf(fact1, fact2, rule1))
val result = Program.query(Atom("c")).toList()
assertEquals(1, result.size, "Expected 1 result")
}
@Test
fun `requires querying with variable`() {
val fact1 = Fact(Atom("a"))
val fact2 = Fact(Atom("b"))
val rule1 = Rule(
Structure(Atom("has fact"), listOf(Variable("X"))),
Variable("X")
)
Program.load(listOf(fact1, fact2, rule1))
val result = Program.query(Structure(Atom("has fact"), listOf(Atom("a")))).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Nested
class `requires querying with filled variable` {
@BeforeEach
fun setup() {
val fact1 = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val fact2 = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pasta"))))
val fact3 = Fact(Structure(Atom("likes"), listOf(Atom("bob"), Atom("pasta"))))
val rule1 = Rule(
Structure(Atom("likes_italian_food"), listOf(Variable("Person"))),
Disjunction(
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pasta")))
)
)
Program.reset()
Program.load(listOf(fact1, fact2, fact3, rule1))
}
@Test
fun `likes_italian_food(alice)`() {
val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Atom("alice")))).toList()
assertEquals(2, result.size, "Expected 2 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs1 = result[0].getOrNull()!!
assertEquals(0, subs1.size, "Expected no substitutions")
assertTrue(result[1].isSuccess, "Expected success")
val subs2 = result[1].getOrNull()!!
assertEquals(0, subs2.size, "Expected no substitutions")
}
@Test
fun `likes_italian_food(X)`() {
val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Variable("X")))).toList()
assertEquals(3, result.size, "Expected 3 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs3 = result[0].getOrNull()!!
assertEquals(1, subs3.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("alice"), subs3[Variable("X")], "Expected alice")
assertTrue(result[1].isSuccess, "Expected success")
val subs4 = result[1].getOrNull()!!
assertEquals(1, subs4.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("alice"), subs4[Variable("X")], "Expected alice")
assertTrue(result[2].isSuccess, "Expected success")
val subs5 = result[2].getOrNull()!!
assertEquals(1, subs5.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("bob"), subs5[Variable("X")], "Expected bob")
}
@Test
fun `likes_italian_food(Person)`() {
val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Variable("Person")))).toList()
assertEquals(3, result.size, "Expected 3 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs3 = result[0].getOrNull()!!
assertEquals(1, subs3.size, "Expected 1 substitution")
assertEquals(Atom("alice"), subs3[Variable("Person")], "Expected alice")
assertTrue(result[1].isSuccess, "Expected success")
val subs4 = result[1].getOrNull()!!
assertEquals(1, subs4.size, "Expected 1 substitution")
assertEquals(Atom("alice"), subs4[Variable("Person")], "Expected alice")
assertTrue(result[2].isSuccess, "Expected success")
val subs5 = result[2].getOrNull()!!
assertEquals(1, subs5.size, "Expected 1 substitution")
assertEquals(Atom("bob"), subs5[Variable("Person")], "Expected bob")
}
}
@Test
fun `leq Peano`() {
val fact = Fact(Structure(Atom("leq"), listOf(Integer(0), AnonymousVariable.create())))
val rule = Rule(
Structure(
Atom("leq"),
listOf(Structure(Atom("s"), listOf(Variable("X"))), Structure(Atom("s"), listOf(Variable("Y"))))
),
Structure(Atom("leq"), listOf(Variable("X"), Variable("Y"))),
)
Program.db.load(listOf(fact, rule))
val result1 = Program.query(Structure(Atom("leq"), listOf(Variable("X"), Integer(0)))).toList()
assertEquals(1, result1.size, "Expected 1 result")
assertTrue(result1[0].isSuccess, "Expected success")
val subs = result1[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(Integer(0), subs[Variable("X")], "Expected X to be 0")
val result2 =
Program.query(Structure(Atom("leq"), listOf(Variable("X"), Structure(Atom("s"), listOf(Integer(0))))))
.toList()
assertEquals(2, result2.size, "Expected 2 results")
assertTrue(result2[0].isSuccess, "Expected success")
val subs2a = result2[0].getOrNull()!!
assertEquals(1, subs2a.size, "Expected 1 substitution")
assertEquals(Integer(0), subs2a[Variable("X")], "Expected X to be 0")
assertTrue(result2[1].isSuccess, "Expected success")
val subs2b = result2[1].getOrNull()!!
assertEquals(1, subs2b.size, "Expected 1 substitution")
assertEquals(Structure(Atom("s"), listOf(Integer(0))), subs2b[Variable("X")], "Expected X to be s(0)")
val result3 = Program.query(
Structure(
Atom("leq"),
listOf(Variable("X"), Structure(Atom("s"), listOf(Structure(Atom("s"), listOf(Integer(0))))))
)
).toList()
assertEquals(3, result3.size, "Expected 3 results")
assertTrue(result3[0].isSuccess, "Expected success")
val subs3a = result3[0].getOrNull()!!
assertEquals(1, subs3a.size, "Expected 1 substitution")
assertEquals(Integer(0), subs3a[Variable("X")], "Expected X to be 0")
assertTrue(result3[1].isSuccess, "Expected success")
val subs3b = result3[1].getOrNull()!!
assertEquals(1, subs3b.size, "Expected 1 substitution")
assertEquals(Structure(Atom("s"), listOf(Integer(0))), subs3b[Variable("X")], "Expected X to be s(0)")
assertTrue(result3[2].isSuccess, "Expected success")
val subs3c = result3[2].getOrNull()!!
assertEquals(1, subs3c.size, "Expected 1 substitution")
assertEquals(Structure(Atom("s"), listOf(Structure(Atom("s"), listOf(Integer(0))))), subs3c[Variable("X")], "Expected X to be s(s(0))")
}
@Test
fun `foo(emptyList) = foo(X)`() {
val list = Empty
val left = Structure(Atom("foo"), listOf(list))
val right = Structure(Atom("foo"), listOf(Variable("X")))
val unific = Unify(left, right)
val result = unific.satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(list, subs[Variable("X")], "Expected X to be list(1, 2)")
}
}