Skip to content
The Lumenary

Math laboratory

Images, composites and quantifiers on all small examples

Checks on every function between sets of size at most 3 that images preserve intersections exactly for injective functions, that g o f injective forces f (but not g) to be injective, and on every relation between sets of size at most 3 that exists-forall implies forall-exists but not conversely, printing the smallest counterexamples.

Back to the labExhaustive finite checkCounterexample
Images, composites and quantifiers on all small examplesPythonExhaustive finite checkCounterexampleComputationally tested
"""Checking statements about sets and functions on every small example.

Before proving a statement in Lean it pays to test it. Here we check, on all
functions between sets of size at most 3:

* f(S & T) = f(S) & f(T) for all subsets S, T holds exactly when f is
  injective (Mathlib's Set.image_inter needs that hypothesis);
* if g o f is injective then f is injective - and g need not be;
* for every relation P on small sets, "exists x, for all y, P(x, y)" implies
  "for all y, exists x, P(x, y)", but not conversely.

An exhaustive search over a finite universe can refute a general claim (one
counterexample suffices) but can never prove it for all sets.
"""

import itertools

def functions(a, b):
    """All functions from range(a) to range(b), as tuples of values."""
    return list(itertools.product(range(b), repeat=a))

def subsets(n):
    """All subsets of range(n), as frozensets."""
    return [frozenset(c) for k in range(n + 1)
            for c in itertools.combinations(range(n), k)]

def image(f, s):
    """The image f(s) of a set s under a function given as a tuple."""
    return frozenset(f[x] for x in s)

def is_injective(f):
    """True if no two inputs share an output."""
    return len(set(f)) == len(f)

def preserves_intersections(f):
    """True if f(S & T) == f(S) & f(T) for all subsets S, T of the domain."""
    subs = subsets(len(f))
    return all(image(f, s & t) == image(f, s) & image(f, t)
               for s in subs for t in subs)

def compose(g, f):
    """The composite g o f of functions given as tuples."""
    return tuple(g[y] for y in f)

def exists_forall(rel, a, b):
    """Exists x in range(a) such that for all y in range(b), (x, y) in rel."""
    return any(all((x, y) in rel for y in range(b)) for x in range(a))

def forall_exists(rel, a, b):
    """For all y in range(b) there exists x in range(a) with (x, y) in rel."""
    return all(any((x, y) in rel for x in range(a)) for y in range(b))

def relations(a, b):
    """All relations between range(a) and range(b), as frozensets of pairs."""
    pairs = [(x, y) for x in range(a) for y in range(b)]
    return [frozenset(p for p, keep in zip(pairs, bits) if keep)
            for bits in itertools.product([False, True], repeat=len(pairs))]

def main():
    total = agree = 0
    for a in range(4):
        for b in range(1, 4):
            for f in functions(a, b):
                total += 1
                agree += preserves_intersections(f) == is_injective(f)
    print("1. f(S & T) = f(S) & f(T) for all S, T  <=>  f injective")
    print(f"   checked on all {total} functions between sets of size <= 3:"
          f" agrees in {agree}")
    f = (0, 0)
    s, t = frozenset({0}), frozenset({1})
    print(f"   smallest counterexample: f = {f}, S = {{0}}, T = {{1}}:")
    print(f"   f(S & T) = {set(image(f, s & t))},"
          f" f(S) & f(T) = {set(image(f, s) & image(f, t))}")
    print()
    bad_f = bad_g = 0
    example = None
    for a, b, c in itertools.product(range(1, 4), repeat=3):
        for f in functions(a, b):
            for g in functions(b, c):
                if is_injective(compose(g, f)):
                    bad_f += not is_injective(f)
                    if not is_injective(g):
                        bad_g += 1
                        example = example or (f, g)
    print("2. g o f injective: cases where f is not injective:", bad_f)
    print("   cases where g is not injective:", bad_g,
          f"(e.g. f = {example[0]}, g = {example[1]})")
    print()
    imp = conv = count = 0
    for a in range(1, 4):
        for b in range(1, 4):
            for rel in relations(a, b):
                count += 1
                ef, fe = exists_forall(rel, a, b), forall_exists(rel, a, b)
                imp += ef and not fe
                conv += fe and not ef
    print(f"3. all {count} relations between sets of size 1 to 3:")
    print(f"   exists-forall true but forall-exists false: {imp}")
    print(f"   forall-exists true but exists-forall false: {conv}")
    print("   e.g. P = equality on {0, 1}: every y has x = y, no x fits all")
    print()
    print("Epistemic status: exhaustive checks over finitely many small sets.")
    print("The counterexamples refute the general claims for good; the")
    print("agreements are evidence only. The general theorems are proved in")
    print("the lesson and in Lean (image_inter_of_injective, injective_of_comp,")
    print("exists_forall_imp).")

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

Used in