CMPUT-272 · Lesson 9 of 12 · 30 min

Relations, equivalence and partial orders

A function pairs each input with exactly one output. Many relationships in computing are less restrictive: a student takes several courses, a web page links to many pages, a task must finish before several others start. A relation captures any such relationship. This lesson defines relations, studies their key properties, and focuses on two especially important kinds: equivalence relations, which group objects that are alike, and partial orders, which arrange objects by precedence. It ends with closures and Warshall's algorithm, which computes reachability.

Relations

A binary relation \(R\) from a set \(A\) to a set \(B\) is any subset of \(A \times B\). We write \(a\, R\, b\) or \((a, b) \in R\) when \(a\) is related to \(b\). A relation on \(A\) is a relation from \(A\) to itself. Every function is a relation, but a relation can relate one element to many others, or to none.

Relations on a finite set can be represented in three common ways, and each is useful in programming. A set of pairs is the direct definition. A directed graph draws each element as a dot and each pair \((a, b)\) as an arrow from \(a\) to \(b\). A Boolean matrix \(M\) has \(M[i][j] = 1\) exactly when element \(i\) is related to element \(j\).

The divides relation on {1, 2, 3, 4} as pairs and as a matrix
A = [1, 2, 3, 4]
divides = {(a, b) for a in A for b in A if b % a == 0}
print("pairs:", sorted(divides))
print("matrix (row a, column b):")
for a in A:
    print("   ", [1 if (a, b) in divides else 0 for b in A])
Output
pairs: [(1, 1), (1, 2), (1, 3), (1, 4), (2, 2), (2, 4), (3, 3), (4, 4)]
matrix (row a, column b):
    [1, 1, 1, 1]
    [0, 1, 0, 1]
    [0, 0, 1, 0]
    [0, 0, 0, 1]

Relational databases are named after this idea. A table with columns student and course is a relation from Students to Courses, and a table with more columns is an \(n\)-ary relation, a subset of an \(n\)-fold Cartesian product. The operations of relational algebra, which SQL implements, are set operations on relations. You will use them in CMPUT-291.

Properties of relations

Four properties of a relation \(R\) on a set \(A\) appear again and again.

Four key properties of relations on a set
PropertyDefinitionExample that has itExample that lacks it
Reflexive∀a (a R a)≤ on integers; equality< on integers
Symmetric∀a ∀b (a R b → b R a)is a sibling of; equality≤ on integers
Antisymmetric∀a ∀b ((a R b ∧ b R a) → a = b)≤ on integers; divides on ℕ⁺is a sibling of
Transitive∀a ∀b ∀c ((a R b ∧ b R c) → a R c)≤; divides; is an ancestor ofis a parent of

Antisymmetric is not the opposite of symmetric. A relation can be both (equality is both symmetric and antisymmetric) or neither. Antisymmetry says that the only way two elements can be related in both directions is if they are the same element. In the graph picture, a reflexive relation has a loop at every vertex, a symmetric relation has every arrow paired with a reverse arrow, and an antisymmetric relation has no pair of distinct vertices with arrows both ways.

Checking the four properties by program
def props(R, A):
    return {
        "reflexive": all((a, a) in R for a in A),
        "symmetric": all((b, a) in R for (a, b) in R),
        "antisymmetric": all(a == b for (a, b) in R if (b, a) in R),
        "transitive": all((a, d) in R for (a, b) in R for (c, d) in R if b == c),
    }

A = range(1, 7)
relations = {
    "divides": {(a, b) for a in A for b in A if b % a == 0},
    "less than": {(a, b) for a in A for b in A if a < b},
    "same parity": {(a, b) for a in A for b in A if a % 2 == b % 2},
    "differ by 1": {(a, b) for a in A for b in A if abs(a - b) == 1},
}
for name, R in relations.items():
    p = props(R, A)
    print(f"{name:12s}", "  ".join(f"{k[:5]}={'Y' if v else 'n'}" for k, v in p.items()))
