mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-30 04:40:16 +00:00
153 lines
5.9 KiB
Python
153 lines
5.9 KiB
Python
import pytest
|
|
from semantica.reasoning import (
|
|
DeductiveReasoner,
|
|
AbductiveReasoner,
|
|
Premise,
|
|
Observation,
|
|
HypothesisRanking,
|
|
Argument
|
|
)
|
|
|
|
# --- DeductiveReasoner Tests ---
|
|
|
|
@pytest.fixture
|
|
def deductive_reasoner():
|
|
return DeductiveReasoner()
|
|
|
|
def test_deductive_apply_logic(deductive_reasoner):
|
|
# Rule: IF Human(?x) THEN Mortal(?x)
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF Human(?x) THEN Mortal(?x)")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
|
|
premises = [Premise("p1", "Human(Socrates)")]
|
|
|
|
# We need to ensure the reasoner can handle variable matching or at least exact matching
|
|
# Based on my reading of DeductiveReasoner, it uses _can_apply_rule which checks strict containment
|
|
# if variables aren't handled.
|
|
# Let's check if DeductiveReasoner uses InferenceEngine's unification or its own simple logic.
|
|
# It uses self._can_apply_rule which does: condition in premise_statements.
|
|
# This implies EXACT string match unless updated.
|
|
|
|
# So for now, let's test exact match to verify baseline behavior
|
|
deductive_reasoner.rule_manager.clear_rules()
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF Human(Socrates) THEN Mortal(Socrates)")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
|
|
conclusions = deductive_reasoner.apply_logic(premises)
|
|
|
|
assert len(conclusions) >= 1
|
|
assert conclusions[0].statement == "Mortal(Socrates)"
|
|
|
|
def test_deductive_apply_logic_with_variables(deductive_reasoner):
|
|
# This test checks if DeductiveReasoner supports variables like InferenceEngine
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF Human(?x) THEN Mortal(?x)")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
|
|
premises = [Premise("p1", "Human(Plato)")]
|
|
|
|
# If DeductiveReasoner supports variables, it should deduce Mortal(Plato)
|
|
conclusions = deductive_reasoner.apply_logic(premises)
|
|
|
|
assert len(conclusions) > 0
|
|
assert conclusions[0].statement == "Mortal(Plato)"
|
|
|
|
def test_deductive_prove_theorem_with_variables(deductive_reasoner):
|
|
# Rule: IF Parent(?a, ?b) THEN Ancestor(?a, ?b)
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF Parent(?a, ?b) THEN Ancestor(?a, ?b)")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
deductive_reasoner.add_fact("Parent(Zeus, Ares)")
|
|
|
|
# Prove Ancestor(Zeus, Ares)
|
|
proof = deductive_reasoner.prove_theorem("Ancestor(Zeus, Ares)")
|
|
|
|
assert proof is not None
|
|
assert proof.valid is True
|
|
assert proof.steps[-1].statement == "Ancestor(Zeus, Ares)"
|
|
|
|
def test_deductive_prove_theorem(deductive_reasoner):
|
|
# Rule: IF P THEN Q
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF P THEN Q")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
deductive_reasoner.add_fact("P")
|
|
|
|
proof = deductive_reasoner.prove_theorem("Q")
|
|
|
|
assert proof is not None
|
|
assert proof.valid is True
|
|
assert len(proof.steps) > 0
|
|
assert proof.steps[-1].statement == "Q"
|
|
|
|
def test_deductive_validate_argument(deductive_reasoner):
|
|
rule = deductive_reasoner.rule_manager.define_rule("IF Rain THEN Wet")
|
|
deductive_reasoner.rule_manager.add_rule(rule)
|
|
deductive_reasoner.add_fact("Rain")
|
|
|
|
premises = [Premise("p1", "Rain")]
|
|
# Note: Conclusion object needed for argument? Argument class has 'conclusion' field which is Conclusion type.
|
|
# But usually we validate if premises lead to a conclusion statement.
|
|
# The validate_argument method checks if argument.conclusion.statement follows.
|
|
|
|
from semantica.reasoning.deductive_reasoner import Conclusion
|
|
conc = Conclusion("c1", "Wet")
|
|
|
|
arg = Argument("arg1", premises=premises, conclusion=conc)
|
|
|
|
result = deductive_reasoner.validate_argument(arg)
|
|
|
|
assert result["valid"] is True
|
|
|
|
# --- AbductiveReasoner Tests ---
|
|
|
|
@pytest.fixture
|
|
def abductive_reasoner():
|
|
return AbductiveReasoner()
|
|
|
|
def test_abductive_generate_hypotheses(abductive_reasoner):
|
|
# Setup a rule that could explain an observation
|
|
# Rule: IF Rain THEN WetGrass
|
|
rule = abductive_reasoner.rule_manager.define_rule("IF Rain THEN WetGrass")
|
|
abductive_reasoner.rule_manager.add_rule(rule)
|
|
|
|
obs = Observation("o1", "WetGrass")
|
|
|
|
# Current implementation of _rule_explains_observation returns True for everything
|
|
# So it should find the rule as a hypothesis
|
|
hypotheses = abductive_reasoner.generate_hypotheses([obs])
|
|
|
|
assert len(hypotheses) > 0
|
|
assert "Rain" in hypotheses[0].premises # The premise of the rule is the hypothesis (Rain caused WetGrass)
|
|
|
|
def test_abductive_filtering(abductive_reasoner):
|
|
# Rule 1: IF Rain THEN WetGrass
|
|
r1 = abductive_reasoner.rule_manager.define_rule("IF Rain THEN WetGrass")
|
|
abductive_reasoner.rule_manager.add_rule(r1)
|
|
|
|
# Rule 2: IF Fire THEN Smoke
|
|
r2 = abductive_reasoner.rule_manager.define_rule("IF Fire THEN Smoke")
|
|
abductive_reasoner.rule_manager.add_rule(r2)
|
|
|
|
obs = Observation("o1", "WetGrass")
|
|
|
|
hypotheses = abductive_reasoner.generate_hypotheses([obs])
|
|
|
|
# Should only find hypothesis related to WetGrass (Rain)
|
|
# Should NOT find hypothesis related to Smoke (Fire)
|
|
|
|
relevant_hypotheses = [h for h in hypotheses if "Rain" in h.premises or "IF Rain" in h.explanation]
|
|
irrelevant_hypotheses = [h for h in hypotheses if "Fire" in h.premises or "IF Fire" in h.explanation]
|
|
|
|
assert len(relevant_hypotheses) > 0
|
|
assert len(irrelevant_hypotheses) == 0
|
|
|
|
def test_abductive_find_explanations(abductive_reasoner):
|
|
rule = abductive_reasoner.rule_manager.define_rule("IF Fire THEN Smoke")
|
|
abductive_reasoner.rule_manager.add_rule(rule)
|
|
obs = Observation("o1", "Smoke")
|
|
|
|
explanations = abductive_reasoner.find_explanations([obs])
|
|
|
|
assert len(explanations) == 1
|
|
assert explanations[0].best_hypothesis is not None
|
|
assert "Fire" in explanations[0].best_hypothesis.premises
|
|
|