#!/usr/bin/env python3 """GeneticBraidBridge — map ANY genetic alphabet to BraidState. Accepts DNA, RNA, mRNA, Hachimoji (8-letter), XNA (16-letter hex), 6-state (genetic ground-up), and custom alphabets. Each symbol maps to a braid generator on one of 8 strands (BraidStorm topology). Codons compose into braid words. The bridge emits a BraidState JSON ready for: - eigensolid_convergence (Lean) - receipt_invertible (Lean) - RRC classification (LogogramProjection) - crossStep iteration - Sidon slack measurement Usage: python3 genetic_braid_bridge.py --alphabet dna --seq ATGCCGTAA python3 genetic_braid_bridge.py --alphabet hachimoji --seq ACGTZPSB python3 genetic_braid_bridge.py --alphabet xna --seq 0123456789ABCDEF python3 genetic_braid_bridge.py --alphabet rna --seq AUGCCGUAA --translate """ from __future__ import annotations import argparse import hashlib import json import math import sys from dataclasses import dataclass, field, asdict from typing import Optional # ── Canonical BraidStorm constants ────────────────────────────────────── SIDON_LABELS: list[int] = [1, 2, 4, 8, 16, 32, 64, 128] N_STRANDS = 8 # Golden ratio inverse Q16_16: φ⁻¹ ≈ 0x00009E70 PHI_INV_Q16 = 0x9E70 # ── Genetic alphabet definitions ──────────────────────────────────────── @dataclass(frozen=True) class GeneticAlphabet: name: str symbols: str bits_per_symbol: int strand_map: dict[str, int] # symbol → strand index (0..N_STRANDS-1) complement_map: dict[str, str] codon_length: int = 3 description: str = "" # Strand-to-Sidon mapping: # Each symbol claims a primary strand; the braid generator crosses # that strand with its pair (strand ^ 1). # For alphabets ≤4, symbols map to strands 0-3 (4 pairs). # For alphabets ≤8, symbols map to strands 0-7 (4 pairs). # For 16-letter alphabets, symbols map in pairs to strands 0-7. def _build_4sym_strand_map(symbols: str) -> dict[str, int]: return {s: i for i, s in enumerate(symbols)} def _build_8sym_strand_map(symbols: str) -> dict[str, int]: return {s: i for i, s in enumerate(symbols)} def _build_16sym_strand_map(symbols: str) -> dict[str, int]: return {s: i % 8 for i, s in enumerate(symbols)} ALPHABETS: dict[str, GeneticAlphabet] = { "dna": GeneticAlphabet( name="dna", symbols="ACGT", bits_per_symbol=2, strand_map=_build_4sym_strand_map("ACGT"), complement_map={"A": "T", "C": "G", "G": "C", "T": "A"}, codon_length=3, description="Standard DNA: 4 bases, 64 codons (NCBI Table 1)", ), "rna": GeneticAlphabet( name="rna", symbols="ACGU", bits_per_symbol=2, strand_map=_build_4sym_strand_map("ACGU"), complement_map={"A": "U", "C": "G", "G": "C", "U": "A"}, codon_length=3, description="RNA: 4 bases, catalytic/regulatory", ), "mrna": GeneticAlphabet( name="mrna", symbols="ACGU", bits_per_symbol=2, strand_map=_build_4sym_strand_map("ACGU"), complement_map={"A": "U", "C": "G", "G": "C", "U": "A"}, codon_length=3, description="mRNA: messenger RNA, same alphabet as RNA", ), "hachimoji": GeneticAlphabet( name="hachimoji", symbols="ACGTZPSB", bits_per_symbol=3, strand_map=_build_8sym_strand_map("ACGTZPSB"), complement_map={ "A": "T", "C": "G", "G": "C", "T": "A", "Z": "P", "P": "Z", "S": "B", "B": "S", }, codon_length=3, description="Hachimoji: 8 bases, 512 codons (Benner et al.)", ), "xna": GeneticAlphabet( name="xna", symbols="0123456789ABCDEF", bits_per_symbol=4, strand_map=_build_16sym_strand_map("0123456789ABCDEF"), complement_map={}, # no canonical complement for generic XNA codon_length=2, description="XNA: 16-symbol hex, 256 2-codons", ), "genetic6": GeneticAlphabet( name="genetic6", symbols="ACGTUX", bits_per_symbol=3, strand_map=_build_8sym_strand_map("ACGTUX"), # uses 6 of 8 strands complement_map={"A": "U", "C": "G", "G": "C", "U": "A", "X": "X"}, codon_length=3, description="6-state quantum nucleotide (A/C/G/T/U/X from GeneticGroundUp)", ), } # ── Sidon / Braid Core ────────────────────────────────────────────────── @dataclass class BraidStrand: phase_x: int = 0 phase_y: int = 0 slot: int = 0 residue: int = 0 jitter: int = 0 bracket_lower: int = 0 bracket_upper: int = 0 bracket_gap: int = 0 bracket_kappa: int = 0 bracket_phi: int = 0 admissible: bool = True @dataclass class BraidState: strands: list[BraidStrand] = field(default_factory=lambda: [BraidStrand() for _ in range(N_STRANDS)]) step_count: int = 0 def _xor_slot(a: int, b: int) -> int: return a ^ b def _braid_cross(si: BraidStrand, sj: BraidStrand) -> tuple[BraidStrand, int]: """Simulate braidCross: merge two strands, return merged strand + residual.""" zx = si.phase_x + sj.phase_x zy = si.phase_y + sj.phase_y slot = _xor_slot(si.slot, sj.slot) residue = abs(zx - si.phase_x) + abs(zy - si.phase_y) jitter = si.jitter + sj.jitter merged = BraidStrand( phase_x=zx, phase_y=zy, slot=slot, residue=residue, jitter=jitter, admissible=True, ) return merged, residue def cross_step(state: BraidState) -> BraidState: """One BraidStorm crossStep iteration on 4 adjacent pairs.""" def _cross_pair(i: int, j: int) -> BraidStrand: merged, _ = _braid_cross(state.strands[i], state.strands[j]) return merged pairs = [(0, 1), (2, 3), (4, 5), (6, 7)] new_strands = list(state.strands) for i, j in pairs: new_strands[i] = _cross_pair(i, j) new_strands[j] = _braid_cross(state.strands[j], state.strands[i])[0] return BraidState(strands=new_strands, step_count=state.step_count + 1) def is_eigensolid(state: BraidState) -> bool: """Check if crossStep(state) == state (strand data unchanged).""" stepped = cross_step(state) for i in range(N_STRANDS): s1 = state.strands[i] s2 = stepped.strands[i] if (s1.phase_x != s2.phase_x or s1.phase_y != s2.phase_y or s1.slot != s2.slot or s1.residue != s2.residue): return False return True def iterate_to_convergence(state: BraidState, max_steps: int = 100) -> BraidState: """Apply crossStep until eigensolid or max_steps.""" for _ in range(max_steps): if is_eigensolid(state): break state = cross_step(state) return state # ── Genetic → Braid translation ──────────────────────────────────────── def normalize_sequence(alphabet: GeneticAlphabet, seq: str) -> str: allowed = set(alphabet.symbols) clean = "".join(ch.upper() for ch in seq if not ch.isspace()) bad = sorted({ch for ch in clean if ch not in allowed}) if bad: raise ValueError(f"{alphabet.name}: unsupported symbols: {''.join(bad)}") return clean def symbols_to_braid_word(alphabet: GeneticAlphabet, seq: str) -> list[tuple[int, int]]: """Map a genetic sequence to a list of (strand_i, strand_j) crossings. Each symbol maps to its assigned strand. The braid generator crosses that strand with its pair (strand ^ 1). Codons (3 symbols) produce 3 crossings that engage their respective strand pairs. """ clean = normalize_sequence(alphabet, seq) word: list[tuple[int, int]] = [] for sym in clean: s = alphabet.strand_map[sym] word.append((s, s ^ 1)) return word def braid_word_to_state(word: list[tuple[int, int]], initial_slots: Optional[list[int]] = None) -> BraidState: """Apply a braid word to an initial BraidState. Each crossing (i, j) XORs the slots and accumulates phase. """ slots = list(initial_slots) if initial_slots else list(SIDON_LABELS) strands = [BraidStrand(slot=slots[i]) for i in range(N_STRANDS)] state = BraidState(strands=strands, step_count=0) for i, j in word: merged, _ = _braid_cross(state.strands[i], state.strands[j]) state.strands[i] = merged return state def sequence_to_braid_state( alphabet: GeneticAlphabet, seq: str, initial_slots: Optional[list[int]] = None, ) -> tuple[BraidState, list[tuple[int, int]]]: """Full pipeline: genetic sequence → braid word → BraidState.""" word = symbols_to_braid_word(alphabet, seq) state = braid_word_to_state(word, initial_slots) return state, word # ── Receipt emission ──────────────────────────────────────────────────── def encode_receipt(state: BraidState, alphabet: GeneticAlphabet, seq: str, seq_hash: str, word: list[tuple[int, int]], logogram_hash: str = "", ) -> dict: """Emit a genetic braid receipt JSON. Matches BraidReceipt structure from BraidEigensolid.lean: crossing_matrix, sidon_slack, step_count, residuals, write_time, scar_absent """ strand0 = state.strands[0] strand7 = state.strands[7] sidon_slack = 128 - strand7.slot residuals = [s.residue for s in state.strands] max_slot = max(s.slot for s in state.strands) max_slot_index = max(range(N_STRANDS), key=lambda i: state.strands[i].slot) receipt = { "schema": "genetic_braid_receipt_v1", "generated_at_utc": __import__("datetime").datetime.utcnow().isoformat() + "Z", "genetic": { "alphabet": alphabet.name, "symbols": alphabet.symbols, "codon_length": alphabet.codon_length, "sequence_length": len(seq), "sequence_hash_sha256": seq_hash, "sequence": seq if len(seq) <= 200 else seq[:100] + "...[truncated]..." + seq[-100:], }, "braid": { "word_length": len(word), "sidon_labels": SIDON_LABELS, "sidon_slack": max(0, sidon_slack), "max_slot_used": max_slot, "max_slot_strand": max_slot_index, "slots": [s.slot for s in state.strands], "residues": residuals, "residue_sum": sum(residuals), }, "receipt": { "crossing_matrix": { "lower": strand0.bracket_lower, "upper": strand0.bracket_upper, "gap": strand0.bracket_gap, "kappa": strand0.bracket_kappa, "phi": strand0.bracket_phi, "admissible": strand0.admissible, }, "sidon_slack": max(0, sidon_slack), "step_count": state.step_count, "residuals": residuals, "write_time": 0, "scar_absent": all(s.admissible for s in state.strands), }, "meta_solid": { "sidon_doublings_total": 7, "sidon_doublings_consumed": int(math.log2(max(max_slot, 1))).bit_length() if max_slot > 0 else 0, "capacity_ratio": round(max_slot / 128.0, 4) if max_slot > 0 else 0, }, "logogram_hash_sha256": logogram_hash or seq_hash, "claim_boundary": "genetic-to-braid-bridge;no-lean-spectral;no-classifier", } preimage = json.dumps(receipt, sort_keys=True) receipt["receipt_hash_sha256"] = hashlib.sha256(preimage.encode()).hexdigest() return receipt # ── Codon tables ──────────────────────────────────────────────────────── CODON_TABLES: dict[str, dict[str, str]] = { "standard": { "TTT": "Phe", "TTC": "Phe", "TTA": "Leu", "TTG": "Leu", "TCT": "Ser", "TCC": "Ser", "TCA": "Ser", "TCG": "Ser", "TAT": "Tyr", "TAC": "Tyr", "TAA": "Stop", "TAG": "Stop", "TGT": "Cys", "TGC": "Cys", "TGA": "Stop", "TGG": "Trp", "CTT": "Leu", "CTC": "Leu", "CTA": "Leu", "CTG": "Leu", "CCT": "Pro", "CCC": "Pro", "CCA": "Pro", "CCG": "Pro", "CAT": "His", "CAC": "His", "CAA": "Gln", "CAG": "Gln", "CGT": "Arg", "CGC": "Arg", "CGA": "Arg", "CGG": "Arg", "ATT": "Ile", "ATC": "Ile", "ATA": "Ile", "ATG": "Met", "ACT": "Thr", "ACC": "Thr", "ACA": "Thr", "ACG": "Thr", "AAT": "Asn", "AAC": "Asn", "AAA": "Lys", "AAG": "Lys", "AGT": "Ser", "AGC": "Ser", "AGA": "Arg", "AGG": "Arg", "GTT": "Val", "GTC": "Val", "GTA": "Val", "GTG": "Val", "GCT": "Ala", "GCC": "Ala", "GCA": "Ala", "GCG": "Ala", "GAT": "Asp", "GAC": "Asp", "GAA": "Glu", "GAG": "Glu", "GGT": "Gly", "GGC": "Gly", "GGA": "Gly", "GGG": "Gly", }, } # ── CLI ───────────────────────────────────────────────────────────────── def _build_arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(description=__doc__) p.add_argument("--alphabet", choices=sorted(ALPHABETS), default="dna", help="Genetic alphabet") p.add_argument("--seq", default="", help="Genetic sequence (raw text)") p.add_argument("--seq-file", default="", help="Read sequence from file (FASTA or raw)") p.add_argument("--codon-table", choices=list(CODON_TABLES), help="Codon translation table") p.add_argument("--translate", action="store_true", help="Translate to amino acids (requires --codon-table or standard)") p.add_argument("--complement", action="store_true", help="Show complement sequence") p.add_argument("--converge", action="store_true", help="Run crossStep to eigensolid convergence") p.add_argument("--output", default="", help="Write receipt JSON to file") p.add_argument("--table", action="store_true", help="List supported alphabets") return p def main() -> int: args = _build_arg_parser().parse_args() if args.table: rows = [] for name, alpha in sorted(ALPHABETS.items()): rows.append({ "name": alpha.name, "symbols": alpha.symbols, "bits_per_symbol": alpha.bits_per_symbol, "codon_length": alpha.codon_length, "n_codons": len(alpha.symbols) ** alpha.codon_length, "description": alpha.description, }) print(json.dumps(rows, indent=2)) return 0 alphabet = ALPHABETS[args.alphabet] # Read sequence seq = args.seq if args.seq_file: with open(args.seq_file) as f: content = f.read().strip() if content.startswith(">"): lines = content.splitlines() seq = "".join(l for l in lines[1:] if not l.startswith(">")) else: seq = content if not seq: print("Error: no sequence provided (use --seq or --seq-file)", file=sys.stderr) return 1 seq = normalize_sequence(alphabet, seq) seq_hash = hashlib.sha256(seq.encode()).hexdigest() # Complement if args.complement: comp = "".join(alphabet.complement_map.get(s, s) for s in seq) print(f"Complement: {comp}") return 0 # Translation if args.translate: table_name = args.codon_table or "standard" table = CODON_TABLES.get(table_name, {}) if not table: print(f"Error: unknown codon table '{table_name}'", file=sys.stderr) return 1 # Convert T→U for RNA alphabets working_seq = seq if alphabet.name in ("rna", "mrna"): working_seq = working_seq.replace("T", "U") codons = [working_seq[i:i+3] for i in range(0, len(working_seq) - 2, 3)] aa_seq = [] for codon in codons: aa = table.get(codon, "?") aa_seq.append(aa) if aa == "Stop": break print(f"Sequence ({alphabet.name}, {len(seq)} bp):") print(f" {seq}") print(f"Translation ({table_name}):") print(f" {'-'.join(aa_seq)}") degeneracy_map = { "Phe": 2, "Leu": 6, "Ile": 3, "Met": 1, "Val": 4, "Ser": 6, "Pro": 4, "Thr": 4, "Ala": 4, "Tyr": 2, "His": 2, "Gln": 2, "Asn": 2, "Lys": 2, "Asp": 2, "Glu": 2, "Cys": 2, "Trp": 1, "Arg": 6, "Gly": 4, "Stop": 3, } degeneracies = [degeneracy_map.get(aa, 0) for aa in aa_seq] avg_degeneracy = round(sum(degeneracies) / max(len(degeneracies), 1), 2) print(f"\nAvg degeneracy: {avg_degeneracy}") # Braid bridge state, word = sequence_to_braid_state(alphabet, seq) if args.converge: initial_state = BraidState( strands=[BraidStrand(slot=SIDON_LABELS[i]) for i in range(N_STRANDS)], step_count=0, ) pre_state, pre_word = sequence_to_braid_state(alphabet, seq) state = iterate_to_convergence(pre_state) receipt = encode_receipt(state, alphabet, seq, seq_hash, word) receipt["sidon_meta"] = { "standard_code_capacity_pct": round(64 / 128 * 100, 1), "actual_capacity_pct": round(max(s.slot for s in state.strands) / 128 * 100, 1), "sidon_doublings_consumed": int(math.log2(max(max(s.slot for s in state.strands), 1))) + 1, "slack_regime": _classify_slack(state), } if args.output: with open(args.output, "w") as f: json.dump(receipt, f, indent=2) print(f"Receipt written to {args.output}") else: print(json.dumps(receipt, indent=2)) return 0 def _classify_slack(state: BraidState) -> str: max_slot = max(s.slot for s in state.strands) slack = 128 - max_slot if slack >= 64: return "gas (abundant slack, sparse encoding)" elif slack >= 8: return "liquid (moderate slack, efficient encoding)" elif slack > 0: return "meta-solid (tight encoding, near capacity)" else: return "solid (capacity exceeded, compression regime)" if __name__ == "__main__": raise SystemExit(main())