berna-r8 / code /cells /dna_kernel.py
muhamedkamil's picture
Upload folder using huggingface_hub
d997548 verified
Raw History Blame Contribute Delete
5.5 kB
#!/usr/bin/env python3
"""
R5-compliant DNA Kernel.
100 chromosomes × 4 genes = 400 regulatory parameters per cell.
10 functional groups (each 10 chromosomes):
0: language
1: math
2: code
3: science
4: sensory
5: reasoning
6: memory
7: coordination
8: adaptation
9: growth
4 genes per chromosome (per R5 Section 3.5):
a = activation
s = sensitivity
p = persistence
pi = plasticity
Maps (D_c, K_c, u_c) -> {route, grow, split, retain}
"""
import hashlib
import json
import random
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Optional
N_CHROMOSOMES = 100
N_GENES = 4
GENE_NAMES = ["a", "s", "p", "pi"]
GROUPS = [
"language", "math", "code", "science", "sensory",
"reasoning", "memory", "coordination", "adaptation", "growth",
]
@dataclass
class DNA:
"""DNA of one cell. 100 chromosomes x 4 genes."""
cell_id: str
genes: list = field(default_factory=list) # shape [100][4]
generation: int = 0
parent_id: Optional[str] = None
mutations: int = 0
signature: str = ""
def __post_init__(self):
if not self.genes:
self.genes = [[0.5, 0.5, 0.5, 0.5] for _ in range(N_CHROMOSOMES)]
if not self.signature:
self.signature = self._compute_signature()
def _compute_signature(self) -> str:
raw = json.dumps(self.genes, sort_keys=True).encode()
return hashlib.sha256(raw).hexdigest()[:32]
def random_dna(cell_id: str, seed: int = 42) -> DNA:
rng = random.Random(seed)
genes = [[rng.random() for _ in range(N_GENES)] for _ in range(N_CHROMOSOMES)]
return DNA(cell_id=cell_id, genes=genes, generation=0)
def mutate_gene(g: float, rate: float = 0.01) -> float:
"""Small mutation; clamped to [0,1]."""
delta = random.uniform(-rate, rate)
return max(0.0, min(1.0, g + delta))
def mutate_dna(dna: DNA, rate: float = 0.01) -> DNA:
"""Copy with small random changes (used on split)."""
new_genes = [[mutate_gene(g, rate) for g in row] for row in dna.genes]
new = DNA(
cell_id=dna.cell_id + "_m" + str(dna.mutations + 1),
genes=new_genes,
generation=dna.generation + 1,
parent_id=dna.cell_id,
mutations=dna.mutations + 1,
)
return new
def group_mean(dna: DNA, group_idx: int, gene_idx: int) -> float:
"""Mean of a specific gene across the 10 chromosomes of one group."""
start = group_idx * 10
end = start + 10
vals = [dna.genes[i][gene_idx] for i in range(start, end)]
return sum(vals) / len(vals)
def signal_route(dna: DNA) -> float:
"""Probability of routing to this cell. Mean of 'a' gene across all groups."""
return sum(dna.genes[i][0] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
def signal_grow(dna: DNA) -> float:
"""Readiness to grow. High plasticity (pi) + low persistence (p)."""
pi = sum(dna.genes[i][3] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
p = sum(dna.genes[i][2] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
return max(0.0, min(1.0, pi - p + 0.5))
def signal_split(dna: DNA, S: float, s_star: float = 0.85) -> float:
"""Readiness to split. Driven by S vs S* + sensitivity (s)."""
s = sum(dna.genes[i][1] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
return max(0.0, min(1.0, (S - s_star) + s * 0.2))
def signal_retain(dna: DNA) -> float:
"""Readiness to retain (freeze). High persistence (p) + low plasticity (pi)."""
p = sum(dna.genes[i][2] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
pi = sum(dna.genes[i][3] for i in range(N_CHROMOSOMES)) / N_CHROMOSOMES
return max(0.0, min(1.0, p - pi + 0.5))
def decide(dna: DNA, S: float, S_star: float = 0.85,
t_consecutive: int = 3) -> dict:
"""
(D_c, K_c, u_c) -> {route, grow, split, retain}
Priority-based decision per R5 Section 3.5.
Priority order (per R5):
1. split - if S >= S* sustained (handled by caller's S_history)
2. retain - if persistence high (frozen state)
3. grow - if plasticity high and not saturated
4. route - default behavior
"""
route = signal_route(dna)
grow = signal_grow(dna)
split = signal_split(dna, S, S_star)
retain = signal_retain(dna)
signals = {"route": route, "grow": grow, "split": split, "retain": retain}
# Priority-based decision (R5 Section 3.5)
if S >= S_star:
decision = "split"
elif retain > 0.7:
decision = "retain"
elif grow > 0.6:
decision = "grow"
else:
decision = "route"
signals["decision"] = decision
return signals
def save_dna(dna: DNA, path: Path):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(asdict(dna), indent=2))
def load_dna(path: Path) -> DNA:
return DNA(**json.loads(Path(path).read_text()))
if __name__ == "__main__":
dna = random_dna("cell-0001", seed=42)
print(f"DNA cell: {dna.cell_id}")
print(f"Signature: {dna.signature}")
print(f"Genes[0][0:4]: {dna.genes[0]}")
print(f"Genes[50][0:4]: {dna.genes[50]}")
print()
for S in [0.5, 0.7, 0.85, 0.95]:
d = decide(dna, S)
print(f"S={S:.2f} -> route={d['route']:.3f} grow={d['grow']:.3f} "
f"split={d['split']:.3f} retain={d['retain']:.3f} "
f"=> {d['decision'].upper()}")
print()
m = mutate_dna(dna, rate=0.05)
print(f"Mutated: {m.cell_id} (generation {m.generation})")