Skip to content

Instantly share code, notes, and snippets.

@ryanwebster90
Created November 3, 2025 16:30
Show Gist options
  • Select an option

  • Save ryanwebster90/73545b64f5b40ee9b937d22e6f24ae43 to your computer and use it in GitHub Desktop.

Select an option

Save ryanwebster90/73545b64f5b40ee9b937d22e6f24ae43 to your computer and use it in GitHub Desktop.
tm visualization
# Dynamic visualization of 2-symbol TM's. After --warmup steps, bits on the tape are regrouped into bytes and color-mapped,
# then the 1D tape is reshaped and truncated from the left to a 64x64 frame to be recorded every --frame-every steps for --vis frames.
# ## Example usage:
# https://bbchallenge.org/76708232
# python vis_tm_bytes.py
# https://bbchallenge.org/43374927
# python vis_tm_bytes.py --tm=1RB0RD_1LC1LB_1RA0LB_0RE1RD_---1RA --warmup=200_000_000
# For 6-state machines, use a larger warmup (current BB(6) champion family)
# python vis_tm_bytes.py --warmup=200_000_000 --vis=80_000_000 --frame-every=100_000 --tm=1RB1RA_1RC1RZ_1LD0RF_1RA0LE_0LD1RC_1RA0RE --side=64
import os, time, argparse
import numpy as np
import imageio
import matplotlib.pyplot as plt
HALT = -1
def parse_tm(spec, flip_lr=True):
rows = spec.strip().split('_')
n = len(rows[0]) // 3
m = len(rows)
trans = {}
for si, row in enumerate(rows):
for a in range(n):
tok = row[a*3:(a+1)*3]
if tok == '---':
w, mv, nx = 1, -1, HALT
else:
w = int(tok[0])
mv = -1 if (tok[1] == 'L') else 1
if flip_lr: mv = -mv
nx = HALT if tok[2] == 'H' else (ord(tok[2]) - ord('A'))
trans[(si, a)] = (w, mv, nx)
return trans, m, n
def step_once(S, TRANS):
sym = 1 if (S['pos'] in S['ones']) else 0
w, m, nxt = TRANS[(S['state'], sym)]
if w == 1:
if S['pos'] not in S['ones']:
S['ones'].add(S['pos']); S['sigma'] += 1
else:
if S['pos'] in S['ones']:
S['ones'].remove(S['pos']); S['sigma'] -= 1
S['pos'] += m
S['steps'] += 1
if nxt == HALT:
return True
S['state'] = nxt
return False
def enclosed_left_anchor(ones):
return min(ones) if ones else None
def bits_to_bytes_MSB(bits):
pad = (-len(bits)) % 8
if pad: bits += '0' * pad
return bytes(int(bits[i:i+8], 2) for i in range(0, len(bits), 8))
def frame_bytes(ones, side=64):
FRAMESZ = side * side
if not ones:
return bytes(FRAMESZ)
L = enclosed_left_anchor(ones)
total_bits = FRAMESZ * 8
bits = ''.join('1' if (L + i) in ones else '0' for i in range(total_bits))
by = bits_to_bytes_MSB(bits)
if len(by) < FRAMESZ:
by = by + bytes(FRAMESZ - len(by))
return by[:FRAMESZ]
def bytes_to_img_u8(by, side=64):
arr = np.frombuffer(by, dtype=np.uint8)
return arr.reshape(side, side)
def upscale4(img):
return np.repeat(np.repeat(img, 4, axis=0), 4, axis=1)
def colorize_u8(img_u8, cmap_name='turbo'):
cmap = plt.get_cmap(cmap_name)
return (cmap(img_u8.astype(np.float32)/255.0)[:, :, :3] * 255.0).astype(np.uint8)
def sanitize(s):
return ''.join(ch if ch.isalnum() else '_' for ch in s)
def run(
spec="1RB1LB_1LC0RE_0LD0RA_1RA0LD_---0RC",
warmup_steps=50_000_000,
vis_steps=200_000_000,
frame_every=10_000,
side=64,
out_dir='',
video_name='',
final_png='',
cmap_name='turbo',
fps=30,
print_every=1_000_000
):
TRANS, M, N = parse_tm(spec, flip_lr=True)
tag = sanitize(spec)
if not out_dir: out_dir = f'out_{tag}_{side}x{side}_x4'
if not video_name: video_name = f'{tag}_{side}x{side}_x4.mp4'
if not final_png: final_png = f'{tag}_{side}x{side}_x4_final.png'
os.makedirs(out_dir, exist_ok=True)
video_path = os.path.join(out_dir, video_name)
png_path = os.path.join(out_dir, final_png)
S = {'ones':set(), 'state':0, 'pos':0, 'steps':0, 'sigma':0}
t0 = time.time()
while S['steps'] < warmup_steps:
if step_once(S, TRANS):
break
if print_every and (S['steps'] % print_every == 0):
print(f'[warmup] steps={S["steps"]:,} sigma={S["sigma"]:,} pos={S["pos"]:,} dt={(time.time()-t0):.1f}s')
print(f'[warmup] done at steps={S["steps"]:,}, sigma={S["sigma"]:,}, halted={S["steps"] < warmup_steps}')
with imageio.get_writer(video_path, fps=fps, codec='libx264', quality=8, pixelformat='yuv420p') as writer:
by = frame_bytes(S['ones'], side=side)
img = bytes_to_img_u8(by, side=side)
frame = colorize_u8(upscale4(img), cmap_name=cmap_name)
writer.append_data(frame)
target2 = S['steps'] + vis_steps
while S['steps'] < target2:
if step_once(S, TRANS):
break
if (S['steps'] % frame_every) == 0:
by = frame_bytes(S['ones'], side=side)
img = bytes_to_img_u8(by, side=side)
frame = colorize_u8(upscale4(img), cmap_name=cmap_name)
writer.append_data(frame)
if print_every and (S['steps'] % print_every == 0):
print(f'[vis] steps={S["steps"]:,} sigma={S["sigma"]:,} pos={S["pos"]:,} dt={(time.time()-t0):.1f}s')
by = frame_bytes(S['ones'], side=side)
img = bytes_to_img_u8(by, side=side)
frame = colorize_u8(upscale4(img), cmap_name=cmap_name)
writer.append_data(frame)
plt.figure(figsize=(4,4)); plt.imshow(frame); plt.axis('off'); plt.tight_layout()
plt.savefig(png_path, dpi=180, bbox_inches='tight'); plt.close()
print(f'[done] steps={S["steps"]:,} sigma={S["sigma"]:,}')
print(f'[out] video={video_path}\n[out] png={png_path}')
def main():
parser = argparse.ArgumentParser(description='M-state N-symbol TM visualizer')
parser.add_argument('--tm', type=str, default="1RB1LB_1LC0RE_0LD0RA_1RA0LD_---0RC")
parser.add_argument('--warmup', type=int, default=50_000_000)
parser.add_argument('--vis', type=int, default=10_000_000)
parser.add_argument('--frame-every', type=int, default=40_000)
parser.add_argument('--side', type=int, default=64)
parser.add_argument('--out', type=str, default='tm_vis/')
parser.add_argument('--fps', type=int, default=30)
parser.add_argument('--cmap', type=str, default='turbo')
parser.add_argument('--print-every', type=int, default=1_000_000)
args = parser.parse_args()
print('states:', parse_tm(args.tm)[1], 'symbols:', parse_tm(args.tm)[2])
run(
spec=args.tm,
warmup_steps=args.warmup,
vis_steps=args.vis,
frame_every=args.frame_every,
side=args.side,
out_dir=args.out,
fps=args.fps,
cmap_name=args.cmap,
print_every=args.print_every,
)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment