#!/usr/bin/env python3 """ PROVENANCE: PROOF Computation 169, the atom criterion for non-balance, and the step that does not close it ========================================================================================= sec:open-structural carries one CONDITION rather than a derivation: the elementary directions must not balance, |sum_a v(a)| > 0. comp 127 showed it is independent of magnitude positivity; comp 152 replaced a circular measure-zero defence with an open-condition statement and then found the obstacle, that the articulated symmetry package does NOT force it, exhibiting an invariant law that balances 31% of the time at |D| = 6. comp 152 called the residue "a coordination premise, weaker than independence". THIS COMPUTATION SHARPENS THAT RESIDUE. IT DOES NOT CLOSE IT. An earlier version of this file claimed to close it, and the book removed non-balance from its named-premise register on that claim. The closing step was wrong; it is withdrawn here, the refutation is part 3, and the register entry is restored. What remains is worth having, and is stated at its true strength below. THE CHAIN, AND WHERE IT STOPS ------------------------------ (i) Balance requires v(a) = -sum_{b != a} v(b) for each a: ONE point, determined by the others. So P(balance) = E[ P( v(a) = c(rest) | rest ) ], c = the cancelling value, an identity rather than an estimate. SOUND, and measured in part 1. (ii) If the conditional law of v(a) given the others is atomless for even ONE a in D, the inner probability is a.s. zero and so is P(balance). SOUND. (iii) So balance needs EVERY property's conditional law to carry an atom, sitting exactly on the cancelling value. SOUND, and this is the sharpening: balance needs TOTAL coordination, and one unpinned property anywhere destroys it. Measured in part 2, as a codimension. (iv) WITHDRAWN. The earlier version argued: an atom is a distinguished direction, a law on S^6 charging a single point names that point, naming a point costs six parameters under G_2, and P1 supplies none, therefore non-balance. WHY (iv) FAILS, AND THE COUNTEREXAMPLE IS ALREADY IN THE BOOK -------------------------------------------------------------- Steps (i)-(iii) deliver a CONDITIONAL atom, whose location c(rest) depends on the other properties. Step (iv) prices an UNCONDITIONAL atom sitting at a FIXED point of S^6. Those are different objects. A conditional atom is a CORRELATION between properties; it names no direction, and it is perfectly compatible with a fully invariant joint law. comp 152's own mu_pm is the counterexample, and part 3 measures it: v(a) = eps_a u with one shared u drawn UNIFORMLY on S^6 and i.i.d. signs. The law is SO(7)-invariant and names no direction; every property's conditional law given the others is a PURE atom, so it carries the maximal possible atomic mass; the atom sits on the cancelling value whenever the other signs sum to +-1; and it pays ZERO parameters, because u is random rather than named. It balances 31% of the time. So a law with maximal conditional atoms and no parameter cost balances, and step (iv)'s pricing does not apply to the object steps (i)-(iii) deliver. WHAT THIS LEAVES ----------------- Balance requires TOTAL coordination between the properties: every one of them pinned by the rest to an exact value with positive weight. That is a much sharper statement of comp 152's residue than "a coordination premise", and part 2 shows how sharp: five maximally coordinated properties cannot rescue balance from ONE that is not. But PST does not forbid total coordination. mu_pm exhibits it while satisfying every symmetry the theory has articulated. So NON-BALANCE REMAINS A CONDITION, adopted and not derived, and the book says so. WHAT WOULD ACTUALLY CLOSE IT: a reason the substrate cannot carry a shared hidden variable of mu_pm's kind, i.e. an argument about correlation rather than about direction. That is a different argument from comp 168's and this computation does not have it. A "pass": P(balance) tracks a planted conditional atom's mass; one unpinned property turns 31% into a codimension-6 scaling with the exponent read from an exact law; mu_pm satisfies (i)-(iii) at zero parameter cost and balances at C(6,3)/2^6; and the quantised variant balances at 1/14 for |D| = 2 while the continuum does not balance. """ import itertools import math import numpy as np def rule(ch="="): print(ch * 100) def unit(rng, n): v = rng.normal(size=(n, 7)) return v / np.linalg.norm(v, axis=1, keepdims=True) # --------------------------------------------------------------------------------- def part1(rng): rule() print(" PART 1. The identity: P(balance) = E[conditional mass on the cancelling value]") rule() print(""" Five properties are drawn, and the sixth has a conditional law putting mass p EXACTLY on the cancelling value c(rest) and spreading 1 - p elsewhere. Balance then happens precisely when the atom is hit, so the identity predicts P = p with no free constant. NOTE ON THE MODEL: the cancelling value c(rest) is a point of R^7 and generally not of unit norm, so this part works in R^7 rather than on S^6. The identity and step (ii) are insensitive to that, since both are statements about a conditional law charging a single point, whatever the ambient set. An earlier draft placed the atom at c/|c| instead, which is a DIFFERENT point and cannot cancel; that version measured P = 0 at every p, correctly. """) n, T = 6, 40000 print(f" {'planted p':>12} {'measured P(balance)':>22} {'|difference|':>15}") worst = 0.0 for p in (0.0, 0.1, 0.25, 0.5, 0.75, 1.0): hits = 0 for _ in range(T): rest = unit(rng, n - 1) c = -rest.sum(0) v = np.vstack([rest, c]) if rng.random() < p else np.vstack([rest, unit(rng, 1)]) if np.linalg.norm(v.sum(0)) < 1e-9: hits += 1 meas = hits / T worst = max(worst, abs(meas - p)) print(f" {p:>12.2f} {meas:>22.4f} {abs(meas - p):>15.4f}") ok = worst < 0.02 print(f"\n P(balance) tracks the planted atom mass, max error {worst:.4f} : {ok}") print(" At p = 0 that is step (ii): an atomless conditional gives null balance.") return ok def part2(rng): rule() print(" PART 2. One unpinned property destroys balance: the sharpening that stands") rule() print(""" Take comp 152's mu_pm at |D| = 6, which balances 31% of the time, and leave ONE property unpinned while the other five stay maximally coordinated. Balance does not become rare, it becomes null, and the right way to see that is as a codimension. THE EXPONENT IS 6, NOT 7. The free property is a UNIT direction moving on S^6 rather than in R^7, and the coordinated five are all parallel, which is exactly comp 152's all-parallel stratum where it measured codimension 6 independently. """) n, T = 6, 300000 eps_signs = rng.integers(0, 2, size=(T, n)) * 2 - 1 p_full = float((eps_signs.sum(1) == 0).mean()) pred = math.comb(n, n // 2) / 2 ** n print(f" mu_pm as comp 152 has it P(balance) = {p_full:.4f} " f"(C(6,3)/2^6 = {pred:.4f})") u = rng.normal(size=7); u /= np.linalg.norm(u) s5 = (rng.integers(0, 2, size=(T, n - 1)) * 2 - 1).sum(1) tot = s5[:, None] * u[None, :] + unit(rng, T) exact = int((np.linalg.norm(tot, axis=1) < 1e-12).sum()) print(f" one property unpinned exact hits at 0 = {exact} of {T}") def exact_P(e): """Cancellation-free. The naive form subtracts two nearly equal numbers and loses every digit below eps ~ 3e-3, returning a negative probability.""" h = e * e / 2.0 return (4.0 * h ** 3 / 3.0 - h ** 4 + h ** 5 / 5.0) / (16.0 / 15.0) M = 4000000 # Counted a block at a time. Four million unit vectors in R^7 is 224 MB in one # array and another 224 MB once u is added, and the browser runtime this book # publishes to cannot be relied on for that; a 100k block is 5.6 MB. Splitting the # draw does not change the stream, and a count of hits does not care how the rows # are grouped, so every number below is the number it was. EPS_LEVELS = (0.80, 0.55, 0.38, 0.26) near_hits = {e: 0 for e in EPS_LEVELS} for _i in range(0, M, 100_000): nb = np.linalg.norm(u[None, :] + unit(rng, min(100_000, M - _i)), axis=1) for _e in EPS_LEVELS: near_hits[_e] += int((nb < _e).sum()) print(f"\n {'eps':>9} {'measured':>13} {'hits':>9} {'exact':>13} {'meas/exact':>12} {'tol':>9}") agree = True for e in EPS_LEVELS: c = near_hits[e]; f = c / M; ex = exact_P(e) # Tolerance derived from the hit count, not a magic number: 5 Poisson sigma. # A flat 0.12 was used before and failed for about one seed in ten, because at # eps = 0.26 there are ~190 hits and 0.12 is only 1.65 sigma. tol = 5.0 / math.sqrt(max(c, 1)) agree &= abs(f / ex - 1.0) < tol print(f" {e:>9.4f} {f:>13.7f} {c:>9d} {ex:>13.7f} {f / ex:>12.3f} {tol:>9.3f}") print(f" Monte Carlo matches the exact law within 5 Poisson sigma : {agree}") prev, slope = None, float("nan") print(f"\n the exponent, read from the exact law where it is asymptotic:") for e in (1e-2, 3e-3, 1e-3, 3e-4): ex = exact_P(e) if prev: slope = (math.log(ex) - math.log(prev[1])) / (math.log(e) - math.log(prev[0])) print(f" {e:>12.5f} {ex:>16.3e} {slope:>14.4f}") prev = (e, ex) ok = abs(p_full - pred) < 0.01 and exact == 0 and agree and abs(slope - 6.0) < 0.01 print(f"\n 31% becomes a clean codimension-6 scaling : {ok}") print(" Five coordinated properties cannot rescue balance from one that is not.") return ok def part3(rng): rule() print(" PART 3. THE REFUTATION: mu_pm satisfies (i)-(iii) and pays nothing") rule() print(""" This is the part that withdraws the earlier version's step (iv). mu_pm sets v(a) = eps_a u with ONE shared u drawn uniformly on S^6 and i.i.d. signs. If step (iv) were right, this law would have to pay six parameters to place its conditional atoms. It pays none, and it balances. """) n, T = 6, 400000 u = unit(rng, T) eps = rng.integers(0, 2, size=(T, n)) * 2 - 1 # The checks below read only v(1) and the sum of the other five, so the full # (T, n, 7) array is never needed: it is 134 MB at T = 400000, and another 134 MB # once rotated, which the browser runtime this book publishes to cannot be relied # on for. v(1) is formed directly and the cancelling sum a block of rows at a # time; both are the same arithmetic on the same draws. V0 = eps[:, 0, None] * u # v(1) = eps_1 u, the slice used below p_bal = float((np.abs(eps.sum(1)) == 0).mean()) mean_dir = float(np.abs(V0.mean(0)).max()) R, _ = np.linalg.qr(rng.normal(size=(7, 7))) cov_shift = float(np.abs(np.cov(V0.T) - np.cov((V0 @ R.T).T)).max()) cancel = np.empty_like(V0) for _i in range(0, T, 50_000): _j = min(_i + 50_000, T) cancel[_i:_j] = -(eps[_i:_j, 1:, None] * u[_i:_j, None, :]).sum(1) on_atom = float(np.isclose(np.linalg.norm(V0 - cancel, axis=1), 0, atol=1e-9).mean()) print(f" (a) it BALANCES P = {p_bal:.4f} " f"(C(6,3)/2^6 = {math.comb(6,3)/64:.4f})") print(f" (b) it names NO direction max |E[v(1)]| = {mean_dir:.5f}") print(f" and is rotation invariant cov shift = {cov_shift:.5f}") print(f" (c) its conditional law is a PURE atom, supported on {{+u, -u}}") print(f" and the atom sits on the cancelling value P = {on_atom:.4f}") print(f" which equals P(balance) = {p_bal:.4f}") print(f" (d) it pays ZERO parameters: u is drawn uniformly, not named") print(""" So a law with MAXIMAL conditional atoms, naming no direction and costing nothing, balances 31% of the time. Step (iv) priced an unconditional atom at a fixed point; steps (i)-(iii) deliver a conditional atom at a random one. The pricing does not apply, and non-balance is not closed by this route. """) ok = (abs(p_bal - math.comb(6, 3) / 64) < 0.01 and mean_dir < 0.01 and cov_shift < 0.01 and abs(on_atom - p_bal) < 1e-9) print(f" mu_pm satisfies (i)-(iii) at zero cost and balances : {ok}") return ok def part4(rng): rule() print(" PART 4. Where balance IS possible, and mu_pm is not that case") rule() print(""" The earlier version claimed comp 152's counterexample and its quantised-directions caveat were one phenomenon, both confining the directions to a distinguished finite set. That is true of the quantised variant and FALSE of mu_pm, whose two directions are a uniformly random antipodal pair. Corrected here. """) axes = [(i, s) for i in range(7) for s in (1, -1)] print(f" {'model':<34} {'|D|':>4} {'P(balance)':>13} directions live on") quant = {} for n in (2, 4): tot = bal = 0 for combo in itertools.product(range(14), repeat=n): v = np.zeros(7) for c in combo: i, sg = axes[c]; v[i] += sg tot += 1; bal += int(not v.any()) quant[n] = bal / tot print(f" {'quantised to +-e_1..+-e_7':<34} {n:>4} {bal/tot:>13.6f}" f" 14 FIXED axes") T = 200000 mu = {} for n in (2, 4): e = rng.integers(0, 2, size=(T, n)) * 2 - 1 mu[n] = float((e.sum(1) == 0).mean()) print(f" {'mu_pm (comp 152 part 3)':<34} {n:>4} {mu[n]:>13.6f}" f" a RANDOM antipodal pair") cont = {} for n in (2, 4): V = unit(rng, T * n).reshape(T, n, 7) cont[n] = int((np.linalg.norm(V.sum(1), axis=1) < 1e-12).sum()) / T print(f" {'P1 continuum, independent':<34} {n:>4} {cont[n]:>13.6f}" f" all of S^6") print(""" Two different mechanisms, not one. The quantised variant balances because the directions are confined to fixed axes, which is a different theory. mu_pm balances because the properties are CORRELATED, with no confinement and no fixed axes at all. It is the second that step (iv) failed to price. """) ok = (abs(quant[2] - 1/14) < 1e-9 and abs(mu[2] - 0.5) < 0.01 and cont[2] == 0.0 and cont[4] == 0.0) print(f" quantised 1/14, mu_pm 1/2, continuum 0, at |D| = 2 : {ok}") return ok def main(): rng = np.random.default_rng(20260928) print() rule() print(" COMPUTATION 169. The atom criterion, and the step that does not close it") rule() print(""" An earlier version of this computation claimed to close the non-balance condition, and the book removed it from the named-premise register on that claim. The closing step was wrong. It is withdrawn, the refutation is part 3, and what survives is stated at its true strength: balance needs TOTAL coordination, and the theory does not forbid it. """) r1, r2, r3, r4 = part1(rng), part2(rng), part3(rng), part4(rng) print() rule() print(" ASSESSMENT") rule() for lbl, r in [("1. P(balance) equals the planted conditional atom mass", r1), ("2. one unpinned property gives codimension-6 scaling", r2), ("3. mu_pm satisfies (i)-(iii) at zero cost and balances", r3), ("4. the quantised and correlated cases are different", r4)]: print(f" {lbl:<58}: {r}") allok = all([r1, r2, r3, r4]) print() if allok: print(""" RESULT: NON-BALANCE REMAINS A CONDITION. Steps (i)-(iii) hold and sharpen comp 152's residue considerably: balance requires EVERY property to be pinned by the rest, so one unpinned property anywhere destroys it, measured as a codimension-6 scaling that five maximally coordinated neighbours cannot rescue. But the step that would have turned that into a derivation is withdrawn: it priced an unconditional atom at a fixed direction, while what the chain delivers is a conditional atom at a random one, and comp 152's mu_pm exhibits exactly that at zero parameter cost while balancing 31% of the time. WHAT WOULD CLOSE IT is an argument about CORRELATION rather than about direction: a reason the substrate cannot carry a shared hidden variable of mu_pm's kind. comp 168's selector argument is not that argument, and this computation does not have it.""") else: print(" RESULT: at least one check failed; see above.") print() return allok if __name__ == "__main__": main()