SilverSight/tests/test_dna_nn.py
allaunthefox 5331d2cc4e feat(dna): unified theory — DNA encoding, epigenetic computation, logarithmic vector spaces
Derivation from first principles:

1. Hachimoji DNA encoding (8 bases, ASCII-ordered, monotone LUT)
2. Imaginary Semantic Time (observer-independent semantic axis)
3. Sieve observers with CRT reconciliation (mod ℓ projections)
4. Semantic mass (E - E_min, E_s = m · 8²)
5. Gap preservation theorem (cleanMerge_preservesGap from GraphRank.lean)
6. Epigenetic computation (bistability, spreading, memory, attractors)
7. Logarithmic vector spaces (Kritchevsky: log N is a geometric vector)
8. Uncomputability framework (baseless logarithm = truth, based = computation)

Epigenetic optimizer breaks the freeze point:
  n=20: 0.7s (brute: 0.3s)
  n=24: 1.5s (brute: FROZEN)
  n=30: 3.4s (brute: FROZEN)
  n=50: 23.9s (brute: FROZEN)

Files:
  docs/UNIFIED_THEORY.md — full theory derivation
  docs/HACHIMOJI_DNA_SYNTAX.md — formal syntax specification
  docs/EPIGENETIC_COMPUTATION.md — epigenetic optimizer
  docs/UNCOMPUTABILITY.md — logarithmic vector space framework
  docs/REDERIVATION.md — rederivation from first principles
  python/dna_*.py — implementation (codec, LUT, GPU, surface)
  tests/test_dna_*.py — 68 tests, all green

Build: N/A (Python + Lean documentation)
2026-06-23 02:18:16 +00:00

268 lines
9 KiB
Python

