LatentFactorGLM supports Gaussian/identity, Bernoulli/logit and Poisson/log families. The offset o_{it} is known, not estimated. Unit and time IDs index factor rows. The model does not automatically add separate unit and time intercepts: represent desired additive terms explicitly or choose an appropriate model specification.
Trex minimizes the mean observed-cell negative log likelihood, plus
The Gaussian loss is half the squared error. The factorization makes optimization nonconvex; factor rescalings and rotations also make individual factor coordinates non-unique. Interpret the product AB' or predicted means, not an arbitrary fitted axis as an identified economic trait.
Worked example: Gaussian panel with masked cells
Show Python
import numpy as npimport pandas as pdimport torchimport matplotlib.pyplot as pltfrom trex import LatentFactorGLMtorch.set_num_threads(1)torch.manual_seed(403)N, T =35, 20X = torch.randn(N, T, 2, dtype=torch.float64)beta = torch.tensor([0.9, -0.6], dtype=torch.float64)A = torch.randn(N, 2, dtype=torch.float64)*0.6B = torch.randn(T, 2, dtype=torch.float64)*0.6offset =0.1*torch.randn(N, T, dtype=torch.float64)mean = X @ beta + A @ B.T + offsety = mean +0.1*torch.randn_like(mean)mask = torch.rand(N, T) <0.8fit = LatentFactorGLM( family="gaussian", rank=2, penalty=1e-3, optimizer_kwargs={"lr": 0.04, "weight_decay": 0.0}, maxiter=1400, tol=1e-8, device="cpu",).fit(X, y, mask=mask, offset=offset)pred = fit.predict_mean(X, offset=offset).detach()pd.DataFrame({"Truth": beta.numpy(), "Estimate": fit.params["coef"].numpy()}, index=["x1", "x2"]).round(4)
Figure 1: Predictions for held-out cells. The diagonal represents exact prediction of the latent conditional mean.
Held-out RMSE against noisy outcomes: 0.11158441136709693
Binary and count outcomes
Changing the family changes the likelihood and inverse link, not merely a post-processing transform of a least-squares fit. Here is a rank-zero Poisson fit, a useful reference case for the nonlinear implementation.
Poisson coefficients: [-0.206 0.341]
Mean fitted count: 1.0108175447360908
For Bernoulli responses choose family="bernoulli" and call predict_proba. For either family, predict_index returns \eta, whereas predict_mean applies the inverse link. Trex caps very large log rates in Poisson prediction for numerical safety; inspect extreme-index fits rather than treating saturation as evidence of a well-behaved model.
Layout and limitations
Dense input uses X.shape == (N,T,p) and y.shape == (N,T). An observation list uses (n_obs,p) and explicit unit_ids and time_ids. A boolean mask selects training observations in either layout; predictions still require levels represented in the fitted model. This is not a cold-start embedding model for new units or dates.
Use multiple starts and time- or block-aware validation for rank and penalty. The current implementation does not report identification-aware coefficient or treatment-effect covariance estimates. With short panels, nonlinear incidental parameters can matter. Interactive effects are a model for unobserved heterogeneity, not by themselves a causal identification strategy.