When the linear predict is used¶
At model setup time, is_all_linear checks whether every latent factor’s transition
function name belongs to {"linear", "constant"}. This is an all-or-nothing decision:
if even one factor uses a nonlinear transition (e.g. translog), the entire model falls
back to the unscented predict.
The check happens in get_maximization_inputs, where the predict function is selected
via functools.partial. When the linear path is chosen, extra keyword arguments
(latent_factors, constant_factor_indices, n_all_factors) are bound at setup time
so the predict function has the same call signature as the unscented variant.
Why it is faster and uses less memory¶
The unscented predict generates sigma points (where is the number of latent factors), transforms each one through the transition function, then recovers predicted means and covariances from weighted statistics. Its QR decomposition operates on a matrix of shape : the weighted deviation rows plus rows for the shock standard deviations.
The linear predict skips sigma-point generation entirely. Because the transition is linear, the predicted mean is just a matrix--vector product, and the predicted covariance follows from the standard linear Gaussian formula. Its QR decomposition operates on a matrix: rows from the propagated Cholesky factor and rows for the shocks. The reduction from to rows speeds up the QR step and removes all sigma-point overhead.
The memory savings can be more important than the speed gains. The unscented path materialises sigma points for every observation and mixture component, and JAX’s automatic differentiation retains intermediate buffers for the backward pass. The linear path replaces all of this with a single matrix multiply whose memory footprint scales with rather than with the number of sigma points times the number of observations. On memory-constrained GPUs this can be the difference between fitting the model and running out of memory.
Building F and c¶
The linear predict assembles a transition matrix of shape
and a constant vector of length
from the trans_coeffs dictionary. Here includes both latent and
observed factors.
For each latent factor :
Linear factor:
trans_coeffs[factor]is a 1-d array whose last element is the intercept and whose preceding elements are the coefficients on all factors (latent and observed). Row of is set tocoeffs[:-1]and is set tocoeffs[-1].Constant factor: row of is the unit vector (identity row) and , so the factor value is simply carried forward.
The implementation uses a stack-then-mask approach: all coefficient arrays are stacked
into a single matrix (with zero-padded rows for constant factors), an identity matrix
provides the constant-factor rows, and jnp.where selects between them using a boolean
mask. This avoids per-element .at[i].set() calls and conditional branching, producing
a cleaner trace for JAX’s compiler.
Three construction strategies were benchmarked (loop with conditional .at[i].set(),
stack-then-mask with jnp.where, and index-scatter with pre-separated sub-matrices).
All three produced identical XLA graphs and showed no meaningful runtime difference
(~6.3--6.7 ms per call on CPU, 4-factor model, 5000 observations), confirming that the
construction is fully resolved at trace time. The stack-then-mask variant was kept for
its cleaner, more idiomatic JAX style.
Mean prediction¶
The mean prediction incorporates anchoring, which rescales factors to a common metric across periods. Let and be the input-period scaling factors and constants, and and the output-period counterparts. The steps are:
Anchor the input states: .
Concatenate observed factors to form the full state vector .
Apply the linear transition: .
Un-anchor to get the predicted states: .
Covariance prediction (square-root form)¶
skillmodels maintains covariances in square-root (upper Cholesky) form throughout. Let denote the current upper Cholesky factor so that . The linear predict propagates as follows.
Define the effective transition matrix
where is the first columns of (the columns corresponding to latent factors). folds the anchoring scales into the transition so that the covariance update works directly in the un-anchored (internal) scale.
The predicted covariance satisfies
where and is the vector of shock standard deviations. In square-root form, the upper Cholesky factor of is obtained via a single QR decomposition of the stacked matrix
which has shape . The upper-triangular -factor of (its first rows) gives .
Observed factors¶
Observed factors (e.g. investment measures whose values are known from data) appear as columns in and therefore influence the predicted mean through the matrix--vector product. However, they carry no uncertainty: their columns are excluded from the covariance propagation. This is why uses only the first columns of rather than the full matrix.
Practical impact¶
Benchmarks on a 4-factor linear model (health-cognition,
no_feedback_to_investments_linear, 8 GiB GPU) show a modest ~6 % speed-up on GPU (8.4
vs 8.9 s per optimizer iteration) and negligible difference on CPU. The speed gain is
small because with only 4 latent factors the unscented transform generates just 9 sigma
points — a trivially cheap operation on modern hardware.
The memory reduction is the more significant benefit. Under the same conditions the unscented path ran out of GPU memory when only ~5 GiB was free, while the linear path ran without issues. For models with more latent factors both advantages grow: the sigma-point count scales as and the QR matrix shrinks from to .