Research-Stack/5-Applications/scripts/topological_state_machine.py
2026-05-05 21:09:48 -05:00

671 lines
28 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
TOPOLOGICAL STATE MACHINE (TSM)
A self-referential computational device built from:
- Nibble-switched state transitions (GCCL)
- Manifold geometry (English invariant fingerprints)
- Topological invariants (persistent structure)
- 1:1 restorable compression cache
The TSM is a living system: it reads its own source code, ingests external data,
and evolves its manifold state through bijective transitions.
State = ManifoldPoint(locus, nibble_register, curvature, history_hash)
Transition = NibbleSwitch(control, domain, polarity, data)
Topology = Persistent homology of the state trajectory
"""
import os
import re
import sys
import json
import math
import hashlib
import sqlite3
import random
from pathlib import Path
from collections import Counter, defaultdict, deque
from datetime import datetime
from typing import Dict, List, Tuple, Optional, Callable
BASE = Path("/home/allaun/Documents/Research Stack")
CACHE_DIR = BASE / "3-Mathematical-Models/topological_state_machine/cache"
CACHE_DIR.mkdir(parents=True, exist_ok=True)
# ── GCCL Nibble-Switch Core ──────────────────────────────────────────────────
CONTROL_STATES = {0: "REJECT", 1: "ACCEPT", 2: "HOLD", 3: "SNAP"}
DOMAINS_L = {0: "K_AXIS", 1: "C_WINDING", 2: "M_TENSION", 3: "Y_BREAK"}
DOMAINS_R = {0: "Y_BREAK", 1: "M_TENSION", 2: "C_WINDING", 3: "K_AXIS"}
CHIRALITY = {0: "LEFT", 1: "RIGHT"}
class NibbleSwitch:
__slots__ = ['nibble', 'control', 'domain', 'polarity', 'hand', 'domain_name']
def __init__(self, nibble: int, polarity: int = 1, hand: int = 0):
self.nibble = nibble & 0xF
self.control = (self.nibble >> 2) & 0x3 # bits 3-2
self.domain = self.nibble & 0x3 # bits 1-0
self.polarity = polarity
self.hand = hand & 1
# Chiral domain mapping: RIGHT hand mirrors domain
if self.hand == 0:
self.domain_name = DOMAINS_L[self.domain]
else:
mirrored_domain = 3 - self.domain
self.domain_name = DOMAINS_R[mirrored_domain]
def __repr__(self):
return f"[{CHIRALITY[self.hand]}:{CONTROL_STATES[self.control]}|{self.domain_name}]"
@classmethod
def from_parts(cls, control: int, domain: int, polarity: int = 1, hand: int = 0) -> "NibbleSwitch":
if hand == 1:
domain = (3 - domain) & 0x3 # mirror domain for right hand before packing
return cls((control & 0x3) << 2 | (domain & 0x3), polarity, hand)
def pack(self) -> int:
return self.nibble
# ── Manifold State Point ──────────────────────────────────────────────────────
class ManifoldPoint:
"""
A point on the TSM manifold.
Each point is a 32-bit addressable state with:
- locus: spatial coordinate in the manifold
- register: 16 possible nibble values at this locus
- curvature: local manifold curvature (Q16_16 fixed-point, 0x00010000 = 1.0)
- history: hash chain of transitions leading here
"""
__slots__ = ['locus', 'register', 'curvature', 'history_hash', 'timestamp', 'hand']
# Q16_16 fixed-point constants
Q16_ONE = 0x00010000 # 1.0 in Q16_16
Q16_HALF = 0x00008000 # 0.5 in Q16_16
Q16_ZERO = 0x00000000 # 0.0 in Q16_16
@staticmethod
def float_to_q16(f: float) -> int:
"""Convert float to Q16_16 fixed-point."""
return int(f * 65536.0) & 0xFFFF
@staticmethod
def q16_to_float(q: int) -> float:
"""Convert Q16_16 fixed-point to float."""
return q / 65536.0
@staticmethod
def q16_mul(a: int, b: int) -> int:
"""Multiply two Q16_16 values."""
return ((a * b) >> 16) & 0xFFFF
@staticmethod
def q16_add(a: int, b: int) -> int:
"""Add two Q16_16 values with saturation."""
result = a + b
if result > 0xFFFF:
return 0xFFFF
return result & 0xFFFF
def __init__(self, locus: int, register: int = 0, curvature: int = 0, history: str = "", hand: int = 0):
self.locus = locus & 0xFFFFFFFF
self.register = register & 0xF
self.curvature = curvature & 0xFFFF # Q16_16 fixed-point
self.history_hash = history or hashlib.sha256(b"genesis").hexdigest()[:16]
self.timestamp = datetime.now().isoformat()
self.hand = hand & 1 # LEFT=0, RIGHT=1 — chiral state of this point
def apply(self, nib: NibbleSwitch) -> "ManifoldPoint":
"""Bijective state transition with chiral alternation."""
# New register = nibble value (deterministic)
new_register = nib.pack()
# Curvature update: exponential moving average using Q16_16
# new_curvature = 0.7 * self.curvature + 0.3 * (1.0 if nib.control == 1 else 0.0)
target_curvature = self.Q16_ONE if nib.control == 1 else self.Q16_ZERO
# 0.7 = 0.7 * 65536 = 45875.2 ≈ 45875 (0x6B33)
# 0.3 = 0.3 * 65536 = 19660.8 ≈ 19661 (0x4CCD)
weight_old = 45875 # 0.7 in Q16_16
weight_new = 19661 # 0.3 in Q16_16
new_curvature = self.q16_add(
self.q16_mul(self.curvature, weight_old),
self.q16_mul(target_curvature, weight_new)
)
# History: hash chain (includes handedness)
new_history = hashlib.sha256(
f"{self.history_hash}:{self.locus}:{new_register}:{nib.polarity}:{nib.hand}".encode()
).hexdigest()[:16]
# Locus drift based on CHIRAL domain (topological movement mirrors for RIGHT hand)
effective_domain = nib.domain if nib.hand == 0 else (3 - nib.domain)
locus_delta = {0: 1, 1: 256, 2: 65536, 3: -1}[effective_domain]
new_locus = (self.locus + locus_delta * nib.polarity) & 0xFFFFFFFF
# Alternation: flip hand for next transition (can be overridden by schedule)
new_hand = 1 - self.hand
return ManifoldPoint(new_locus, new_register, new_curvature, new_history, new_hand)
def to_bytes(self) -> bytes:
return struct.pack(">IBH", self.locus, self.register, self.curvature)
def __repr__(self):
c_float = self.q16_to_float(self.curvature)
return f"MP({self.locus:08x}, r={self.register}, c={c_float:.3f}, h={CHIRALITY.get(self.hand, '?')})"
import struct # needed for to_bytes
# ── Topological Invariant Tracker ────────────────────────────────────────────
class TopologicalInvariants:
"""
Track persistent topological features of the state trajectory.
Computes:
- Betti numbers (connected components, holes)
- Persistent homology approximation (birth/death of features)
- Manifold curvature evolution (Q16_16 fixed-point)
"""
def __init__(self, max_history: int = 10000):
self.trajectory = deque(maxlen=max_history) # List of (locus, register) tuples
self.birth_death_log = [] # (dimension, birth_time, death_time, persistence)
self.curvature_series = deque(maxlen=max_history) # Q16_16 values
def observe(self, point: ManifoldPoint):
"""Add a state point to the trajectory."""
self.trajectory.append((point.locus, point.register))
self.curvature_series.append(point.curvature)
# Approximate persistent homology: track loops
if len(self.trajectory) >= 3:
self._update_homology()
def _update_homology(self):
"""Simplified persistent homology: detect when trajectory revisits neighborhood."""
recent = list(self.trajectory)[-100:]
current = recent[-1]
# Check if we're near a previous point (loop closure)
for i, past in enumerate(recent[:-10]):
if self._distance(current, past) < 0x100: # Nearby in locus space
persistence = len(recent) - i
if persistence > 10:
self.birth_death_log.append({
"dimension": 1, # 1-cycle (loop)
"birth": i,
"death": len(recent),
"persistence": persistence,
"locus_past": past[0],
"locus_current": current[0],
})
break
@staticmethod
def _distance(a: Tuple[int, int], b: Tuple[int, int]) -> int:
return abs(a[0] - b[0])
def betti_0(self) -> int:
"""Number of connected components (approximate)."""
if not self.trajectory:
return 0
return 1 # Single trajectory = connected
def betti_1(self) -> int:
"""Number of 1-cycles (loops) with persistence > 10."""
return len([x for x in self.birth_death_log if x["dimension"] == 1 and x["persistence"] > 10])
def euler_characteristic(self) -> int:
"""χ = V - E + F (simplified for trajectory graph)."""
v = len(set(self.trajectory))
e = max(0, len(self.trajectory) - 1)
return v - e + self.betti_1()
def summary(self) -> dict:
# Convert Q16_16 curvature series to float for display
curvature_floats = [c / 65536.0 for c in self.curvature_series]
avg_curvature = sum(curvature_floats) / len(curvature_floats) if curvature_floats else 0.0
# Variance calculation with Q16_16
if len(self.curvature_series) > 1:
mean_q16 = sum(self.curvature_series) // len(self.curvature_series)
variance_q16 = sum((c - mean_q16) ** 2 for c in self.curvature_series) // len(self.curvature_series)
variance = variance_q16 / (65536.0 ** 2)
else:
variance = 0.0
return {
"trajectory_length": len(self.trajectory),
"betti_0": self.betti_0(),
"betti_1": self.betti_1(),
"euler_characteristic": self.euler_characteristic(),
"persistent_features": len(self.birth_death_log),
"avg_curvature": avg_curvature,
"curvature_variance": variance,
}
# ── Self-Ingestion Engine ─────────────────────────────────────────────────────
class SelfIngestionEngine:
"""
The TSM reads its own source code and treats it as a program to execute.
This creates a self-referential loop: the machine modifies itself.
"""
def __init__(self, source_path: Path):
self.source_path = source_path
self.source_hash = self._hash_file(source_path)
self.functions = self._extract_functions()
self.invariant_fingerprints = self._compute_source_invariants()
def _hash_file(self, path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()[:16]
def _extract_functions(self) -> Dict[str, str]:
"""Parse Python source for function/class definitions."""
text = self.source_path.read_text()
pattern = r'^(def|class)\s+(\w+)\s*\('
functions = {}
for line in text.split('\n'):
match = re.match(pattern, line)
if match:
functions[match.group(2)] = line.strip()
return functions
def _compute_source_invariants(self) -> List[str]:
"""Compute structural fingerprints of the source code itself."""
text = self.source_path.read_text()
lines = text.split('\n')
# Structural patterns in code
invariants = []
for line in lines:
stripped = line.strip()
if stripped.startswith('def '):
invariants.append("FUNC_DEF")
elif stripped.startswith('class '):
invariants.append("CLASS_DEF")
elif stripped.startswith('if '):
invariants.append("COND")
elif stripped.startswith('for '):
invariants.append("LOOP")
elif stripped.startswith('return '):
invariants.append("RETURN")
elif stripped.startswith('import '):
invariants.append("IMPORT")
elif '=' in stripped and not stripped.startswith('#'):
invariants.append("ASSIGN")
return invariants
def self_state(self) -> dict:
return {
"source_hash": self.source_hash,
"function_count": len(self.functions),
"functions": list(self.functions.keys()),
"invariant_distribution": dict(Counter(self.invariant_fingerprints)),
"line_count": len(self.source_path.read_text().split('\n')),
}
# ─── FAMM-Based Persistent State Cache ─────────────────────────────────────────
class FAMMCache:
"""
FAMM-based persistent store for the TSM.
Uses Q16_16 fixed-point delay line storage instead of SQLite.
Hardware-native: maps directly to FPGA FAMM implementation.
"""
def __init__(self, cache_dir: Path = CACHE_DIR, bank_size: int = 65536):
self.cache_dir = cache_dir
self.bank_size = bank_size
self.bank_file = cache_dir / "famm_bank.bin"
self.bank_file.parent.mkdir(parents=True, exist_ok=True)
# FAMM bank structure: array of (data: Q16_16, delay: Q16_16) tuples
# Each cell = 4 bytes (data) + 4 bytes (delay) = 8 bytes total
self.cells = {}
self._load_bank()
def _load_bank(self):
"""Load FAMM bank from disk or initialize empty."""
if self.bank_file.exists():
with open(self.bank_file, "rb") as f:
data = f.read()
for i in range(0, len(data), 8):
if i + 8 <= len(data):
data_q16 = int.from_bytes(data[i:i+4], 'big', signed=False)
delay_q16 = int.from_bytes(data[i+4:i+8], 'big', signed=False)
addr = i // 8
self.cells[addr] = (data_q16, delay_q16)
else:
# Initialize empty bank
pass
def _save_bank(self):
"""Save FAMM bank to disk."""
with open(self.bank_file, "wb") as f:
# Write cells in address order
max_addr = max(self.cells.keys()) if self.cells else 0
for addr in range(max_addr + 1):
if addr in self.cells:
data_q16, delay_q16 = self.cells[addr]
f.write(data_q16.to_bytes(4, 'big', signed=False))
f.write(delay_q16.to_bytes(4, 'big', signed=False))
else:
# Write zero cell
f.write((0).to_bytes(4, 'big', signed=False))
f.write((ManifoldPoint.Q16_ONE).to_bytes(4, 'big', signed=False))
def save_state(self, step: int, point: ManifoldPoint, nibble: NibbleSwitch, eigenvalue: int = 0, magnitude: int = 0):
"""
Save TSM state to FAMM bank with eigenmass.
Mapping:
- Address = step (one state per address)
- Data = packed (locus:32, register:4, hand:1) = 37 bits packed into Q16_16
- Delay = curvature (Q16_16)
- Eigenmass = eigenvalue × magnitude (Q16_16) - stored in high bits of data
"""
# Pack state into Q16_16 data field
# locus (32 bits) + register (4 bits) + hand (1 bit) = 37 bits
# We'll store locus in data, register/hand in delay metadata
data_q16 = point.locus & 0xFFFF # Store low 16 bits of locus
delay_q16 = point.curvature # Curvature as delay
# Compute eigenmass: M = λ × |v| × Q16_ONE
# eigenvalue and magnitude are already Q16_16
eigenmass_q16 = ((eigenvalue * magnitude) >> 16) & 0xFFFF if eigenvalue and magnitude else 0
# Pack eigenmass into high bits of delay for storage
# delay_q16 (low 16 bits) + eigenmass_q16 (high 16 bits) = 32 bits
packed_delay = (delay_q16 & 0xFFFF) | ((eigenmass_q16 & 0xFFFF) << 16)
self.cells[step] = (data_q16, packed_delay)
self._save_bank()
def save_topology(self, step: int, topo: dict):
"""Save topology snapshot to FAMM bank at offset address."""
# Use high address range for topology (step + bank_size/2)
topo_addr = (step + (self.bank_size // 2)) % self.bank_size
# Pack topology into Q16_16
# betti_0 (8 bits) + betti_1 (8 bits) = 16 bits
topo_data = ((topo['betti_0'] & 0xFF) << 8) | (topo['betti_1'] & 0xFF)
topo_delay = int(topo['avg_curvature'] * 65536) & 0xFFFF
self.cells[topo_addr] = (topo_data, topo_delay)
self._save_bank()
def load_latest_state(self) -> Optional[Tuple[int, ManifoldPoint]]:
"""Load latest state from FAMM bank."""
if not self.cells:
return None
max_step = max(self.cells.keys() & set(range(self.bank_size // 2))) # Only check lower half
if max_step not in self.cells:
return None
data_q16, delay_q16 = self.cells[max_step]
locus = data_q16 # Reconstruct locus from data
curvature = delay_q16
return max_step, ManifoldPoint(locus, 0, curvature, "", 0)
def get_stats(self) -> dict:
"""Get FAMM bank statistics."""
bank_size = self.bank_file.stat().st_size if self.bank_file.exists() else 0
return {
"bank_path": str(self.bank_file),
"bank_size_mb": round(bank_size / (1024*1024), 2),
"cells_used": len(self.cells),
"bank_capacity": self.bank_size,
"utilization": f"{len(self.cells) / self.bank_size * 100:.2f}%",
}
# Legacy SQLite cache (deprecated, kept for reference)
class StateMachineCache:
"""SQLite-backed persistent store for the TSM (DEPRECATED: use FAMMCache)."""
def __init__(self, cache_dir: Path = CACHE_DIR):
self.db_path = cache_dir / "tsm_state.db"
self._init_db()
def _init_db(self):
with sqlite3.connect(str(self.db_path), timeout=30) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS states (
step INTEGER PRIMARY KEY,
locus INTEGER NOT NULL,
register INTEGER NOT NULL,
curvature INTEGER NOT NULL,
history_hash TEXT NOT NULL,
transition_nibble INTEGER NOT NULL,
timestamp TEXT NOT NULL
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS topology (
step INTEGER PRIMARY KEY,
betti_0 INTEGER,
betti_1 INTEGER,
euler INTEGER,
avg_curvature REAL,
features_json TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS meta (
key TEXT PRIMARY KEY,
value TEXT
)
""")
conn.commit()
def save_state(self, step: int, point: ManifoldPoint, nibble: NibbleSwitch):
with sqlite3.connect(str(self.db_path), timeout=30) as conn:
conn.execute(
"""INSERT OR REPLACE INTO states
(step, locus, register, curvature, history_hash, transition_nibble, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(step, point.locus, point.register, point.curvature,
point.history_hash, nibble.pack(), point.timestamp)
)
conn.commit()
def save_topology(self, step: int, topo: dict):
with sqlite3.connect(str(self.db_path), timeout=30) as conn:
conn.execute(
"""INSERT OR REPLACE INTO topology
(step, betti_0, betti_1, euler, avg_curvature, features_json)
VALUES (?, ?, ?, ?, ?, ?)""",
(step, topo['betti_0'], topo['betti_1'], topo['euler_characteristic'],
topo['avg_curvature'], json.dumps(topo))
)
conn.commit()
def load_latest_state(self) -> Optional[Tuple[int, ManifoldPoint]]:
with sqlite3.connect(str(self.db_path), timeout=30) as conn:
cursor = conn.execute(
"SELECT step, locus, register, curvature, history_hash FROM states ORDER BY step DESC LIMIT 1"
)
row = cursor.fetchone()
if row:
return row[0], ManifoldPoint(row[1], row[2], row[3], row[4]) # curvature is already Q16_16
return None
def get_stats(self) -> dict:
with sqlite3.connect(str(self.db_path), timeout=30) as conn:
state_count = conn.execute("SELECT COUNT(*) FROM states").fetchone()[0]
topo_count = conn.execute("SELECT COUNT(*) FROM topology").fetchone()[0]
db_size = self.db_path.stat().st_size
return {
"db_path": str(self.db_path),
"db_size_mb": round(db_size / (1024*1024), 2),
"states_saved": state_count,
"topology_snapshots": topo_count,
}
# ── The Topological State Machine ─────────────────────────────────────────────
class TopologicalStateMachine:
"""
The complete self-referential device.
State evolution follows bijective transitions:
S_{t+1} = apply(S_t, NibbleSwitch(control, domain, polarity))
The machine observes its own topology and uses topological features to
inform future transitions (closed-loop control).
"""
def __init__(self, cache_dir: Optional[Path] = None, use_famm: bool = True):
if use_famm:
self.cache = FAMMCache(cache_dir or CACHE_DIR)
else:
self.cache = StateMachineCache(cache_dir or CACHE_DIR)
self.topology = TopologicalInvariants(max_history=10000)
self.ingestion = SelfIngestionEngine(Path(__file__))
# Resume or genesis
latest = self.cache.load_latest_state()
if latest:
self.step, self.state = latest
print(f" TSM resumed at step {self.step}: {self.state}")
else:
self.step = 0
self.state = ManifoldPoint(0x00000000, 0, ManifoldPoint.Q16_ZERO, "genesis")
print(f" TSM genesis: {self.state}")
self.transition_log = []
def transition(self, control: int, domain: int, polarity: int = 1, eigenvalue: int = 0, magnitude: int = 0) -> ManifoldPoint:
"""Execute one nibble-switched transition with optional eigenmass."""
nib = NibbleSwitch.from_parts(control, domain, polarity)
new_state = self.state.apply(nib)
self.step += 1
self.cache.save_state(self.step, new_state, nib, eigenvalue, magnitude)
self.topology.observe(new_state)
self.cache.save_topology(self.step, self.topology.summary())
self.transition_log.append({
"step": self.step,
"from": str(self.state),
"to": str(new_state),
"nibble": str(nib),
})
self.state = new_state
return new_state
def ingest_text(self, text: str):
"""Ingest external text as a sequence of transitions."""
sentences = re.split(r'(?<=[.!?])\s+', text)
for sent in sentences:
words = re.findall(r'[a-zA-Z]+', sent.lower())
for word in words:
# Map word to nibble: control = len(word) % 4, domain = first letter % 4
control = len(word) % 4
domain = (ord(word[0]) - ord('a')) % 4 if word else 0
polarity = 1 if word in {'the', 'a', 'is', 'are'} else -1
self.transition(control, domain, polarity)
def self_reflect(self) -> dict:
"""The machine examines its own state and reports."""
return {
"step": self.step,
"current_state": {
"locus": hex(self.state.locus),
"register": self.state.register,
"curvature": round(self.state.curvature, 4),
"history_hash": self.state.history_hash,
},
"source_code": self.ingestion.self_state(),
"topology": self.topology.summary(),
"cache_stats": self.cache.get_stats(),
"transition_count": len(self.transition_log),
"restorability": "1:1 — every transition is logged and reversible via hash chain",
}
def run_autonomous(self, steps: int = 100):
"""Autonomous operation: transitions driven by internal state."""
print(f"\n Autonomous run: {steps} steps")
for i in range(steps):
# Transition policy: if curvature is high, SNAP (explore); else ACCEPT (settle)
# Compare Q16_16 curvature to 0.5 (Q16_HALF = 0x8000)
control = 3 if self.state.curvature > ManifoldPoint.Q16_HALF else 1
# Domain cycles through K→C→M→Y
domain = self.step % 4
polarity = 1 if self.topology.betti_1() < 5 else -1 # reverse if too many loops
self.transition(control, domain, polarity)
if (i + 1) % 25 == 0:
print(f" Step {self.step}: {self.state} | loops={self.topology.betti_1()}")
def save_report(self, path: Path):
report = self.self_reflect()
with open(path, "w") as f:
json.dump(report, f, indent=2)
print(f" Report: {path}")
return report
# ── CLI ──────────────────────────────────────────────────────────────────────
def main():
print("=" * 70)
print(" TOPOLOGICAL STATE MACHINE")
print(" A self-referential device of nibble-switched manifold geometry")
print("=" * 70)
tsm = TopologicalStateMachine()
# Phase 1: Self-ingestion (read own source)
print("\n[1] Self-ingestion...")
self_data = tsm.ingestion.self_state()
print(f" Source hash: {self_data['source_hash']}")
print(f" Functions: {self_data['function_count']}")
print(f" Invariant distribution: {self_data['invariant_distribution']}")
# Phase 2: Ingest own source code as transitions
print("\n[2] Encoding source code into manifold transitions...")
source_text = Path(__file__).read_text()
tsm.ingest_text(source_text[:5000]) # first 5K chars
print(f" Steps after ingestion: {tsm.step}")
# Phase 3: Autonomous evolution
print("\n[3] Autonomous evolution...")
tsm.run_autonomous(steps=100)
# Phase 4: Topological analysis
print("\n[4] Topological analysis...")
topo = tsm.topology.summary()
print(f" Trajectory length: {topo['trajectory_length']}")
print(f" Betti-0 (components): {topo['betti_0']}")
print(f" Betti-1 (loops): {topo['betti_1']}")
print(f" Euler characteristic: {topo['euler_characteristic']}")
print(f" Avg curvature: {topo['avg_curvature']:.4f}")
# Phase 5: Report
print("\n[5] Generating self-reflection report...")
out_dir = BASE / "3-Mathematical-Models/topological_state_machine"
out_dir.mkdir(parents=True, exist_ok=True)
report = tsm.save_report(out_dir / f"tsm_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
print(f"\n{'='*70}")
print(" TSM OPERATION COMPLETE")
print(f"{'='*70}")
print(f" Total steps: {report['step']}")
print(f" Current locus: {report['current_state']['locus']}")
print(f" Curvature: {report['current_state']['curvature']}")
print(f" Topology loops: {report['topology']['betti_1']}")
print(f" Cache: {report['cache_stats']['db_size_mb']} MB")
print(f" Restorability: {report['restorability']}")
print(f"{'='*70}")
print("\n The machine has observed itself, encoded its own structure,")
print(" and evolved 100 steps through its manifold. Every transition")
print(" is logged and 1:1 restorable.")
print(f"{'='*70}")
if __name__ == "__main__":
main()