Chapter 10 — Putting an LTMS to Work (LTRE)
Turning the propositional LTMS into a usable reasoning engine: facts, rules, assumptions, proofs, and search. companion index
The big idea
The LTMS of chapter 9 speaks only propositional clauses over abstract
nodes. To use it you need a layer that lets you talk about facts — ground
statements like (human socrates) — and rules that fire when patterns match,
while the LTMS quietly does all the propositional bookkeeping underneath.
That layer is the LTRE (Logic-based Tiny Rule Engine),
src/ltms/ltre.py. The division of labour is clean:
- the LTRE does universal instantiation — matching rule patterns against the fact database and firing rule bodies;
- the LTMS does all propositional reasoning — clause normalization, BCP, belief revision, contradictions.
On top of that sit three classic facilities — indirect proof, closed-world assumptions, and dependency-directed search — each a small program that exploits the LTMS’s contradiction-handler stack.
Concepts, step by step
One datum, one node
Every ground proposition is interned as a Datum that owns exactly one LTMS
TmsNode. Belief is read straight off the node’s label. Crucially, negation is
a sign, not an object: (not P) and P share one node; a query for (not P)
just inverts P’s label. The stored form is always the unsigned proposition.
assert vs. assume (the load-bearing asymmetry)
assert!installs a formula as permanent clauses by direct translation: simple propositions become their nodes, connectives stay, and the result is normalized to CNF. No node is created for the compound — it is just constraints.assume!must be retractable, so it is different. For a simple proposition it makes the node an assumption directly. For a compound formula it creates a guard nodeN_Fand installsN_F → formula; disablingN_Fswitches the whole formula off. This asymmetry is deliberate and important.
Rules and the enqueue bridge
A rule has a trigger pattern, a condition, and a body (a Python callable receiving the match bindings). The condition is one of:
INTERN— fire as soon as a matching datum exists, regardless of belief;TRUE/FALSE— fire only when the matched proposition is believed true/false.
A belief-conditioned rule that matches before the label is decided is parked
on the node. The connection back to the LTMS is the enqueue procedure: when
BCP labels a node, it pushes that node’s parked rules onto the LTRE’s queue, which
run_rules later drains. So rules wake up exactly when belief changes — the
single wire that couples the two layers.
Conjunctive (“A and B”) rules are built by nesting: matching the first pattern installs a rule for the second (capturing the first’s bindings), and so on — the same technique the TRE uses, now belief-aware.
Three facilities built on the handler stack
All three work by pushing a contradiction handler, making some assumptions, running rules, and reacting to whatever conflict arises:
- Indirect proof (
indirect.py): to prove a fact, assume its negation and run. If that assumption participates in a contradiction, retract it and install a nogood that forces the fact. (Proof by contradiction, mechanized.) - Closed-world assumptions (
cwa.py): assume false every currently-undecided instance of a predicate (negation as failure), as retractable assumptions under one informant, so the closure can be withdrawn cleanly and any later positive evidence surfaces as a contradiction. - Dependency-directed search (
dds.py): assign mutually-exclusive choice sets depth-first; on a conflict, identify the responsible choices viaassumptions_of_clauseand learn a nogood that forces the dead choice false — so the search never re-explores it.
The examples, explained
- Forward chaining with belief. A rule
(human ?x) →[TRUE] assert (mortal ?x)plusassert (human socrates): assertinghuman socratesmakes it believed, which wakes the rule, which assertsmortal socrates. - Propositional reasoning for free. Assert
(p ∨ q)and¬p; the LTMS forcesqwith no rule involved — the engine layer inherits all of BCP. - Belief revision.
assume rain, derivewet groundviarain → wet ground, thenretract rainand watchwet groundgoUNKNOWN— unless a second support (a sprinkler) keeps it. This is theexamples/kb/belief_revision.kbwalkthrough. - Proof by cases via indirect proof. From
(p ∨ q),p → r,q → r, plain BCP cannot getr, buttry_indirect_proof(r)assumes¬r, derives¬pand¬q, hits the contradiction with(p ∨ q), and concludesr.
Exercises walk-through
Full set: ../exercises/ch10/; runnable in
solutions.py.
- Why
(:TRUE (:NOT ?x))can’t be a trigger — there is no node/datum for a negation, so nothing is ever filed under that pattern. Use aFALSEtrigger on?xinstead. - Adding XOR — no new machinery needed; it is the derived expansion
(a ∨ b) ∧ ¬(a ∧ b), shown as a one-line term macro (and as a.kbfile). - NEEDS (abduction) — one-step: for each clause mentioning the goal literal,
the other literals pinned false form a candidate support set; deeper abduction
recurses. Minimum-cost abduction (
LABDUCE) layers a cost search on top. - N-queens via DD-search — one choice set per row, a nogood per attacking
pair;
dd_searchreturns all solutions (counts 4→2, 5→10, 6→4). Cryptarithmetic and zebra-style logic puzzles are the same shape on a richer constraint set.
Try it in this repo
. .venv/bin/activate
python exercises/ch10/solutions.py # XOR, NEEDS, N-queens counts
python examples/run_kb.py exercises/ch10/kb/belief_revision.kb
python examples/run_kb.py exercises/ch10/kb/modus_chain.kb
python examples/coloring_dds.py # map coloring via DD-search
A minimal session in Python:
from ltms.ltre import LTRE
e = LTRE()
e.assert_(("implies", ("rain",), ("wet", "ground")))
e.assume(("rain",), "guess")
assert e.is_true(("wet", "ground"))
e.retract(("rain",), "guess")
assert e.is_unknown(("wet", "ground")) # belief revised
Or describe the world in a data file (the DSL) with no Python theory at all:
rain -> wet ground
sprinkler on -> wet ground
assume rain
expect wet ground true
Takeaways
- The LTRE adds facts and rules; the LTMS still does all the logic.
- One datum per proposition; negation is a sign;
assertis permanent,assumeis guarded and retractable. - Rules and belief are coupled by a single enqueue-on-label-change bridge.
- Indirect proof, closed-world assumptions, and dependency-directed search are all small programs over the contradiction-handler stack — the real power of a TMS-backed engine.
Back to the companion index · previous: Chapter 9 — LTMS.