hv-tctn-universal

A universal delay-line coincidence detector for velocity estimation across sensor arrays. One algorithm. Four substrates. No training.

Runtime: ~2 s per substrate (10 velocities Γ— 20k-step drives). Dependencies: NumPy only.

What it does

Given a bank of sensors at known positions and a propagating spatial pattern, estimate the pattern's velocity. The algorithm is the TCTN (Temporal Coincidence of Triggered Neurons-style cell):

cell (i, j, k) fires when:
    sensor i was active at delay Ο„_k ago
    AND
    sensor j is active now

preferred velocity of cell (i, j, k):
    v_pref(i, j, k) = (x_j βˆ’ x_i) / Ο„_k

For a pulse moving at velocity v, the coincidence is strong exactly when Ο„_k β‰ˆ (x_j βˆ’ x_i) / v. Classification is by top-k cell voting over the peaks of all cells.

The substrate changes. Sensor response shape, decay constants, and noise models differ. The algorithm does not. This is the central claim of the XuanJi SKILLS archive (chapters 4–7), reproduced here on synthetic drive signals.

Headline numbers

Four substrates, ten velocities each (five positive, five negative), N = 8–16 sensors, no noise:

substrate mean rel err max rel err all < 15%
spaced_8 (photodiode-style) 4.4% 11.3% βœ“
spaced_16 (EEG-style) 2.2% 3.5% βœ“
dielectric_8 (Ο„ ladder) 5.5% 10.1% βœ“
qubit_8 (T2 ladder) 6.1% 12.6% βœ“

Sign correct in all 80 substrate-velocity combinations. 85/85 consistency checks pass.

The four substrates

name positions Ο„ ladder mode notes
spaced_8 linspace(0, 1, 8) uniform 0.1 pulse photodiode-like
spaced_16 linspace(0, 1, 16) uniform 0.05 pulse EEG-like, denser array
dielectric_8 linspace(0, 1, 8) geomspace(0.05, 0.5, 8) step relaxation ladder
qubit_8 linspace(0, 1, 8) geomspace(0.1, 1.0, 8) step T2 ladder

The substrate spec is a dictionary: positions, response_tau, mode (pulse or step), and differentiate (True for step-mode).

The four rules from SKILLS chapter 7

Enforced in the code and validated in the benchmark:

  1. Single pulse per sensor. Never periodic drive.
  2. Differentiate step responses. A step response is a plateau; the TCTN needs a pulse.
  3. Direction-aware reference sensor. For v < 0, use the rightmost sensor as reference; for v > 0, the leftmost.
  4. Adaptive pulse width. Cap at spacing / |v| / 4 so adjacent pulses do not overlap at high velocity.

Noise robustness

spaced_8, v = 2.0, white noise added to the drive:

noise v_est rel err
0.00 2.013 0.7%
0.01 1.959 2.1%
0.02 1.904 4.8%
0.05 1.876 6.2%
0.10 1.984 0.8%
0.20 1.252 37.4%

Noise floor at ~0.05–0.10. The 0.10 result is a lucky estimate; the 0.20 result is a real failure. On real hardware with calibrated sensors, the effective noise is usually well below 0.05.

The differentiate on/off demonstration

For step-mode substrates, the difference is categorical:

substrate differentiate=False differentiate=True
dielectric_8, v=1 0.000 (100% err) 0.993 (0.7%)
dielectric_8, v=2 0.000 (100% err) 2.195 (9.7%)
qubit_8, v=1 0.000 (100% err) 1.007 (0.7%)
qubit_8, v=2 0.000 (100% err) 2.139 (7.0%)

A step response is invisible to the TCTN. The derivative converts it to a pulse. This is rule 2, and it is the single most important rule in the archive: the raw readout of a physical sensor is usually a step, not a pulse.

Periodic drive failure (rule 1)

The benchmark includes one negative control: a periodic drive at period 0.5 across the array. The TCTN gives a velocity far from the intended 2.0, because the period aliases with the delay ladder. This is why the algorithm requires single pulses.

How to use

from hv_tctn_universal import (
    substrate, measure_velocity, UniversalTCTN, choose_delays, make_drive)

# One-shot velocity estimate
spec = substrate('qubit_8')
v_hat, confidence = measure_velocity(spec, velocity=2.0, noise=0.01)
print(f"v_hat = {v_hat:.3f}, confidence = {confidence:.4f}")

# Custom substrate
my_spec = {
    'positions': np.linspace(0, 1, 12),
    'response_tau': np.geomspace(0.02, 0.4, 12),
    'mode': 'step',
    'differentiate': True,
    'description': 'custom photodiode array',
}
v_hat, _ = measure_velocity(my_spec, velocity=4.0)

# Manual pipeline (for streaming)
positions = np.linspace(0, 1, 8)
delays = choose_delays(positions, v_min=0.5, v_max=8.0, n_delays=30)
tctn = UniversalTCTN(positions, delays, dt=0.02, differentiate=False)
for t in range(n_steps):
    tctn.step(readout[t])
v_hat, confidence = tctn.classify(k_top=3)
Downloads last month
17
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support