SilverSight/scripts/iteration_dag.py
allaun 3362d554d1 feat(braid/dag): land untracked research WIP + register 4 formal libs; ignore build artifacts
- lakefile.lean: register SilverSight.{AngrySphinx,CollatzBraid,GoldenSpiral,GCCL}
- docs/research/: braid group action, iteration DAG/regime, Sidon
  preservation/creation, unified CRT-torus DAG notes
- docs/diagrams/: DAG + heatmap + 8-strand search JSON/dot outputs
- formal/CoreFormalism/StrandCapacityBound.lean: capacity bound (passes
  hardened anti-smuggle --ci)
- scripts/, python/: braid word solver, collapse/DAG search + tuning,
  heatmap gen, YB search/verification, wrapping verifier
- .gitignore: exclude rust/**/target and coq compiled artifacts
  (*.vo/*.vok/*.vos/*.glob/*.aux) that were polluting the tree

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:11:37 -05:00

261 lines
9.1 KiB
Python

#!/usr/bin/env python3
"""
Iteration DAG for CRT Torus Embedding.
Traces paths through modulus space, searching for a sequence of
modulus choices that transforms A into a Sidon set.
"""
import sys, math, random, itertools
from typing import List, Tuple, Optional, Dict, Set
sys.path.insert(0, '/home/allaun/SilverSight/scripts')
from verify_wrapping import *
# ---------- DAG Node ----------
class DAGNode:
__slots__ = ('step', 'A', 'moduli', 'S', 'M', 'sidon', 'parent', 'children',
'terminal', 'reason', 'id')
_next_id = 0
def __init__(self, A, moduli, S, parent=None, step=0):
self.id = DAGNode._next_id; DAGNode._next_id += 1
self.step = step
self.A = sorted(A)
self.moduli = list(moduli)
self.S = S
self.M = 1
for Li in moduli: self.M *= Li
self.sidon = is_sidon(self.A)
self.parent = parent
self.children = []
self.terminal = False
self.reason = ""
def key(self):
return (tuple(self.A), tuple(self.moduli), self.S)
def __repr__(self):
return f"Node#{self.id}(step={self.step}, |A|={len(self.A)}, M={self.M}, sidon={self.sidon})"
# ---------- Regeneration Rules ----------
class GeometricRule:
"""Geometric growth: L1' = alpha * L1, L2' = beta * L2, ensuring coprimality."""
def __init__(self, alpha=2, beta=3):
self.alpha = alpha
self.beta = beta
def next_moduli(self, current_moduli, maxA=None):
# Ensure next moduli remain coprime by using distinct growth factors
results = []
L1, L2 = current_moduli
for a in [1, 2, 3]:
for b in [1, 2, 3]:
if a == b: continue # keep moduli distinct
nL1 = a * L1 if a > 0 else L1
nL2 = b * L2 if b > 0 else L2
if math.gcd(nL1, nL2) == 1:
results.append([nL1, nL2])
return results[:5] # limit branching
class AdaptiveRule:
"""Try all coprime modulus pairs with M in (maxA, 2*maxA]."""
def __init__(self, max_val=16):
self.numbers = [n for n in range(2, max_val + 1)]
def next_moduli(self, current_moduli, maxA):
candidates = []
for L1 in self.numbers:
for L2 in self.numbers:
if L1 == L2:
continue
if math.gcd(L1, L2) != 1:
continue
M = L1 * L2
if maxA < M <= 2 * maxA:
candidates.append([L1, L2])
return candidates
class ExhaustiveRule:
"""Try all coprime k-modulus tuples within a bound."""
def __init__(self, max_val=16):
self.numbers = [n for n in range(2, max_val + 1)]
def next_moduli(self, current_moduli, maxA):
candidates = []
for k in range(2, 5):
for combo in itertools.permutations(self.numbers, k):
# Check pairwise coprimality
ok = True
for i in range(k):
for j in range(i+1, k):
if math.gcd(combo[i], combo[j]) != 1:
ok = False
break
if not ok: break
if not ok: continue
M = 1
for n in combo: M *= n
if maxA < M <= 2 * maxA:
candidates.append(list(combo))
return candidates[:self.max_branch] if hasattr(self, 'max_branch') else candidates
# ---------- DAG Builder ----------
class IterationDAG:
def __init__(self, A0, S, regen_rule, max_steps=5, max_branch=100):
self.root = DAGNode(A0, [3, 4], S) # default initial moduli
self.regen_rule = regen_rule
self.max_steps = max_steps
self.max_branch = max_branch
self.all_nodes: Dict[str, DAGNode] = {self.root.key(): self.root}
self.sidon_paths: List[List[DAGNode]] = []
self.stats = {"explored": 0, "sidon_found": 0, "terminal": 0}
def apply_F(self, node, new_moduli):
"""Apply F with new moduli to node.A, return child node or None."""
new_A = [f_k(a, node.S, new_moduli) for a in node.A]
child = DAGNode(new_A, new_moduli, node.S, parent=node, step=node.step + 1)
return child
def should_terminate(self, node):
"""Check if a node is terminal."""
if node.sidon:
node.terminal = True
node.reason = "Sidon (goal reached)"
return True
if node.M > 2 * max(node.A):
node.terminal = True
node.reason = f"Preservation regime (M={node.M} > 2*maxA)"
return True
if node.step >= self.max_steps:
node.terminal = True
node.reason = f"Max steps ({self.max_steps}) reached"
return True
return False
def build(self):
"""BFS build of the DAG."""
queue = [self.root]
visited = set()
while queue:
node = queue.pop(0)
if node.key() in visited:
continue
visited.add(node.key())
self.stats["explored"] += 1
if self.should_terminate(node):
self.stats["terminal"] += 1
if node.sidon:
# Trace path to root
path = []
n = node
while n:
path.append(n)
n = n.parent
path.reverse()
self.sidon_paths.append(path)
self.stats["sidon_found"] += 1
continue
maxA = max(node.A)
candidates = self.regen_rule.next_moduli(node.moduli, maxA)
# Limit branching
if len(candidates) > self.max_branch:
candidates = candidates[:self.max_branch]
for new_moduli in candidates:
child = self.apply_F(node, new_moduli)
if child.key() not in self.all_nodes:
self.all_nodes[child.key()] = child
node.children.append(child)
queue.append(child)
return self
def print_path(self, path):
"""Pretty-print a path from root to Sidon."""
for i, node in enumerate(path):
sidon = "★ SIDON" if node.sidon else ""
term = "" if node.terminal else ""
print(f" Step {i}: A={node.A} M={node.M} {sidon}{term}")
if node.parent and i > 0:
print(f" moduli={node.moduli}")
def to_dot(self, filename=None):
"""Export DAG as DOT graph for visualization."""
lines = ["digraph IterationDAG {"]
lines.append(" rankdir=TB;")
lines.append(" node [shape=record];")
for key, node in self.all_nodes.items():
sidon_style = "style=filled, fillcolor=lightgreen" if node.sidon else ""
term_style = "style=filled, fillcolor=lightyellow" if node.terminal else ""
style = sidon_style or term_style or ""
label = f"A={node.A}\\nM={node.M} step={node.step}"
if node.sidon: label += " ★SIDON"
if style:
lines.append(f" n{node.id} [{style}, label=\"{label}\"];")
else:
lines.append(f" n{node.id} [label=\"{label}\"];")
for key, node in self.all_nodes.items():
for child in node.children:
lines.append(f" n{node.id} -> n{child.id} [label=\"{child.moduli}\"];")
lines.append("}")
dot = "\n".join(lines)
if filename:
with open(filename, 'w') as f:
f.write(dot)
print(f" DOT written to {filename}")
return dot
def summary(self):
"""Print DAG statistics."""
print(f"DAG Statistics:")
print(f" Nodes explored: {self.stats['explored']}")
print(f" Sidon paths found: {self.stats['sidon_found']}")
print(f" Terminal nodes: {self.stats['terminal']}")
print(f" Total nodes: {len(self.all_nodes)}")
if self.sidon_paths:
print(f" Shortest path length: {len(min(self.sidon_paths, key=len))}")
print(f"\n Shortest path:")
self.print_path(min(self.sidon_paths, key=len))
# ---------- Main ----------
def test_sidon_example():
"""Trace the known Sidon creation example."""
print("=== Sidon Creation Example ===")
A0, S0 = [1, 2, 5, 6], 7
rule = AdaptiveRule()
dag = IterationDAG(A0, S0, rule, max_steps=3)
dag.build()
dag.summary()
def test_evolution():
"""Trace evolution of a non-Sidon set through modulus choices."""
print("\n=== Evolution of A={0,1,3,8,13} ===")
A0, S0 = [0, 1, 3, 8, 13], 27
rule = AdaptiveRule()
dag = IterationDAG(A0, S0, rule, max_steps=3)
dag.build()
dag.summary()
def test_geometric_cascade():
"""Trace a deterministic geometric cascade."""
print("\n=== Geometric Cascade ===")
A0, S0 = [1, 2, 5, 6], 7
rule = GeometricRule(alpha=2, beta=2)
dag = IterationDAG(A0, S0, rule, max_steps=5)
dag.build()
dag.summary()
if __name__ == "__main__":
test_sidon_example()
test_evolution()
test_geometric_cascade()