Chapter 02 · Logic
Can a machine reason?
The first plan for artificial intelligence was not to learn but to deduce: write knowledge down as logical formulas and let the machine draw the consequences. It produced a beautiful theory, a programming language in which you describe the problem instead of the solution, and a lesson about its limits.
In this chapter
In the seventeenth century Leibniz dreamed of a calculus ratiocinator: a language so precise that disputes could be settled by calculating. “Let us calculate”, two philosophers would say, and sit down with pencil and paper. Two centuries later George Boole wrote the laws of thought as algebra (The Laws of Thought, 1854), and in 1879 Gottlob Frege published the Begriffsschrift, the first complete formal language for mathematics: variables, quantifiers and rules of inference.
When computers arrived, the dream looked within reach. If reasoning is calculating, and a computer calculates, then a computer can reason. In 1956, at the same Dartmouth workshop that named the field, Allen Newell, Herbert Simon and Cliff Shaw presented the Logic Theorist, which proved 38 of the first 52 theorems in chapter 2 of Russell and Whitehead's Principia Mathematica. Symbolic AI was born: intelligence as the manipulation of symbols according to logical rules.
Truth and proof
First-order logic talks about objects (, , ), functions (), relations (), connectives () and the quantifiers and . A set of formulas can say, for example, that every parent of an ancestor is an ancestor:
There are two ways of saying that a formula “follows” from . The semantic one, : is true in every world (every interpretation of the symbols) in which all of is true. And the syntactic one, : there is a proof, a finite sequence of formulas in which each step applies a mechanical rule. The first is about meaning, the second about pushing symbols around. The miracle is that they coincide.
For every set of first-order formulas and every formula ,
Every truth that follows from the axioms has a proof, and every proof establishes a truth. This is what makes the Leibniz dream plausible: “true in all worlds”, an infinite notion, is reduced to “there is a finite proof”, which a machine can search for. Do not confuse it with Gödel's other, more famous theorem: incompleteness (1931) says that no consistent, effective set of axioms proves every truth about the natural numbers. Completeness is about what follows from the axioms; incompleteness, about what the axioms fail to pin down.
But searching is not deciding. Proofs can be listed one by one, so if the search will eventually find a proof. If does not follow, the search may never end. In 1936 Church and Turing proved that this cannot be fixed: there is no algorithm that decides whether a first-order formula is valid. It is the logical face of the halting problem. First-order logic is semi-decidable: a machine can confirm every consequence, but it cannot always rule one out.
Resolution: one rule is enough
Proof systems for humans have many rules. In 1965 John Alan Robinson found one that is enough by itself, and is made for machines. First, every formula is turned into clausal form: a conjunction of clauses, each a disjunction of literals (atoms or negated atoms), with the existential quantifiers replaced by new functions (Skolem functions) and the universal ones left implicit. Satisfiability is preserved, and that is enough, because to prove from it suffices to show that has no model.
From two clauses with complementary literals and that can be made equal by a substitution , derive their resolvent:
A set of clauses is unsatisfiable if and only if the empty clause can be derived from by resolution.
Deriving means having reached a contradiction, as in a proof by reductio ad absurdum. Behind the theorem lies Herbrand's (1930): a set of clauses is unsatisfiable if and only if some finite set of its ground instances (with constants substituted for the variables) already is, as plain propositional logic. Resolution searches for those instances lazily, substituting only what is needed. The key to doing that is the operation in the rule's subscript, which deserves a section of its own.
Unification
To resolve against we need a substitution that makes both atoms equal: . A substitution like that is a unifier. There can be infinitely many (to unify and you can set both to , to , to …), but they are not all equally good: commits to nothing more than it has to.
If two terms and have a unifier, they have a most general one, : every unifier of and can be written as for some substitution . It is unique up to renaming variables, and there is an algorithm that computes it or reports that it does not exist.
Proof (the Martelli–Montanari algorithm)
Start from the set of equations and apply any of these rules while one applies:
- Decompose: replace by .
- Clash: if with different symbols or arities, fail.
- Delete: remove .
- Swap: replace , with not a variable, by .
- Occurs check: if with occurring inside , fail.
- Eliminate: if with not in but present in other equations, substitute for in all of them.
Each rule preserves the set of unifiers of the system, and the two failure rules apply only to systems without unifiers: no substitution makes equal to , and none makes equal to a term strictly larger than itself. The process terminates, because each step reduces, in lexicographic order, the number of variables not yet solved, the total size of the terms and the number of equations of the form . When no rule applies, the system has the form , with distinct variables that appear nowhere else. Then is a unifier, and since the original system has the same unifiers as the final one, any other unifier must satisfy , that is, . So factors through .
Prolog: logic as a program
Resolution on arbitrary clauses explodes combinatorially. But if every clause has at most one positive literal (a Horn clause), it can be read as a rule, “ is true if are”:
In 1972 Alain Colmerauer and Philippe Roussel, in Marseille, built a language on that idea to process natural language: Prolog (programmation en logique). Robert Kowalski, in Edinburgh, gave the theory: a set of Horn clauses can be read in two ways, as statements that are true or as procedures that are executed. He summed it up as “algorithm = logic + control”. You write what is true; the machine decides how to search.
parent(tom, bob). % facts: clauses with no body
parent(bob, ann).
parent(bob, pat).
anc(X, Y) :- parent(X, Y). % rules: ":-" reads "if"
anc(X, Y) :- parent(X, Z), anc(Z, Y). % "," reads "and"
A query such as ?- anc(tom, Who). is a goal to refute: the machine assumes that nobody is a descendant of Tom and looks for the contradiction. Resolving always the leftmost goal against the program's clauses, in order, is SLD resolution, and the substitution it collects along the way is the answer: Who = bob, Who = ann, Who = pat.
What a logic program means
A program has a meaning independent of how it is run. Call the set of all ground atoms (atoms without variables) that can be written with the program's symbols, and the set of ground instances of its clauses. Consider the operator that takes a set and returns everything that can be deduced from it in one step:
Every definite program has a least model . It is the least fixed point of , it is reached by iterating from the empty set, and it contains exactly the ground atoms that follow from :
Proof
A set of ground atoms is a model of exactly when : every rule whose body holds in has its head in . The intersection of models is a model (if the body is in all of them, so is the head), so there is a least model, , the intersection of all of them.
is monotone ( implies ) and, since each body is finite, continuous: an atom deduced from a union of an increasing chain is already deduced at some finite stage. By Kleene's fixed-point theorem, is the least fixed point. It is contained in every model, by induction on , and is itself a model, so it equals . Finally, for a ground means that is true in every model of , and it is enough to look at models made of ground atoms (any model induces one with the same true ground atoms). If is true in all of them it is in ; and every atom of is true in all of them, because they all contain .
For the family program: contains the three facts; one more step adds , and ; one more, and ; and after that nothing new appears. That is the meaning of the program, and the procedure matches it:
Let be a definite program and a query. Every answer computed by SLD resolution is correct ( implies the instantiated query), and for every correct answer there is a computed answer that is at least as general, provided the tree of possibilities is explored fairly.
The proviso matters. Prolog explores the tree depth first, clause by clause, because it is fast and uses little memory. But if a branch is infinite, Prolog falls into it and never comes back, even though there are answers further to the right. Write the recursive rule as anc(X, Y) :- anc(Z, Y), parent(X, Z). and put it first: the logical meaning is exactly the same, but the program no longer answers anything. Compare both versions in the figure.
Prolog also adds pieces from outside pure logic: arithmetic (X is Y + 1), the cut !, which prunes alternatives in the tree, and negation as failure (Clark, 1978): \+ P succeeds if cannot be proved. It is the closed-world assumption, “what I cannot deduce is false”. It is very practical in databases, and not monotonic: adding a fact can turn a conclusion false.
Your own Prolog
The figure below is a complete Prolog interpreter running in your browser: unification, backtracking, cut, arithmetic with integers of any size, lists, definite clause grammars and the usual built-in predicates. Choose an example, edit it or write your own program, and ask it questions. Turn on the trace to see the four classic ports of each call: Call, Exit, Redo and Fail.
Expert systems, and their limits
In the 1970s and 1980s symbolic AI went to industry. Expert systems stored the knowledge of a specialist as hundreds or thousands of rules: MYCIN (Stanford, 1976) recommended treatments for blood infections, and in a 1979 blind evaluation its prescriptions were rated as highly as those of infectious-disease specialists; XCON configured DEC computers and saved the company millions a year. In 1982 Japan launched the Fifth Generation Computer Systems project, ten years of public money to build machines that would run logic programs in parallel. Logic programming was its core.
It did not deliver what it promised. The obstacles were not in the logic but in the world:
- The knowledge acquisition bottleneck. Experts know more than they can put into rules, and every rule has exceptions to the exceptions.
- Brittleness. Outside the cases foreseen by the rules, an expert system does not degrade gracefully: it simply fails.
- Uncertainty. Logic speaks of true and false, and the world is made of “probably”. MYCIN had to invent ad hoc “certainty factors”.
- The combinatorial explosion. Semi-decidability and the size of the search space are not technicalities: they make reasoning expensive exactly where it matters.
The Lisp machine market collapsed in 1987, and with it came the second AI winter. But logic did not disappear. It lives on in Datalog and in the recursive queries of SQL, in type inference, in SAT and SMT solvers that verify chips and programs, in theorem provers like Lean, which mathematicians now use to check proofs, and in systems that combine language models with symbolic tools. The difference from that era is the order: today the rules are not written by hand, they are learned.
Learning from examples means accepting that conclusions are never certain, only more or less likely. Reasoning with degrees of belief instead of with truth values requires another mathematics. That is probability.
References
- G. Boole (1854). An Investigation of the Laws of Thought. Walton and Maberly.
- G. Frege (1879). Begriffsschrift, eine der arithmetischen nachgebildete Formelsprache des reinen Denkens. Halle.
- K. Gödel (1930). “Die Vollständigkeit der Axiome des logischen Funktionenkalküls”. Monatshefte für Mathematik und Physik, 37.
- A. Newell and H. A. Simon (1956). “The Logic Theory Machine”. IRE Transactions on Information Theory, 2(3).
- J. A. Robinson (1965). “A Machine-Oriented Logic Based on the Resolution Principle”. Journal of the ACM, 12(1).
- R. Kowalski (1974). “Predicate Logic as Programming Language”. Proceedings of IFIP Congress 74.
- M. H. van Emden and R. Kowalski (1976). “The Semantics of Predicate Logic as a Programming Language”. Journal of the ACM, 23(4).
- K. L. Clark (1978). “Negation as Failure”. In Logic and Data Bases, Plenum Press.
- A. Martelli and U. Montanari (1982). “An Efficient Unification Algorithm”. ACM Transactions on Programming Languages and Systems, 4(2).
- J. W. Lloyd (1987). Foundations of Logic Programming, 2nd ed. Springer.
- A. Colmerauer and P. Roussel (1993). “The Birth of Prolog”. ACM SIGPLAN Notices, 28(3).