Skip to content
The Lumenary

Math laboratory

Products of finite sets: existence and uniqueness

Checks exhaustively that maps X -> A x B correspond one-to-one to pairs of maps for small finite sets, then counts factorisations through three candidate products: an encoding of pairs as numbers passes, a set with a spare coordinate fails uniqueness, and the disjoint union fails existence and uniqueness. Exhaustive only for the sizes shown.

Back to the labExact computationExhaustive finite check
Products of finite sets: existence and uniquenessPythonExact computationExhaustive finite checkComputationally tested
"""The universal property of the product, checked on finite sets.

For finite sets X, A, B, maps X -> A x B correspond exactly to pairs of maps
(X -> A, X -> B). We check this bijection exhaustively for small sizes. Then
we test three candidate "products" P with maps p : P -> A and q : P -> B by
counting, for every pair (f, g) of maps from a test set X, how many maps
u : X -> P satisfy p . u = f and q . u = g. A product needs exactly one every
time. The numbers model k = a*|B| + b passes; a set with a spare coordinate
fails uniqueness; the disjoint union fails existence whatever maps we choose
(it has 5 elements but there are 6 pairs to hit), and with the maps chosen
here it also fails uniqueness. Exhaustive and exact for these small sets.
"""

from itertools import product

def maps(domain, codomain):
    """All functions domain -> codomain, as dicts."""
    domain, codomain = list(domain), list(codomain)
    return [dict(zip(domain, values))
            for values in product(codomain, repeat=len(domain))]

def pairing(f, g):
    """The map x -> (f(x), g(x)) into the product."""
    return {x: (f[x], g[x]) for x in f}

def factorisations(xs, P, p, q, f, g):
    """Number of u : X -> P with p(u(x)) = f(x) and q(u(x)) = g(x) for all x."""
    count = 1
    for x in xs:
        count *= sum(1 for z in P if p(z) == f[x] and q(z) == g[x])
    return count

def candidates(A, B):
    """Three candidate products: (name, P, p, q)."""
    nb = len(B)
    numbers = list(range(len(A) * nb))
    spare = list(product(A, B, [0, 1]))
    union = [("A", a) for a in A] + [("B", b) for b in B]
    return [
        ("numbers k = a*|B| + b", numbers,
         lambda k: A[k // nb], lambda k: B[k % nb]),
        ("A x B x {0, 1}", spare, lambda z: z[0], lambda z: z[1]),
        ("disjoint union A + B", union,
         lambda z: z[1] if z[0] == "A" else A[0],
         lambda z: z[1] if z[0] == "B" else B[0]),
    ]

def factorisation_counts(X, A, B, P, p, q):
    """The set of factorisation counts over all pairs (f, g)."""
    return {factorisations(X, P, p, q, f, g)
            for f in maps(X, A) for g in maps(X, B)}

def main():
    print("1. Maps X -> A x B versus pairs of maps (X -> A, X -> B)")
    for nx, na, nb in [(1, 2, 3), (2, 2, 2), (2, 3, 2), (3, 2, 2)]:
        X, A, B = range(nx), range(na), range(nb)
        n_pairs = len(maps(X, A)) * len(maps(X, B))
        images = {tuple(sorted(pairing(f, g).items()))
                  for f in maps(X, A) for g in maps(X, B)}
        total = len(maps(X, list(product(A, B))))
        ok = n_pairs == len(images) == total
        print(f"   |X|={nx} |A|={na} |B|={nb}: {n_pairs:>3} pairs, "
              f"{total:>3} maps, pairing is a bijection: {ok}")
    print()
    print("2. Factorisations through candidate products, |A| = 2, |B| = 3")
    A, B = ["a0", "a1"], ["b0", "b1", "b2"]
    for nx in (1, 2):
        X = range(nx)
        print(f"   test set X with {nx} element(s):")
        for name, P, p, q in candidates(A, B):
            counts = sorted(factorisation_counts(X, A, B, P, p, q))
            verdict = "product" if counts == [1] else "not a product"
            print(f"     {name:<22} counts {str(counts):<13} {verdict}")
    print()
    print("Epistemic status: exhaustive and exact for these small sets. Part 1")
    print("illustrates |A x B|^|X| = |A|^|X| |B|^|X|; part 2 shows how")
    print("existence (count >= 1) and uniqueness (count <= 1) can each fail.")
    print("The lesson proves the universal property for all sets.")

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

Used in