""" phi.output — FASTQ, Adleman graph, and PCR primer protocol Transforms Φ encoding records (from phi.embed.encode_phi) into standard bioinformatics formats consumable by external tools. Formats: FASTQ — pipe through Jellyfish/KMC/BLAST Adleman graph — feed into BioComputing's connectNodes() PCR protocol — wet-lab filtering with allele-specific primers Dependencies: phi.embed (for encode_phi output dict format). """ from __future__ import annotations import math from typing import Dict, List # ── Primer sequences ───────────────────────────────────────────────────── PRIMER_DESIGN = { "pas_primer": { "sequence": "CCCCCC", "target": "consistency_dna = GGGGGG (all 6 rules pass)", "tm": 36.0, "purpose": "Capture PASS strands — amplify consistent equations only", }, "fail_primer": { "sequence": "AAAAAA", "target": "consistency_dna = TTTTTT (all 6 rules fail)", "tm": 12.0, "purpose": "Capture FAIL strands — quarantine inconsistent equations", }, "universal_primer": { "sequence": "ABCABC", "target": "F_dna region (first 8 bases)", "tm": 15.0, "purpose": "Amplify all encoded equations regardless of consistency", }, } # ── FASTQ ──────────────────────────────────────────────────────────────── def to_fastq(record: Dict) -> str: """Format a Φ encoding record as a 4-line FASTQ entry. FASTQ is the standard DNA sequencing format. This lets us pipe equation encodings through any existing bioinformatics pipeline without tool modification. """ eq = record["equation"] seq = record["dna_sequence"] qual = record["quality_scores"] n_cons = sum(1 for v in record["consistency"].values() if v) eq_hash = record["sha256_prefix"] pass_flag = "PASS" if record["consistency_pass"] else "FAIL" header = f"@{eq_hash} n_consistency={n_cons}/6 pass={pass_flag} len={len(eq)}" qual_line = qual * (len(seq) // len(qual)) if len(seq) > len(qual) else qual return f"{header}\n{seq}\n+\n{qual_line}\n" # ── Adleman integration ────────────────────────────────────────────────── def to_adleman_graph(records: List[Dict]) -> Dict: """Build an Adleman/Lipton-compatible graph from Φ-encoded equations. Each equation is a vertex. Edges represent Fisher-distance similarity (d_F < 0.5) between their F(E) byte-class histograms. Feed the output into BioComputing's `hampath.connectNodes()` or `sattv.createNodes()` to generate wet-lab DNA sequences. """ vertices = {} for i, rec in enumerate(records): vid = f"E{i}" vertices[vid] = { "equation": rec["equation"], "dna": rec["dna_sequence"], "consistency": rec["consistency_pass"], } edges = [] vert_ids = list(vertices.keys()) for i in range(len(vert_ids)): for j in range(i + 1, len(vert_ids)): Fi = records[i]["F"] Fj = records[j]["F"] dist = _fisher_distance(Fi, Fj) if dist < 0.5: edges.append((vert_ids[i], vert_ids[j], round(dist, 4))) return { "vertices": vertices, "edges": edges, "format": "adleman_graph_v1", "n_vertices": len(vertices), "n_edges": len(edges), } # ── PCR protocol ───────────────────────────────────────────────────────── def design_filtering_protocol(records: List[Dict]) -> Dict: """Design a PCR filtering protocol for a batch of encoded equations. Uses allele-specific PCR (Newton et al. 1989) — the 24°C Tm gap between PASS and FAIL primers provides single-base discrimination. """ n_pass = sum(1 for r in records if r["consistency_pass"]) n_fail = len(records) - n_pass return { "protocol": "allele_specific_pcr_v1", "reference": "Newton et al. (1989) Nucleic Acids Res 17:2503; " "allele-specific PCR for single-base discrimination", "pas_primer": PRIMER_DESIGN["pas_primer"]["sequence"], "fail_primer": PRIMER_DESIGN["fail_primer"]["sequence"], "universal_primer": PRIMER_DESIGN["universal_primer"]["sequence"], "annealing_tm_pass": PRIMER_DESIGN["pas_primer"]["tm"], "annealing_tm_fail": PRIMER_DESIGN["fail_primer"]["tm"], "annealing_tm_universal": PRIMER_DESIGN["universal_primer"]["tm"], "expected_amplification_pass": n_pass, "expected_amplification_fail": n_fail, } # ── Fisher distance ↓ (internal; no circular dep) ──────────────────────── def _fisher_distance(p: List[float], q: List[float]) -> float: """Fisher-Rao distance between two points on Δ₇: d_F = 2·acos(Σ√(p_i·q_i)).""" dot = sum(math.sqrt(pi * qi) for pi, qi in zip(p, q)) dot = max(-1.0, min(1.0, dot)) return 2.0 * math.acos(dot)