Files
LeonSGPandLeonSGP43 b13cc1cca2 docs(cookbook): add Reasoning module notebook (#990)
* docs(cookbook): add Reasoning module notebook

Add cookbook/introduction/23_Reasoning.ipynb covering the reasoning
module with verified, executable examples:

- Reasoner facade: add_fact / add_rule / forward_chain
- one-shot infer_facts(facts, rules)
- backward_chain goal proving with premises
- re-run-safe rule deduplication (#732)
- DatalogReasoner: semi-naive fixpoint evaluation + variable queries
- ExplanationGenerator: Explanation / ReasoningPath records

The reasoning module currently has no cookbook coverage even though it
ships reasoning_usage.md in the package. All API calls and outputs were
verified against semantica/reasoning/reasoner.py,
datalog_reasoner.py, and explanation_generator.py.

Signed-off-by: LeonSGP43 <LeonSGP43@users.noreply.github.com>

* docs(cookbook): correct infer_facts semantics description (appends to instance state, no reset)

Signed-off-by: LeonSGP43 <leonsgp43@users.noreply.github.com>

* docs(cookbook): execute reasoning notebook in Jupyter (real kernel run, stream outputs, execution counts)

Signed-off-by: LeonSGP43 <cine.dreamer.one@gmail.com>

---------

Signed-off-by: LeonSGP43 <LeonSGP43@users.noreply.github.com>
Signed-off-by: LeonSGP43 <leonsgp43@users.noreply.github.com>
Signed-off-by: LeonSGP43 <cine.dreamer.one@gmail.com>
Co-authored-by: LeonSGP43 <LeonSGP43@users.noreply.github.com>
2026-08-27 12:55:02 +05:00

12 KiB

Open In Colab

Reasoning Module — Practical Guide

Semantica's reasoning module derives new knowledge from existing facts and knowledge graphs. It ships several strategies behind one facade:

  • Reasoner — unified facade with forward chaining, backward chaining, and one-shot infer_facts
  • DatalogReasoner — semi-naive Datalog fixpoint evaluation with variable queries
  • ExplanationGenerator — human-readable explanations and reasoning paths for inferred conclusions
  • Plus lower-level engines: ReteEngine, SPARQLReasoner, GraphReasoner, temporal reasoning

This notebook walks through the facade, the Datalog engine, and explanations. All APIs are verified against semantica/reasoning/.

In [1]:
!pip install -q semantica

1) Forward chaining with the Reasoner facade

Facts are simple Predicate(args) strings. Rules use IF <conditions> THEN <conclusion> with ?x-style variables. forward_chain() derives everything possible and returns a list of InferenceResult objects.

In [2]:
from semantica.reasoning import Reasoner

reasoner = Reasoner()

reasoner.add_fact("Person(John)")
reasoner.add_fact("Person(Jane)")
reasoner.add_rule("IF Person(?x) THEN Human(?x)")

results = reasoner.forward_chain()
print(f"Inferred {len(results)} new facts")
for res in results:
    print(f"  {res.conclusion}  (rule: {res.rule_used.name}, confidence: {res.confidence})")

🧠 Semantica - 📊 Current Progress

StatusActionModuleSubmoduleProgressETARateTimeExtracted
Semantica is reasoning🤔 reasoningReasoner100.0%--0.00s-
Semantica is reasoning🤔 reasoningDatalogReasoner100.0%--0.00s-
Semantica is reasoning🤔 reasoningExplanationGenerator100.0%--0.00s-
🔄 Semantica is reasoning: Performing forward chaining 🤔 reasoning Reasoner |░░░░░░░░░░░░░░░| 0.0% ETA: - Rate: - Time: 0.00s Extracted: -
Inferred 2 new facts
  Human(Jane)  (rule: Rule 1, confidence: 1.0)
  Human(John)  (rule: Rule 1, confidence: 1.0)

2) One-shot inference with infer_facts

infer_facts(facts, rules) adds the given facts and rules to this Reasoner instance, runs forward chaining to fixpoint, and returns the derived facts as strings. It does not reset the instance's existing state — create a fresh Reasoner() first if you need isolation between runs.

In [3]:
from semantica.reasoning import Reasoner

derived = Reasoner().infer_facts(
    facts=["WorksFor(John, Acme)", "WorksFor(Jane, Acme)"],
    rules=["IF WorksFor(?x, ?y) THEN Employee(?x, ?y)"],
)
derived
Out [3]:
['Employee(Jane, Acme)', 'Employee(John, Acme)']

3) Backward chaining: proving a goal

backward_chain(goal) works backwards from a conclusion through the rules. It returns the InferenceResult that proves the goal, or None.

In [4]:
from semantica.reasoning import Reasoner

reasoner = Reasoner()
reasoner.add_fact("Person(John)")
reasoner.add_rule("IF Person(?x) THEN Human(?x)")

proof = reasoner.backward_chain("Human(John)")
print(proof.conclusion if proof else "not provable")
print("premises:", proof.premises if proof else None)
Human(John)
premises: ['Person(John)']

4) Re-run safety

add_rule deduplicates rules with identical conditions and conclusion, so re-executing a setup cell (the common Jupyter re-run) does not duplicate rules — see issue #732.

In [5]:
from semantica.reasoning import Reasoner

reasoner = Reasoner()
reasoner.add_fact("Person(John)")

# Simulate a Jupyter cell re-run: add the same rule twice
r1 = reasoner.add_rule("IF Person(?x) THEN Human(?x)")
r2 = reasoner.add_rule("IF Person(?x) THEN Human(?x)")

len(reasoner.rules)
Out [5]:
Skipping duplicate rule (same conditions/conclusion as 'rule_1'): IF Person(?x) THEN Human(?x)
1

5) Datalog reasoning

DatalogReasoner uses classic Datalog syntax (head :- body.) and semi-naive fixpoint evaluation. Queries return variable bindings as a list of dicts — use uppercase variables to ask which facts hold.

In [6]:
from semantica.reasoning import DatalogReasoner

datalog = DatalogReasoner()
datalog.add_fact("parent(tom, mary)")
datalog.add_fact("parent(mary, ann)")
datalog.add_rule("grandparent(X, Z) :- parent(X, Y), parent(Y, Z)")

datalog.derive_all()
datalog.query("grandparent(X, Z)")
Out [6]:
[{'X': 'tom', 'Z': 'ann'}]

6) Explanations for inferred conclusions

ExplanationGenerator turns InferenceResult objects into structured Explanation and ReasoningPath records, so agents can show why they believe a derived fact.

In [7]:
from semantica.reasoning import Reasoner, ExplanationGenerator

reasoner = Reasoner()
reasoner.add_fact("Person(John)")
reasoner.add_rule("IF Person(?x) THEN Human(?x)")
results = reasoner.forward_chain()

gen = ExplanationGenerator()
explanation = gen.generate_explanation(results[0])
path = gen.show_reasoning_path(results[0])

type(explanation).__name__, type(path).__name__
Out [7]:
('Explanation', 'ReasoningPath')

Summary

Task API
Derive all new facts Reasoner.forward_chain()
One-shot inference Reasoner.infer_facts(facts, rules)
Prove a goal Reasoner.backward_chain(goal)
Datalog fixpoint DatalogReasoner.derive_all() + query("p(X, Y)")
Explain a conclusion ExplanationGenerator.generate_explanation(result)

See also semantica/reasoning/reasoning_usage.md and the module docstrings for ReteEngine, SPARQLReasoner, and temporal reasoning.