Spaces:
Running
Running
| """Versioned population checkpoint envelope (v4.1, Phase 2). | |
| Separates simulation state from research/provenance history while storing | |
| both, so save -> terminate -> restore -> continue is exactly equivalent to | |
| uninterrupted execution: | |
| state_hash - Population.population_hash() (organisms + world) | |
| research_hash - event history + lineages + teaching sessions | |
| combined_hash - sha256(state_hash + research_hash) | |
| version - FlyBrain version (src/version.py) | |
| schema_version - this envelope format (population_checkpoint_v1) | |
| graph_identity - canonical graph identity (REAL_SUBGRAPH / ...) | |
| experiment_identity - experiment seed + seed bundle + graph/circuit fingerprint | |
| rng_bundle - full SeedBundle (deterministic continuation) | |
| world/population state - complete Population.snapshot() | |
| event history - population EventLog + teaching sessions (dropped by the | |
| legacy Population.restore; preserved here) | |
| Legacy Population.snapshot()/restore() are untouched (backward compatible). | |
| """ | |
| import dataclasses | |
| import hashlib | |
| import json | |
| from typing import Any, Dict | |
| from src.common.events import EventLog | |
| CHECKPOINT_SCHEMA_VERSION = "population_checkpoint_v1" | |
| def _canon(obj: Any) -> bytes: | |
| return json.dumps(obj, sort_keys=True, default=str).encode() | |
| def research_hash_of(pop) -> str: | |
| h = hashlib.sha256() | |
| h.update(pop.events.compute_hash().encode()) | |
| h.update(_canon({"genetic": pop.genetic_lineage, "cultural": pop.cultural_lineage})) | |
| h.update(_canon([dataclasses.asdict(s) for s in pop.teaching_sessions])) | |
| return h.hexdigest() | |
| def experiment_identity_of(pop) -> str: | |
| seeds = pop.seeds | |
| return hashlib.sha256(_canon({ | |
| "experiment_seed": pop.experiment_seed, | |
| "seeds": {k: getattr(seeds, k) for k in | |
| ("experiment_seed", "generation_seed", "organism_seed", | |
| "development_seed", "mutation_seed", "world_seed", "teacher_seed")}, | |
| "graph_mode": pop.graph_mode.value, | |
| "circuit_size": pop.circuit_size, | |
| "genome_version": pop.genome_version, | |
| "autonomy_mode": pop.autonomy_mode, | |
| })).hexdigest() | |
| def save_checkpoint(pop) -> Dict[str, Any]: | |
| from src.version import VERSION | |
| from src.connectome.types import GraphMode as _GM | |
| state_hash = pop.population_hash() | |
| research_hash = research_hash_of(pop) | |
| combined = hashlib.sha256(f"{state_hash}|{research_hash}".encode()).hexdigest() | |
| seeds = pop.seeds | |
| return { | |
| "version": VERSION, | |
| "schema_version": CHECKPOINT_SCHEMA_VERSION, | |
| "state_hash": state_hash, | |
| "research_hash": research_hash, | |
| "combined_hash": combined, | |
| "graph_identity": _GM.canonical(pop.graph_mode), | |
| "graph_mode": pop.graph_mode.value, | |
| "experiment_identity": experiment_identity_of(pop), | |
| "rng_bundle": {k: getattr(seeds, k) for k in | |
| ("experiment_seed", "generation_seed", "organism_seed", | |
| "development_seed", "mutation_seed", "world_seed", "teacher_seed")}, | |
| "population": pop.snapshot(), | |
| "event_history": list(pop.events.events), | |
| "teaching_sessions": [dataclasses.asdict(s) for s in pop.teaching_sessions], | |
| } | |
| def restore_checkpoint(payload: Dict[str, Any]): | |
| """Restore a population from an envelope. Verifies hashes; raises on mismatch.""" | |
| from src.common.determinism import SeedBundle | |
| from src.connectome.types import GraphMode as _GM | |
| from src.culture.transmission import TeachingSession | |
| from src.population.population import Population | |
| if payload.get("schema_version") != CHECKPOINT_SCHEMA_VERSION: | |
| raise ValueError(f"unsupported checkpoint schema {payload.get('schema_version')!r}") | |
| rb = payload["rng_bundle"] | |
| seeds = SeedBundle(experiment_seed=rb["experiment_seed"], | |
| generation_seed=rb["generation_seed"], | |
| organism_seed=rb["organism_seed"], | |
| development_seed=rb["development_seed"], | |
| mutation_seed=rb["mutation_seed"], | |
| world_seed=rb["world_seed"], | |
| teacher_seed=rb["teacher_seed"]) | |
| pop = Population.restore(payload["population"], seeds) | |
| # Preserve research history the legacy path drops. | |
| pop.events = EventLog() | |
| pop.events.events = [dict(e) for e in payload.get("event_history", [])] | |
| pop.teaching_sessions = [TeachingSession(**s) | |
| for s in payload.get("teaching_sessions", [])] | |
| # Verify: restored hashes must equal the recorded ones. | |
| if pop.population_hash() != payload["state_hash"]: | |
| raise ValueError("checkpoint state_hash mismatch after restore") | |
| if research_hash_of(pop) != payload["research_hash"]: | |
| raise ValueError("checkpoint research_hash mismatch after restore") | |
| return pop | |