206 lines
12 KiB
Python
206 lines
12 KiB
Python
"""Pure epoch-END adapter to the frozen AM/J eligibility implementation.
|
|
|
|
Only the two SHA-pinned source files are read. No event files, brain state,
|
|
weights, base arrays or artifacts are written. The caller supplies actual sparse
|
|
events and externally authenticated COMPLETE KC/mask mappings. Hashing supplied
|
|
arrays identifies their bytes; it does not establish anatomical completeness.
|
|
The returned overlay is NOT installed. Scheduled D is not actual DAN delivery.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import hashlib
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
import sys
|
|
from types import ModuleType
|
|
|
|
import numpy as np
|
|
|
|
|
|
SCHEMA = "phase4an-conditioning-update-draft-v1"
|
|
BRIDGE_SHA256 = "174fb8d14619189e74b095e77a6db18b14c93046dc431c0b55710a5aa2dd2e61"
|
|
PHASE4J_SHA256 = "abb4574da85f00bd9ef61024c65993ef81d42651ff8d8d63c15dd69c6222471e"
|
|
|
|
|
|
def _require(condition, message):
|
|
if not condition:
|
|
raise ValueError(message)
|
|
|
|
|
|
def _canonical(value):
|
|
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False)
|
|
|
|
|
|
def _sha(value):
|
|
return hashlib.sha256(_canonical(value).encode("utf-8")).hexdigest()
|
|
|
|
|
|
def _array(value, dtype, name):
|
|
_require(isinstance(value, np.ndarray) and value.ndim == 1 and value.dtype == np.dtype(dtype),
|
|
name + " requires an exact one-dimensional " + str(np.dtype(dtype)) + " array")
|
|
return value
|
|
|
|
|
|
def _evidence(array):
|
|
"""Original dtype and logical C-order raw bytes; no cast or rounding."""
|
|
return dict(dtype=array.dtype.str, shape=list(array.shape), nbytes=int(array.nbytes),
|
|
raw_c_order_sha256=hashlib.sha256(array.tobytes(order="C")).hexdigest())
|
|
|
|
|
|
def _owned(array):
|
|
result = np.array(array, copy=True, order="C")
|
|
result.setflags(write=False)
|
|
return result
|
|
|
|
|
|
def _pinned_bytes(path, expected, label):
|
|
raw = Path(path).read_bytes()
|
|
_require(hashlib.sha256(raw).hexdigest() == expected, label + " source SHA256 mismatch")
|
|
return raw
|
|
|
|
|
|
def _load_bridge(path):
|
|
raw = _pinned_bytes(path, BRIDGE_SHA256, "AM eligibility bridge")
|
|
# Execute precisely the verified bytes, not a second path-based import.
|
|
# Temporary isolated module registration supports the frozen dataclass;
|
|
# restore any prior module, rather than colliding with old globals.
|
|
name = "_conditioning_update_bridge_" + BRIDGE_SHA256
|
|
module = ModuleType(name)
|
|
module.__file__ = str(path)
|
|
previous = sys.modules.get(name)
|
|
sys.modules[name] = module
|
|
try:
|
|
exec(compile(raw, str(path), "exec"), module.__dict__)
|
|
finally:
|
|
if previous is None:
|
|
del sys.modules[name]
|
|
else:
|
|
sys.modules[name] = previous
|
|
_require(module.PHASE4J_SHA256 == PHASE4J_SHA256, "Frozen bridge/J pin disagreement")
|
|
return module
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class EpochUpdate:
|
|
delta_after: np.ndarray
|
|
sampled_trace: np.ndarray
|
|
final_trace: np.ndarray
|
|
receipt: dict
|
|
|
|
|
|
def update_epoch(*, step, neuron_idx, kc, candidate_edge_id, candidate_sources,
|
|
candidate_targets, candidate_compartments, delta_before, base_candidate,
|
|
steps, n_neurons, compartment, us_tick, d_command, frozen,
|
|
bridge_source, phase4j_source, trace_backend):
|
|
"""Return the epoch-boundary update; never apply it to the running brain.
|
|
|
|
trace_backend is mandatory: ``torch`` executes the frozen Torch CPU trace
|
|
expression; ``numpy`` remains its candidate compatibility implementation,
|
|
without a claim of per-call Torch equivalence. One declared US or no US is
|
|
supported. A US-less epoch requires D=0. Actual DAN delivery is deliberately
|
|
absent from this API and must be bound as separate coordinator evidence.
|
|
"""
|
|
_require(type(steps) is int and steps > 0, "steps must be a positive integer")
|
|
_require(type(n_neurons) is int and n_neurons > 0, "n_neurons must be a positive integer")
|
|
_require(us_tick is None or (type(us_tick) is int and 0 <= us_tick < steps), "us_tick must be None or an integer inside the epoch")
|
|
_require(type(d_command) in (int, float) and math.isfinite(d_command) and d_command in (0, 1),
|
|
"D must be the explicit scheduled scalar 0 or 1, not Boolean or measured DAN")
|
|
_require(us_tick is not None or d_command == 0, "No declared US tick requires D=0")
|
|
_require(type(frozen) is bool, "frozen must be Boolean")
|
|
_require(trace_backend in ("numpy", "torch"), "Explicit trace_backend must be numpy or torch")
|
|
arrays = dict(step=_array(step, "int32", "step"), neuron_idx=_array(neuron_idx, "int32", "neuron_idx"),
|
|
kc=_array(kc, "int32", "kc"), candidate_edge_id=_array(candidate_edge_id, "int64", "candidate_edge_id"),
|
|
candidate_sources=_array(candidate_sources, "int32", "candidate_sources"),
|
|
candidate_targets=_array(candidate_targets, "int32", "candidate_targets"),
|
|
delta_before=_array(delta_before, "float32", "delta_before"),
|
|
base_candidate=_array(base_candidate, "float32", "base_candidate"))
|
|
_require(isinstance(candidate_compartments, np.ndarray) and candidate_compartments.ndim == 1 and
|
|
candidate_compartments.dtype.kind == "U", "candidate_compartments must be a Unicode vector")
|
|
arrays["candidate_compartments"] = candidate_compartments
|
|
size = len(candidate_edge_id)
|
|
_require(size > 0 and np.all(candidate_edge_id >= 0) and np.all(candidate_edge_id[1:] > candidate_edge_id[:-1]),
|
|
"Candidate edge IDs must be nonempty, canonical sorted unique int64 IDs")
|
|
_require(all(len(arrays[name]) == size for name in ("candidate_sources", "candidate_targets",
|
|
"candidate_compartments", "delta_before", "base_candidate")), "Candidate mapping columns differ in length")
|
|
_require(np.all((candidate_targets >= 0) & (candidate_targets < n_neurons)), "Candidate target outside supplied graph size")
|
|
before_evidence = {name: _evidence(value) for name, value in arrays.items()}
|
|
# Every computation receives owned copies. Caller views/strides are accepted
|
|
# without changing their values or flags; dtype is never silently repaired.
|
|
work = {name: np.array(value, copy=True, order="C") for name, value in arrays.items()}
|
|
bridge = _load_bridge(bridge_source)
|
|
_pinned_bytes(phase4j_source, PHASE4J_SHA256, "Phase 4J")
|
|
trace = bridge.trace_from_events(work["step"], work["neuron_idx"], work["kc"], steps,
|
|
[] if us_tick is None else [us_tick], phase4j_source=phase4j_source,
|
|
backend=trace_backend, n_neurons=n_neurons)
|
|
sampled = trace.last_command_trace
|
|
after, original = bridge.apply_eligibility(sampled, work["kc"], work["candidate_sources"],
|
|
work["candidate_compartments"], compartment, work["delta_before"], work["base_candidate"],
|
|
d=d_command, frozen=frozen, phase4j_source=phase4j_source, n_neurons=n_neurons)
|
|
# Diagnostics only; the update is exclusively the frozen apply_v2 above.
|
|
selected = work["candidate_compartments"] == compartment
|
|
local = np.searchsorted(work["kc"], work["candidate_sources"])
|
|
eligible = (sampled[local] > 0) & selected
|
|
changed = after.view(np.uint32) != work["delta_before"].view(np.uint32)
|
|
value_changed = after != work["delta_before"]
|
|
floor = -bridge.FLOOR_FRACTION * work["base_candidate"]
|
|
floor_before = work["delta_before"] == floor
|
|
floor_after = after == floor
|
|
_require(bridge.bit_equal(after[~selected], work["delta_before"][~selected]), "Off-compartment identity failed")
|
|
if frozen or d_command == 0:
|
|
_require(bridge.bit_equal(after, work["delta_before"]), "Zero-D/frozen identity failed")
|
|
original_report = original["original_report"]
|
|
attempted = 0 if original_report is None else original_report["updated_edges"]
|
|
if original_report is not None:
|
|
_require(original_report["eligible_edges"] == int(eligible.sum()), "Frozen eligible-edge report disagrees with its sampled trace")
|
|
delta_after, sampled_trace, final_trace = (_owned(a) for a in (after, sampled, trace.final))
|
|
_require(all(_evidence(value) == before_evidence[name] for name, value in arrays.items()), "Caller input bytes changed")
|
|
_pinned_bytes(bridge_source, BRIDGE_SHA256, "AM eligibility bridge")
|
|
_pinned_bytes(phase4j_source, PHASE4J_SHA256, "Phase 4J")
|
|
receipt = dict(schema=SCHEMA, status="DRAFT-COMPUTED-EPOCH-UPDATE-NOT-INSTALLED",
|
|
source_pins=dict(eligibility_bridge_sha256=BRIDGE_SHA256, phase4j_sha256=PHASE4J_SHA256),
|
|
input_arrays=before_evidence,
|
|
command=dict(steps=steps, n_neurons=n_neurons, compartment=compartment, us_tick=us_tick,
|
|
D_scheduled=float(d_command), frozen=frozen, trace_backend=trace_backend),
|
|
semantics=dict(application="epoch_boundary_after_all_recorded_ticks; affects subsequent epoch only",
|
|
sampling="after_current_tick_spike", trace_initial_state="zero at epoch boundary",
|
|
no_US_sample="canonical float32 +0, not final trace", D_definition="artificial scheduled scalar",
|
|
actual_DAN_delivery_observed=False, actual_DAN_delivery_used_to_gate=False,
|
|
actual_DAN_evidence_authority="separate externally bound epoch record, not an input to this function",
|
|
original_base_units="mV", delta_units="mV", delta_dtype="float32",
|
|
candidate_target_role="identity evidence only; update uses original source/compartment mapping"),
|
|
fixed_rule=dict(dt_ms=bridge.DT_MS, trace_tau_ms=bridge.TAU_MS, trace_cap=bridge.TRACE_CAP,
|
|
eta_mv=bridge.ETA_MV, polarity=bridge.POLARITY, floor_fraction=bridge.FLOOR_FRACTION),
|
|
trace_audit=trace.audit, apply_audit=original,
|
|
counts=dict(events=len(step), actual_KC_events=trace.audit["actual_kc_events"],
|
|
candidates=size, selected_compartment_edges=int(selected.sum()), sampled_eligible_selected_edges=int(eligible.sum()),
|
|
original_updated_edges_attempted=int(attempted), original_apply_v2_executed=original["original_apply_v2_executed"],
|
|
actual_value_changed_edges=int(value_changed.sum()), actual_bitwise_changed_edges=int(changed.sum()),
|
|
actual_off_compartment_bitwise_changed_edges=int(changed[~selected].sum()),
|
|
selected_at_floor_before=int((selected & floor_before).sum()),
|
|
selected_at_floor_after=int((selected & floor_after).sum()),
|
|
selected_newly_at_floor=int((selected & floor_after & ~floor_before).sum()),
|
|
sampled_eligible_selected_already_at_floor=int((eligible & floor_before).sum())),
|
|
count_limitations="original updated_edges counts nonzero attempted increments, including saturation; floor counts do not prove why any edge was unchanged",
|
|
outputs={"delta_after": _evidence(delta_after), "sampled_trace": _evidence(sampled_trace), "final_trace": _evidence(final_trace)},
|
|
inputs_exact_unchanged=True, off_compartment_exact_unchanged=True,
|
|
numpy_torch_bitwise_comparison_performed=False,
|
|
frozen_Torch_expression_executed=trace_backend == "torch",
|
|
complete_KC_identity_verified=False, complete_mask_identity_verified=False, full_graph_authority_verified=False,
|
|
mapping_hash_scope="supplied logical array bytes only; coordinator must bind complete canonical KC and candidate mask",
|
|
brain_dynamics_executed=False, weights_installed=False, base_modified=False,
|
|
training_executed_by_this_function=False, scientific_learning_pass_claimed=False, candidate_generated=False)
|
|
receipt["payload_sha256"] = _sha(receipt)
|
|
return EpochUpdate(delta_after, sampled_trace, final_trace, receipt)
|
|
|
|
|
|
def verify_receipt(receipt, **epoch_inputs):
|
|
"""Recompute from supplied arrays/sources; never trust a self-hash alone."""
|
|
_require(type(receipt) is dict and "payload_sha256" in receipt, "Invalid epoch receipt")
|
|
_require(receipt["payload_sha256"] == _sha({k: v for k, v in receipt.items() if k != "payload_sha256"}),
|
|
"Epoch receipt payload SHA mismatch")
|
|
actual = update_epoch(**epoch_inputs)
|
|
_require(_canonical(receipt) == _canonical(actual.receipt), "Receipt differs from exact recomputation in this environment")
|
|
return True
|