pascal_shear.py
7.1 kB · python · 254 lines
1import sys2import urllib.error3import urllib.request4from pathlib import Path56HERE = Path(__file__).resolve().parent7GOULD_URL = "https://oeis.org/A001316/b001316.txt"8GASKET_URL = "https://oeis.org/A047999/b047999.txt"9GASKET_CACHE = "a047999.txt"10CODE = 711TOP_LEVEL = 912DIGIT_LEVEL = 1413EXACT = 12814ROWS = 102415SHEAR_LEVEL = 616TRUNCATION_LEVEL = 8171819def popcount(value):20 return bin(value).count("1")212223def tile_of(code):24 grid = [[0, 0], [0, 0]]25 for index, (x, y) in enumerate([(0, 0), (0, 1), (1, 0), (1, 1)]):26 grid[x][y] = (code >> index) & 127 return grid282930def kron(a, b):31 ra, ca = len(a), len(a[0])32 rb, cb = len(b), len(b[0])33 out = [[0] * (ca * cb) for _ in range(ra * rb)]34 for i in range(ra):35 for j in range(ca):36 if a[i][j]:37 for u in range(rb):38 for v in range(cb):39 out[i * rb + u][j * cb + v] = b[u][v]40 return out414243def fractal(tile, level):44 out = [row[:] for row in tile]45 for _ in range(1, level):46 out = kron(out, tile)47 return out484950def pascal_mod2(rows):51 out = [[1]]52 for n in range(1, rows):53 previous = out[n - 1]54 row = [1]55 for k in range(1, n):56 row.append((previous[k - 1] + previous[k]) & 1)57 row.append(1)58 out.append(row)59 return out606162def carry_count(i, j):63 count = 064 carry = 065 while i or j or carry:66 total = (i & 1) + (j & 1) + carry67 carry = 1 if total >= 2 else 068 count += carry69 i >>= 170 j >>= 171 return count727374def binomial(n, k):75 k = min(k, n - k)76 out = 177 for i in range(k):78 out = out * (n - i) // (i + 1)79 return out808182def valuation2(value):83 return (value & -value).bit_length() - 1848586def parse_terms(text):87 terms = []88 for line in text.splitlines():89 line = line.strip()90 if not line or line.startswith("#"):91 continue92 parts = line.split()93 terms.append((int(parts[0]), int(parts[1])))94 return terms959697def read_cache(name):98 return parse_terms((HERE / name).read_text())99100101def read_live(url):102 request = urllib.request.Request(url, headers={"User-Agent": "curl/8"})103 try:104 with urllib.request.urlopen(request, timeout=30) as handle:105 return parse_terms(handle.read().decode())106 except (urllib.error.URLError, OSError, TimeoutError):107 return None108109110def triangular_rows(count):111 rows = 0112 seen = 0113 while seen < count:114 rows += 1115 seen += rows116 return rows117118119def level_sets():120 print("KRONECKER LEVEL SET OF CODE 7")121 tile = tile_of(CODE)122 print(f" tile [[{tile[0][0]}, {tile[0][1]}], [{tile[1][0]}, {tile[1][1]}]]")123 for level in range(1, TOP_LEVEL + 1):124 grid = fractal(tile, level)125 side = 1 << level126 cells = {(i, j) for i in range(side) for j in range(side) if grid[i][j]}127 anded = {(i, j) for i in range(side) for j in range(side) if i & j == 0}128 agree = cells == anded and len(cells) == 3**level129 print(130 f" L={level:2d} side {side:4d} cells {len(cells):6d}"131 f" 3^L {3 ** level:6d} set == (i AND j == 0) {agree}"132 )133 same = all(134 sum(1 << (level - popcount(i)) for i in range(1 << level)) == 3**level135 for level in range(1, DIGIT_LEVEL + 1)136 )137 print(f" digit sum gives 3^L for L = 1..{DIGIT_LEVEL}: {same}")138139140def kummer():141 print("KUMMER ON EXACT BINOMIALS")142 faults = 0143 for i in range(EXACT):144 for j in range(EXACT):145 value = binomial(i + j, i)146 if valuation2(value) != carry_count(i, j):147 faults += 1148 if (value & 1 == 1) != (i & j == 0):149 faults += 1150 print(f" 0 <= i, j < {EXACT}: {EXACT * EXACT} binomials, {faults} faults")151152153def recurrence(triangle):154 print("PASCAL MOD 2 FROM THE ADDITIVE RECURRENCE")155 faults = 0156 entries = 0157 for n, row in enumerate(triangle):158 for k, value in enumerate(row):159 entries += 1160 if (value == 1) != (k & (n - k) == 0):161 faults += 1162 print(f" rows 0..{ROWS - 1}: {entries} entries, {faults} mismatched cells")163164165def shear(triangle):166 print("THE SHEAR (i, j) -> (i, i + j)")167 side = 1 << SHEAR_LEVEL168 cells = {(i, j) for i in range(side) for j in range(side) if i & j == 0}169 image = {(i, i + j) for (i, j) in cells}170 odd = {171 (k, n)172 for n, row in enumerate(triangle[: 2 * side - 1])173 for k, value in enumerate(row)174 if value and k < side and n - k < side175 }176 print(177 f" L={SHEAR_LEVEL}: {len(cells)} cells map onto {len(image)} points,"178 f" odd entries in range {len(odd)}, bijection {image == odd}"179 )180181182def gould(triangle):183 print("ANTIDIAGONAL POPULATION")184 faults = sum(1 for n, row in enumerate(triangle) if sum(row) != 1 << popcount(n))185 print(f" row sums == 2^popcount(n) for n = 0..{ROWS - 1}, {faults} faults")186 side = 1 << TRUNCATION_LEVEL187 counts = [0] * (2 * side - 1)188 for i in range(side):189 for j in range(side):190 if i & j == 0:191 counts[i + j] += 1192 inside = all(counts[n] == 1 << popcount(n) for n in range(side))193 outside = all(counts[n] < 1 << popcount(n) for n in range(side, 2 * side - 1))194 print(195 f" inside [0, 2^{TRUNCATION_LEVEL})^2 the count is 2^popcount(n)"196 f" for n < 2^L {inside}, strictly smaller above {outside}"197 )198199200def oeis(triangle):201 print("OEIS")202 flat = [value for row in triangle for value in row]203 sums = [sum(row) for row in triangle]204 terms = read_live(GOULD_URL)205 if terms is None:206 print(" A001316 live read unavailable, its b-file is too large to keep here")207 else:208 wrong = sum(209 1210 for n, (index, value) in enumerate(terms)211 if index != n or value != 1 << popcount(n)212 )213 print(214 f" A001316 live: {len(terms)} terms, n = 0..{len(terms) - 1},"215 f" {wrong} differences"216 )217 head = [value for _, value in terms[:ROWS]]218 print(f" A001316 first {ROWS} terms == our row sums: {head == sums}")219 cache = read_cache(GASKET_CACHE)220 gasket = [("cache", cache)]221 live = read_live(GASKET_URL)222 if live is None:223 print(" A047999 live read unavailable, cache only")224 else:225 gasket.append(("live", live))226 for label, terms in gasket:227 wrong = sum(228 1229 for n, (index, value) in enumerate(terms)230 if index != n or value != flat[n]231 )232 rows = triangular_rows(len(terms))233 print(234 f" A047999 {label}: {len(terms)} terms, rows 0..{rows - 1},"235 f" {wrong} differences"236 )237 if live is not None:238 print(f" A047999 cache == live term for term: {cache == live}")239240241def main():242 print(f"DOMAIN levels 1..{TOP_LEVEL}, binomials < {EXACT}, Pascal rows < {ROWS}")243 level_sets()244 kummer()245 triangle = pascal_mod2(ROWS)246 recurrence(triangle)247 shear(triangle)248 gould(triangle)249 oeis(triangle)250 return 0251252253if __name__ == "__main__":254 sys.exit(main())