Fit a linear (flat) dictionary by block coordinate descent: each sweep
re-routes rows to atoms (the assignment step) and then refines every atom and
its assignment column by a penalized least-squares update against the residual.
Encode held-out rows x (M x P) against a frozen dictionary atoms
(K x P) using the same top-top_k ridge least-squares routing the fit
uses against its final atoms. Returns the (M, K) sparse code matrix.
Centered rank-1 PCA ceiling for the K=1 lane, exposed for callers that want the
ceiling reconstruction/EV directly. Subtracts the column means, takes the
leading eigenvector of the CENTERED second-moment matrix, fits the rank-1 code
on the centered data with the same ridge shrink the uncentered lane uses, then
adds the mean back so the reconstruction lives in the original space. Returns
(fitted, explained_variance); the EV is measured against the same centered
denominator as the rest of the crate, so it is directly comparable to (and an
upper bound on) the uncentered lane’s EV. Prefer setting
LinearDictionaryConfig::center_rank_one = true to route the K=1 lane through
this computation as part of a full LinearDictionaryFit.