#!/usr/bin/env python3 """ hn_hoffman_bound.py — Hadwiger-Nelson Hoffman bound via spectral method. Computes χ(G) ≥ 1 - λ_max / λ_min for a unit-distance graph G. Built-in graphs: - Moser spindle (7 vertices, χ=4) - de Grey 1581-vertex graph (χ=5) using coordinates from arXiv:1804.02385v3 §5.1 Usage: python3 hn_hoffman_bound.py [--graph moser|degrey] """ import sys, json, math, hashlib, time from pathlib import Path HERE = Path(__file__).resolve().parent OUT_DIR = HERE.parent / ".openresearch" / "artifacts" OUT_DIR.mkdir(parents=True, exist_ok=True) s3 = math.sqrt(3) s11 = math.sqrt(11) s33 = math.sqrt(33) s12 = 2 * s3 # ── Coordinate set S from de Grey §5.1 ──────────────────────────── S_COORDS = [ (0, 0), (1/3, 0), (1, 0), (2, 0), ((s33 - 3)/6, 0), (1/2, 1/s12), (1, 1/s3), (3/2, s3/2), (7/6, s11/6), (1/6, (s12 - s11)/6), (5/6, (s12 - s11)/6), (2/3, (s11 - s3)/6), (2/3, (3*s3 - s11)/6), (s33/6, 1/s12), ((s33 + 3)/6, 1/s3), ((s33 + 1)/6, (3*s3 - s11)/6), ((s33 - 1)/6, (3*s3 - s11)/6), ((s33 + 1)/6, (s11 - s3)/6), ((s33 - 1)/6, (s11 - s3)/6), ((s33 - 2)/6, (2*s3 - s11)/6), ((s33 - 4)/6, (2*s3 - s11)/6), ((s33 + 13)/12, (s11 - s3)/12), ((s33 + 11)/12, (s3 + s11)/12), ((s33 + 9)/12, (s11 - s3)/4), ((s33 + 9)/12, (3*s3 + s11)/12), ((s33 + 7)/12, (s3 + s11)/12), ((s33 + 7)/12, (3*s3 - s11)/12), ((s33 + 5)/12, (5*s3 - s11)/12), ((s33 + 5)/12, (s11 - s3)/12), ((s33 + 3)/12, (3*s11 - 5*s3)/12), ((s33 + 3)/12, (s3 + s11)/12), ((s33 + 3)/12, (3*s3 - s11)/12), ((s33 + 1)/12, (s11 - s3)/12), ((s33 - 1)/12, (3*s3 - s11)/12), ((s33 - 3)/12, (s11 - s3)/12), ((15 - s33)/12, (s11 - s3)/4), ((15 - s33)/12, (7*s3 - 3*s11)/12), ((13 - s33)/12, (3*s3 - s11)/12), ((11 - s33)/12, (s11 - s3)/12), ] def rotate(pt, angle): c, s = math.cos(angle), math.sin(angle) return (pt[0]*c - pt[1]*s, pt[0]*s + pt[1]*c) def translate(pt, dx, dy): return (pt[0]+dx, pt[1]+dy) def negate_y(pt): return (pt[0], -pt[1]) def is_unit(p, q, eps=1e-9): d2 = (p[0]-q[0])**2 + (p[1]-q[1])**2 return abs(d2 - 1.0) < eps def build_adjacency(points, eps=1e-9): n = len(points) adj = [[] for _ in range(n)] for i in range(n): pi = points[i] for j in range(i+1, n): if is_unit(pi, points[j], eps): adj[i].append(j) adj[j].append(i) return adj def remove_duplicates(points, eps=1e-9): uniq = [] for p in points: if not any(abs(p[0]-q[0])