ThomasHeisig's picture
Sync GitHub main 963d68d523b38096619e41743c8c4b5b2ac1d42c (part 7)
91e2c8b verified
Raw History Blame Contribute Delete
5.41 kB
"""CPU scalar reference backend for canonical Wave-4 parity."""
from __future__ import annotations
from collections.abc import Mapping, Sequence
from copy import deepcopy
from typing import Any
from src.runtime.backend import BackendCapabilities, BackendState, RunResult, StepResult
from src.verification.parity import config_fingerprint, execution_fingerprint
from .backend import cpu_recurrent_reference
from .state import (
canonical_int,
prefix_inputs,
recurrent_inputs_from_mapping,
recurrent_inputs_to_mapping,
set_external_tick_config,
stable_digest,
step_payload,
validate_checkpoint,
)
class CPUReferenceBackend:
backend_name = "cpu-reference"
backend_version = "wave4-scalar-reference-v1"
def __init__(self) -> None:
self._config: dict[str, object] | None = None
self._seed = 0
self._tick = 0
def initialize(self, config: Mapping[str, object], seed: int) -> None:
inputs = recurrent_inputs_from_mapping(config, seed=seed)
self._config = recurrent_inputs_to_mapping(inputs)
self._seed = seed
self._tick = 0
def _require_config(self) -> dict[str, object]:
if self._config is None:
raise RuntimeError("backend is not initialized")
return self._config
def _execute_prefix(self, total_ticks: int) -> Mapping[str, object]:
inputs = recurrent_inputs_from_mapping(self._require_config(), seed=self._seed)
return cpu_recurrent_reference(prefix_inputs(inputs, total_ticks))
def _step_from_outputs(
self, outputs: Mapping[str, object], tick: int
) -> StepResult:
config = self._require_config()
payload = step_payload(
outputs,
tick=tick,
n_neurons=canonical_int(config["n_neurons"], field="n_neurons"),
)
spikes_raw = payload["spikes"]
if not isinstance(spikes_raw, list):
raise ValueError("spike payload must be a list")
spikes = tuple(
index for index, value in enumerate(spikes_raw) if int(value) == 1
)
return StepResult(
tick=tick + 1,
spikes=spikes,
state_digest=stable_digest(payload),
metrics={
"voltage": payload["voltage"],
"adaptation": payload["adaptation"],
"spike_count": len(spikes),
},
)
def set_external_tick(self, tick: int, currents: Sequence[float]) -> None:
if tick != self._tick:
raise ValueError("live external input tick must equal continuation cursor")
set_external_tick_config(self._require_config(), tick=tick, currents=currents)
def step(self, tick: int) -> StepResult:
if tick != self._tick:
raise ValueError("tick must equal the backend continuation cursor")
config = self._require_config()
if tick >= canonical_int(config["ticks"], field="ticks"):
raise ValueError("configured tick limit reached")
outputs = self._execute_prefix(tick + 1)
self._tick = tick + 1
return self._step_from_outputs(outputs, tick)
def run(self, ticks: int) -> RunResult:
if type(ticks) is not int or ticks < 1:
raise ValueError("ticks must be a positive int")
config = self._require_config()
total = self._tick + ticks
if total > canonical_int(config["ticks"], field="ticks"):
raise ValueError("requested run exceeds configured tick limit")
start = self._tick
outputs = self._execute_prefix(total)
steps = tuple(
self._step_from_outputs(outputs, tick) for tick in range(start, total)
)
self._tick = total
return RunResult(
ticks_requested=ticks,
steps=steps,
final_state=self.snapshot(),
execution_fingerprint=execution_fingerprint(
seed=self._seed,
config_hash=config_fingerprint(config),
backend_name=self.backend_name,
backend_version=self.backend_version,
ticks=total,
),
)
def snapshot(self) -> BackendState:
config = self._require_config()
payload: dict[str, Any] = {
"backend": self.backend_version,
"seed": self._seed,
"tick": self._tick,
"config": deepcopy(config),
"replay_required": True,
}
return BackendState(
tick=self._tick,
payload=payload,
state_digest=stable_digest(payload),
)
def restore(self, state: BackendState) -> None:
raw_config, raw_seed = validate_checkpoint(state, self.backend_version)
self.initialize(raw_config, raw_seed)
self._tick = state.tick
def capabilities(self) -> BackendCapabilities:
return BackendCapabilities(
supports_recurrent=True,
supports_plasticity=True,
supports_pan_hyperstate=False,
supports_structural_plasticity=False,
max_neurons=4096,
max_ticks=2000,
max_edges=65536,
deterministic=True,
plasticity_semantics="REFERENCE_MATCH_TARGET_DRAFT",
execution_mode="BOUNDED_REPLAY_REFERENCE",
supports_live_external_input=True,
)