File size: 2,996 Bytes
5e0b58b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
"""Deterministic laboratory protocol for pair-based STDP timing curves."""

from __future__ import annotations

from statistics import fmean
from typing import Any

from src.learning.stdp_plugin import STDPParameters, STDPSynapse

PROTOCOL_ID = "stdp_pair_timing_v1"
DELTA_T_MS = (-50, -20, -10, -5, -1, 0, 1, 5, 10, 20, 50)
REPLICATIONS = 10
INITIAL_WEIGHT = 0.5
PARAMETERS = STDPParameters(
    a_plus=0.1,
    a_minus=0.12,
    tau_plus=20.0,
    tau_minus=20.0,
)


def run_pair_timing_protocol() -> dict[str, Any]:
    """Measure the STDP timing curve with fixed, isolated conditions.

    The protocol does not access the Brain-5D runtime, filesystem, network,
    or any language model. Each replication receives a new synapse and the
    same pre/post timing, making the measurement deterministic and auditable.
    """
    rows: list[dict[str, float | int]] = []
    for delta_t in DELTA_T_MS:
        changes = [_measure_delta_weight(delta_t) for _ in range(REPLICATIONS)]
        rows.append(
            {
                "delta_t_ms": delta_t,
                "repeated_evaluations": REPLICATIONS,
                "independent_runs": 0,
                "mean_delta_weight": fmean(changes),
                "min_delta_weight": min(changes),
                "max_delta_weight": max(changes),
            }
        )

    positive = [row["mean_delta_weight"] for row in rows if row["delta_t_ms"] > 0]
    negative = [row["mean_delta_weight"] for row in rows if row["delta_t_ms"] < 0]
    zero = next(row["mean_delta_weight"] for row in rows if row["delta_t_ms"] == 0)
    supported = (
        all(change > 0 for change in positive)
        and all(change < 0 for change in negative)
        and zero == 0.0
    )
    return {
        "protocol": PROTOCOL_ID,
        "seed": 42,
        "conditions": {
            "initial_weight": INITIAL_WEIGHT,
            "parameters": PARAMETERS.to_dict(),
            "repeated_evaluations_per_condition": REPLICATIONS,
            "independent_runs_per_condition": 0,
        },
        "measurements": rows,
        "summary": {
            "total_replications": len(DELTA_T_MS) * REPLICATIONS,
            "conditions": len(DELTA_T_MS),
            "repeated_evaluations": len(DELTA_T_MS) * REPLICATIONS,
            "independent_runs": 0,
            "mean_ltp": fmean(positive),
            "mean_ltd": fmean(negative),
            "zero_delta_weight": zero,
            "hypothesis_supported": supported,
            "deterministic_identity": all(
                row["min_delta_weight"] == row["max_delta_weight"] for row in rows
            ),
        },
    }


def _measure_delta_weight(delta_t_ms: int) -> float:
    synapse = STDPSynapse.from_parameters(INITIAL_WEIGHT, PARAMETERS)
    reference_tick = 100
    if delta_t_ms < 0:
        synapse.post_spike(reference_tick + delta_t_ms)
        return synapse.pre_spike(reference_tick)
    synapse.pre_spike(reference_tick)
    return synapse.post_spike(reference_tick + delta_t_ms)