CMP-250 · Lesson 4 of 12 · 32 min
Predicate logic and quantifiers
Propositional logic cannot express a statement like every list has a length, or some user has administrator rights. Those statements talk about all or some members of a collection. Predicate logic, also called first-order logic, adds variables, predicates and quantifiers so that we can say such things precisely. It is the language of mathematical definitions, of database queries and of program specifications.
Predicates and domains
A predicate is a statement containing variables that becomes a proposition when the variables are given values. Write \(P(x)\) for x is prime. Then \(P(7)\) is true and \(P(8)\) is false. A predicate can have several variables: \(L(x, y)\) for x is less than y gives \(L(2, 5)\) true and \(L(5, 2)\) false. In programming terms, a predicate is a function that returns a Boolean.
Every variable ranges over a domain (also called the universe of discourse): the set of values it may take. The domain must be stated, because the truth of a quantified statement depends on it. The statement every number has a square root that is also a number is false if the domain is the integers (2 has no integer square root) and true if the domain is the non-negative real numbers.
The two quantifiers
The universal quantifier \(\forall\) means for all. The statement \(\forall x\, P(x)\) is true when \(P(x)\) is true for every \(x\) in the domain. The existential quantifier \(\exists\) means there exists. The statement \(\exists x\, P(x)\) is true when \(P(x)\) is true for at least one \(x\) in the domain. Over a finite domain \(\{a_1, \ldots, a_n\}\), the universal is a big conjunction and the existential is a big disjunction.
Python's built-in functions all and any implement exactly these two quantifiers over a finite collection. all returns True for an empty collection and any returns False, which matches the logic: a universal statement about nothing is vacuously true, and an existential statement about nothing is false.
nums = [4, 8, 15, 16, 23, 42]
is_even = lambda n: n % 2 == 0
print("for all n, n even? ", all(is_even(n) for n in nums))
print("exists n, n even? ", any(is_even(n) for n in nums))
print("for all n, n < 50? ", all(n < 50 for n in nums))
print("exists n, n > 100? ", any(n > 100 for n in nums))
print("empty domain: all ->", all(is_even(n) for n in []), " any ->", any(is_even(n) for n in []))for all n, n even? False exists n, n even? True for all n, n < 50? True exists n, n > 100? False empty domain: all -> True any -> False
| Statement | Domain | Truth value | Reason |
|---|---|---|---|
| ∀x (x² ≥ 0) | Real numbers | True | Squares are never negative |
| ∃x (x² = 2) | Integers | False | No integer squares to 2 |
| ∃x (x² = 2) | Real numbers | True | x = √2 |
| ∀x (x + 1 > x) | Integers | True | Adding 1 increases any integer |
| ∀x (x + 1 > x) | Python int8 values with wraparound | False | 127 + 1 wraps to −128 |
| ∃x (x < 0) | Natural numbers | False | Natural numbers are non-negative |
The fourth and fifth rows make a point that matters in practice. A property that holds for mathematical integers can fail for the fixed-width integers inside a computer, as you saw with two's complement overflow in CMPUT-174 and as C programmers meet in CMPUT-201. Stating the domain is not pedantry; it is part of a correct specification.
Restricted quantifiers
We often want to quantify over part of a domain: every student in the class passed, or some prime is even. There are standard patterns. A universal restricted to elements with property \(S\) uses a conditional: \(\forall x\, (S(x) \to P(x))\). An existential restricted to \(S\) uses a conjunction: \(\exists x\, (S(x) \wedge P(x))\). Getting these the wrong way round is a classic error.
Worked problem
Choosing the right connective
Domain: all people. S(x) means x is a student; P(x) means x passed. Translate (a) every student passed and (b) some student passed.
Show solution
- (a) For each person, if they are a student then they passed: ∀x (S(x) → P(x)).
- The tempting ∀x (S(x) ∧ P(x)) says everyone is a student and everyone passed, which is far too strong.
- (b) Some person is a student and passed: ∃x (S(x) ∧ P(x)).
- The tempting ∃x (S(x) → P(x)) is true if anyone at all is not a student, because the conditional is vacuously true for that person.
Answer: (a) ∀x (S(x) → P(x)); (b) ∃x (S(x) ∧ P(x)). Universal goes with →, existential goes with ∧.
Negating quantified statements
Negation interacts with quantifiers through De Morgan's laws for quantifiers. It is not the case that every \(x\) has property \(P\) means some \(x\) lacks it. It is not the case that some \(x\) has property \(P\) means every \(x\) lacks it.
To negate a statement with several quantifiers, move the negation inward one quantifier at a time, flipping each \(\forall\) to \(\exists\) and each \(\exists\) to \(\forall\), and finally negate the predicate using the propositional laws from lesson 3. This mechanical procedure is how you find what a counterexample must look like.
Worked problem
Negating a restricted universal
Negate: every function in the module has a test. Use F(x) for x is a function in the module and T(x) for x has a test.
Show solution
- Original: ∀x (F(x) → T(x)).
- Negate: ¬∀x (F(x) → T(x)) ≡ ∃x ¬(F(x) → T(x)).
- ¬(F(x) → T(x)) ≡ ¬(¬F(x) ∨ T(x)) ≡ F(x) ∧ ¬T(x).
- Result: ∃x (F(x) ∧ ¬T(x)).
Answer: Some function in the module has no test. The negation tells you exactly what a counterexample is: one untested function.
Nested quantifiers
Statements with more than one quantifier are where predicate logic becomes powerful and where most errors happen. The order of quantifiers of different kinds matters. Consider the domain of people and \(L(x, y)\) meaning x loves y.
| Formula | English reading |
|---|---|
| ∀x ∃y L(x, y) | Everyone loves someone (possibly different people) |
| ∃y ∀x L(x, y) | There is one person whom everyone loves |
| ∃x ∀y L(x, y) | Someone loves everyone |
| ∀y ∃x L(x, y) | Everyone is loved by someone |
| ∀x ∀y L(x, y) | Everyone loves everyone |
| ∃x ∃y L(x, y) | Someone loves someone |
The difference between \(\forall x\, \exists y\) and \(\exists y\, \forall x\) is the difference between a choice that depends on \(x\) and a single choice that works for all \(x\). For every integer there is a larger integer, \(\forall x\, \exists y\, (y > x)\), is true: take \(y = x + 1\). There is an integer larger than every integer, \(\exists y\, \forall x\, (y > x)\), is false: no largest integer exists. Quantifiers of the same kind can be swapped freely: \(\forall x\, \forall y\) and \(\forall y\, \forall x\) mean the same.
D = range(5) # a small finite domain {0,...,4}
L = lambda x, y: (x + y) % 5 == 0 # "y is an additive inverse of x mod 5"
forall_exists = all(any(L(x, y) for y in D) for x in D)
exists_forall = any(all(L(x, y) for x in D) for y in D)
print("forall x exists y L(x,y):", forall_exists)
print("exists y forall x L(x,y):", exists_forall)
for x in D:
print(f" x = {x}: witness y = {[y for y in D if L(x, y)][0]}")forall x exists y L(x,y): True exists y forall x L(x,y): False x = 0: witness y = 0 x = 1: witness y = 4 x = 2: witness y = 3 x = 3: witness y = 2 x = 4: witness y = 1
Nested quantifiers are how mathematics defines its central concepts. In calculus, a sequence converges to \(L\) if for every tolerance \(\varepsilon > 0\) there exists an index \(N\) such that for every \(n \ge N\), \(|a_n - L| < \varepsilon\). In algorithm analysis, which you will study in CMPUT-204, a function \(f(n)\) is \(O(g(n))\) if there exist constants \(c > 0\) and \(n_0\) such that for all \(n \ge n_0\), \(f(n) \le c\, g(n)\). Reading such definitions requires exactly the skills of this lesson.
Worked problem
Negating a nested statement
Negate the definition of f(n) = O(g(n)): ∃c ∃n₀ ∀n (n ≥ n₀ → f(n) ≤ c·g(n)), with c > 0 assumed.
Show solution
- Move the negation past the quantifiers, flipping each: ∀c ∀n₀ ∃n ¬(n ≥ n₀ → f(n) ≤ c·g(n)).
- Negate the conditional: ¬(A → B) ≡ A ∧ ¬B.
- Result: ∀c ∀n₀ ∃n (n ≥ n₀ ∧ f(n) > c·g(n)).
- In words: no matter which constant and starting point you choose, there is some n past the start where f(n) exceeds c times g(n).
Answer: To prove f is not O(g), show that for every c and n₀ you can find such an n. This is how one proves n² is not O(n).
Translating English and specifications
Translating English into predicate logic forces you to resolve ambiguity. Words such as every, each, all and any usually signal \(\forall\); some, there is, at least one and a usually signal \(\exists\). But English is slippery: a dog is a mammal is universal (every dog), while a dog is barking is existential (some dog). Any can mean either: if any test fails, stop is existential, while any integer squared is non-negative is universal.
Worked problem
Formalizing a uniqueness claim
Formalize: every user has exactly one primary email address. Use U(x) for x is a user and E(x, y) for y is a primary email of x.
Show solution
- At least one: ∃y E(x, y).
- At most one: ∀y ∀z ((E(x, y) ∧ E(x, z)) → y = z).
- Combine for each user: ∀x (U(x) → (∃y E(x, y) ∧ ∀y ∀z ((E(x, y) ∧ E(x, z)) → y = z))).
- This is often abbreviated with the uniqueness quantifier ∃!y E(x, y).
Answer: ∀x (U(x) → ∃!y E(x, y)), where ∃! expands to "at least one and at most one".
Predicate logic and databases
SQL is applied predicate logic. A query such as SELECT name FROM users WHERE age >= 18 AND country = 'CA' evaluates a predicate on each row. EXISTS in SQL is the existential quantifier. SQL has no direct FOR ALL, so universal queries are written with double negation: find the students for whom there does not exist a required course they have not taken. That is \(\neg \exists c\, (R(c) \wedge \neg T(s, c))\), which is equivalent to \(\forall c\, (R(c) \to T(s, c))\). You will write such queries in CMPUT-291.
required = {"CMPUT-174", "CMPUT-272", "MATH-125"}
taken = {
"Ana": {"CMPUT-174", "CMPUT-272", "MATH-125", "STAT-151"},
"Ben": {"CMPUT-174", "MATH-125"},
"Chloe": {"CMPUT-174", "CMPUT-272", "MATH-125"},
}
for s, courses in taken.items():
universal = all(c in courses for c in required) # for all c: R(c) -> T(s,c)
double_neg = not any(c not in courses for c in required) # not exists c: R(c) and not T(s,c)
print(f"{s:6s} completed all required: {universal} (double-negation form: {double_neg})")Ana completed all required: True (double-negation form: True) Ben completed all required: False (double-negation form: False) Chloe completed all required: True (double-negation form: True)
Free and bound variables
In \(\forall x\, P(x, y)\) the variable \(x\) is bound by the quantifier, and \(y\) is free. A formula with free variables is a predicate, not a proposition; its truth depends on the free variables. This is the same idea as local and global variables in programming: inside the quantifier, \(x\) is a local name, and renaming it consistently, as in \(\forall z\, P(z, y)\), does not change the meaning. Using the same letter for two different bound variables in one formula is legal but confusing; good style gives each quantifier its own variable.
Common mistakes
MistakeTranslating 'every S is P' as ∀x (S(x) ∧ P(x)).
InsteadUse ∀x (S(x) → P(x)); the conjunction claims everything is an S.
MistakeTranslating 'some S is P' as ∃x (S(x) → P(x)).
InsteadUse ∃x (S(x) ∧ P(x)); the conditional is made true by any non-S.
MistakeNegating ∀x P(x) as ∀x ¬P(x).
InsteadThe negation is ∃x ¬P(x): it takes only one counterexample.
MistakeSwapping ∀x ∃y to ∃y ∀x as if nothing changed.
InsteadMixed quantifiers cannot be swapped; ∃y ∀x is a much stronger claim.
MistakeIgnoring the domain.
InsteadState the domain; the same formula can be true over the reals and false over the integers.
MistakeExpecting all([]) to be False.
InsteadA universal over an empty domain is vacuously true, so all([]) is True.
Summary and key terms
- A predicate becomes a proposition when its variables get values from a stated domain.
- ∀x P(x) means P holds for every x; ∃x P(x) means P holds for at least one x.
- Python's all and any are the universal and existential quantifiers over finite collections.
- Restricted universals use →; restricted existentials use ∧.
- ¬∀x P(x) ≡ ∃x ¬P(x) and ¬∃x P(x) ≡ ∀x ¬P(x).
- The order of mixed quantifiers matters: ∀x ∃y allows y to depend on x; ∃y ∀x does not.
- SQL queries, Big-O definitions and specifications are written in predicate logic.
- Predicate
- A statement with variables that becomes a proposition when values are supplied.
- Domain
- The set of values a variable may take.
- Universal quantifier
- The symbol ∀, meaning for all.
- Existential quantifier
- The symbol ∃, meaning there exists at least one.
- Uniqueness quantifier
- The symbol ∃!, meaning there exists exactly one.
- Bound variable
- A variable governed by a quantifier.
- Free variable
- A variable not bound by any quantifier.
- Nested quantifiers
- Two or more quantifiers in sequence, whose order can change meaning.
- Witness
- A specific value showing that an existential statement is true.
- Counterexample
- A specific value showing that a universal statement is false.
Practice problems
Work each problem on paper first, then reveal the answer to check.
Domain: integers. Is ∀x ∃y (x + y = 0) true?
Show answer
Yes; for each x take y = −x.
Domain: natural numbers. Is ∃y ∀x (x + y = 0) true?
Show answer
No; only x = 0 works with y = 0, so no single y works for every x.
Negate ∃x (x > 0 ∧ x² < 0).
Show answer
∀x (x ≤ 0 ∨ x² ≥ 0).
Translate 'no bug is harmless' with B(x) and H(x).
Show answer
∀x (B(x) → ¬H(x)), equivalently ¬∃x (B(x) ∧ H(x)).
Write Python to test 'every word in words has length at most 10'.
Show answer
all(len(w) <= 10 for w in words)
What does any(x < 0 for x in []) return, and why?
Show answer
False; an existential over an empty domain has no witness.
Translate 'every node has a parent except the root' using N(x), P(x, y) for y is the parent of x, and r for the root.
Show answer
∀x ((N(x) ∧ x ≠ r) → ∃y P(x, y)).
Negate 'for every ε > 0 there is a δ > 0 that works'.
Show answer
There is an ε > 0 such that no δ > 0 works.
Which variable is free in ∃y (x < y)?
Show answer
x.
Explain why ∀x ∀y P(x, y) ≡ ∀y ∀x P(x, y).
Show answer
Both say P holds for every pair; quantifiers of the same kind commute.
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.