When a GLS problem involves hundreds of equations, the
Install the library from PyPI:
pip install alsglsFor local development, clone the repo and use an editable install:
pip install -e .from alsgls import ALSGLS, ALSGLSSystem, simulate_sur
Xs_tr, Y_tr, Xs_te, Y_te = simulate_sur(N_tr=240, N_te=120, K=60, p=3, k=4)
# Scikit-learn style estimator
est = ALSGLS(rank="auto", max_sweeps=12)
est.fit(Xs_tr, Y_tr)
test_score = est.score(Xs_te, Y_te) # negative test NLL per observation
# Statsmodels-style system interface
system = {f"eq{j}": (Y_tr[:, j], Xs_tr[j]) for j in range(Y_tr.shape[1])}
sys_model = ALSGLSSystem(system, rank="auto")
sys_results = sys_model.fit()
params = sys_results.params_as_series() # pandas optionalThe package supports automatic rank selection via BIC or cross-validation:
from alsgls import ALSGLS
# BIC-based rank selection
est = ALSGLS(rank="bic", max_sweeps=15)
est.fit(Xs, Y)
print(f"Selected rank: {est.rank_}")
# Cross-validation rank selection
est = ALSGLS(rank="cv", cv_folds=5, cv_random_state=42)
est.fit(Xs, Y)
print(f"Selected rank: {est.rank_}")See examples/real_data_fama_french.py for a demonstration using Fama-French 49 industry portfolios.
The benchmarks/compare_sur.py script contrasts ALS-GLS with statsmodels and
linearmodels SUR implementations on matched simulation grids while recording
peak memory (via Memray, Fil, or the POSIX RSS high-water mark).
Background material and reproducible experiments are available in the notebooks under als_sim/, such as als_sim/als_comparison.ipynb and als_sim/als_sur.ipynb.
This package provides a modern, type-safe implementation of Alternating-Least-Squares (ALS) for low-rank GLS problems. The Woodbury identity reduces the expensive inverse to a tiny k × k system, and the β-update can be written without explicitly forming dense matrices.
Inference:
- Standard errors (
bse), t-statistics (tvalues), p-values (pvalues), confidence intervals (conf_int()) and summary tables (summary()), statsmodels-style. By default these carry the Kackar–Harville correction for Σ being estimated —(X'Σ̂⁻¹X)⁻¹ + Λafter the degrees-of-freedom rescale — which no other SUR implementation applies.results.covariance("plugin")gives the uncorrected plug-in that linearmodels, systemfit and Stata report. Measured on a 4-equation system, reported SE over actual spread: plug-in 0.85, corrected 0.89 atn = 20; 0.94 / 0.96 atn = 40; both ~1.0 byn = 200. - Calibrated small-sample inference via
results.bootstrap(B=999), which refits the whole model on each replicate and returns percentile-t intervals and bootstrap-t p-values. This is the object to report whennis small relative to the number of equations; seedocs/formal_methods.md§7 for the measurements.
New in v1.1.0:
- Rank selection: BIC and cross-validation for automatic rank selection
- Gradient-based factor update: Cleaner theory, same convergence guarantees
- Real-world example: Fama-French 49 industry portfolios demonstration
- Formal methods documentation: Rigorous mathematical foundations
Core features:
- Full type safety with mypy compliance and comprehensive type hints
- Numerically stable implementation using Cholesky factorization throughout
- Clean API with single computational path and enhanced error messages
- Memory efficient with O(K k) complexity, converging in 5–6 sweeps
Rule of thumb: if your GLS routine keeps looping between
Random‑effects models, feasible GLS with estimated heteroskedastic weights, optimal‑weight GMM, and spatial autoregressive GLS all iterate β ↔ Σ̂. Each can adopt the same ALS trick: treat the weight matrix as low‑rank + diagonal, invert only the k × k core, and avoid the dense K × K algebra. Memory savings in published examples range from 5× to 20×, depending on k.
To demonstrate performance, we benchmark ALS against traditional methods with N = 300 observations, three regressors, rank‑3 factors, and K ranging from 50 to 120 equations. The largest array that traditional methods need is the dense Σ⁻¹ (K×K), whereas ALS's largest is the skinny factor matrix F (K×k).
| K | β‑RMSE EM | β‑RMSE ALS | Peak MB EM | Peak MB ALS | Memory ratio |
|---|---|---|---|---|---|
| 50 | 0.021 | 0.021 | 0.020 | 0.002 | 10× |
| 80 | 0.020 | 0.020 | 0.051 | 0.003 | 17× |
| 120 | 0.020 | 0.020 | 0.115 | 0.004 | 29× |
The ALS implementation achieves the same statistical performance while using only a few megabytes of memory, providing substantial computational advantages for large systems.
- Rank (
k) – By default the high-level APIs pickmin(8, ceil(K / 10)), a conservative fraction of the number of equations. Increaserankif the cross-equation correlation matrix is slow to decay; decrease it when the diagonal dominates. - Ridge term (
lam_B) – Defaults to1e-3on the regression update, and is applied relative to the residual variance scale so the fit does not depend on the units ofY. Raise it (e.g.1e-2) if CG struggles to converge. There is no penalty on the factor loadings: the Σ-step is the exact conditional solution, so there is nothing for one to regularise. - Noise floor (
d_floor) – Keeps the diagonal component positive; the default1e-8is a fraction of the mean residual variance, not an absolute variance, so it transforms correctly under a change of units. Increase it in highly ill-conditioned settings. - Stopping criteria – ALS stops when the relative drop in NLL per sweep is
below
1e-6(configurable viarel_tol) or aftermax_sweeps. Inspectinfo["nll_trace"]to diagnose stagnation. - Possible failures – Large condition numbers or nearly-collinear regressors
can make the β-step CG solve slow; adjust
cg_tol/cg_maxit, add stronger ridge, or re-scale predictors.info["sigma_iters"]reports how many inner iterations each Σ-step needed; counts that sit at the cap mean the alternation is crawling, which happens when some diagonal variances are near zero (a Heywood case) and usually indicates the factor rank is too large relative to the sample size.