sizeless.py

Checks: GROWTH: The properness criterion criterion; GROWTH: The route is the deleted place's theorem; observation.

Cited on: GROWTH.

To run it: save sizeless.py, with growth.py beside it; standard library only.

"""sizeless.py -- growth with size taken out of the cost: what the
divisibility order alone can author, and what only the ordering of the
integers by size supplies.

QUESTION. Every growth law of growth.py extends by the LEAST
admissible m, so it orders its candidates by size, the archimedean
place the rungs delete. Take size out. Between candidate windows Z/m
the only canonical structure is the unital ring maps, and a
window-visible cost is one that order sees: invariant under the
order's automorphisms, which permute the primes, so a function of m's
factorization shape. Which of the primorial tower's
features (fields, squarefree-ness, every prime arriving, the
increasing order of arrival) can such a cost buy, and which are size's
alone?

THE ARGUMENT (written before the engine).
  (1) THE HOM ORDER. A unital map Z/a -> Z/b sends 1 to 1, so it is
      x mod b, well defined iff b | a. Window maps order the moves by
      divisibility, and distinct primes are an antichain: nothing in
      the window world ranks one prime above another.
  (2) COSTLESS INDEPENDENCE. With no cost every m coprime to M is
      admissible, composites and prime powers included: seed 2 may
      pick 9 and lose squarefree-ness, and seed 3 may pick 5, 7, 11,
      ... with 2 never entering. So fields, squarefree-ness and
      healing are authored by the selection, not by the demand.
  (3) FIELDS ARE WINDOW-PURCHASABLE. Let the cost be integer-valued,
      bounded below and STRICTLY MONOTONE under proper divisibility
      (d | m, d < m gives c(d) < c(m)); Omega,
      the prime factor count with multiplicity, is one. A composite
      coprime to M has a prime divisor, also coprime and cheaper, so
      every pick is a prime, under every tie-break.
  (4) THE PROPERNESS CRITERION. Under such a cost a prime p enters
      under every tie-break iff its SUBLEVEL SET {q prime : c(q) <=
      c(p)} is finite. While p is admissible every pick costs at most
      c(p), so picks come from the sublevel set, each consumed for
      good; a finite set runs out. An infinite one feeds a tie-break
      that picks the others forever: under Omega every prime costs 1,
      and the tie-break preferring odd primes starves 2. So every
      prime arrives under every tie-break iff the cost is PROPER, every
      sublevel set finite.
  (5) THE ROUTE. Rank the primes by any bijection rho with the positive
      integers and put c(m) = sum of a rho(p) over p^a || m. It is
      strictly monotone under proper divisibility and proper, and from 1
      its greedy walk takes the primes in the order rho. So every
      proper strictly monotone cost buys the destination, properness
      alone making the least cost exist, and its ranking, up to the
      tie-break among primes of equal cost, the route: every
      enumeration is some proper cost's. (A
      window-visible cost, a function of shape, is never proper: every
      prime costs the same. Corrected on review from "window data buys
      the destination".)
  (6) THE PRATT ORDER. One arrival rule reads only the rings Z/q: admit q only
      when every prime factor of q - 1, the order of the unit group of
      Z/q, already divides the modulus. That is the Pratt certificate
      order (the parents of q are the primes of q - 1). It forces 2
      first, and while 2 is the only prime seated, exactly the Fermat
      primes are admissible; the increasing order is one of its linear
      extensions, since every parent is smaller than its child.
      Counting the extensions measures how much route the
      rule supplies against the log2(n!) bits of an
      arbitrary enumeration of the first n primes.

DESIGN. Standard library and growth.py.
  H  the hom order: x mod b is a unital ring map Z/a -> Z/b iff b | a,
     every a, b in 2..60, checked by well-definedness, additivity and
     multiplicativity on all of Z/a.
  I  costless independence: the two specimens of (2) as legal
     trajectories.
  F  fields: greedy under Omega from seeds 1..6 for 20 steps under
     three tie-breaks (least, largest below 200, odd first), candidates
     every m <= 200 coprime to M; every pick prime.
  P  properness: under Omega with the odd-first tie-break, 30 steps
     from 1, 2 never picked. Under three scrambled rank costs on the
     first 30 primes (fixed seeds), 30 steps from 1: the picks are the
     primes in rank order, each run's window set the same 30 primes.
  R  the Pratt order on the first n primes: the primes admissible from
     the empty set and from {2} among the first 20; the increasing
     order an extension over the first 300 primes; the extension count
     at n = 5, 10, 15, 20 by a dynamic programme over down-sets.

PREDICTIONS, fixed before the run (from the script this replaces).
  H1 3481 pairs (a, b), a map exactly at b | a.
  I1 seed 2 then 9 is legal and Z/18 not squarefree; seed 3 then
     5, 7, ..., 37 is legal and 2 never enters.
  F1 every pick prime at 3 x 6 x 20 steps.
  P1 2 is starved for 30 steps; three scrambled costs give three
     distinct routes, each the rank order, one destination.
  R1 only 2 from the empty set; after {2} exactly 3, 5, 17 among the
     first 20 primes; the increasing order extends Pratt at 300
     primes; the counts 6, 6048, 108662400 and 8506240142400, so at
     n = 20 the rule supplies about 18 of the 61 route bits.
A KILL is any printed disagreement.

FINDINGS. Every prediction landed: 8/8 checks PASS. (Review turned
four checks into prints: I1's two specimens and R1's extension of
Pratt, which held by construction, and P1's one destination, which
the three route checks imply; its three distinct routes are printed.)
  H1 3481 pairs, x mod b a unital map exactly at b | a, 0 off.
  I1 seed 2 may pick 9 (Z/18 not squarefree); seed 3 may pick 5, ...,
     37 (M = 3710369067405, odd).
  F1 every pick prime under Omega, 3 tie-breaks x 6 seeds x 20 steps,
     which holds by construction, Omega being 1 exactly at primes: a
     specimen of (3), whose proof is the argument's.
  P1 under Omega the odd-first walk from 1 picks 3, 5, 7, 11, ... and
     never 2 in 30 steps; three scrambled rank costs give three
     distinct routes, each its rank order, one destination.
  R1 from the empty set only 2 is admissible, from {2} exactly 3, 5,
     17; the increasing order extends Pratt over 300 primes; the
     extension counts 6, 6048, 108662400, 8506240142400 at n = 5, 10,
     15, 20, so at 20 primes 42.95 of the 61.08 route bits stay free
     and the rule supplies 18.13.
  Tiers: the hom order is a property; fields by a strict cost a rule,
  proved; the properness criterion a criterion, proved both ways (the
  specimen realizes the converse); the route a theorem (any
  enumeration is a proper cost's), the Pratt count a measurement at
  n <= 20.

RUN RECORD. 8/8, 0.3 s wall, 10 MB peak commit under a memory guard.
"""

