Files
semantica/docs/reference/reasoning.md
T
KaifAhmad1 5eefadaa7f docs: apply full Mintlify component overhaul to all 27 reference pages and concepts.md
Replace plain markdown in every docs/reference/ file and docs/concepts.md with
rich Mintlify JSX components — CardGroup, Steps, Tabs, AccordionGroup, Tip,
Warning, Note, and CodeGroup — for a consistent, navigable, production-grade
developer experience.
2026-05-23 23:02:03 +05:30

16 KiB

title, description, icon
title description icon
Reasoning Module Forward chaining, Rete, deductive, abductive, SPARQL, Datalog, and temporal reasoning with explainable inference paths. microchip

semantica.reasoning derives new knowledge from existing facts using logical rules. Every engine produces explainable inference paths — traceable chains of rules and facts, not black-box conclusions.

Why Reasoning?

Knowledge graphs encode what you know explicitly. Reasoning lets you derive what must logically follow — without manually asserting every implication:

  • If A located_in B and B located_in C, then A located_in C — without storing that triple
  • If Alice parent_of Bob and Bob parent_of Charlie, then Alice ancestor_of Charlie
  • If a drug is contraindicated for a condition class, it's also contraindicated for all subclasses — inferred from the ontology hierarchy
  • If an employee's CEO tenure ended in 2020, they cannot have signed contracts as CEO in 2021 — caught by temporal reasoning

Reasoning turns sparse explicit knowledge into a dense, coherent, contradiction-free knowledge base.

What You Get

Main facade — IF/THEN forward-chaining with variable substitution and rule templates. Inference over full knowledge graph structure: transitivity, symmetry, inverses. High-performance pattern matching via the Rete algorithm for large rule sets. Query expansion and property chain inference over RDF graphs. Recursive Horn clause rules with guaranteed fixpoint termination (v0.4.0). All 13 Allen interval algebra relations for time-aware inference.

<img src="/assets/img/diagrams/reasoning-chain.svg" alt="Forward chaining inference: known facts + IF/THEN rules produce derived facts with a full traceable explanation path" style={{ width: '100%', borderRadius: '12px', margin: '0 0 24px' }} />

Choosing a Reasoning Engine

Engine When to Use
Reasoner Simple IF/THEN rules, transitivity/symmetry templates, one-shot inference
GraphReasoner Rules that operate on graph structure (paths, neighborhoods, multi-hop)
ReteEngine Large rule sets (100+), rules fire repeatedly, performance is critical
SPARQLReasoner Already using RDF/Turtle, need property chains, SPARQL ecosystem tools
DatalogReasoner Recursive rules (ancestry, reachability), guaranteed termination required
TemporalReasoningEngine Time-aware facts, interval relationships, historical validity

Engines

The unified entry point for rule-based forward-chaining inference. Start here for most use cases.
```python
from semantica.reasoning import Reasoner, Rule, Fact, RuleType

reasoner = Reasoner()

# Add base facts
reasoner.add_fact(Fact(subject="John", predicate="is_a", obj="Manager"))
reasoner.add_fact(Fact(subject="John", predicate="is_a", obj="Employee"))

# Add an IF/THEN rule
reasoner.add_rule(Rule(
    rule_type=RuleType.FORWARD_CHAIN,
    conditions=[
        {"subject": "?x", "predicate": "is_a", "object": "Manager"}
    ],
    conclusion={"subject": "?x", "predicate": "has_authority", "object": "true"}
))

# Run inference — always call explicitly after adding facts/rules
result = reasoner.infer()
for inference in result.derived_facts:
    print(f"{inference.subject} {inference.predicate} {inference.obj}")
    print(f"  Derived via: {inference.explanation}")
```

### Built-In Rule Templates

No manual rule authoring required for the three most common patterns:

```python
engine = Reasoner()

# Transitive closure: A→B, B→C ⟹ A→C
engine.apply_transitivity("located_in")

# Symmetry: A knows B ⟹ B knows A
engine.apply_symmetry("knows")

# Inverse: A parent_of B ⟹ B child_of A
engine.apply_inverse("parent_of", "child_of")

result = engine.infer()
```

| Template | Parameters | Description |
| -------- | ---------- | ----------- |
| `apply_transitivity(predicate)` | `predicate: str` | Adds A→C rule for all A→B, B→C chains |
| `apply_symmetry(predicate)` | `predicate: str` | Adds B→A rule for every A→B fact |
| `apply_inverse(predicate, inverse)` | `predicate, inverse: str` | Adds inverse direction for every fact |

<Warning>
  Always call `reasoner.infer()` after adding facts and rules. Adding them updates internal state but does **not** trigger inference automatically.