#!/usr/bin/env python3
"""
test_dna_nn.py — Tests for nearest-neighbor QUBO-DNA encoding
Tests the critical property: NN Tm sort = energy sort for banded QUBOs.
"""
import math
import os
import random
import sys
import unittest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "python"))
from dna_qubo_nn import (
NNQuboCandidate,
apply_permutation,
base_to_value,
choose_base,
classify_qubo,
decode_nn_sequence,
demo_banded_qubo,
demo_full_qubo,
demo_ising_chain,
encode_nn_candidate,
generate_nn_candidates,
inverse_permutation,
nn_stack_dg,
nn_tm_estimate,
reorder_qubo_for_bandwidth,
sort_by_energy,
sort_by_tm_proxy,
spearman_rank_correlation,
verify_nn_sort,
)
class TestBaseChoice(unittest.TestCase):
"""§1: Base selection encodes value + diagonal sign."""
def test_zero_positive_diagonal(self):
"""x=0, Q_ii >= 0 → A"""
self.assertEqual(choose_base(0, 3.0), "A")
self.assertEqual(choose_base(0, 0.0), "A")
def test_zero_negative_diagonal(self):
"""x=0, Q_ii < 0 → T"""
self.assertEqual(choose_base(0, -1.0), "T")
def test_one_positive_diagonal(self):
"""x=1, Q_ii >= 0 → G"""
self.assertEqual(choose_base(1, 3.0), "G")
def test_one_negative_diagonal(self):
"""x=1, Q_ii < 0 → C"""
self.assertEqual(choose_base(1, -1.0), "C")
def test_base_to_value(self):
"""Low-Tm bases → 0, high-Tm bases → 1."""
self.assertEqual(base_to_value("A"), 0)
self.assertEqual(base_to_value("T"), 0)
self.assertEqual(base_to_value("G"), 1)
self.assertEqual(base_to_value("C"), 1)
class TestNNThermodynamics(unittest.TestCase):
"""§2: Nearest-neighbor thermodynamic model."""
def test_gc_richer_higher_tm(self):
"""GC-rich sequences have higher Tm proxy than AT-rich."""
at_rich = "AATT" * 10
gc_rich = "GGCC" * 10
self.assertGreater(nn_tm_estimate(gc_rich), nn_tm_estimate(at_rich))
def test_stacking_energies_finite(self):
"""All dinucleotide stacking energies are finite."""
for b1 in "ATGC":
for b2 in "ATGC":
dg = nn_stack_dg(b1, b2)
self.assertTrue(math.isfinite(dg))
def test_gc_gc_most_stable(self):
"""GC/GC stacking is among the most stable."""
gc_gc = nn_stack_dg("G", "C")
at_at = nn_stack_dg("A", "T")
self.assertLess(gc_gc, at_at) # more negative = more stable
def test_empty_sequence(self):
"""Empty sequence has zero Tm."""
self.assertEqual(nn_tm_estimate(""), 0.0)
def test_single_base(self):
"""Single base has per-base Tm only."""
tm_g = nn_tm_estimate("G")
tm_a = nn_tm_estimate("A")
self.assertGreater(tm_g, tm_a)
class TestEncodeDecode(unittest.TestCase):
"""§3: NN encoding and decoding."""
def test_roundtrip(self):
"""Encode → decode roundtrip preserves binary vector."""
Q = [[3.0, 0, 0], [0, 2.0, 0], [0, 0, 1.0]]
for x in [[0, 0, 0], [1, 1, 1], [0, 1, 0], [1, 0, 1]]:
cand = encode_nn_candidate(x, Q)
decoded = decode_nn_sequence(cand.sequence)
self.assertEqual(decoded, x, f"Failed for x={x}")
def test_all_zeros(self):
"""All-zero vector → all low-Tm bases."""
n = 5
Q = [[2.0] * n for _ in range(n)]
x = [0] * n
cand = encode_nn_candidate(x, Q)
for base in cand.sequence:
self.assertIn(base, "AT")
def test_all_ones(self):
"""All-one vector → all high-Tm bases."""
n = 5
Q = [[2.0] * n for _ in range(n)]
x = [1] * n
cand = encode_nn_candidate(x, Q)
for base in cand.sequence:
self.assertIn(base, "GC")
def test_negative_diagonal_uses_different_bases(self):
"""Negative Q_ii uses T/C instead of A/G."""
Q_pos = [[3.0]]
Q_neg = [[-3.0]]
self.assertEqual(encode_nn_candidate([0], Q_pos).sequence, "A")
self.assertEqual(encode_nn_candidate([0], Q_neg).sequence, "T")
self.assertEqual(encode_nn_candidate([1], Q_pos).sequence, "G")
self.assertEqual(encode_nn_candidate([1], Q_neg).sequence, "C")
class TestTmSortBanded(unittest.TestCase):
"""§4: THE CRITICAL TEST — banded QUBOs."""
def test_diagonal_perfect(self):
"""Diagonal QUBO: Tm sort = energy sort exactly."""
n = 6
Q = [[3.0 if i == j else 0.0 for j in range(n)] for i in range(n)]
result = verify_nn_sort(Q, n)
self.assertTrue(result.tm_sort_matches_energy)
self.assertGreater(result.rank_correlation, 0.99)
def test_banded_high_correlation(self):
"""Banded QUBO: Tm sort has high rank correlation."""
Q = demo_banded_qubo(8, seed=42)
result = verify_nn_sort(Q, 8)
self.assertGreater(result.rank_correlation, 0.8)
self.assertGreater(result.top_k_agreement, 0)
def test_ising_chain_perfect(self):
"""1D Ising chain: NN encoding captures all interactions."""
Q = demo_ising_chain(8, h=1.0, J=0.5)
result = verify_nn_sort(Q, 8)
# All interactions are adjacent → NN captures everything
self.assertGreater(result.rank_correlation, 0.9)
def test_ising_chain_optimal_match(self):
"""Ising chain: Tm-optimal is energy-optimal."""
Q = demo_ising_chain(6, h=1.0, J=0.5)
result = verify_nn_sort(Q, 6)
self.assertTrue(result.tm_sort_matches_energy)
class TestTmSortFull(unittest.TestCase):
"""§5: Full QUBO — tests the limits."""
def test_full_qubo_degraded(self):
"""Full QUBO: NN encoding loses non-adjacent interactions."""
Q = demo_full_qubo(8, seed=42)
result = verify_nn_sort(Q, 8)
# Should be worse than banded but still positive
self.assertGreater(result.rank_correlation, 0.0)
def test_full_qubo_still_finds_decent(self):
"""Full QUBO: Tm-optimal is at least in the top-k by energy."""
Q = demo_full_qubo(8, seed=42)
result = verify_nn_sort(Q, 8, top_k=10)
self.assertGreater(result.top_k_agreement, 0)
class TestBandwidthReordering(unittest.TestCase):
"""§6: QUBO reordering for bandwidth optimization."""
def test_identity_permutation(self):
"""Already-banded QUBO keeps similar ordering."""
Q = demo_banded_qubo(6, seed=42)
Q_reordered, perm = reorder_qubo_for_bandwidth(Q, 6)
# Should be similar (not necessarily identical)
self.assertEqual(len(perm), 6)
def test_permutation_inverse(self):
"""Permutation and inverse are correct."""
perm = [2, 0, 1, 3]
inv = inverse_permutation(perm)
for i in range(4):
self.assertEqual(perm[inv[i]], i)
def test_apply_permutation(self):
"""Apply permutation correctly reorders vector."""
x = [10, 20, 30, 40]
perm = [2, 0, 3, 1]
result = apply_permutation(x, perm)
self.assertEqual(result, [30, 10, 40, 20])
def test_reordering_preserves_energy(self):
"""Reordering doesn't change QUBO energies."""
Q = demo_full_qubo(6, seed=42)
Q_reord, perm = reorder_qubo_for_bandwidth(Q, 6)
inv = inverse_permutation(perm)
# Energy of a solution should be the same
x_orig = [1, 0, 1, 0, 1, 0]
x_reord = apply_permutation(x_orig, perm)
e_orig = sum(Q[i][j] * x_orig[i] * x_orig[j] for i in range(6) for j in range(6))
e_reord = sum(Q_reord[i][j] * x_reord[i] * x_reord[j] for i in range(6) for j in range(6))
self.assertAlmostEqual(e_orig, e_reord, places=10)
class TestQuboClassification(unittest.TestCase):
"""§7: QUBO type classification."""
def test_diagonal(self):
"""Diagonal matrix classified as 'diagonal'."""
Q = [[3.0, 0], [0, 2.0]]
self.assertEqual(classify_qubo(Q, 2), "diagonal")
def test_banded(self):
"""Tridiagonal matrix classified as 'banded'."""
Q = [[3.0, 1.0, 0], [1.0, 2.0, 0.5], [0, 0.5, 1.0]]
self.assertEqual(classify_qubo(Q, 3), "banded")
def test_full(self):
"""Full matrix classified as 'full'."""
Q = [[3.0, 1.0, 0.5], [1.0, 2.0, 0.3], [0.5, 0.3, 1.0]]
self.assertEqual(classify_qubo(Q, 3), "full")
class TestSpearman(unittest.TestCase):
"""§8: Rank correlation."""
def test_perfect_correlation(self):
"""Identical lists → correlation = 1.0."""
vals = [1.0, 2.0, 3.0, 4.0, 5.0]
self.assertAlmostEqual(spearman_rank_correlation(vals, vals), 1.0)
def test_inverse_correlation(self):
"""Reversed list → correlation = -1.0 (approximately)."""
vals = [1.0, 2.0, 3.0, 4.0, 5.0]
rev = list(reversed(vals))
corr = spearman_rank_correlation(vals, rev)
self.assertLess(corr, -0.9)
def test_empty(self):
"""Empty lists → 0.0."""
self.assertEqual(spearman_rank_correlation([], []), 0.0)
if __name__ == "__main__":
unittest.main(verbosity=2)