Output
divides      refle=Y  symme=n  antis=Y  trans=Y
less than    refle=n  symme=n  antis=Y  trans=Y
same parity  refle=Y  symme=Y  antis=n  trans=Y
differ by 1  refle=n  symme=Y  antis=n  trans=n

Worked problem

Proving transitivity

On ℤ, define a R b if a − b is divisible by 3. Prove R is transitive.

Show solution
  1. Assume a R b and b R c. Then a − b = 3m and b − c = 3n for some integers m, n.
  2. Add: a − c = (a − b) + (b − c) = 3m + 3n = 3(m + n).
  3. So a − c is divisible by 3, and a R c.

Answer: R is transitive. It is also reflexive (a − a = 0 = 3 · 0) and symmetric (b − a = −(a − b)), which makes it an equivalence relation.

Equivalence relations

A relation that is reflexive, symmetric and transitive is an equivalence relation. It expresses a notion of sameness: two objects are related if they are alike in some respect. Equality is the strictest equivalence relation. Other examples: having the same remainder when divided by \(n\); having the same file extension; being reachable from each other along roads; being anagrams of each other.

The equivalence class of an element \(a\), written \([a]\), is the set of all elements related to \(a\): \([a] = \{x \in A \mid x\, R\, a\}\). The central theorem is that the equivalence classes of an equivalence relation partition the set: every element belongs to exactly one class, and two classes are either identical or disjoint. Conversely, every partition defines an equivalence relation, namely being in the same part. Equivalence relations and partitions are two descriptions of the same thing.

Worked problem

Congruence classes modulo 3

Describe the equivalence classes of a R b if 3 divides a − b, on the integers.

Show solution
  1. a R b exactly when a and b have the same remainder on division by 3.
  2. The possible remainders are 0, 1 and 2, giving three classes.
  3. [0] = {..., −6, −3, 0, 3, 6, ...}; [1] = {..., −5, −2, 1, 4, 7, ...}; [2] = {..., −4, −1, 2, 5, 8, ...}.
  4. Every integer lies in exactly one of these classes, so they partition ℤ.

Answer: Three classes, the residue classes mod 3. This relation is called congruence modulo 3, written a ≡ b (mod 3), and lesson 11 builds arithmetic on it.

Grouping by an equivalence relation: anagram classes
words = ["listen", "silent", "enlist", "google", "banana", "tinsel", "elgoog", "nabana"]
classes = {}
for w in words:
    key = "".join(sorted(w))            # anagrams share the same sorted letters
    classes.setdefault(key, []).append(w)
for key, members in classes.items():
    print(f"[{members[0]}] = {members}")
print("classes are disjoint and cover all words:",
      sum(len(m) for m in classes.values()) == len(words))
Output
[listen] = ['listen', 'silent', 'enlist', 'tinsel']
[google] = ['google', 'elgoog']
[banana] = ['banana', 'nabana']
classes are disjoint and cover all words: True

The program uses a standard technique: compute a canonical key for each element so that two elements are equivalent exactly when their keys are equal, then group by key. Hash-based grouping, database GROUP BY, and duplicate detection all work this way. Union–find, a data structure you will meet in CMPUT-204, maintains equivalence classes as pairs are added, and is used in Kruskal's minimum spanning tree algorithm.

Partial orders

A relation that is reflexive, antisymmetric and transitive is a partial order. A set with a partial order is called a poset. Partial orders describe precedence or containment: \(\le\) on numbers, \(\subseteq\) on sets, divides on positive integers, and is a prerequisite of on courses (taking the relation to include each course itself). The word partial means that some pairs may be incomparable: neither \(\{1\} \subseteq \{2\}\) nor \(\{2\} \subseteq \{1\}\). If every pair is comparable, the order is a total order, like \(\le\) on the integers.

A Hasse diagram draws a finite poset compactly. Place each element above the elements it is greater than, draw a line from \(a\) up to \(b\) only when \(a\) is below \(b\) with nothing in between, and omit loops and lines implied by transitivity. Reading upward along lines recovers the whole order.