</Warning>
Inference over the full knowledge graph structure — rules that operate on graph paths, neighborhoods, and multi-hop connections.
```python
from semantica.reasoning import GraphReasoner

graph_reasoner = GraphReasoner(kg)

# Define a transitive ancestor rule
graph_reasoner.add_rule({
    "if": [
        {"subject": "?a", "predicate": "parent_of", "object": "?b"},
        {"subject": "?b", "predicate": "parent_of", "object": "?c"}
    ],
    "then": {"subject": "?a", "predicate": "ancestor_of", "object": "?c"}
})

inferences = graph_reasoner.infer(kg)
for inf in inferences:
    print(f"{inf['subject']} {inf['predicate']} {inf['object']}")
```
High-performance pattern matching using the Rete algorithm — far faster than naive forward chaining for large rule sets because it caches partial matches across iterations.
```python
from semantica.reasoning import ReteEngine

engine = ReteEngine()
engine.load_rules("rules/domain_rules.json")
results = engine.run(kg)

# Inspect the Rete network
root        = engine.get_root()
alpha_nodes = engine.get_alpha_nodes()   # single-condition filters
beta_nodes  = engine.get_beta_nodes()    # join nodes
```

Rule file format (JSON):

```json
{
  "rules": [
    {
      "name": "manager_authority",
      "conditions": [
        { "subject": "?x", "predicate": "role",  "object": "Manager" },
        { "subject": "?x", "predicate": "dept",  "object": "?dept"   }
      ],
      "action": {
        "subject": "?x",
        "predicate": "has_authority_over",
        "object": "?dept"
      },
      "priority": 10
    }
  ]
}
```

| Field | Type | Description |
| ----- | ---- | ----------- |
| `name` | `str` | Unique rule identifier — appears in `ExplanationGenerator` output |
| `conditions` | `List[Dict]` | Pattern to match — use `?variable` for wildcards |
| `action` | `Dict` | Fact to derive when all conditions match |
| `priority` | `int` | Higher priority rules fire first |

<Tip>
  Use `ReteEngine` when you have more than ~20 rules or when rules can fire repeatedly. `Reasoner` re-evaluates all rules from scratch each cycle; `ReteEngine` caches partial matches and is orders of magnitude faster.
</Tip>
Query-based inference over RDF graphs with property chain support. Use this when you're already in the RDF/Turtle ecosystem.
```python
from semantica.reasoning import SPARQLReasoner

reasoner = SPARQLReasoner(graph=rdf_graph)

result = reasoner.query("""
    PREFIX ex: <http://example.org/>
    SELECT ?person ?company WHERE {
        ?person ex:founded ?company .
        ?company ex:located_in ex:SiliconValley .
    }
""")

for row in result.bindings:
    print(row["person"], row["company"])

# Property chain inference: A knows B, B colleague_of C ⟹ A knows C
reasoner.add_property_chain("knows", ["knows", "colleague_of"])
inferences = reasoner.infer_property_chains()
```
Pure-Python bottom-up semi-naive fixpoint evaluation for recursive Horn clause rules. Termination is **guaranteed** — the engine detects fixpoint convergence and stops.
<Note>
  Added in **v0.4.0**. Use `DatalogReasoner` whenever your rules can create cycles — it's the only engine with a termination guarantee.
</Note>

```python
from semantica.reasoning import DatalogReasoner, DatalogFact, DatalogRule

datalog = DatalogReasoner()

# Base facts
datalog.add_fact(DatalogFact("parent", ("alice", "bob")))
datalog.add_fact(DatalogFact("parent", ("bob",   "charlie")))

# Recursive rules (Horn clauses)
datalog.add_rule(DatalogRule("ancestor(?X, ?Y) :- parent(?X, ?Y)."))
datalog.add_rule(DatalogRule("ancestor(?X, ?Z) :- parent(?X, ?Y), ancestor(?Y, ?Z)."))

# Evaluate to fixpoint
datalog.evaluate()

# Query
results = datalog.query("ancestor(alice, ?Z)")
# → [{"Z": "bob"}, {"Z": "charlie"}]
```

<Warning>
  `Reasoner` has **no cycle detection** — rules that create cycles (A derives B, B derives C, C re-derives A) will loop infinitely. Use `DatalogReasoner` whenever recursive rules are involved.
</Warning>
Reason about time intervals using all 13 Allen interval algebra relations.
```python
from semantica.reasoning import TemporalReasoningEngine, TemporalInterval, IntervalRelation

engine = TemporalReasoningEngine()

ceo_tenure   = TemporalInterval(start="1997-09-16", end="2011-08-24")
board_member = TemporalInterval(start="2000-01-01", end="2012-06-01")

relation = engine.get_relation(ceo_tenure, board_member)
# → IntervalRelation.DURING  (ceo_tenure is fully inside board_member)
```

All 13 Allen interval algebra relations:

