#!/usr/bin/env python3 """ Multi-Strand Braid Word Solver. Full 16-modulus chiral torus: up to 8 strands, each with (L_id, L_ref). Generates multi-strand braid words with coprimality constraints. """ import sys, math, itertools, random from typing import List, Tuple sys.path.insert(0, '/home/allaun/SilverSight/scripts') from verify_wrapping import is_sidon # ---------- Coprime CRT ---------- def pairwise_coprime(mods: List[int]) -> bool: for i in range(len(mods)): for j in range(i+1, len(mods)): if math.gcd(mods[i], mods[j]) != 1: return False return True def crt_lift(residues: List[int], moduli: List[int]) -> int: assert pairwise_coprime(moduli), f"not coprime: {moduli}" x = residues[0] m = moduli[0] for i in range(1, len(moduli)): inv = pow(m % moduli[i], -1, moduli[i]) t = ((residues[i] - x) * inv) % moduli[i] x += t * m m *= moduli[i] return x def F_multi(a: int, S: int, moduli: List[int]) -> int: residues = [a % moduli[0]] + [(S - a) % Li for Li in moduli[1:]] return crt_lift(residues, moduli) def moduli_from_pairs(pairs: List[Tuple[int,int]]) -> List[int]: return [v for p in pairs for v in p] # ---------- Generate valid coprime configurations ---------- PRIME_POOL = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53] def coprime_pairs(count: int, pool: List[int] = None) -> List[Tuple[int,int]]: """Generate `count` coprime pairs using distinct primes.""" if pool is None: pool = PRIME_POOL used = set() pairs = [] p_idx = 0 for _ in range(count): p1, p2 = pool[p_idx], pool[p_idx+1] pairs.append((p1, p2)) p_idx += 2 return pairs def cross(pairs: List[Tuple[int,int]], strand: int, over: bool) -> List[Tuple]: """Cross strand `strand` (over or under), return new config or None.""" new = [p for p in pairs] Li, Lr = new[strand] if over: new[strand] = (Li + 2, max(Lr - 1, 2)) else: new[strand] = (max(Li - 1, 2), Lr + 2) mods = moduli_from_pairs(new) return new if pairwise_coprime(mods) else None def braid_word(pairs_seq: List[List[Tuple]]) -> str: """Build braid word from a sequence of configurations.""" parts = [] for i in range(1, len(pairs_seq)): prev, curr = pairs_seq[i-1], pairs_seq[i] for s in range(len(curr)): if curr[s] == prev[s]: continue Li, Lr = curr[s] typ = "⁺" if Li > Lr else "⁻" parts.append(f"σ_{s+1}{typ}") return " · ".join(parts) if parts else "1" # ---------- Multi-strand Sidon search ---------- def multi_search(A0: List[int], S: int, num_strands: int = 2, max_steps: int = 3): """BFS for multi-strand braid words to Sidon.""" init = coprime_pairs(num_strands) queue = [(init, 0, A0, [init])] visited = set() results = [] while queue and len(results) < 20: pairs, depth, A, path = queue.pop(0) key = (tuple(pairs), tuple(A)) if key in visited: continue visited.add(key) mods = moduli_from_pairs(pairs) M = 1 for m in mods: M *= m maxA = max(A) if M > maxA: FA = [F_multi(a, S, mods) for a in A] if is_sidon(FA): results.append({ 'word': braid_word(path), 'steps': depth, 'FA': FA, 'M': M, 'path': path }) continue if depth >= max_steps: continue for s in range(num_strands): for over in [True, False]: crossed = cross(pairs, s, over) if crossed is None: continue mods2 = moduli_from_pairs(crossed) new_A = [F_multi(a, S, mods2) for a in A] queue.append((crossed, depth+1, new_A, path + [crossed])) return results # ---------- Braid axiom tests ---------- def test_involution(): """σᵢ² = id: two over-crossings should return to original.""" print("=" * 60) print("BRAID AXIOM TESTS") print("=" * 60) A0, S = [1, 2, 5, 6], 7 init = coprime_pairs(2) # [(2,3), (5,7)] # σ₁ over then σ₁ over s1 = cross(init, 0, True) s1a = cross(s1, 0, True) if s1 else None print(f"\n σ₁²: {(2,3)} → over→ {s1[0] if s1 else '? ()'}" f" → over→ {s1a[0] if s1a else '? ()'}" f" back to initial: {s1a == init if s1a else False}") def test_far_commute(): """σᵢσⱼ = σⱼσᵢ for |i−j| ≥ 2: strand 1 and 3 commute.""" A0, S = [1, 2, 5, 6], 7 init = coprime_pairs(3) print(f"\n σ₁σ₃ vs σ₃σ₁ on {init}:") # σ₁ then σ₃ s1 = cross(init, 0, True) s1s3 = cross(s1, 2, False) if s1 else None # σ₃ then σ₁ s3 = cross(init, 2, False) s3s1 = cross(s3, 0, True) if s3 else None if s1s3 and s3s1: # Same final configuration? same = s1s3 == s3s1 w1 = braid_word([init, s1, s1s3]) w2 = braid_word([init, s3, s3s1]) print(f" σ₁σ₃: {w1} → {s1s3}") print(f" σ₃σ₁: {w2} → {s3s1}") print(f" Same: {same}") def test_single_sidon(): """Find single-step Sidon paths for each strand.""" print("\n" + "=" * 60) print("MULTI-STRAND SIDON SEARCH (2 strands)") print("=" * 60) A0, S = [1, 2, 5, 6], 7 results = multi_search(A0, S, num_strands=2, max_steps=2) print(f" Results: {len(results)}") for r in sorted(results, key=lambda x: x['steps'])[:5]: print(f" Word: {r['word']:20s} Steps={r['steps']} M={r['M']:4d} FA={r['FA']}") def test_strand_interaction(): """Test 2-strand configurations produce distinct results.""" print("\n" + "=" * 60) print("STRAND INTERACTION") print("=" * 60) A0, S = [1, 2, 5, 6], 7 configs = [ ([(2, 3), (5, 7)], "under, under"), ([(4, 2), (5, 7)], "over on 1, under on 2"), # but gcd(4,2)=2! ] for pairs, desc in configs: mods = moduli_from_pairs(pairs) if not pairwise_coprime(mods): continue FA = [F_multi(a, S, mods) for a in A0] M = 1 for m in mods: M *= m sidon = is_sidon(FA) print(f" {desc:30s} mods={mods} M={M:3d} Sidon={sidon} FA={FA}") if __name__ == "__main__": test_involution() test_far_commute() test_single_sidon() test_strand_interaction()