The code that vanishes when the reader changes
Suppose a sensory neuron emits ten spikes after stimulus A and twenty after stimulus B. Calling this a rate code feels obvious. Yet the description has silently supplied a counting window, a set of alternatives and a reader capable of integrating over that window.
The firing rate is usually written as r = N/T, where N is the number of spikes observed and T is the observation duration. If A produces ten spikes in half a second, the measured rate is 20 hertz. This is a useful compression when precise order does not matter and downstream integration is slow enough to average local irregularity. Change T, however, and the code can change. A 500-millisecond window may show a stable rate difference while a 20-millisecond window reveals bursts, onset latency or no spikes at all.
Panzeri and colleagues showed formally that the information attributed to counts and timing depends on the observation timescale. Short windows can support a spike-count regime; longer windows can reveal stimulus-dependent temporal structure that contributes additional information.[2] This does not make timing universally superior. It makes the window part of the hypothesis.
Two input fibres each emit four spikes. In pattern A, their spikes arrive within two milliseconds of one another. In pattern B, the same eight spikes are staggered across forty milliseconds. Reader C is a coincidence detector with a short membrane time constant. Reader I is a slow integrator.
Keep every upstream spike fixed. Connect the fibres to C and pattern A produces an output spike while B does not. Connect them to I and both patterns produce the same depolarisation. What did the spikes represent? They supported a synchrony distinction for C and only a count distinction for I. The representational claim changed when the causal reader changed, although the recorded activity did not.
This is the hidden assumption inside many decoding papers. A machine-learning classifier can recover stimulus identity from a neural population, but the fitted classifier may use boundaries no plausible downstream circuit learns or implements. Kriegeskorte and Douglas therefore distinguish what information a decoder can exploit from what the biological system actually uses.[3] Panzeri and colleagues make the same point operationally: sensory encoding, information readout and behaviour must be connected, preferably with intervention.[4]
This paper proposes a three-gate test. A candidate neural code is explanatorily viable only when it separates relevant alternatives, is causally readable by the circuit and is learnable or maintainable by available plasticity. “Encode” asks whether the activity distinction generalises. “Read” asks whether a downstream mechanism uses it. “Learn” asks how the relation is acquired and kept stable. Passing only the first gate establishes information availability, not a complete mechanism.
Record a spike train that predicts a choice. Now shuffle spike times within each trial while preserving every neuron’s spike count. If behaviour and downstream activity remain unchanged, the lost timing was not required under that condition. If both deteriorate, precise temporal structure carried causally relevant information. If an offline decoder deteriorates but behaviour does not, the decoder depended on timing the animal did not need.
The intervention is stronger than comparing classifier scores because it asks what changes in the operating system. It is still not enough by itself. Shuffling can also alter synchrony, adaptation and network state, so the manipulation must name which temporal property it destroys.
Optional depth: information is not yet mechanism
Mutual information asks how much observing response R reduces uncertainty about stimulus S. A positive value means the response statistically distinguishes stimulus alternatives. It does not identify the downstream reader, establish that the animal uses the information, or specify how the relation was learned. A perfect barcode in a brain region could be epiphenomenal to the tested behaviour. Conversely, a low-dimensional, noisy variable could be causally decisive if the reader is adapted to it.
Romain Brette argues more radically that the coding metaphor can misrepresent the brain’s circular, dynamical causality by treating spikes as messages about experimenter-defined variables.[5] The encode-read-learn framework keeps the useful, modest sense of code: a named distinction available to a named process under a named intervention. It rejects the stronger claim that experimenter decodability automatically reveals what the brain represents.
flowchart LR A["The frequency that was hidden<br/>in silence"] --> B["The code that vanishes when<br/>the reader changes"] B --> C["Four views of one moving<br/>population"] C --> D["Plasticity chooses the useful<br/>timescale"] D -. "re-test" .-> A classDef first fill:#0a1424,stroke:#7B4CD8,stroke-width:2px,color:#e6f3ff classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff class A first class B,C,D step
Four views of one moving population
Rate, timing, population and prediction are often presented as competing candidate languages. A better picture is a moving neural population seen through four instruments, each tuned to a different invariant.
Rate preserves amount across local timing variation
A rate description is powerful when the downstream reader integrates many inputs, the relevant variable changes slowly and temporal jitter is nuisance rather than signal. It can be read quickly if enough neurons contribute in parallel. It is also experimentally convenient, which partly explains its prevalence.
Its failure condition is equally clear. Two stimuli can generate the same total count while differing in onset, interval, burst structure or coordination. A rate measured over a long window can also blur the very transition that triggered action. Rate is not “the average code”; it is a resolution choice that treats selected timing differences as irrelevant.
Timing preserves order, latency and intervals
A temporal code can use first-spike latency, inter-spike intervals, phase relative to an oscillation, synchrony across cells or an entire spike sequence. The tactile study in the opening case is unusually direct because the stimulus construction made the temporal feature behaviourally testable. A recent longitudinal mouse visual-cortex study also reported that temporal structure over tens of milliseconds improved stimulus discriminability and was more stable across days than rate-only tuning in the recorded populations.[6]
That result should not be inflated into “the cortex uses precise timing”. The finding concerns a particular preparation, stimulus set, analysis and temporal scale. A temporal decoder also needs a reference. “Spike at 23 milliseconds” is useless if the reader lacks stimulus onset, phase or an internally generated clock. Timing becomes a code only to a reader whose own dynamics resolve the timing.
Population geometry preserves relations across neurons
A single neuron can be ambiguous while a population is decisive. Represent each trial as a vector x = (r₁, r₂, …, rₙ), with one component per neuron or feature. A downstream linear reader computes y = sign(wᵀx − b). The weight vector w defines which direction through population activity matters for the task.
This language exposes why “more information” is not one scalar property. Dimensionality, correlations and factorisation alter how easily a new reader can learn a task. Wakhloo, Slatton and Chung recently linked four population-geometry statistics to the generalisation of linear readouts across tasks sharing latent structure.[7] Their result is not a universal decoder for the brain. It shows that readout performance follows measurable geometry, rather than only the selectivity of isolated cells. A population code is useful when task-relevant distinctions have geometry a plausible reader can learn.
Let two neurons respond with binary activity. Stimulus A produces patterns (1,0) or (0,1), each half the time. Stimulus B produces (0,0) or (1,1), each half the time. Each individual neuron fires on half the trials for A and half for B, so neither neuron alone carries stimulus information in its mean rate.
The joint pattern is perfectly informative. A is the “different” relation and B is the “same” relation. A reader computing exclusive OR can classify every trial. A linear reader cannot separate the four corners with one line. The population therefore contains a usable distinction for a nonlinear reader but not for that linear reader. “Encoded in the population” is still incomplete until the readout class is named.
Predictive coding preserves deviation from a model
In predictive coding, a signal may be represented relative to what the system expected. In the simplest notation, e = s − ŝ: the residual e is the sensed quantity s minus its prediction ŝ. If the prediction is accurate, the residual shrinks even though the external stimulus remains present.
Rao and Ballard proposed a hierarchical visual model in which feedback carries predictions of lower-level activity while feedforward connections carry residual error.[8] This was a concrete computational account of selected receptive-field effects, not proof that every cortical pathway literally transmits a single error variable. Predictive explanations must distinguish a mathematical objective, an implemented circuit and a measured biological signal.
Prediction also changes learning. Halvagal and Zenke derived a local rule combining predictive and Hebbian components in models, where temporal continuity pulls related representations together while Hebbian variance and decorrelation terms prevent collapse.[9] The model shows how predictive structure could shape invariance. It does not establish that the same objective governs all sensory cortex. A prediction error is meaningful only relative to the generative relation that produced the prediction.
On Monday, two stimuli form clusters separated along the horizontal axis of population activity. A downstream neuron reads the horizontal difference. Over a month, synaptic turnover rotates both clusters by thirty degrees. In branch one, the reader is frozen; performance falls. In branch two, local plasticity rotates the reader with the population; performance remains stable.
An experimenter who applies Monday’s decoder to Friday’s data reports representational drift. That is true relative to the fixed decoder. It does not imply lost information or unstable behaviour. Local adaptive decoders can track drift when the changing population remains constrained.[10] The relevant invariant may be geometry plus co-adaptation, not the identity of individually tuned neurons.
| View | Preserves | Reader assumption | Typical break condition |
|---|---|---|---|
| Rate | Spike amount in a window | Integration over that window | Equal counts hide order or latency |
| Timing | Latency, interval, phase or sequence | Clock and dynamics fine enough to resolve it | Jitter, unknown reference or slow integration |
| Population | Relations and geometry across units | Learnable readout class | Wrong grain, correlation shift or decoder mismatch |
| Predictive | Residual relative to an internal model | Implemented source of prediction and comparison | Adaptation or common input mimics an error signal |
Plasticity chooses the useful timescale
A downstream reader is not usually delivered with its weights already correct. The circuit must acquire sensitivity to some distinctions, ignore others and remain usable while synapses, neurons and environments change.
The simplest story is Hebbian: correlated activity strengthens a connection. Even this story contains several unresolved choices. How close in time must the activities occur? Does postsynaptic spiking matter, or local dendritic depolarisation? Is the update gated by reward, novelty or a plateau potential? What prevents every active synapse from saturating?
Spike-timing-dependent plasticity, or STDP, makes one timing relation explicit. In a common pair-based model, a presynaptic spike shortly before a postsynaptic spike potentiates a synapse; the reverse order depresses it. Let Δt = tpost − tpre. One illustrative window is:
Δw = −A₋ exp(Δt/τ₋), if Δt < 0 Δw is the synaptic weight change. A₊ and A₋ set maximum potentiation and depression. τ₊ and τ₋ set how quickly the effect decays with separation. This phenomenological rule assumes isolated pairs and fixed parameters.
With A₊ = 0.012, τ₊ = 20 milliseconds and a pre-before-post lag of 10 milliseconds, one pairing gives Δw ≈ 0.012e−0.5 ≈ 0.0073. Reverse the order with A₋ = 0.014 and the update is about −0.0085. The equation makes a causal hypothesis visible: connections that reliably contribute before an output strengthen, while those arriving after it weaken.
Bi and Poo demonstrated timing-dependent potentiation and depression in cultured hippocampal neurons, with dependence on timing, initial strength and postsynaptic cell type.[11] Yet a neat two-spike window is not a universal learning law. In visual cortical slices, Sjöström, Turrigiano and Nelson found that rate, timing and cooperativity jointly determined plasticity.[12] Plasticity reads patterns through cellular state, not through spike order alone.
One neuron can host several learning rules
Plasticity depends on location as well as timing. Longitudinal single-synapse imaging during mouse motor learning found different predictors of strengthening in apical and basal dendrites of layer 2/3 pyramidal neurons. Local coactivity predicted apical changes, while coincidence with postsynaptic action potentials predicted basal potentiation. Blocking postsynaptic spiking reduced basal potentiation without the same effect on apical plasticity.[13]
This is a direct warning against assigning one scalar “learning rule” to an entire neuron. Dendritic branches receive different sources, operate under different voltage conditions and can expose different local signals to synapses. A synapse may therefore be eligible for change because of neighbouring input, back-propagating spikes, plateau potentials, neuromodulators or their combination.
Behavioural time calls for behavioural-timescale rules
A mouse running through a location receives relevant inputs over seconds, not only within a twenty-millisecond spike-pair window. In hippocampal area CA1, behavioural-timescale synaptic plasticity, or BTSP, can form a place field after a dendritic plateau potential. The influential experiment by Bittner and colleagues found a strong form of potentiation spanning seconds and capable of rapid place-field formation.[14]
Recent work has broadened and refined the picture. A review by Magee describes BTSP as strong, bidirectional, plateau-induced plasticity operating across many seconds and capable of one-trial changes.[15] In vivo work has reported BTSP-consistent rapid non-spatial odour representations and causally induced new odour fields in CA1 neurons.[16] These findings remain specific to defined hippocampal circuits and tasks. BTSP is more than STDP stretched on the time axis; it uses a different instructive event and eligibility relation.
Imagine CA3 inputs to a CA1 neuron becoming active as a mouse traverses positions 20 through 60 centimetres on a track. At 50 centimetres, a dendritic plateau occurs. Synapses active several seconds before and around the plateau carry eligibility traces. The plateau gates a large update, strengthening selected inputs. On the next lap, ordinary input near that region can drive a place field without another imposed plateau.
A millisecond coincidence rule would struggle to bind inputs spread across the behavioural episode. A seconds-long eligibility rule can. The code in the later CA1 firing pattern and the plasticity that created it therefore share a timescale. This does not mean every place field is created identically or that a place cell stores a map symbol. It means a concrete learning mechanism can reshape the population relation on the timescale of experience.
Learning requires both change and restraint
Hebbian amplification by itself is unstable. Frequently active neurons can grow more excitable, fire more often and potentiate further. Homeostatic plasticity, inhibitory plasticity, heterosynaptic changes and metaplastic thresholds can counter this positive feedback. These processes do more than protect firing rates. They can preserve the reliability of a code under perturbation.
In the Drosophila olfactory system, reducing presynaptic release probability degraded postsynaptic response reliability and odour classification. A target-specific homeostatic increase in active-zone number emerged within a day and rescued coding and behavioural reliability under tested conditions.[17] A stable code can be an actively repaired achievement, not an unchanged pattern.
An animal learns a new cue-response mapping within one trial. In branch A, synaptic weights can change immediately. In branch B, all synapses are experimentally frozen for ten minutes, but slow neuronal variables, adaptation and recurrent state remain available. Behaviour improves in both branches, then only branch A retains the mapping the next day.
The result would separate fast expression from durable synaptic storage. A modelling study has shown that slow neuronal variables can support fast learning-like behaviour before synaptic change becomes effective.[18] The model is not evidence that a particular brain circuit uses that route, but it identifies a rival generator. Improvement during the frozen window would weaken the claim that immediate synaptic modification is necessary for initial performance, while leaving open a later consolidation role.
No single plasticity rule currently licenses a universal statement about how “the brain learns”. STDP windows vary by synapse, protocol and state. BTSP has a defined circuit and instructive event. Compartment-specific rules can coexist in one neuron. Fast state changes can precede durable weight changes. The correct inference is plural but not vague: name the cell, synapse, compartment, gate, timescale and behavioural demand.
Optional depth: what the STDP equation hides
The pair rule assumes that each pre-post pair contributes independently. Real spike trains contain triplets, bursts and voltage histories. Calcium thresholds can convert similar timing into different outcomes. Initial synaptic strength, dendritic location and neuromodulators alter the sign or magnitude. Repeated pairing frequency changes the result, which is why rate and timing cannot always be separated.
For implementation work, treat the pair window as a minimal hypothesis. Add only the state variables needed to explain a failed prediction. A voltage-based rule may add postsynaptic membrane potential. A three-factor rule adds a delayed modulatory signal to an eligibility trace. A homeostatic term adds a slower target. Every new variable should correspond to an intervention or measurement, rather than only improve fit.
stateDiagram-v2 [*] --> Claim state "Rate preserves amount across<br/>local timing variation" as Claim state "Timing preserves order,<br/>latency and intervals" as Test state "Population geometry preserves<br/>relations across neurons" as Revision Claim --> Test: expose mechanism Test --> Decision: evidence survives Test --> Revision: rival survives Revision --> Claim: narrow or rebuild Decision --> [*]
A code claim that survives intervention
The practical problem is not to choose a favourite code. It is to design a test that prevents an informative correlation from becoming a causal story by accident.
The code viability audit
Start with a candidate variable, such as spike count in 100 milliseconds, first-spike latency, a population subspace or a prediction residual. Then ask three questions in order. Does it distinguish the task alternatives out of sample? Can a plausible downstream mechanism read it? Can local learning acquire or maintain that readout at the required timescale?
A positive encoding result permits the statement that the recorded activity contains information for the specified decoder and distribution. A positive readout intervention permits a stronger claim that the targeted circuit uses the distinction under that condition. A positive learning intervention links the relation to an acquisition or maintenance mechanism. Only the conjunction begins to explain representation as an operating biological achievement.
| Outcome | Strongest permitted statement | What remains open |
|---|---|---|
| Encode only | The activity contains information for the declared decoder, alternatives and sampling distribution. | Biological use, acquisition and maintenance. |
| Encode + read | The targeted pathway causally uses the distinction under the tested state and intervention. | How the reader was learned or retuned. |
| Encode + learn | The candidate variable and plasticity process change together as predicted. | Whether the changed relation drives behaviour. |
| Encode + read + learn | A configured circuit earns a code claim within the tested boundary and timescale. | Generality across tasks, states, species and mechanisms. |
A camera flash predicts stimulus A and a dim light predicts B. The animal also makes a small anticipatory movement on A trials. A decoder trained on motor cortex classifies A and B at 95 per cent. The tempting conclusion is that motor cortex represents stimulus identity.
Now equalise movement or condition on it. Accuracy falls to chance. The original decoder read a behaviour correlated with the stimulus, not a sensory variable required for the choice. Next, perturb the candidate motor subspace while preserving movement through a compensating controller. If choice remains intact, the decoded feature was not necessary for the decision. The failure is useful: it relocates the mechanism rather than merely lowering a score.
Executable lab: the multi-code oscilloscope
The lab below creates two synthetic stimuli. Each stimulus produces four spikes in each of four neurons, so a rate-only decoder receives identical features. The temporal arrangement differs. A binned population decoder succeeds while its resolution exceeds the injected timing jitter. A separate pair-based STDP calculation shows how changing pre-post order changes a local weight update.
Fix the synthetic seed, generate 400 balanced trials and give every neuron four spikes under either stimulus. With 10-millisecond bins and 8 milliseconds of Gaussian timing jitter, the count reader scores exactly 50.0 per cent because its class centroids coincide. The temporal reader scores 93.5 per cent because order remains resolvable. A +10-millisecond pre-post relation adds 0.007278 per illustrative pairing; twenty pairings with slow stabilisation finish at weight 0.581 rather than running directly to the boundary. The example makes three assumptions visible: aligned trial time, a binned nearest-centroid reader and a chosen pair rule.
Run the encode-read-learn toy experiment
Change one causal property at a time. Timing jitter attacks the candidate code. Bin width changes the reader. Pre-post lag changes the plasticity rule’s update. The stabilisation toggle adds a deliberately simple slow pull towards the starting weight.
A positive temporal result permits only a conditional statement: the synthetic timing distinction is available to this binned decoder under this jitter. A negative result can mean the candidate structure was destroyed, the decoder resolution was mismatched or the data were insufficient. The STDP output demonstrates sensitivity to order; it cannot establish that any biological synapse follows the chosen parameters.
Run the companion script with Python 3.10 or newer:
Use --sweep-csv results.csv to reproduce the jitter curve. The script needs only the Python 3.10+ standard library. It validates ranges, fixes the random seed and reports both accuracies plus the local weight change.
#!/usr/bin/env python3
"""Multi-code oscilloscope and plasticity-rule notebook.
Synthetic demonstration for Paper 66, "Neural codes, synapses and plasticity".
It compares a spike-count decoder with a temporally binned population decoder
when two stimuli have identical per-neuron spike counts but different timing.
It also applies an illustrative pair-based STDP rule.
Requires Python 3.10 or newer. No external packages are required.
"""
from __future__ import annotations
import argparse
import csv
import math
from dataclasses import dataclass
from pathlib import Path
from typing import Sequence
DURATION_MS = 120.0
UINT32_MASK = 0xFFFFFFFF
class Mulberry32:
"""Tiny deterministic generator mirrored by the browser lab.
This is used for reproducible synthetic data, not for cryptography or
statistical simulation where generator quality is itself under study.
"""
def __init__(self, seed: int) -> None:
self.state = seed & UINT32_MASK
def random(self) -> float:
self.state = (self.state + 0x6D2B79F5) & UINT32_MASK
value = self.state
value = ((value ^ (value >> 15)) * (value | 1)) & UINT32_MASK
value ^= (
value
+ (((value ^ (value >> 7)) * (value | 61)) & UINT32_MASK)
) & UINT32_MASK
value &= UINT32_MASK
return ((value ^ (value >> 14)) & UINT32_MASK) / 4294967296.0
def standard_normal(rng: Mulberry32) -> float:
"""Box-Muller normal draw matching the browser implementation."""
u = max(rng.random(), 1e-12)
v = max(rng.random(), 1e-12)
return math.sqrt(-2.0 * math.log(u)) * math.cos(2.0 * math.pi * v)
# Four-neuron population templates. Every neuron emits four spikes for either
# stimulus. The candidate distinction therefore exists in timing, not count.
STIMULUS_A: tuple[tuple[float, ...], ...] = (
(12, 28, 76, 94),
(16, 34, 72, 100),
(20, 40, 68, 104),
(24, 46, 64, 108),
)
STIMULUS_B: tuple[tuple[float, ...], ...] = (
(12, 50, 76, 94),
(16, 56, 72, 100),
(20, 62, 68, 104),
(24, 58, 64, 108),
)
@dataclass(frozen=True)
class Result:
jitter_ms: float
bin_ms: float
rate_accuracy: float
temporal_accuracy: float
stdp_delta_per_pair: float
final_weight: float
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Compare rate and temporal readouts, then simulate pair-based STDP."
)
parser.add_argument("--jitter-ms", type=float, default=8.0, help="Spike-time noise standard deviation.")
parser.add_argument("--bin-ms", type=float, default=10.0, help="Temporal decoder bin width.")
parser.add_argument("--lag-ms", type=float, default=10.0, help="Post-spike time minus pre-spike time.")
parser.add_argument("--trials", type=int, default=400, help="Balanced total trials; must be divisible by four.")
parser.add_argument("--seed", type=int, default=66, help="Random seed.")
parser.add_argument("--pairings", type=int, default=20, help="Number of STDP pairings.")
parser.add_argument(
"--stabilise",
action="store_true",
help="Add a slow illustrative pull towards weight 0.5 after each pairing.",
)
parser.add_argument(
"--sweep-csv",
type=Path,
help="Optional path for a jitter sweep CSV instead of a single run.",
)
args = parser.parse_args()
if args.jitter_ms < 0:
parser.error("--jitter-ms must be non-negative")
if args.bin_ms <= 0 or args.bin_ms > DURATION_MS:
parser.error(f"--bin-ms must be in (0, {DURATION_MS}]")
if args.trials < 40 or args.trials % 4:
parser.error("--trials must be at least 40 and divisible by four")
if args.pairings < 1:
parser.error("--pairings must be positive")
return args
def jittered_template(
template: Sequence[Sequence[float]], jitter_ms: float, rng: Mulberry32
) -> tuple[tuple[float, ...], ...]:
return tuple(
tuple(
min(
DURATION_MS - 1e-9,
max(0.0, time_ms + standard_normal(rng) * jitter_ms),
)
for time_ms in neuron
)
for neuron in template
)
def rate_features(spikes: Sequence[Sequence[float]]) -> list[float]:
return [float(len(neuron)) for neuron in spikes]
def temporal_features(spikes: Sequence[Sequence[float]], bin_ms: float) -> list[float]:
bin_count = math.ceil(DURATION_MS / bin_ms)
features: list[float] = []
for neuron in spikes:
counts = [0.0] * bin_count
for time_ms in neuron:
index = min(bin_count - 1, int(time_ms / bin_ms))
counts[index] += 1.0
features.extend(counts)
return features
def centroid(rows: Sequence[Sequence[float]]) -> list[float]:
width = len(rows[0])
return [sum(row[column] for row in rows) / len(rows) for column in range(width)]
def squared_distance(left: Sequence[float], right: Sequence[float]) -> float:
return sum((a - b) ** 2 for a, b in zip(left, right, strict=True))
def nearest_centroid(
features: Sequence[float], centroids: Sequence[Sequence[float]]
) -> int:
"""Return the closest class; resolve exact ties deterministically.
Because the held-out set is balanced, a completely non-discriminating
feature reports exactly 50 per cent rather than a noisy chance estimate.
"""
distances = [squared_distance(features, centre) for centre in centroids]
if math.isclose(distances[0], distances[1], abs_tol=1e-12):
return 0
return 0 if distances[0] < distances[1] else 1
def decoder_accuracy(
jitter_ms: float, bin_ms: float, trials: int, seed: int, temporal: bool
) -> float:
rng = Mulberry32(seed)
templates = (STIMULUS_A, STIMULUS_B)
train: list[list[list[float]]] = [[], []]
test: list[tuple[int, list[float]]] = []
per_class = trials // 2
train_per_class = per_class // 2
for label, template in enumerate(templates):
for index in range(per_class):
spikes = jittered_template(template, jitter_ms, rng)
features = temporal_features(spikes, bin_ms) if temporal else rate_features(spikes)
if index < train_per_class:
train[label].append(features)
else:
test.append((label, features))
centres = [centroid(rows) for rows in train]
correct = sum(
nearest_centroid(features, centres) == label for label, features in test
)
return correct / len(test)
def stdp_delta(
lag_ms: float,
a_plus: float = 0.012,
a_minus: float = 0.014,
tau_plus_ms: float = 20.0,
tau_minus_ms: float = 20.0,
) -> float:
"""Pair-based illustrative STDP window.
lag_ms = t_post - t_pre. Positive values mean the pre-synaptic spike
precedes the post-synaptic spike.
"""
if lag_ms > 0:
return a_plus * math.exp(-lag_ms / tau_plus_ms)
if lag_ms < 0:
return -a_minus * math.exp(lag_ms / tau_minus_ms)
return 0.0
def evolve_weight(
lag_ms: float, pairings: int, stabilise: bool, initial_weight: float = 0.5
) -> tuple[float, float]:
change = stdp_delta(lag_ms)
weight = initial_weight
for _ in range(pairings):
weight += change
if stabilise:
# Deliberately simple homeostatic term for mechanism exploration,
# not a fitted biological model.
weight += 0.06 * (initial_weight - weight)
weight = min(1.0, max(0.0, weight))
return change, weight
def run_once(args: argparse.Namespace, jitter_ms: float | None = None) -> Result:
jitter = args.jitter_ms if jitter_ms is None else jitter_ms
rate = decoder_accuracy(jitter, args.bin_ms, args.trials, args.seed, temporal=False)
temporal = decoder_accuracy(jitter, args.bin_ms, args.trials, args.seed, temporal=True)
delta, final_weight = evolve_weight(args.lag_ms, args.pairings, args.stabilise)
return Result(jitter, args.bin_ms, rate, temporal, delta, final_weight)
def write_sweep(args: argparse.Namespace, path: Path) -> None:
jitter_values = (0, 2, 4, 6, 8, 10, 12, 15, 20)
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", newline="", encoding="utf-8") as handle:
writer = csv.writer(handle)
writer.writerow(
("jitter_ms", "bin_ms", "rate_accuracy", "temporal_accuracy", "stdp_delta", "final_weight")
)
for jitter in jitter_values:
result = run_once(args, float(jitter))
writer.writerow(
(
result.jitter_ms,
result.bin_ms,
f"{result.rate_accuracy:.4f}",
f"{result.temporal_accuracy:.4f}",
f"{result.stdp_delta_per_pair:.6f}",
f"{result.final_weight:.6f}",
)
)
print(f"Wrote {len(jitter_values)} rows to {path}")
def main() -> None:
args = parse_args()
if args.sweep_csv:
write_sweep(args, args.sweep_csv)
return
result = run_once(args)
print("Synthetic neural-code audit")
print(f" jitter: {result.jitter_ms:.1f} ms")
print(f" temporal bin: {result.bin_ms:.1f} ms")
print(f" rate accuracy: {100 * result.rate_accuracy:.1f}%")
print(f" temporal accuracy: {100 * result.temporal_accuracy:.1f}%")
print(f" STDP change/pair: {result.stdp_delta_per_pair:+.6f}")
print(f" final weight: {result.final_weight:.4f}")
print()
print("Interpretation:")
print(" The rate decoder receives identical spike counts by construction.")
print(" The temporal decoder succeeds only while its bins resolve the pattern")
print(" more finely than jitter destroys it. The STDP result is a local rule")
print(" demonstration, not evidence that a biological circuit uses this rule.")
if __name__ == "__main__":
main()
Optional depth: assumptions and extended tests
The two stimuli have equal counts by construction. A rate decoder should therefore remain near chance. Temporal features use fixed-width bins and a nearest-centroid classifier. This is deliberately interpretable, but it is not an optimal decoder. A kernel method, spiking reader or recurrent network could fail at different jitter levels.
Useful extensions include dropping spikes, adding count differences, changing the reference time, rotating the population basis and learning the reader online. A causal version would embed the reader in a dynamical circuit, perturb its time constant and measure an action. A biological study would need preregistered exclusion criteria, blinded analysis, adequate sampling and an explicit welfare protocol.
Across circuits, the most behaviourally stable neural variable should be the one whose temporal precision matches both the downstream integration kernel and the dominant eligibility trace. The hypothesis is strengthened if changing reader time constants and plasticity windows shifts the same behavioural boundary. It is weakened if behaviour remains stable after both are mismatched. A rival explanation is a third circuit that reads another variable and merely correlates with both interventions. The smallest useful implementation is a two-layer spiking circuit with independently tunable membrane and eligibility timescales.
Do not apply the hypothesis as a claim that every brain area optimises one matched timescale. Circuits can multiplex codes, switch regimes with state and use several plasticity mechanisms in parallel.
sequenceDiagram participant Q as Question participant M as Mechanism participant E as Evidence participant D as Decision Q->>M: The frequency that was hidden in silence M->>E: Rate preserves amount across local timing variation E-->>M: compare rival explanations E->>D: Earn the word code through encode-read-learn closure D-->>Q: revise scope or act
Earn the word code through encode-read-learn closure
When a neural recording separates two stimuli, the next decision should not be whether to label it a rate, temporal, population or predictive code. The next decision is which relation to test.
Specify the candidate variable and its observation window. Name the alternatives it distinguishes. State what downstream process could read it, including the reader’s integration time and computational class. Then identify the plasticity or state change that could acquire, maintain or retune that relation. Finally, intervene on one link while holding the others as constant as the system permits.
This changes experimental design. A count-preserving shuffle is more informative than another classifier when timing is at issue. A reader perturbation is more informative than a saliency map when use is at issue. A plasticity blockade or timescale mismatch is more informative than a post-training snapshot when learning is at issue. A homeostatic perturbation matters when apparent stability may be actively repaired.
It also changes comparisons with machine learning. Artificial activations are not neural rates merely because both are numbers. Attention weights are not population codes merely because both distribute influence. Backpropagation is not synaptic plasticity merely because both change weights. A disciplined comparison names the preserved relation: perhaps a decoder geometry, an eligibility trace or a stability-plasticity trade-off. It also names where the biological mechanism adds dendrites, local state, neuromodulation, stochastic release, embodiment or behavioural time.
The strongest practical conclusion is modest and consequential. Treat a neural code as an experimentally earned encode-read-learn closure. If encoding passes but readout fails, report available information. If readout passes but learning is unknown, report a causal channel with an acquisition gap. If plasticity reshapes activity without behavioural use, report learning-related change, not representation. When all three relations survive the relevant interventions, the word “code” finally compresses an explanation rather than substituting for one.
Glossary
- Encoding
- A statistical relation by which neural activity distinguishes specified stimulus, state or action alternatives.
- Readout
- A downstream process that transforms neural activity into another neural variable, decision or action.
- Rate code
- A description using spike count or estimated firing intensity within a declared time window.
- Temporal code
- A description using latency, intervals, phase, synchrony or ordered spike patterns.
- Population geometry
- The arrangement of activity patterns in a space whose axes are neurons or derived features.
- Predictive residual
- The difference between an observed signal and a model-generated expectation.
- STDP
- Spike-timing-dependent plasticity, a family of rules in which synaptic change depends partly on relative spike timing.
- BTSP
- Behavioural-timescale synaptic plasticity, a plateau-gated form of hippocampal plasticity operating across seconds.
- Eligibility trace
- A temporary local state marking synapses as candidates for later modification by an instructive signal.
- Homeostatic plasticity
- Slower adjustment that counteracts destabilising activity or transmission changes and preserves operating ranges.
References
- Birznieks, I. and Vickery, R. M. (2017). “Spike timing matters in novel neuronal code involved in vibrotactile frequency perception.” Current Biology, 27, 1485-1490.e2. DOI.
- Panzeri, S. and Schultz, S. R. (2001). “A unified approach to the study of temporal, correlational, and rate coding.” Neural Computation, 13(6), 1311-1349. DOI.
- Kriegeskorte, N. and Douglas, P. K. (2019). “Interpreting encoding and decoding models.” Current Opinion in Neurobiology, 55, 167-179. DOI.
- Panzeri, S., Harvey, C. D., Piasini, E., Latham, P. E. and Fellin, T. (2017). “Cracking the neural code for sensory perception by combining statistics, intervention, and behavior.” Neuron, 93, 491-507. DOI.
- Brette, R. (2019). “Is coding a relevant metaphor for the brain?” Behavioral and Brain Sciences, 42. DOI.
- Zhu, H. et al. (2025). “Temporal coding carries more stable cortical visual representations than rate coding.” Nature Communications. DOI.
- Wakhloo, A. J., Slatton, W. and Chung, S. (2026). “Neural population geometry and optimal coding of tasks with shared latent structure.” Nature Neuroscience, 29, 682-692. DOI.
- Rao, R. P. N. and Ballard, D. H. (1999). “Predictive coding in the visual cortex: a functional interpretation of some extra-classical receptive-field effects.” Nature Neuroscience, 2, 79-87. DOI.
- Halvagal, M. S. and Zenke, F. (2023). “The combination of Hebbian and predictive plasticity learns invariant object representations in deep sensory networks.” Nature Neuroscience, 26, 1906-1915. DOI.
- Rule, M. E., O’Leary, T. and Harvey, C. D. (2020). “Stable task information from an unstable neural population.” eLife, 9, e51121. DOI.
- Bi, G. Q. and Poo, M. M. (1998). “Synaptic modifications in cultured hippocampal neurons: dependence on spike timing, synaptic strength, and postsynaptic cell type.” Journal of Neuroscience, 18, 10464-10472. DOI.
- Sjöström, P. J., Turrigiano, G. G. and Nelson, S. B. (2001). “Rate, timing, and cooperativity jointly determine cortical synaptic plasticity.” Neuron, 32, 1149-1164. DOI.
- Wright, W. J., Hedrick, N. G. and Komiyama, T. (2025). “Distinct synaptic plasticity rules operate across dendritic compartments in vivo during learning.” Science, 388, 322-328. DOI.
- Bittner, K. C. et al. (2017). “Behavioral time scale synaptic plasticity underlies CA1 place fields.” Science, 357, 1033-1036. DOI.
- Magee, J. C. (2026). “Behavioral timescale synaptic plasticity: properties, elements and functions.” Nature Neuroscience, 29, 520-534. DOI.
- Dorian, C. C. et al. (2026). “Rapid formation of non-spatial hippocampal representations consistent with behavioral timescale synaptic plasticity is modulated by entorhinal input.” Nature Communications, 17, 5098. DOI.
- Rozenfeld, E. et al. (2023). “Homeostatic synaptic plasticity rescues neural coding reliability.” Nature Communications, 14, 2993. DOI.
- Subramoney, A. et al. (2024). “Fast learning without synaptic plasticity in spiking neural networks.” Scientific Reports, 14. DOI.