| Relation | Meaning |
| -------- | ------- |
| `BEFORE` | A ends before B starts |
| `MEETS` | A ends exactly when B starts |
| `OVERLAPS` | A starts before B, ends inside B |
| `DURING` | A is fully inside B |
| `STARTS` | A and B start together, A ends first |
| `FINISHES` | A and B end together, A starts later |
| `EQUALS` | Identical intervals |
| + 6 inverses | `AFTER`, `MET_BY`, `OVERLAPPED_BY`, `CONTAINS`, `STARTED_BY`, `FINISHED_BY` |

ExplanationGenerator

Generate structured step-by-step explanations for any derived conclusion:

from semantica.reasoning import ExplanationGenerator

generator = ExplanationGenerator(reasoner)

explanation = generator.explain(
    conclusion={"subject": "John", "predicate": "has_authority", "object": "true"}
)

print(explanation.conclusion)
print(f"Confidence: {explanation.confidence:.2f}")
print(explanation.justification.summary)

for step in explanation.reasoning_path.steps:
    indent = "  " * step.depth
    print(f"{indent}Step {step.depth}: {step.fact}")
    print(f"{indent}  via rule: '{step.rule_name}'")
    print(f"{indent}  premises: {step.premises}")
Name every rule with a descriptive string. `ExplanationGenerator` includes the rule name in each derivation step — unnamed rules produce useless explanations like "rule_0 fired." Use names like `"manager_authority"` or `"transitive_location"`.
@dataclass
class Explanation:
    conclusion:     Dict[str, str]   # the fact being explained
    confidence:     float            # aggregated rule confidence
    reasoning_path: ReasoningPath    # full derivation trace
    justification:  Justification    # plain-language summary
@dataclass
class ReasoningPath:
    steps: List[ReasoningStep]       # ordered derivation steps

@dataclass
class ReasoningStep:
    depth:       int                 # 0 = base fact, n = nth inference
    fact:        Dict[str, str]      # the fact derived at this step
    rule_name:   str                 # name of the rule that fired
    premises:    List[Dict]          # facts that triggered this rule
    confidence:  float               # confidence at this step
@dataclass
class Justification:
    summary:   str         # one-sentence natural language explanation
    evidence:  List[str]   # list of supporting source facts

Combining Multiple Reasoning Engines

Different engines cover different expressivity levels — compose them for richer inference:

```python from semantica.reasoning import Reasoner
engine = Reasoner()
engine.apply_transitivity("located_in")
engine.apply_symmetry("colleague_of")
structural_result = engine.infer()
```
```python from semantica.reasoning import DatalogReasoner, DatalogFact, DatalogRule
datalog = DatalogReasoner()
for fact in structural_result.derived_facts:
    datalog.add_fact(DatalogFact(fact.predicate, (fact.subject, fact.obj)))

datalog.add_rule(DatalogRule("reachable(?X, ?Z) :- located_in(?X, ?Y), reachable(?Y, ?Z)."))
datalog.evaluate()
```
```python from semantica.reasoning import TemporalReasoningEngine from datetime import datetime
temporal = TemporalReasoningEngine()
active_facts = [
    f for f in datalog.query("reachable(?X, ?Z)")
    if temporal.is_active(f, at=datetime(2024, 1, 1))
]
```
```python from semantica.reasoning import ExplanationGenerator
generator = ExplanationGenerator(engine)
explanation = generator.explain(
    {"subject": "london_office", "predicate": "located_in", "object": "UK"}
)
print(explanation.summary)
```

Tips and Common Pitfalls

**Always call `reasoner.infer()` after adding facts and rules.** Adding facts and rules updates internal state but doesn't trigger inference automatically. Inference is a separate, explicit step. **Use `ReteEngine` for large rule sets.** If you have more than ~20 rules and rules can fire repeatedly, `Reasoner` re-evaluates all rules from scratch on each cycle. `ReteEngine` caches partial matches and is orders of magnitude faster for complex rule sets. **`DatalogReasoner` guarantees termination; `Reasoner` does not.** If your rules can create cycles (A derives B, B derives C, C re-derives A), `DatalogReasoner`'s semi-naive fixpoint evaluation will stop when no new facts are added. `Reasoner` has no cycle detection and may loop infinitely. **Name every rule for readable explanations.** `ExplanationGenerator.explain()` includes the rule name in each derivation step. Unnamed or generic rule names produce useless explanations like "rule_0 fired." Use descriptive names: `"manager_authority"`, `"transitive_location"`. **Use rule `priority` to control inference order.** When multiple rules could fire on the same facts, higher-priority rules fire first. This matters when a higher-priority rule produces a fact that gates a lower-priority rule's conditions. **Combine engines for maximum expressivity.** Forward-chain structural rules with `Reasoner`, then pass derived facts to `DatalogReasoner` for recursive closure, then filter by time with `TemporalReasoningEngine`. Each engine covers a different expressivity class — they compose cleanly. The knowledge graph being reasoned over. Ontology axioms and SHACL constraints for logical reasoning. RDF backend for SPARQL-based reasoning. Reasoning integrated into agent decision intelligence.