| """neural_ca -- a Loschmidt echo in a reversible cellular automaton. |
| |
| The Margolus block rule is a bijection, so a gas of ~2,000 particles mixed for |
| 500 steps can be un-mixed by iterating the same rule backward: the initial |
| configuration returns cell-for-cell (particle number conserved throughout). Yet |
| flipping a single cell of the mixed state before reversing corrupts roughly half |
| the reconstructed past -- exact reversibility and sensitive dependence in the |
| same automaton. |
| |
| python demos/neural_ca_loschmidt_echo.py |
| """ |
| import os, sys, time, random, statistics |
| HERE = os.path.dirname(os.path.abspath(__file__)) |
| REPO = os.path.dirname(HERE) |
| sys.path.insert(0, os.path.join(REPO, "src")) |
| import ca |
|
|
|
|
| def coarse(g, H, W, k=8): |
| out = [] |
| for by in range(0, H, k): |
| for bx in range(0, W, k): |
| out.append(sum(g[y][x] for y in range(by, by + k) for x in range(bx, bx + k))) |
| return out |
|
|
|
|
| if __name__ == "__main__": |
| H = W = 64 |
| rng = random.Random(2026) |
| grid = [[1 if rng.random() < 0.5 else 0 for _ in range(W)] for _ in range(H)] |
| n0 = sum(map(sum, grid)) |
| STEPS = 500 |
|
|
| print("neural_ca: Loschmidt echo (mix, then run time backward)") |
| print("=" * 56) |
| t0 = time.perf_counter() |
| fwd = ca.run(grid, STEPS, 0) |
| n1 = sum(map(sum, fwd)) |
| back = ca.run_back(fwd, STEPS, 0) |
| echo = back == grid |
| dt = time.perf_counter() - t0 |
|
|
| print(f"particles: {n0} at t=0, {n1} at t={STEPS} " |
| f"({'conserved' if n0 == n1 else 'NOT CONSERVED'})") |
| print(f"coarse 8x8 occupancy stdev: t=0 {statistics.pstdev(coarse(grid, H, W)):.2f} " |
| f"-> t={STEPS} {statistics.pstdev(coarse(fwd, H, W)):.2f} (mixed)") |
| print(f"{STEPS} steps forward + {STEPS} reversed in {dt:.1f}s: " |
| f"t=0 recovered {'EXACTLY' if echo else 'FAILED'}") |
|
|
| flip = [row[:] for row in fwd] |
| flip[0][0] ^= 1 |
| back2 = ca.run_back(flip, STEPS, 0) |
| ham = sum(back2[y][x] != grid[y][x] for y in range(H) for x in range(W)) |
| print(f"butterfly: flip ONE cell at t={STEPS}, reverse again -> reconstructed " |
| f"past wrong in {ham}/{H * W} cells") |
|
|