Four cards
Four cards lie on a table. Each card has a letter on one side and a whole number on the other. You can see
Someone claims: "If a card has a vowel on one side, then it has an even number on the other side." Which cards must you turn over to find out whether the claim is false?
Take a moment and decide before reading on.
This is the selection task devised by the psychologist Peter Wason in 1966. In experiment after experiment, only a small minority of people answer it correctly the first time — usually people turn over the A and the 4, or just the A. The difficulty is not cleverness; it is that the everyday word "if" is slippery. In this lesson we make "and", "or", "not" and "if … then" completely precise, and the puzzle will become easy.
A promise and when it is broken
Suppose your teacher says: "If you hand in your project by Friday, then you will get a free afternoon next week." Is the promise kept or broken? There are only four possibilities.
| You hand it in by Friday | You get the free afternoon | Was the promise broken? |
|---|---|---|
| yes | yes | no — kept |
| yes | no | yes — broken |
| no | yes | no |
| no | no | no |
The promise is broken in exactly one case: you did your part and the teacher did not. If you did not hand the project in, the promise says nothing about what happens — whatever the teacher does, the promise has not been broken.
Mathematicians read every "if then " this way. It is a claim that rules out one situation only: true and false.
Connectives and truth tables
Definition— proposition
A proposition (or statement) is a sentence that is either true or false. Propositional variables stand for propositions whose truth value we leave open.
" is prime" and "" are propositions (true and false). "Close the door" and "" are not: the first is not true or false, and the second becomes true or false only once is known — or once we say "for every " or "for some ".
Definition— the connectives
From propositions and we build new ones, whose truth values are given by the table ( = true, = false):
| T | T | F | T | T | T | T |
| T | F | F | F | T | F | F |
| F | T | T | F | T | T | F |
| F | F | T | F | F | T | T |
- , "not ", is true exactly when is false.
- , " and ", is true exactly when both are true.
- , " or ", is true when at least one is true (inclusive or).
- , "if then " (the conditional), is false only when is true and is false. is its hypothesis and its conclusion.
- , " if and only if ", is true when and have the same truth value.
The exclusive "or" — "one or the other but not both", written — is true in the middle two rows only. In mathematics a bare "or" always means the inclusive one.
Definition— formula, truth table
A formula is built from propositional variables with the connectives (and brackets). An assignment gives each variable the value T or F; the truth table of a formula lists its value under every assignment. A formula in variables has rows, because each variable doubles the number of cases.
Brackets are removed by a convention: binds most tightly, then and , then and . So means . When in doubt, write the brackets.
Definition— tautology, contradiction, equivalence
A formula is a tautology if it is true in every row of its truth table, a contradiction if it is false in every row, and contingent otherwise. Two formulas and are logically equivalent, written , if they have the same truth value in every row — equivalently, if is a tautology.
Note that is not a new connective: it is a statement about two formulas.
Definition— converse, inverse, contrapositive
For the conditional :
- its converse is ;
- its inverse is ;
- its contrapositive is .
English has many ways to say , and it pays to learn them:
| English | Formula |
|---|---|
| if then ; if ; whenever | |
| only if | |
| is sufficient for ; is necessary for | |
| if and only if ("iff") | |
| unless | , which is equivalent to |
Worked examples
Example— A truth table with sub-formulas
Let us show that is equivalent to :
| T | T | F | T | T |
| T | F | F | F | F |
| F | T | T | T | T |
| F | F | T | T | T |
The last two columns agree in all four rows, so . In words: "if then " says "either fails, or holds".
Example— The four cards, solved
Write for "this card has a vowel" and for "this card has an even number". The claim, for each card, is . It is broken only by a card with true and false — a vowel with an odd number.
- A: is true. If the back is odd, the claim is broken. Turn it over.
- K: is false, so is true whatever is on the back. Leave it.
- 4: is true, so is true whatever is on the back. Leave it. (Turning it is checking the converse , which nobody claimed.)
- 7: is false. If the back is a vowel, the claim is broken. Turn it over. This is the contrapositive at work: is the same claim as .
The answer is A and 7.
Example— Necessary versus sufficient
"You may vote in the national election only if you are at least 18" is , with = "you may vote" and = "you are at least 18". Being 18 is necessary. Its converse "if you are at least 18, you may vote" is a different claim — in many countries you must also be a citizen and registered. So being 18 is not sufficient.
Example— Negating a conditional
What is the negation of "If it is a public holiday, then the bakery is closed"? It is not "If it is a public holiday, then the bakery is open". The original claim is false exactly when there is a public holiday on which the bakery is open, so the negation is
"It is a public holiday and the bakery is open."
In symbols, . The negation of an "if" statement is an "and" statement.
Example— Negating a quantified statement
"Every prime number is odd" means: for every number , if is prime then is odd — . Its negation is "some prime is not odd":
and this negation is true: is an even prime. The original statement is false, refuted by one counterexample. Quantifiers were introduced in set-builder notation and quantifiers.
Explore: truth tables that build themselves
Type any formula; the table, its sub-formula columns and its classification update as you type. The second box tests equivalence and marks the rows where two formulas differ.
A conditional and its contrapositive
Now change the second formula to q -> p (the converse) and then to ~p -> ~q (the inverse).
Read as: p → q
Read as: ¬q → ¬p
Use the symbols ¬ ∧ ∨ → ↔, the words not, and, or, implies, iff, or ASCII ~ & | -> <->. Variables are single lowercase letters. ¬ binds tightest, then ∧, ∨, →, ↔; p → q → r means p → (q → r).
Contingent — true in 3 of 4 rows, false in the others.
Equivalent — the two formulas agree in all 4 rows.
| p | q | p → q | ¬q → ¬p | Rows where they differ |
|---|---|---|---|---|
| T | T | T | T | |
| T | F | F | F | |
| F | T | T | T | |
| F | F | T | T |
4 rows: one for every way of making the 2 variables true or false.
Things to try:
- Replace the second formula by
q -> p. Which rows differ? Is the converse equivalent to the inverse? - Check De Morgan's laws: compare
not (p and q)withnot p or not q, andnot (p or q)withnot p and not q. Then try the wrong versionnot p and not qagainstnot (p and q). - Is
(p -> q) -> (q -> p)a tautology? What about((p -> q) and p) -> q?
A chain of implications
Three variables give 2³ = 8 rows. Is this argument form always valid?
Read as: ((p → q) ∧ (q → r)) → (p → r)
Use the symbols ¬ ∧ ∨ → ↔, the words not, and, or, implies, iff, or ASCII ~ & | -> <->. Variables are single lowercase letters. ¬ binds tightest, then ∧, ∨, →, ↔; p → q → r means p → (q → r).
Tautology — true in all 8 rows.
| p | q | r | (p → q) ∧ (q → r) → (p → r) |
|---|---|---|---|
| T | T | T | T |
| T | T | F | T |
| T | F | T | T |
| T | F | F | T |
| F | T | T | T |
| F | T | F | T |
| F | F | T | T |
| F | F | F | T |
8 rows: one for every way of making the 3 variables true or false.
The laws, and why they hold
For propositional formulas, a truth table is a proof: it checks every possible case, and there are only finitely many. But tables grow fast, and reasoning often explains more. We give both.
Theorem— Theorem 1 (contrapositive and converse)
. But is not equivalent to its converse , nor to its inverse .
Proof
The truth table:
| T | T | T | T | T | T |
| T | F | F | F | T | T |
| F | T | T | T | F | F |
| F | F | T | T | T | T |
The third and fourth columns agree in every row, so the conditional and its contrapositive are equivalent. In the row , the conditional is true but the converse and inverse are false, so neither is equivalent to it. (The converse and inverse agree with each other: the inverse is the contrapositive of the converse.)
In words: is false exactly when is true and is false; is false exactly when is true and is false — the same situation.
∎
Theorem— Theorem 2 (De Morgan's laws)
and .
Proof
is true exactly when is false, that is, when at least one of is false, that is, when at least one of is true — which is exactly when is true. Similarly, is true exactly when is false, that is, when both and are false, that is, when is true. (Each argument checks all four rows at once; you can also write out the tables.)
∎
Theorem— Theorem 3 (negating a conditional)
. Also .
Proof
is false in exactly one row, , . So is true in exactly that row — and is true in exactly that row too. For the second equivalence, see the first worked example; or apply De Morgan to the first: .
∎
Equivalences can be chained like equalities, which gives a second proof of Theorem 1 with no table at all:
Here we used Theorem 3 (twice), that is commutative, and that .
Theorem— Theorem 4 (negating quantifiers)
For any property : and . Consequently
Proof
"Not every has " says that at least one fails to have , which is ; and "no has " says that every fails to have . For the last line, apply the first rule with and then Theorem 3 inside: .
∎
Quantified statements about infinitely many cannot be checked by a finite truth table — this is one reason mathematics needs proofs, which the next lesson, proof techniques, is all about.
Valid arguments. An argument with premises and conclusion is valid when is a tautology: in every row where all the premises are true, so is the conclusion.
| Argument form | Premises | Conclusion | Valid? |
|---|---|---|---|
| modus ponens | , | yes | |
| modus tollens | , | yes | |
| hypothetical syllogism | , | yes | |
| affirming the consequent | , | no: row , | |
| denying the antecedent | , | no: row , |
Modus tollens is the contrapositive in action — exactly why the 7 in the card puzzle must be turned over.
Theorem— Theorem 5 (every truth table comes from a formula)
For every and every way of filling in the final column of a truth table with variables, there is a formula using only , and with exactly that truth table. In particular there are exactly different truth tables in variables, and each is the table of some formula.
Proof
For each row in which the final column is T, write the conjunction of literals describing that row: if is true in the row, if it is false. This conjunction is true in that row and in no other. Now join all these conjunctions with . The result is true exactly in the rows marked T. If no row is marked T, use the contradiction . (Such a formula is in disjunctive normal form.) Finally, a table has rows and each can be marked T or F independently, giving possible final columns.
∎
For example, the table of exclusive or (T only in the rows TF and FT) gives .
Truth tables and logic in Lean
We formalise propositional logic twice: once as computation (formulas whose truth values are calculated, exactly as in a table) and once inside Lean's own logic.
Formulas as data. A Formula n is a formula in variables. Its truth value in a row v (an assignment of true/false to the variables) is computed by Formula.eval. Notice how the conditional is evaluated: !a || b, "not , or " — Theorem 3.
/-- Propositional formulas in `n` variables `x₀, …, xₙ₋₁`: variables, the constants true and
false, and the connectives not, and, or, implies and if-and-only-if. -/
inductive Formula (n : ℕ) : Type where
| var : Fin n → Formula n
| tt : Formula n
| ff : Formula n
| not : Formula n → Formula n
| and : Formula n → Formula n → Formula n
| or : Formula n → Formula n → Formula n
| imp : Formula n → Formula n → Formula n
| iff : Formula n → Formula n → Formula n/-- The truth value of a formula in one row of its truth table. A row (a *valuation*) `v`
assigns `true` or `false` to each variable. Note the conditional: `φ → ψ` is false only when
`φ` is true and `ψ` is false, i.e. it has the value `!φ || ψ`. -/
def Formula.eval {n : ℕ} (v : Fin n → Bool) : Formula n → Bool
| .var i => v i
| .tt => true
| .ff => false
| .not φ => !φ.eval v
| .and φ ψ => φ.eval v && ψ.eval v
| .or φ ψ => φ.eval v || ψ.eval v
| .imp φ ψ => !φ.eval v || ψ.eval v
| .iff φ ψ => φ.eval v == ψ.eval vThe rows of a truth table are listed by valuations n. We prove two things about it: there are rows, and no row is missing. The second is what makes the truth-table method trustworthy.
/-- All rows of a truth table in `n` variables, in textbook order (the first variable is true
in the first half of the rows, false in the second half, and so on recursively). -/
def valuations : (n : ℕ) → List (Fin n → Bool)
| 0 => [fun i => i.elim0]
| n + 1 => [true, false].flatMap fun b => (valuations n).map (Fin.cons b)/-- A truth table in `n` variables has `2 ^ n` rows: each new variable doubles the number of
rows, once with the variable true and once with it false. Proved by induction on `n`. -/
theorem length_valuations (n : ℕ) : (valuations n).length = 2 ^ n := by
induction n with
| zero => rfl
| succ n ih => simp [valuations, ih, pow_succ, Nat.mul_two]/-- **Every row appears.** Any assignment of truth values to `n` variables is one of the rows
listed by `valuations n`. Proof by induction on `n`: an assignment to `n + 1` variables is its
value on the first variable followed by an assignment to the remaining `n`
(`Fin.cons_self_tail`). -/
theorem mem_valuations : ∀ (n : ℕ) (v : Fin n → Bool), v ∈ valuations n
| 0, v => by
simp only [valuations, List.mem_singleton]
funext i
exact i.elim0
| n + 1, v => by
simp only [valuations, List.mem_flatMap, List.mem_map]
refine ⟨v 0, by cases v 0 <;> simp, Fin.tail v, mem_valuations n (Fin.tail v), ?_⟩
exact Fin.cons_self_tail v/-- The **truth-table test**: a formula passes when it is true in every row of its table. -/
def isTautology {n : ℕ} (φ : Formula n) : Bool := (valuations n).all fun v => φ.eval v/-- **The truth-table method is correct.** The finite check `isTautology φ` succeeds exactly
when `φ` is true under *every* assignment, i.e. exactly when `φ` is a tautology. The forward
direction needs `mem_valuations`: no row has been forgotten. -/
theorem isTautology_iff {n : ℕ} (φ : Formula n) :
isTautology φ = true ↔ ∀ v : Fin n → Bool, φ.eval v = true := by
unfold isTautology
rw [List.all_eq_true]
exact ⟨fun h v => h v (mem_valuations n v), fun h v _ => h v⟩/-- Two formulas are **logically equivalent** when they have the same truth value in every row
of the truth table. -/
def Equivalent {n : ℕ} (φ ψ : Formula n) : Prop := ∀ v : Fin n → Bool, φ.eval v = ψ.eval v/-- `φ` and `ψ` are equivalent exactly when the biconditional `φ ↔ ψ` is a tautology, so the
truth-table test also decides equivalence. -/
theorem equivalent_iff_tautology {n : ℕ} (φ ψ : Formula n) :
Equivalent φ ψ ↔ isTautology (.iff φ ψ) = true := by
rw [isTautology_iff]
simp [Equivalent, Formula.eval]With these in place, particular laws are proved by letting Lean evaluate their truth tables (decide). Here p and q are the two variables of Formula 2.
/-- **Contrapositive.** `p → q` is equivalent to `¬q → ¬p`: the truth-table test passes on all
four rows (computed by `decide`). -/
theorem contrapositive_table : Equivalent (imp p q) (imp (not q) (not p)) := by
rw [equivalent_iff_tautology]
decide/-- **The converse is different.** `p → q` and `q → p` are *not* equivalent: they disagree in
the row `p = false`, `q = true`, where `p → q` is (vacuously) true but `q → p` is false. -/
theorem converse_table : ¬ Equivalent (imp p q) (imp q p) := by
intro h
have row := h (fun i => if i = 0 then false else true)
revert row
decide/-- **De Morgan's laws** as truth tables: `¬(p ∧ q) ≡ ¬p ∨ ¬q` and `¬(p ∨ q) ≡ ¬p ∧ ¬q`. -/
theorem de_morgan_table :
Equivalent (not (and p q)) (or (not p) (not q)) ∧
Equivalent (not (or p q)) (and (not p) (not q)) := by
rw [equivalent_iff_tautology, equivalent_iff_tautology]
decide/-- **Negating a conditional.** `¬(p → q) ≡ p ∧ ¬q`: a promise "if p then q" is broken exactly
when p happens and q does not. -/
theorem not_imp_table : Equivalent (not (imp p q)) (and p (not q)) := by
rw [equivalent_iff_tautology]
decide/-- **Valid and invalid argument forms.** *Modus ponens* `((p → q) ∧ p) → q` and *modus tollens*
`((p → q) ∧ ¬q) → ¬p` are tautologies; *affirming the consequent* `((p → q) ∧ q) → p` is not. -/
theorem argument_forms :
isTautology (imp (and (imp p q) p) q) = true ∧
isTautology (imp (and (imp p q) (not q)) (not p)) = true ∧
isTautology (imp (and (imp p q) q) p) = false := by
decideLogic in Prop. From we can always get — the proof is a one-line function. But the way back uses proof by contradiction (by_contra), which relies on the law of excluded middle, . Logicians who do not assume that law (constructive mathematics, many type theories) accept the first direction and not, in general, the second. Mathlib's one-line version applies its theorem not_imp_not.
/-- From `p → q` we get `¬q → ¬p`: if `q` fails, then `p` must fail too, since `p` would give `q`.
This direction is *constructive*: it uses no law of excluded middle. -/
theorem contrapositive {p q : Prop} (h : p → q) : ¬q → ¬p :=
fun hnq hp => hnq (h hp)/-- **The contrapositive law** `(p → q) ↔ (¬q → ¬p)`. The way back, from `¬q → ¬p` to `p → q`,
argues by contradiction: given `p`, if `q` failed we would get `¬p`, which is absurd. That step
uses classical logic (`by_contra`). -/
theorem contrapositive_iff (p q : Prop) : (p → q) ↔ (¬q → ¬p) := by
constructor
· exact contrapositive
· intro h hp
by_contra hnq
exact h hnq hp/-- The contrapositive law, by Mathlib's `not_imp_not : (¬a → ¬b) ↔ (b → a)`. -/
theorem contrapositive_iff_library (p q : Prop) : (p → q) ↔ (¬q → ¬p) :=
not_imp_not.symm/-- **An implication does not imply its converse.** Take `p` false and `q` true: `p → q` holds
(its hypothesis is false), but `q → p` would turn a proof of `True` into a proof of `False`. -/
theorem converse_fails : ¬ ∀ p q : Prop, (p → q) → (q → p) := by
intro h
exact h False True (fun hf => hf.elim) trivialThe same split happens for De Morgan: "neither nor " is constructive, but to pass from "not both" to "one of them fails" we must decide which one, and Mathlib's not_and_or uses classical logic.
/-- **De Morgan** `¬(p ∨ q) ↔ ¬p ∧ ¬q`, proved constructively: "neither p nor q". -/
theorem de_morgan_or (p q : Prop) : ¬(p ∨ q) ↔ ¬p ∧ ¬q :=
⟨fun h => ⟨fun hp => h (Or.inl hp), fun hq => h (Or.inr hq)⟩,
fun ⟨hnp, hnq⟩ h => h.elim hnp hnq⟩/-- **De Morgan** `¬(p ∧ q) ↔ ¬p ∨ ¬q`, by Mathlib's `not_and_or`. Going from left to right
needs classical logic: to name *which* of `p`, `q` fails we must decide whether `p` holds. -/
theorem de_morgan_and (p q : Prop) : ¬(p ∧ q) ↔ ¬p ∨ ¬q :=
not_and_or/-- **Negating a conditional**: `¬(p → q) ↔ p ∧ ¬q`, by core Lean's `Classical.not_imp`. -/
theorem not_imp_iff (p q : Prop) : ¬(p → q) ↔ p ∧ ¬q :=
Classical.not_impFinally, negating quantified statements (Theorem 4), the prime-number example, and vacuous truth:
/-- **Negating a quantified conditional**: "not every `x` with `P` has `Q`" means "some `x` has
`P` but not `Q`". It combines `Classical.not_forall` (`¬∀ ↔ ∃¬`) with the negation of a
conditional. -/
theorem not_forall_imp {α : Type*} (P Q : α → Prop) :
(¬ ∀ x, P x → Q x) ↔ ∃ x, P x ∧ ¬ Q x := by
rw [Classical.not_forall]
exact exists_congr fun x => Classical.not_imp/-- "Every prime is odd" is false. By `not_forall_imp`, its negation is "some prime is not odd",
and `2` is a witness. -/
theorem not_every_prime_odd : ¬ ∀ n : ℕ, n.Prime → Odd n := by
rw [not_forall_imp]
exact ⟨2, Nat.prime_two, by decide⟩/-- **Vacuous truth.** "If a natural number is negative, then it equals 17" is *true*: its
hypothesis never holds, so it can never be broken. -/
theorem vacuous_truth : ∀ n : ℕ, n < 0 → n = 17 :=
fun n h => absurd h (Nat.not_lt_zero n)What is formalised: the syntax and truth-table semantics of propositional formulas, the correctness of the truth-table test, every law of this lesson as a truth-table computation, and the same laws for Lean propositions. What is not formalised here: Theorem 5 (disjunctive normal form) for general — it is checked for by the Python experiment below.
Truth tables by machine
"""Truth tables by machine: equivalences, the 16 connectives, and completeness.
A propositional formula in n variables has a truth table with 2**n rows, so
whether two formulas are equivalent can be settled by checking every row. This
experiment builds truth tables for the conditional p -> q and its converse,
inverse and contrapositive, checks De Morgan's laws and the negation of a
conditional, and then shows that every one of the 16 possible two-variable
truth tables is the table of some formula built from "not", "and" and "or"
(its disjunctive normal form), and even from NAND alone.
Unlike checking a formula about integers on many examples, checking every row
of a truth table is a complete verification: there are no other cases.
Runs unchanged in the browser (Pyodide) using only the standard library.
"""
from itertools import product
def implies(a, b):
"""The material conditional: false only when a is true and b is false."""
return (not a) or b
def nand(a, b):
"""NAND: 'not both'."""
return not (a and b)
def rows(n):
"""All assignments of truth values to n variables, in textbook order
(all true first, the first variable changing slowest)."""
if n < 0:
raise ValueError("n must be non-negative")
return list(product([True, False], repeat=n))
def table(formula, n):
"""The truth table of ``formula`` (a function of n booleans) as a tuple."""
return tuple(bool(formula(*row)) for row in rows(n))
def classify(formula, n):
"""'tautology', 'contradiction' or 'contingent'."""
values = table(formula, n)
if all(values):
return "tautology"
if not any(values):
return "contradiction"
return "contingent"
def differing_rows(f, g, n):
"""The rows on which two formulas take different values (empty iff
the formulas are logically equivalent)."""
return [row for row in rows(n) if bool(f(*row)) != bool(g(*row))]
def dnf_from_table(values, names=("p", "q")):
"""A formula in disjunctive normal form with the given truth table.
For each row where the table is true, AND together the literals that
describe that row; then OR these terms. A table that is never true gets
the contradiction 'p and not p'."""
n = len(names)
terms = []
for row, value in zip(rows(n), values):
if value:
lits = [v if b else "not " + v for v, b in zip(names, row)]
terms.append("(" + " and ".join(lits) + ")")
if not terms:
return f"({names[0]} and not {names[0]})"
return " or ".join(terms)
def evaluate_dnf(text, assignment):
"""Evaluate a DNF string produced by ``dnf_from_table`` without eval()."""
for term in text.split(" or "):
lits = term.strip("()").split(" and ")
ok = True
for lit in lits:
if lit.startswith("not "):
ok = ok and not assignment[lit[4:]]
else:
ok = ok and assignment[lit]
if ok:
return True
return False
def nand_only(values):
"""Build the truth function with this two-variable table using only nand,
following the DNF: not x = x nand x, x and y = not(x nand y),
x or y = (not x) nand (not y). Returns a function of (p, q)."""
def neg(x):
return nand(x, x)
def conj(x, y):
return neg(nand(x, y))
def disj(x, y):
return nand(neg(x), neg(y))
def f(p, q):
result = conj(p, neg(p)) # start from "false"
for (a, b), value in zip(rows(2), values):
if value:
lit_p = p if a else neg(p)
lit_q = q if b else neg(q)
result = disj(result, conj(lit_p, lit_q))
return result
return f
def main():
tf = {True: "T", False: "F"}
forms = [
("p -> q", lambda p, q: implies(p, q)),
("converse q -> p", lambda p, q: implies(q, p)),
("inverse ~p -> ~q", lambda p, q: implies(not p, not q)),
("contrapositive ~q -> ~p", lambda p, q: implies(not q, not p)),
]
print("p q | " + " | ".join(name for name, _ in forms))
for p, q in rows(2):
cells = [tf[f(p, q)].center(len(name)) for name, f in forms]
print((f"{tf[p]} {tf[q]} | " + " | ".join(cells)).rstrip())
print()
cond = forms[0][1]
for name, f in forms[1:]:
diff = differing_rows(cond, f, 2)
shown = ", ".join(tf[a] + tf[b] for a, b in diff)
verdict = "equivalent" if not diff else f"differs in rows {shown}"
print(f"p -> q versus {name}: {verdict}")
checks = [
("~(p & q) = ~p | ~q", lambda p, q: (not (p and q)) == ((not p) or (not q))),
("~(p | q) = ~p & ~q", lambda p, q: (not (p or q)) == ((not p) and (not q))),
("~(p -> q) = p & ~q", lambda p, q: (not implies(p, q)) == (p and not q)),
]
for name, f in checks:
print(f"{name:22} is a {classify(f, 2)}")
print()
tables = list(product([True, False], repeat=4))
dnf_ok = sum(
table(lambda p, q, t=t: evaluate_dnf(dnf_from_table(t),
{"p": p, "q": q}), 2) == t
for t in tables)
nand_ok = sum(table(nand_only(t), 2) == t for t in tables)
print(f"Two-variable truth tables: {len(tables)} = 2^(2^2)")
print(f" rebuilt from not/and/or (DNF): {dnf_ok} of {len(tables)}")
print(f" rebuilt from NAND alone: {nand_ok} of {len(tables)}")
print(" e.g. exclusive or: " + dnf_from_table((False, True, True, False)))
print("Truth tables in n variables: " + ", ".join(
f"n={n}: {2 ** 2 ** n}" for n in range(1, 5)))
print()
print("Epistemic status: every row of every table was checked, so these")
print("equivalences and constructions are verified completely (assuming")
print("this short program is correct). For propositional formulas a truth")
print("table is a proof; the lesson explains why the DNF method works for")
print("any number of variables, which no finite run can show.")
if __name__ == "__main__":
main()This is a rare situation in which checking cases is a proof: a two-variable formula has only four rows, and the program checks all of them. The catch is size. A formula with variables has rows; with variables that is more than rows, far more than could ever be checked. Deciding whether some row makes a formula true — the satisfiability problem, SAT — was the first problem shown to be NP-complete (Stephen Cook, 1971, and independently Leonid Levin, 1973). Whether it can always be solved dramatically faster than by trying all rows is the famous open P versus NP problem; see computational complexity.
Exercises
1. Propositional logic and truth tables
Warm-upHumanIs the statement “If is even, then is divisible by ” true or false?
2. Propositional logic and truth tables
Warm-upHumanWhich statement is the contrapositive of “If a shape is a square, then it is a rectangle”?
3. Propositional logic and truth tables
CoreHumanWhich statement is the negation of “If it is a public holiday, then the bakery is closed”?
4. Propositional logic and truth tables
CoreHumanWhat is the negation of “Amara speaks Hausa and Yoruba”?
5. Propositional logic and truth tables
Warm-upHumanHow many rows does the truth table of have?
6. Propositional logic and truth tables
CoreHumanWhich of these formulas are tautologies? (Choose all that apply.)
7. Propositional logic and truth tables
CoreHumanEach card has a letter on one side and a whole number on the other. You see E, M, 6 and 3. Rule: “If a card has a vowel on one side, then it has an even number on the other.” Which cards must you turn over to check the rule? (Choose all that apply.)
8. Propositional logic and truth tables
CoreHumanWhich statement is the negation of “Every whole number divisible by is divisible by ”?
9. Propositional logic and truth tables
CoreFormalWhich Lean statement says that a conditional is equivalent to its contrapositive (for all propositions p and q)?
10. Propositional logic and truth tables
StretchComputationalWrite is_tautology(f, n), which returns True exactly when the Python function f of n boolean arguments returns True for every combination of arguments. Then write equivalent(f, g, n) using it. (Hint: itertools.product([True, False], repeat=n) lists all rows.)
Common misconceptions
Misconception
"An implication with a false hypothesis is false." It is true. "If is even, then is divisible by " is a true statement, because its hypothesis is false and so it can never be broken. Such statements are called vacuously true. They matter: "every element of the empty set is even" is true for the same reason.
Misconception
"An implication and its converse say the same thing." "If a number is divisible by , it is even" is true; its converse "if a number is even, it is divisible by " is false (). Only the contrapositive is equivalent. Mixing up a statement and its converse is the fallacy of affirming the consequent.
Misconception
"If then means causes ." The mathematical conditional is only about truth values. "If , then the Nile flows through Africa" is true — both parts are true — although neither causes the other. Everyday "if" often carries extra meaning (cause, time, relevance); logicians study other kinds of conditional too, but in mathematics means exactly "not ( and not )".
Misconception
"The negation of 'P and Q' is 'not P and not Q'." By De Morgan, . The negation of "Amara speaks Hausa and Yoruba" is "Amara does not speak Hausa or does not speak Yoruba" — she might speak one of them.
Misconception
"The negation of 'if P then Q' is 'if P then not Q'." The negation is . "If it rains, the match is cancelled" is refuted by a rainy day on which the match was played — not by another "if" statement.
Many ways of seeing
Set theory— truth sets and Venn diagrams
Fix a universe and properties , with truth sets and . Then , and have truth sets , and the complement . De Morgan's laws become and . And "for every , " says exactly that : the conditional is the logical form of the subset relation, which is why it is false only on . See sets.
Algebra— logic as arithmetic modulo 2
Write for true and for false, and compute modulo (so ). Then , , , and . Every truth table is the table of a polynomial in which no variable appears squared (because for ) — its algebraic normal form, studied by Ivan Zhegalkin in 1927 — and a formula is a tautology exactly when its polynomial is the constant . George Boole's original insight was that logic could be done by algebra; today with these operations is the field , the arithmetic of computers.
Geometry— rows are the corners of a cube
The rows of a truth table are the corners of an -dimensional cube : for , an ordinary cube with corners such as . A formula is just a choice of which corners are "true". A literal such as picks out a face (half the corners), a conjunction of literals picks out a smaller sub-cube, and a formula in disjunctive normal form covers the true corners by sub-cubes. Simplifying a formula means covering the same corners with fewer, larger sub-cubes — which is exactly what the Karnaugh maps used by circuit designers (Maurice Karnaugh, 1953) do, by unfolding the cube flat. Two rows differing in one variable are neighbouring corners joined by an edge.
Computation— switches, circuits and NAND
In 1937 Claude Shannon showed in his master's thesis that circuits of electrical switches compute exactly these truth functions: switches in series behave like , in parallel like . Every chip in a phone is a vast propositional formula. The computer experiment above checks that the single connective NAND, , can build every truth table — which is why chips can be made from millions of copies of one kind of gate. Henry Sheffer published this property of NAND in 1913; Charles Sanders Peirce had noticed the same for NOR decades earlier in unpublished work.
Logic— truth versus proof
There are two ways to establish a law such as : compute its truth table, or derive it with rules of inference such as modus ponens. For propositional logic the two agree — every tautology can be derived, and only tautologies can (a completeness theorem, proved by Emil Post in 1921). For logics with quantifiers the story is subtler and deeper; see first-order logic. And as the Lean section shows, if one does not assume excluded middle, some classical tautologies stop being derivable — the starting point of constructive logic and of type theory.
History and global context
- Ancient Greece. Aristotle (4th century BCE) analysed arguments built from terms ("all A are B"). The Stoic logicians, above all Chrysippus of Soli (3rd century BCE), developed a logic of whole propositions joined by "if", "and" and "or", including the forms we call modus ponens and modus tollens. Philo of Megara proposed reading "if … then" as false only when the first part is true and the second false — our conditional. The debate with his teacher Diodorus Cronus over which conditionals are true was so famous that, as Sextus Empiricus reports, the poet Callimachus joked that even the crows on the rooftops cawed about it.
- India. The Nyāya school and later Buddhist logicians such as Dignāga (5th–6th century) and Dharmakīrti (7th century) built detailed theories of valid inference (anumāna), in a framework quite different from the Greek one.
- The Islamic world. Ibn Sīnā (Avicenna, c. 980–1037) developed a theory of conditional and disjunctive propositions and the arguments built from them, which was studied and extended by later logicians writing in Arabic and Persian.
- Algebra of logic. In 1847 George Boole published The Mathematical Analysis of Logic and Augustus De Morgan his Formal Logic; the laws now named after De Morgan were known in some form to medieval logicians such as William of Ockham. Gottlob Frege's Begriffsschrift (1879) gave the first fully formal system of logic with quantifiers.
- Truth tables in their modern form appear in the work of Ludwig Wittgenstein and Emil Post (both 1921), with earlier anticipations by Charles Sanders Peirce. Post also proved the completeness of propositional logic.
- Computation. Claude Shannon (1937) connected logic to switching circuits; Stephen Cook (1971) and Leonid Levin (1973) showed that satisfiability is NP-complete.
Further reading
- Daniel J. Velleman, How to Prove It: A Structured Approach (1994), chapter 1 on sentential logic.
- Herbert B. Enderton, A Mathematical Introduction to Logic (1972), chapter 1 on sentential logic, for a rigorous university treatment.
- Raymond Smullyan, What Is the Name of This Book? (1978), puzzles of knights and knaves that train exactly the reasoning of this lesson.
- George Boole, An Investigation of the Laws of Thought (1854), the classic that turned logic into algebra.