JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Cosine Tuning and the Population Vector, Rigorously

The encoding view done properly: the tuning model, the population-vector read-out, and why it is biased where the optimal linear estimator is not.

The tuning model

Cosine tuning is not a law about angles; it is the shadow of a linear velocity-encoding model. A neuron's expected rate is an affine function of the movement velocity vector \mathbf{v}: the weight vector \mathbf{b}_i is the encoding direction, and cosine tuning is what that looks like when you sweep direction at fixed speed. The preferred direction is simply the direction of \mathbf{b}_i; the modulation depth is \lVert\mathbf{b}_i\rVert.

\lambda_i(\mathbf{v}) = b_{0i} + \mathbf{b}_i^{\top}\mathbf{v} = b_{0i} + m_i\lVert\mathbf{v}\rVert\cos(\theta - \theta_i)

The linear encoding model (left) and its cosine form (right) are the same statement; the observation model behind the velocity Kalman filter.

A cell's firing rate is a baseline plus a dot product with the movement velocity — which is algebraically the same as the cosine tuning curve. This linear map is exactly the observation model a velocity Kalman filter inverts.

\mathbf{v},\ \lVert\mathbf{v}\rVert
The movement velocity vector and its magnitude (speed).
\mathbf{b}_i
Cell i's preferred-direction weight vector.
\mathbf{b}_i^{\top}\mathbf{v}
The dot product — largest when velocity aligns with \mathbf{b}_i.
\theta - \theta_i
Angle between the reach and the cell's preferred direction.

Faster reaches (larger \lVert\mathbf{v}\rVert) drive proportionally larger rate changes — the encoding model.

Building the population vector

The population vector read-out: each cell contributes its preferred-direction unit vector \mathbf{c}_i, scaled by its baseline-subtracted, depth-normalized activity; sum over the population. It is a weighted vote.

\mathbf{P} = \sum_{i=1}^{N} w_i\,\mathbf{c}_i, \qquad w_i = \frac{r_i - b_{0i}}{m_i}

The population vector: normalized activity w_i scales each preferred-direction vector \mathbf{c}_i; the sum estimates reach direction.

Let every cell vote by pointing its preferred-direction arrow, scaled by how active it is above baseline; add up all the arrows and the sum points toward the reach direction. Democracy for neurons.

\mathbf{P}
The population vector — the direction estimate.
\mathbf{c}_i
Cell i's preferred-direction unit vector.
w_i = \frac{r_i - b_{0i}}{m_i}
Normalised activity above baseline — the cell's vote strength.
r_i
The cell's observed firing rate.

If up-cells and right-cells are both active, their arrows sum to point up-right — the population vector.

# r: firing rates (N,)   b0: baselines (N,)   m: modulation depths (N,)
# C: preferred-direction unit vectors (N, 2)
w = (r - b0) / m                      # normalized activity per neuron
pop_vec = (w[:, None] * C).sum(axis=0)  # population vector (2,)
theta_hat = np.arctan2(pop_vec[1], pop_vec[0])
The population vector in four lines — each neuron's normalized rate scales its preferred-direction vector, then sum.

The theorem and its fine print. When preferred directions tile the circle (or sphere) uniformly and noise is independent and equal across cells, \mathbf{P} points, in expectation, along the true reach direction. That is a real result — and a fragile one, because both assumptions fail on real arrays.

See it converge (and wobble)

Add cosine-tuned neurons and watch the population vector converge on the true reach direction; then add noise or thin the population and watch it wobble and bias toward crowded preferred directions.

Why the population vector is biased

Real cortex violates both assumptions. Preferred directions are non-uniformly distributed (they cluster), so the vote is pulled toward dense regions and \mathbf{P} is biased. And noise is correlated across cells (shared variability), which the vote ignores entirely. The population vector never looks at the covariance of the rates, so it cannot be optimal when that covariance is structured.

The fix is the optimal linear estimator (OLE): treat decoding as linear regression of kinematics on rates and weight by the measured covariance. The population vector is the special case of the OLE that assumes the rate covariance is isotropic.

\hat{\mathbf{v}} = L\,(\mathbf{r} - \mathbf{b}_0), \qquad L = \Sigma_{vr}\,\Sigma_{rr}^{-1}

The OLE weights by the full rate covariance \Sigma_{rr}; setting \Sigma_{rr}\propto I recovers the population vector.

The optimal linear estimator improves on the population vector by accounting for how the cells' noise is correlated — it weights by the full rate covariance. If you pretend all cells are independent and equally noisy, it collapses back to the population vector.

\hat{\mathbf{v}}
The estimated movement velocity.
\mathbf{r} - \mathbf{b}_0
Firing rates with baseline subtracted off.
L = \Sigma_{vr}\,\Sigma_{rr}^{-1}
The decoding matrix that maps rates to velocity.
\Sigma_{rr}
Covariance of the firing rates; \Sigma_{rr}\propto I recovers the population vector.

Two cells that always fire together shouldn't get to vote twice; \Sigma_{rr}^{-1} discounts them — this is the optimal linear estimator.

From read-out to filter

Two more realities push past a static linear read-out. First, kinematics are correlated in time, so estimating velocity from the previous estimate plus new spikes (a Kalman filter) beats memoryless regression. Second, tuning is not stationary — preferred directions and depths drift across sessions (nonstationarity) — which is why closed-loop recalibration such as ReFIT exists. The encoding model of this guide is the foundation; the state-space track builds the estimator on top of it.