import random
from math import gcd, lgamma, log, log2

from growth import CHECKS, PRIMES, check, factor, section


def omega_mult(m):
    return sum(factor(m).values())


def squarefree(n):
    return all(e == 1 for e in factor(n).values())


def section_h():
    section("H  THE HOM ORDER: unital maps between windows")
    bad = pairs = 0
    for a in range(2, 61):
        for b in range(2, 61):
            pairs += 1
            ok = True
            for x in range(a):
                for y in range(a):
                    if ((x + y) % a) % b != (x + y) % b or \
                            ((x * y) % a) % b != (x * y) % b:
                        ok = False
                        break
                if not ok:
                    break
            bad += ok != (a % b == 0)
    check("H1 x mod b is a unital map Z/a -> Z/b exactly when b | a",
          bad == 0, f"{pairs} pairs, {bad} off")


def section_i():
    section("I  COSTLESS INDEPENDENCE: every coprime m admissible")
    ok1 = gcd(9, 2) == 1 and not squarefree(18)
    M, ok2 = 3, True
    for p in PRIMES[2:12]:
        ok2 = ok2 and gcd(p, M) == 1
        M *= p
    ok2 = ok2 and M % 2 == 1
    print(f"  specimen: seed 2 then 9 is legal, Z/18 not squarefree: {ok1}")
    print(f"  specimen: seed 3 then 5..37 is legal, M = {M}, odd: {ok2}")