Hasse diagram of the divisors of 12 under divisibility: 1 at the bottom; 2 and 3 above it; 4 above 2; 6 above 2 and 3; 12 at the top above 4 and 6.
Hasse diagram of the divisors of 12 ordered by divides

Some elements of a poset are special. A minimal element has nothing below it; a maximal element has nothing above it. A least element is below everything, and a greatest element is above everything. In the divisors of 12, 1 is least and 12 is greatest. In the set \(\{2, 3, 4, 6\}\) under divides, both 2 and 3 are minimal but there is no least element.

Topological sorting

Many scheduling problems ask for a total order that respects a partial order: take courses in an order that respects prerequisites, compile modules so that each comes after the modules it imports, run build steps in a valid sequence. Such a total order is a topological sort. Every finite poset has one, and it can be found by repeatedly removing a minimal element.

Topological sort: repeatedly take a minimal element
prereq = {                 # course: set of courses that must come first
    "CMPUT-174": set(),
    "CMPUT-201": {"CMPUT-174"},
    "CMPUT-272": {"CMPUT-174"},
    "CMPUT-204": {"CMPUT-201", "CMPUT-272"},
    "CMPUT-304": {"CMPUT-204"},
    "CMPUT-474": {"CMPUT-272", "CMPUT-204"},
}
order, remaining = [], {c: set(p) for c, p in prereq.items()}
while remaining:
    minimal = sorted(c for c, p in remaining.items() if not p)   # no unmet prerequisites
    if not minimal:
        raise ValueError("cycle: no valid order")
    c = minimal[0]
    order.append(c)
    del remaining[c]
    for p in remaining.values():
        p.discard(c)
print(" -> ".join(order))
Output
CMPUT-174 -> CMPUT-201 -> CMPUT-272 -> CMPUT-204 -> CMPUT-304 -> CMPUT-474

If the relation contains a cycle, for example A before B and B before A, antisymmetry fails, the relation is not a partial order, and no valid order exists. The program above detects this when no minimal element remains. Build tools and package managers report exactly this situation as a circular dependency.

Closures and Warshall's algorithm

A relation may lack a property we need. The closure of \(R\) with respect to a property is the smallest relation containing \(R\) that has the property. The reflexive closure adds every pair \((a, a)\); the symmetric closure adds every reverse pair \((b, a)\). The transitive closure is the most useful and the hardest: it adds \((a, c)\) whenever there is a path from \(a\) to \(c\) through any number of steps. If \(R\) is is a direct flight to, its transitive closure is can be reached by some sequence of flights.

Warshall's algorithm computes the transitive closure of a relation on \(n\) elements in \(n^3\) steps. It considers each element \(k\) in turn as a possible intermediate stop and adds \((i, j)\) whenever \((i, k)\) and \((k, j)\) are already present.

Warshall's algorithm computes reachability
def warshall(M):
    n = len(M)
    R = [row[:] for row in M]
    for k in range(n):
        for i in range(n):
            for j in range(n):
                R[i][j] = R[i][j] or (R[i][k] and R[k][j])
    return R

names = ["a", "b", "c", "d"]
M = [[0, 1, 0, 0],    # a -> b
     [0, 0, 1, 0],    # b -> c
     [0, 0, 0, 1],    # c -> d
     [0, 0, 0, 0]]
T = warshall(M)
for i, row in enumerate(T):
    print(names[i], "reaches", [names[j] for j in range(4) if row[j]])
Output
a reaches ['b', 'c', 'd']
b reaches ['c', 'd']
c reaches ['d']
d reaches []

Worked problem

Tracing Warshall by hand

For the chain a → b → c → d, which pairs does each round of Warshall add?

Show solution
  1. k = a: no arrows enter a, so nothing is added.
  2. k = b: a → b and b → c give a → c.
  3. k = c: a → c, b → c and c → d give a → d and b → d.
  4. k = d: no arrows leave d, so nothing is added.

Answer: Added pairs: (a, c), (a, d), (b, d). The closure has 6 pairs, every pair (x, y) with x before y.

