Skip to content
The Lumenary

Math laboratory

Hereditarily finite sets: V_omega in Ackermann's coding

Codes hereditarily finite sets as natural numbers (m is in n iff bit m of n is 1), computes ranks, checks that V_k is exactly the codes below |V_k| for k <= 5, that pairing, union and power set stay inside V_omega but leave each V_k, and computes |V_k|. Exact finite checks illustrating, by analogy, why inaccessible kappa make V_kappa a model of ZFC.

Back to the labExact computationExhaustive finite check
Hereditarily finite sets: V_omega in Ackermann's codingPythonExact computationExhaustive finite checkComputationally tested
"""V_omega, the finite shadow of an inaccessible cardinal.

An inaccessible cardinal kappa makes V_kappa a model of ZFC because kappa is
regular and a strong limit. The countable cardinal omega has both properties
too: a finite union of finite sets is finite, and the power set of a finite
set is finite. Correspondingly V_omega, the set of hereditarily finite sets,
satisfies every axiom of ZFC except Infinity.

Ackermann (1937) coded hereditarily finite sets by natural numbers: m is an
element of n exactly when bit m of n is 1. So n = 0 is the empty set, 1 is
{0}, 2 is {1} = {{0}}, 3 is {0, 1}, and so on. We use the coding to compute
ranks, check that V_k consists exactly of the codes below |V_k|, check that
pairing, union and power set never leave V_omega but do leave each V_k, and
compute how fast |V_k| grows.

Runs unchanged in the browser (Pyodide) using only the standard library.
"""

from functools import lru_cache
from itertools import combinations
import math
import textwrap

def members(n):
    """The codes of the elements of the set coded by n."""
    return [m for m in range(n.bit_length()) if n >> m & 1]

@lru_cache(maxsize=None)
def rank(n):
    """Von Neumann rank: 0 for the empty set, else 1 + max rank of members."""
    return 1 + max(rank(m) for m in members(n)) if n else 0

def pair(a, b):
    """The code of {a, b}."""
    return (1 << a) | (1 << b)

def union(n):
    """The code of the union of the members of n."""
    result = 0
    for m in members(n):
        result |= m
    return result

def power_set(n):
    """The code of the set of all subsets of n."""
    elems, result = members(n), 0
    for k in range(len(elems) + 1):
        for subset in combinations(elems, k):
            result |= 1 << sum(1 << e for e in subset)
    return result

def size_of_v(k):
    """|V_k|: |V_0| = 0 and |V_(k+1)| = 2^|V_k|."""
    size = 0
    for _ in range(k):
        size = 2 ** size
    return size

def decimal_digits_of_power_of_two(e):
    """Number of decimal digits of 2^e, without converting to a string."""
    digits = math.floor(e * math.log10(2)) + 1
    value = 1 << e
    while 10 ** digits <= value:
        digits += 1
    while digits > 1 and 10 ** (digits - 1) > value:
        digits -= 1
    return digits

def show(n):
    """Write the set coded by n with braces, e.g. 3 -> {{}, {{}}}."""
    return "{" + ", ".join(show(m) for m in members(n)) + "}"

def main():
    print("Ackermann codes:  " + ";  ".join(f"{n} = {show(n)}" for n in range(4)))
    print("V_k is exactly the set of codes below |V_k|:")
    for k in range(6):
        size = size_of_v(k)
        exact = all(rank(n) < k for n in range(size)) and rank(size) >= k
        print(f"   |V_{k}| = {size:>5}: {exact}")
    digits = decimal_digits_of_power_of_two(65536)
    print(f"   |V_6| = 2^65536 has {digits} decimal digits")
    print()
    v4 = range(16)
    print("Closure inside V_4 (codes 0..15), the first finite stage tested:")
    print(f"   pairs stay in V_4:      {all(pair(a, b) < 16 for a in v4 for b in v4)}")
    print(f"   unions stay in V_4:     {all(union(n) < 16 for n in v4)}")
    print(f"   power sets stay in V_4: {all(power_set(n) < 16 for n in v4)}")
    top = max(v4, key=rank)
    print(f"   e.g. {{{top}}} = {{{top}, {top}}} has code {pair(top, top)} and rank "
          f"{rank(pair(top, top))}: it lies in V_5, not V_4")
    print("In V_omega every pair, union and power set of a code is again a")
    print(f"finite code; e.g. 15 codes V_3, and its power set V_4 has code "
          f"{power_set(15)}.")
    print()
    print(textwrap.fill(
        "Epistemic status: exact computations with finitely many finite sets. "
        "They show the mechanism - each stage V_k fails to be closed, but their "
        "union V_omega is closed, because omega is regular and a strong limit - "
        "and so illustrate by analogy why V_kappa satisfies ZFC when kappa is "
        "inaccessible. That theorem is proved in the lesson; nothing about "
        "uncountable cardinals is computed here.",
        width=78,
    ))

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

Used in