def greedy(seed, steps, cost, tie):
    M, picks = seed, []
    for _ in range(steps):
        cands = [m for m in range(2, 201) if gcd(m, M) == 1]
        low = min(cost(m) for m in cands)
        pick = tie([m for m in cands if cost(m) == low])
        picks.append(pick)
        M *= pick
    return picks


TIES = {
    "least": min,
    "largest": max,
    "odd first": lambda c: min([m for m in c if m % 2] or c),
}


def section_f():
    section("F  FIELDS: a divisibility-strict cost picks primes")
    bad = 0
    for name, tie in TIES.items():
        for seed in range(1, 7):
            picks = greedy(seed, 20, omega_mult, tie)
            bad += sum(factor(p) != {p: 1} for p in picks)
    check("F1 every pick prime under Omega, 3 tie-breaks x 6 seeds x 20 "
          "steps", bad == 0, f"{bad} composite picks")


def section_p():
    section("P  PROPERNESS AND THE ROUTE")
    picks = greedy(1, 30, omega_mult, TIES["odd first"])
    print("  Omega, odd first, from 1:", picks[:8], "...")
    check("P1 under Omega the odd-first tie-break starves 2 for 30 steps",
          2 not in picks)
    first = PRIMES[:30]
    routes = []
    for s in (1, 2, 3):
        order = first[:]
        random.Random(s).shuffle(order)
        rank = {p: i + 1 for i, p in enumerate(order)}
        rank.update({p: i + 1 for i, p in enumerate(PRIMES) if p not in rank})

        def cost(m, rank=rank):
            return sum(a * rank[p] for p, a in factor(m).items())

        route = greedy(1, 30, cost, min)
        routes.append(route)
        check(f"P1 scrambled cost {s}: the route is its rank order",
              route == order)
    dest = {frozenset(r) for r in routes}
    print(f"  {len({tuple(r) for r in routes})} distinct routes, "
          f"{len(dest)} destination, the first 30 primes: "
          f"{dest == {frozenset(first)}}")


def section_r():
    section("R  THE PRATT ORDER: an arrival rule read off the rings Z/q")
    parents = {p: set(factor(p - 1)) if p > 2 else set() for p in PRIMES}
    first20 = PRIMES[:20]
    from_empty = [p for p in first20 if parents[p] <= set()]
    from_two = [p for p in first20 if p != 2 and parents[p] <= {2}]
    print("  admissible from {}:", from_empty, " from {2}:", from_two)
    check("R1 only 2 from the empty set; then exactly 3, 5, 17",
          from_empty == [2] and from_two == [3, 5, 17])
    print("  the increasing order extends Pratt (a parent divides p - 1): "
          f"{all(max(parents[p], default=0) < p for p in PRIMES[:300])} "
          "over the first 300 primes")
    counts = {}
    for n in (5, 10, 15, 20):
        ps = PRIMES[:n]
        idx = {p: i for i, p in enumerate(ps)}
        need = [sum(1 << idx[q] for q in parents[p]) for p in ps]
        full = (1 << n) - 1
        memo = {full: 1}

        def ext(mask):
            if mask in memo:
                return memo[mask]
            tot = 0
            for i in range(n):
                if not mask >> i & 1 and need[i] & mask == need[i]:
                    tot += ext(mask | 1 << i)
            memo[mask] = tot
            return tot

        counts[n] = ext(0)
    bits = log2(counts[20])
    total = lgamma(21) / log(2)
    print(f"  linear extensions: {counts}")
    print(f"  n = 20: {bits:.2f} bits of freedom left of {total:.2f}, the "
          f"rule supplies {total - bits:.2f}")
    check("R1 the counts 6, 6048, 108662400, 8506240142400",
          counts == {5: 6, 10: 6048, 15: 108662400, 20: 8506240142400})


def main():
    section_h()
    section_i()
    section_f()
    section_p()
    section_r()
    print()
    print(f"{sum(CHECKS)}/{len(CHECKS)} checks passed")
    raise SystemExit(0 if all(CHECKS) else 1)


if __name__ == "__main__":
    main()