Warshall's algorithm, published by Stephen Warshall in 1962, is closely related to the Floyd–Warshall algorithm for shortest paths, which you will study in CMPUT-204. Its correctness proof is an induction on \(k\): after round \(k\), the matrix records every path whose intermediate stops all lie among the first \(k\) elements. That is a loop invariant, the topic of lesson 12.

Common mistakes

  • MistakeThinking antisymmetric means not symmetric.

    InsteadAntisymmetric means a R b and b R a force a = b; equality is both symmetric and antisymmetric.

  • MistakeChecking transitivity only for chains of distinct elements.

    InsteadCheck every a R b and b R c, including cases where some elements coincide.

  • MistakeForgetting reflexivity when claiming an equivalence relation.

    InsteadVerify all three properties: reflexive, symmetric and transitive.

  • MistakeDrawing every comparable pair in a Hasse diagram.

    InsteadDraw only covering pairs; transitivity and reflexivity are implied.

  • MistakeAssuming every partial order is total.

    InsteadSome pairs, like {1} and {2} under ⊆, can be incomparable.

  • MistakeComputing the transitive closure by adding only two-step paths once.

    InsteadPaths of any length count; Warshall's algorithm or repeated composition is needed.

Summary and key terms

  • A relation from A to B is a subset of A × B; represent it as pairs, a directed graph or a Boolean matrix.
  • Key properties: reflexive, symmetric, antisymmetric and transitive.
  • An equivalence relation is reflexive, symmetric and transitive; its classes partition the set.
  • A partial order is reflexive, antisymmetric and transitive; a total order also compares every pair.
  • Hasse diagrams draw finite posets using only covering lines.
  • A topological sort lists a poset's elements in an order consistent with it.
  • The transitive closure records reachability; Warshall's algorithm computes it in n cubed steps.
Relation
A subset of a Cartesian product; on A, a subset of A × A.
Reflexive
Every element is related to itself.
Symmetric
a R b implies b R a.
Antisymmetric
a R b and b R a imply a = b.
Transitive
a R b and b R c imply a R c.
Equivalence relation
A reflexive, symmetric and transitive relation.
Equivalence class
The set of all elements related to a given element under an equivalence relation.
Partial order
A reflexive, antisymmetric and transitive relation.
Hasse diagram
A drawing of a finite poset showing only covering relations, with larger elements higher.
Transitive closure
The smallest transitive relation containing R; it records reachability.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Is 'is a sibling of' symmetric? Is it transitive?

    Show answer

    Symmetric, yes. Not transitive: a is a sibling of b and b is a sibling of a, but a is not a sibling of itself.

  2. Is ⊆ on sets a partial order? Is it total?

    Show answer

    It is a partial order (reflexive, antisymmetric, transitive) but not total.

  3. List the equivalence classes of 'same remainder mod 4' on ℤ.

    Show answer

    [0], [1], [2], [3]: the four residue classes.

  4. Which property fails for < on integers to be a partial order?

    Show answer

    Reflexivity: a < a is false.

  5. Give a relation on {1, 2, 3} that is symmetric but not transitive.

    Show answer

    {(1, 2), (2, 1)}: 1 R 2 and 2 R 1 but not 1 R 1.

  6. Draw (in words) the Hasse diagram of the power set of {a, b}.

    Show answer

    ∅ at the bottom; {a} and {b} above it; {a, b} at the top.

  7. Find a topological order for: B needs A; C needs A; D needs B and C.

    Show answer

    A, B, C, D (or A, C, B, D).

  8. Compute the transitive closure of {(1, 2), (2, 3), (3, 1)} on {1, 2, 3}.

    Show answer

    All 9 pairs, since every element reaches every element around the cycle.

  9. How do equivalence relations relate to partitions?

    Show answer

    The classes of an equivalence relation form a partition, and every partition defines one.

  10. Why does a cycle prevent a topological sort?

    Show answer

    Elements on a cycle each need to come before the other, which no total order allows.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz