cf. Jonas & Kording 2017
The local field potential is a pooled, low-resolution signal. In neuroscience it is the summed electrical activity of many neurons near an electrode, and its power spectrum, the strength of each oscillation frequency, is studied as a marker of brain state. We build the analogue on the VCS by pooling the activity of a region of memory into one aggregate signal and taking its power spectrum. The idea is that rhythms in this pooled signal reflect the computation. The problem we test is whether those rhythms are real computation or just the machine's clocks showing through. The VCS is driven by a frame clock and a scanline clock, and their fixed rhythms dominate any pooled signal. So much of the spectrum is epiphenomenal: it is the timing of the hardware, not the work the program does. This is a known danger for field potentials in neuroscience too, where a strong oscillation can reflect a global pacemaker rather than local processing. The method is easy to compute and produces impressive-looking spectra, but on a machine whose clocks we know exactly we can show how little of that structure is actually computational.
Left: the frame. Right: this method's per-cell/per-component result against the ground-truth importance, with the RAM cells labelled. A5_global_pool_clock_explained_variance_fraction_epiphenomenal = 0.752 — this example only (pong, state gameplay(seed=0,prefix=90)+256); the audit aggregate is below.
New to explainable AI? This block explains the method as it is used on neural networks, then how we run the very same method on an Atari 2600, where — unlike for a network — we know the right answer. Every implementation step links to the code. See also the XAI primer for the shared vocabulary.
Put a microphone in a busy restaurant. You cannot follow any single conversation, but you still hear something: a loud hum that swells and fades. Neuroscientists do the same with the brain. An electrode in brain tissue records the local field potential (LFP): the summed electrical activity of thousands of nearby neurons. Nobody sees a single cell. What people study instead is the rhythm of this hum: how strong the slow waves are, how strong the fast ones. That breakdown of a signal into rhythms is called its power spectrum, and LFP bands such as “theta” or “gamma” are read as windows onto what the region computes.
The worry is simple. A strong rhythm might be the computation. Or it might be a pacemaker: a global clock that ticks everywhere and makes every part of the system hum at the same beat, whether or not that part does useful work. Activity that is caused by the clock but carries no computation is called epiphenomenal. In a brain you cannot easily tell the two apart. On the Atari you can, because we know every clock of the machine exactly. A5 measures how much of the “LFP” spectrum is just those clocks.
Neuroscience origin. The LFP is a mesoscale signal: coarser than a single neuron, finer than an EEG over the whole skull. It is the analysis you can run when single-cell access is impossible. Jonas & Kording (2017) recorded the analogous “LFP” of a simulated 6502 chip and found rich rhythms that said nothing about the program it ran.
Deep-learning analogue. In a network, the LFP corresponds to pooled or aggregate activation statistics: the mean activation of a layer, the norm of a block's output, or the average over a group of units, tracked over time steps (tokens, video frames, RL steps). Write the activation of unit \(i\) at time \(t\) as \(a_i(t)\). Standardise each unit (subtract its mean, divide by its standard deviation: a z-score \(z_i\)) and average over a region \(R\):
\[ L(t) \;=\; \frac{1}{|R|}\sum_{i\in R} z_i(t), \qquad P(f_k) \;=\; \Big|\sum_{t=0}^{T-1} L(t)\,w(t)\,e^{-2\pi i k t/T}\Big|^2 . \]\(L(t)\) is the pooled signal, \(w(t)\) a smooth window that tapers the ends, and \(P(f_k)\) the power at frequency \(f_k = k/T\) cycles per step (the discrete Fourier transform). A peak in \(P\) at period \(p = 1/f\) means the pooled activity repeats every \(p\) steps.
Toy example. Take two units over \(T=8\) steps. Unit 1 is a counter that flips every step: \(z_1 = (+1,-1,+1,-1,+1,-1,+1,-1)\). Unit 2 holds a real variable that jumps once: \(z_2 = (-1,-1,-1,-1,+1,+1,+1,+1)\). Their pool is \(L = (0,-1,0,-1,1,0,1,0)\). Its spectrum has a peak at period 2 (from unit 1) and a slow component at period 8 (from unit 2). If we know unit 1 is a clock, we can fit sin/cos waves of period 2 to \(L\) and ask what fraction of \(L\)'s energy they explain. Here it is exactly half (energy 2 of 4): half of this “LFP” is pure clock, however striking the period-2 peak looks.
| Neurons in a brain region / units in a layer | The 128 bytes of the VCS's RAM (random-access memory: the program's working variables, e.g. ball position, score, timers), read once per frame (one full TV picture, 1/60 s). |
|---|---|
| The LFP: pooled, z-scored activity | The mean z-scored value of a set of RAM bytes per frame: the global pool (every byte that changes) and one regional pool per game-variable family (all score bytes, all enemy bytes, …). |
| Theta / gamma bands | Peaks in the per-frame power spectrum, reported as periods in frames (a period-2 blink, a period-8 animation cycle, …). |
| A global pacemaker | The machine's known clocks: the frame cadence itself, plus every RAM byte that is an exact free-running counter (it ticks by a fixed step, or repeats with a fixed short period). Games use these as animation and blink timers. |
| “Is this rhythm computation?” | Answered exactly: the fraction of the pooled signal's energy that lies in the span of the known-clock waves. That part is causally inert timing, not game logic. |
The runner A5_lfp.jl follows the recipe on the real, bit-exact emulator. Concretely:
a5_record_ram calls env_step! and
get_ram once per frame to fill a \(T\times128\) tape.detect_clock_cells scans each RAM byte.
A byte is a ramp if its frame-to-frame difference (mod 256) is one constant non-zero step:
a frame counter. It is periodic if it repeats exactly with some period between 2 and 64
frames: a blink or animation timer. No game knowledge is used.clock_basis stacks sin/cos waves at two slow
frame-cadence periods (\(T\) and \(T/2\), always present) plus the first three harmonics of every
detected clock period. Each wave goes through the same detrend and window as the data. A QR
factorisation makes the basis orthonormal.pool_region builds the global pool and one pool per
variable family from the candidate-cause list. dft_power computes a plain
\(O(T^2)\) DFT (no FFT package is added to the emulator environment). The headline number is the
global pool's clock-explained fraction, from variance_explained_by.Where we differ from the neuroscience original. We sample once per frame. The VCS also has a scanline clock (262 TV lines per frame) and a CPU clock, but those tick far faster than our sampling rate and cannot appear in a per-frame spectrum. The clocks A5 actually removes are the frame cadence and the frame-driven counter bytes. A5 is also descriptive. It never intervenes; the oracle is used only to build and gate the shared gameplay state. Its score asks “how much of this signal is known-inert timing?”, not “does it find the causes of an output?”.
XAI the interpretability method substrate the bit-exact emulator it runs on (jutari, Julia).
A5_lfp.jl:629 — One game end to end: shared gameplay stream → bit-exact check → RAM tape → clocks → regional spectra.A5_lfp.jl:197 — The recording: step the ROM one frame at a time and copy the 128 RAM bytes into the tape.A5_lfp.jl:538 — The LFP itself: z-score every varying byte of a region and average them per frame.A5_lfp.jl:342 — A dependency-free one-sided DFT power spectrum (cycles per frame).A5_lfp.jl:417 — Label-free detection of the known clocks: constant-stride ramps and fixed-period timers.A5_lfp.jl:460 — Frame-cadence waves plus harmonics of every clock period, detrended, windowed and orthonormalised.A5_lfp.jl:511 — The score: the share of a pooled signal's energy that lies in the clock subspace.shared_testbed_impl.jl:115 — The shared gameplay state: seeded random actions, checkpoint at frame 90, oracle cause-density gate.StellaEnvironment.jl:259 — One video frame of the real, bit-exact machine.StellaEnvironment.jl:350 — Direct read of the 128-byte RAM: the “electrode” that records every unit.The figure shows the global pool's power spectrum in cycles per frame. Expect most of the power at the lowest frequencies, slow sweeps over the whole recording (periods of \(T\) or \(T/2\) frames), and in some games sharp peaks at short integer periods: these look like the “brain rhythms” of the game. Then compare them with the clock basis. The slow sweeps are the frame cadence, the short peaks sit on a detected timer period or its harmonics, and in most games the clock-explained fraction is large. The rich-looking spectrum is mostly the machine's timing. Regional pools for real game variables (score, enemies) are often just as clock-dominated, or flat when their bytes never change; either way the spectrum cannot tell you what they compute or what causes them. The live faithfulness number is in the audit box below.
julia --project=jutari tools/xai_study/phaseA_kording/A5_lfp.jl --games core
Writes A5_<game>.json/.npz (pooled signal, spectrum, clock cells, per-region clock fractions) into tools/xai_study/phaseA_kording/out/. Add --selftest to run the controls on the first game without writing. A few minutes per game on one CPU core, most of it building the shared gameplay state.
Read next: A4 · Pairwise correlations · A6 · Granger causality · A7 · Dimensionality reduction (NMF/PCA) · A2 · Single-unit lesions
The score is the fraction of the pooled-activity power spectrum that the known clocks explain. We pool a region of memory into one signal, take its power spectrum, and measure how much of that spectrum is accounted for by the frame clock and the scanline clock, whose frequencies we know exactly. Faithfulness is one minus that clock-explained fraction, because the part of the spectrum that is just the hardware's timing is epiphenomenal, not computation. A high clock-explained fraction means the impressive-looking spectrum is mostly the machine's pacemaker, so the method's faithfulness is low. The grading is always against the intervention oracle, which certifies what is truly computational. There is no sprite-position hard-case here; the output is a spectrum, not a coordinate. This method is a calibration baseline: it shows how a standard neuroscience signal can look rich while carrying little of the real computation. The audit box reports the measured faithfulness across the scored games, so the number reflects the whole scored set rather than one game where the clocks happen to dominate more or less.
The score is measured against the §1 intervention oracle — never against another interpretability method. F (faithful) is always vs the oracle; see the execution stack. How each of F / S / M is actually computed for this method (and whether it matches the paper) is in the box just below; the numbers are in the In the audit box under it.
The exact formula this method uses for each score, read from its runner, and
whether it matches the paper's §3 (F ∧ S ∧ M triad) definition.
From the committed audit
fsm_math_phaseA.json.
| Axis | Formula (as computed) | What it measures | Matches §3? |
|---|---|---|---|
| F faithfulness | — | No faithfulness score. A5 is declared a descriptive method: it reports the pooled-activity spectral structure it reveals (headline = global-pool clock-explained variance fraction), not an F vs the oracle. | — n/a Runner A5_lfp.jl explicitly sets method_kind='descriptive (no oracle F/S/M)',; metric_name=A5_global_pool_clock_explained_variance_fraction_epiphenomenal, value=gp.clock_var_fraction. Record out/A5_pong.json has no triad. |
| S sufficiency | — | Not computed; A5 emits no S. | — n/a No S/held-out estimator in A5_lfp.jl; descriptive method by design. |
| M minimality | — | Not computed; A5 emits no M. | — n/a No minimality set in A5_lfp.jl; descriptive method by design. |
✓ matches = the same quantity as §3; ◐ partial = the same kind of estimator but a differing detail; ✗ does not match = a different quantity (see the note). Definitions: F = agreement with the oracle's true causal effects Δy(u); S = held-out predictive score in [−1, 1]; M = |U*|/|Û| (true-minimal-set size / named-set size).
This is the method's entry in the actual cross-method audit — scored on the paper's correctness triad, each axis a mean over 42 of the 44 scored games (42 committed §R records), not the single example shown above. Tradition: correlational. The example figure (Pong) is one of those records.
F faithfulness (scored vs the oracle for every method) · S sufficiency (held-out predictive score in [−1, 1]; a negative value means the explanation predicts held-out interventions worse than the unperturbed baseline; reported for the predictive methods across all three phases where the calibration/held-out split is defined) · M minimality (true-minimal-set / named-set; where the method names a cause set) — n/a otherwise, per the paper's F ∧ S ∧ M triad.
Source: leaderboard.json · the whole leaderboard is on the
methods page and the
Paper 2 audit.
This method's faithfulness on each of the 42 scored games (all-regime F; this method has no content-vs-position split (see the note below)). Click a header to
sort. Every number is read from site_data.json
(methods.A5_lfp.per_game).
content-F and position-F are n/a for this method. The content-vs-position split applies only to pixel-attribution methods — those that produce an attribution map over the framebuffer, whose score can be read separately on the smooth content outputs and the discrete position outputs (where the naive gradient is provably zero). This method does not attribute to a pixel output; it is graded by its own fidelity metric (A5_global_pool_clock_explained_variance_fraction_epiphenomenal), reported as the F column, so there is no content or position regime to separate.
| Game | F (all) | content-F | position-F | S | M | Note |
|---|---|---|---|---|---|---|
| Kung-Fu Master | 0.774 | n/a | n/a | n/a | n/a | |
| Jamesbond | 0.743 | n/a | n/a | n/a | n/a | |
| Asterix | 0.703 | n/a | n/a | n/a | n/a | |
| Assault | 0.701 | n/a | n/a | n/a | n/a | |
| Video Pinball | 0.625 | n/a | n/a | n/a | n/a | |
| Yars' Revenge | 0.607 | n/a | n/a | n/a | n/a | |
| Riverraid | 0.604 | n/a | n/a | n/a | n/a | |
| Alien | 0.581 | n/a | n/a | n/a | n/a | |
| Fishing Derby | 0.573 | n/a | n/a | n/a | n/a | |
| Atlantis | 0.544 | n/a | n/a | n/a | n/a | |
| Gopher | 0.529 | n/a | n/a | n/a | n/a | |
| Enduro | 0.523 | n/a | n/a | n/a | n/a | |
| Space Invaders | 0.506 | n/a | n/a | n/a | n/a | |
| Skiing | 0.475 | n/a | n/a | n/a | n/a | |
| Private Eye | 0.458 | n/a | n/a | n/a | n/a | |
| Freeway | 0.445 | n/a | n/a | n/a | n/a | |
| Chopper Command | 0.439 | n/a | n/a | n/a | n/a | |
| Name This Game | 0.424 | n/a | n/a | n/a | n/a | |
| Pitfall | 0.415 | n/a | n/a | n/a | n/a | |
| Ice Hockey | 0.410 | n/a | n/a | n/a | n/a | |
| Montezuma's Revenge | 0.402 | n/a | n/a | n/a | n/a | |
| Tennis | 0.395 | n/a | n/a | n/a | n/a | |
| Road Runner | 0.383 | n/a | n/a | n/a | n/a | |
| Demon Attack | 0.342 | n/a | n/a | n/a | n/a | |
| Q*bert | 0.330 | n/a | n/a | n/a | n/a | |
| Phoenix | 0.307 | n/a | n/a | n/a | n/a | |
| Seaquest | 0.306 | n/a | n/a | n/a | n/a | |
| Bowling | 0.297 | n/a | n/a | n/a | n/a | |
| Bank Heist | 0.284 | n/a | n/a | n/a | n/a | |
| Venture | 0.270 | n/a | n/a | n/a | n/a | |
| Pong | 0.248 | n/a | n/a | n/a | n/a | |
| Boxing | 0.229 | n/a | n/a | n/a | n/a | |
| Air Raid | 0.226 | n/a | n/a | n/a | n/a | |
| Double Dunk | 0.225 | n/a | n/a | n/a | n/a | |
| Pacman | 0.213 | n/a | n/a | n/a | n/a | |
| Ms. Pac-Man | 0.194 | n/a | n/a | n/a | n/a | |
| Breakout | 0.186 | n/a | n/a | n/a | n/a | |
| Krull | 0.148 | n/a | n/a | n/a | n/a | |
| Frostbite | 0.139 | n/a | n/a | n/a | n/a | |
| Kangaroo | 0.133 | n/a | n/a | n/a | n/a | |
| Centipede | 0.101 | n/a | n/a | n/a | n/a | |
| Berzerk | 0.089 | n/a | n/a | n/a | n/a |
The path from this method's runner (compute_game) into the bit-exact VCS substrate, and the
computation it involves. Every step links to the exact source on main. From
callstack_phaseA.json.
build_a5_shared_state(game) -> build_shared_testbed(game; ...) runnerfunction build_shared_testbed(game; ...) runnercause_density = count(>(floor), deltas) runnera5_record_ram(game, frames, actions) runnerenv_step!(env, Int(actions[t])) jutariget_ram(env) jutaridetect_clock_cells(tape; p_max=64) runneranalyse_region(name, cells, tape, B, win) runnerScored against the exact intervention oracle Δy(u): run_intervention(checkpoint, actions, target_frame, horizon, cause).
The figure is generated from the committed record by
docs/gen_method_figures.py; the game frame
and each RAM cell's screen footprint are produced by
render_scenes.jl /
cell_footprints.jl.