CMP-250 · Lesson 2 of 12 · 31 min
Propositional logic
Propositional logic is the simplest useful logic. It studies statements that are either true or false and the ways those statements combine. It is the logic of Python's Boolean expressions, of digital circuits and of the conditions in every if statement you have written. This lesson defines propositions and connectives, shows how to compute truth values with truth tables, and translates between English, logic and code.
Propositions
A proposition is a declarative statement that is either true or false, but not both. The number 7 is prime is a proposition, and it is true. Every even number greater than 2 is prime is a proposition, and it is false. Some sentences are not propositions: questions (Is it raining?), commands (Close the door) and statements whose truth depends on an unspecified variable (x is greater than 3). The last kind becomes a proposition once \(x\) is given a value, and lesson 4 handles such statements with predicates.
We name propositions with lowercase letters, called propositional variables: \(p\), \(q\), \(r\) and so on. A propositional variable stands for some statement whose truth value is either T (true) or F (false). In Python these values are True and False, and in digital hardware they are 1 and 0.
| Sentence | Proposition? | Reason |
|---|---|---|
| Ottawa is the capital of Canada. | Yes (true) | Declarative and has a definite truth value |
| 2 + 2 = 5 | Yes (false) | False statements are still propositions |
| Is the server running? | No | A question |
| Restart the server. | No | A command |
| x + 1 = 4 | Not yet | Its truth depends on x; it is a predicate |
| This sentence is false. | No | It cannot consistently be true or false |
The logical connectives
Connectives build compound propositions from simpler ones. Five are standard. Each is defined completely by a truth table that gives the value of the compound for every combination of values of its parts.
| Connective | Symbol | Read as | Python | True exactly when |
|---|---|---|---|---|
| Negation | ¬p | not p | not p | p is false |
| Conjunction | p ∧ q | p and q | p and q | both p and q are true |
| Disjunction | p ∨ q | p or q | p or q | at least one of p, q is true |
| Conditional | p → q | if p then q | (not p) or q | p is false or q is true |
| Biconditional | p ↔ q | p if and only if q | p == q | p and q have the same value |
| Exclusive or | p ⊕ q | p xor q | p != q | exactly one of p, q is true |
In formulas we write negation as \(\neg p\), conjunction as \(p \wedge q\), disjunction as \(p \vee q\), the conditional as \(p \to q\) and the biconditional as \(p \longleftrightarrow q\). The exclusive or, \(p \oplus q\), is not usually counted among the basic five but appears constantly in computing: in parity checks, in encryption and in the XOR operator ^ on bits.
Truth tables
A truth table lists every possible assignment of truth values to the variables and the resulting value of the formula. With \(n\) variables there are \(2^n\) rows, because each variable independently takes one of two values. Two variables give 4 rows, three give 8, and ten give 1,024. The standard order counts in binary with T before F, so the first variable alternates slowest.
Python generates truth tables easily with itertools.product, which produces every combination of values. The program below prints the table for the main connectives; it is worth running yourself and modifying.
from itertools import product
def implies(p, q):
return (not p) or q
def show(b):
return "T" if b else "F"
print(" p q | not p p and q p or q p -> q p <-> q p xor q")
for p, q in product([True, False], repeat=2):
row = [not p, p and q, p or q, implies(p, q), p == q, p != q]
print(f" {show(p)} {show(q)} | " + " ".join(show(x) for x in row))p q | not p p and q p or q p -> q p <-> q p xor q T T | F T T T T F T F | F F T F F T F T | T F T T F T F F | T F F T T F
The conditional deserves special attention
The conditional \(p \to q\) causes more confusion than any other connective. Here \(p\) is called the hypothesis (or antecedent) and \(q\) the conclusion (or consequent). The conditional is false in exactly one case: when the hypothesis is true and the conclusion is false. In particular, if the hypothesis is false the conditional is true no matter what the conclusion is. This is called vacuous truth.
A promise makes the rule natural. Suppose a teacher says: if you score over 90 on the final, you will get an A. When has the teacher broken the promise? Only if you score over 90 and do not get an A. If you score below 90, the promise says nothing about your grade, so whatever happens it has not been broken. The conditional records exactly whether the promise was kept.
Worked problem
Evaluating a conditional
Let p be "the file exists" and q be "the program opens it". Evaluate p → q in each situation: (a) the file exists and is opened; (b) the file exists but is not opened; (c) the file does not exist and the program does nothing.
Show solution
- (a) p is T and q is T, so p → q is T.
- (b) p is T and q is F: this is the only false case, so p → q is F. The requirement was violated.
- (c) p is F, so p → q is T regardless of q. The requirement only applies when the file exists.
Answer: (a) true, (b) false, (c) vacuously true.
English expresses \(p \to q\) in many ways, and some of them reverse the apparent order. All of the following mean \(p \to q\): if p then q; q if p; p implies q; p only if q; p is sufficient for q; q is necessary for p; q whenever p; q unless not p. The phrase only if is the most common trap. You can get into the building only if you have a badge means: if you get in, then you have a badge. It does not say that having a badge gets you in.
| English form | Logical form |
|---|---|
| If p, then q | p → q |
| q if p | p → q |
| p only if q | p → q |
| p is sufficient for q | p → q |
| q is necessary for p | p → q |
| p if and only if q | p ↔ q |
| p unless q | ¬q → p, equivalently p ∨ q |
Converse, inverse and contrapositive
From a conditional \(p \to q\) we can form three related statements. The converse is \(q \to p\). The inverse is \(\neg p \to \neg q\). The contrapositive is \(\neg q \to \neg p\). The contrapositive always has the same truth value as the original; the converse and inverse do not, although they agree with each other. Confusing a conditional with its converse is one of the most common errors in reasoning, in mathematics and in everyday life.
from itertools import product
T = lambda b: "T" if b else "F"
imp = lambda a, b: (not a) or b
print(" p q | p->q converse inverse contrapositive")
for p, q in product([True, False], repeat=2):
print(f" {T(p)} {T(q)} | {T(imp(p,q))} {T(imp(q,p))} {T(imp(not p, not q))} {T(imp(not q, not p))}")p q | p->q converse inverse contrapositive T T | T T T T T F | F T T F F T | T F F T F F | T T T T
For example, the statement if a number is divisible by 4, then it is even is true. Its converse, if a number is even, then it is divisible by 4, is false: 6 is a counterexample. Its contrapositive, if a number is not even, then it is not divisible by 4, is true, and lesson 5 will use exactly this equivalence as a proof technique.
Precedence and parentheses
To avoid writing parentheses everywhere, logic uses precedence rules, just as arithmetic does. Negation binds most tightly, then conjunction, then disjunction, then the conditional, then the biconditional. So \(\neg p \wedge q \vee r\) means \(((\neg p) \wedge q) \vee r\). The conditional is usually taken to group to the right, so \(p \to q \to r\) means \(p \to (q \to r)\), but good style is to use parentheses whenever a reader might hesitate.
Python's precedence for not, and and or follows the same order. One difference to remember is that Python has no implication operator, so you must write (not p) or q. Comparison operators bind more tightly than not, so not x == y means not (x == y).
Worked problem
A three-variable truth table
Construct the truth table for (p ∨ q) → ¬r and find the rows where it is false.
Show solution
- There are 3 variables, so 2³ = 8 rows.
- Compute p ∨ q in each row, then ¬r, then apply the conditional.
- The conditional is false only when p ∨ q is true and ¬r is false, which means r is true.
- p ∨ q is true in every row except p = F, q = F. Combined with r = T, the false rows are (T, T, T), (T, F, T) and (F, T, T).
Answer: False in exactly three rows: pqr = TTT, TFT and FTT; true in the other five.
from itertools import product
T = lambda b: "T" if b else "F"
print(" p q r | p or q not r (p or q) -> not r")
for p, q, r in product([True, False], repeat=3):
a, b = p or q, not r
print(f" {T(p)} {T(q)} {T(r)} | {T(a)} {T(b)} {T((not a) or b)}")p q r | p or q not r (p or q) -> not r T T T | T F F T T F | T T T T F T | T F F T F F | T T T F T T | T F F F T F | T T T F F T | F F T F F F | F T T
Translating English into logic
Translation is a skill that takes practice. Identify the atomic propositions, give each a letter, and then find the connectives. Words like but, although and however are logically just and; they add emphasis or contrast but not a different truth condition. Neither p nor q means \(\neg p \wedge \neg q\). Not both p and q means \(\neg (p \wedge q)\).
Worked problem
Translating a system requirement
Translate: "The alarm sounds if the door is opened while the system is armed, unless the correct code is entered." Use d for door opened, a for armed, c for correct code entered, s for alarm sounds.
Show solution
- The core rule is: if the door is opened and the system is armed, the alarm sounds: (d ∧ a) → s.
- Unless c modifies the rule: the rule applies when the code is not entered.
- Add ¬c to the hypothesis: (d ∧ a ∧ ¬c) → s.
- Check a case: door opened, armed, correct code entered. The hypothesis is false, so the formula makes no demand on s, which matches the requirement.
Answer: (d ∧ a ∧ ¬c) → s. A designer might also want the converse, s → (d ∧ a ∧ ¬c), so the alarm does not sound otherwise; together they give a biconditional.
The last step of that example illustrates a common gap between requirements and their formalization. Most people who write if X then Y in a specification also mean that Y should not happen otherwise. Logic forces you to say which you mean, and many bugs live exactly in that gap.
Logic in bits
Computers apply connectives to many bits at once. Python's bitwise operators &, | , ^ and ~ apply and, or, xor and not to each pair of corresponding bits in two integers. Network masks, permission flags and graphics operations all rely on this. A file permission like 0o755 in Unix is three groups of three bits, and checking whether a permission is set is a bitwise and with a mask.
READ, WRITE, EXEC = 0b100, 0b010, 0b001
perm = READ | EXEC # 0b101
print(f"perm = {perm:03b}")
print("can read? ", bool(perm & READ))
print("can write? ", bool(perm & WRITE))
perm ^= WRITE # toggle write
print(f"after toggling write: {perm:03b}")
print(f"5 xor 3 = {5 ^ 3} ({5:03b} xor {3:03b} = {5 ^ 3:03b})")perm = 101 can read? True can write? False after toggling write: 111 5 xor 3 = 6 (101 xor 011 = 110)
Short-circuit evaluation is the other place where logic meets programming. Python evaluates p and q from left to right and stops as soon as the answer is known: if p is false, q is never evaluated. This is why a guard like i < len(xs) and xs[i] > 0 is safe. Logically, \(p \wedge q\) and \(q \wedge p\) are equivalent, but in a program with side effects or errors the order matters.
Common mistakes
MistakeTreating p → q as false whenever p is false.
InsteadA conditional with a false hypothesis is true (vacuously); it is false only when p is true and q is false.
MistakeReading 'p only if q' as q → p.
Instead'p only if q' means p → q: whenever p holds, q holds.
MistakeAssuming a conditional and its converse say the same thing.
InsteadOnly the contrapositive is equivalent; the converse can be false when the original is true.
MistakeReading 'or' as exclusive.
InsteadLogical or is inclusive; use ⊕ for exactly one.
MistakeTranslating 'neither p nor q' as ¬p ∨ ¬q.
InsteadNeither p nor q is ¬p ∧ ¬q.
MistakeWriting p -> q in Python.
InsteadPython has no implication operator; write (not p) or q.
Summary and key terms
- A proposition is a declarative statement that is either true or false.
- The connectives ¬, ∧, ∨, → and ↔ are defined by truth tables; ⊕ is exclusive or.
- A truth table for n variables has 2 to the n rows.
- p → q is false only when p is true and q is false; a false hypothesis makes it vacuously true.
- 'p only if q', 'q is necessary for p' and 'p is sufficient for q' all mean p → q.
- The contrapositive ¬q → ¬p is equivalent to p → q; the converse q → p is not.
- Precedence: ¬, then ∧, then ∨, then →, then ↔; use parentheses when in doubt.
- Proposition
- A declarative statement that is either true or false.
- Propositional variable
- A letter standing for a proposition, such as p or q.
- Connective
- An operator that forms compound propositions, such as ∧, ∨, ¬, → or ↔.
- Truth table
- A table giving the value of a formula for every assignment of truth values.
- Conditional
- The proposition p → q, false only when p is true and q is false.
- Biconditional
- The proposition p ↔ q, true when p and q have the same truth value.
- Converse
- Of p → q, the statement q → p.
- Contrapositive
- Of p → q, the statement ¬q → ¬p, which is equivalent to it.
- Vacuous truth
- The truth of a conditional whose hypothesis is false.
- Exclusive or
- The connective p ⊕ q, true when exactly one of p and q is true.
Practice problems
Work each problem on paper first, then reveal the answer to check.
How many rows does a truth table with 5 variables have?
Show answer
2 to the 5 = 32.
Is 'x squared is non-negative' a proposition if x is any real number?
Show answer
As a universal statement, yes (true); with x unspecified it is a predicate that is true for every x.
Write the converse and contrapositive of 'if it rains, the game is canceled'.
Show answer
Converse: if the game is canceled, it rains. Contrapositive: if the game is not canceled, it does not rain.
Translate 'you can vote only if you are 18 or older' using v and a.
Show answer
v → a.
When is p ⊕ q true?
Show answer
When exactly one of p and q is true.
Add parentheses to show the meaning of ¬p ∨ q ∧ r.
Show answer
(¬p) ∨ (q ∧ r).
Evaluate (p → q) ∧ (q → p) when p is T and q is F.
Show answer
p → q is F, so the conjunction is F.
Write Python for 'if the list is non-empty then its first element is positive' as a Boolean expression.
Show answer
(not xs) or xs[0] > 0, which also short-circuits safely.
Translate 'the build passes but the tests are slow'.
Show answer
b ∧ s; 'but' is logically 'and'.
Give a true conditional whose converse is false.
Show answer
If n is divisible by 4 then n is even; the converse fails for n = 6.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.