Theory¶
The problem¶
smopt solves
where \(f\) is smooth and \(r\) is a convex, possibly nonsmooth regularizer reachable through its proximal operator. The feasible set
is the Stiefel manifold, a smooth embedded submanifold of \(\mathbb{R}^{n \times p}\) of dimension \(np - p(p+1)/2\).
Constraint dissolving¶
Classical Riemannian methods keep every iterate exactly on \(\mathcal{S}_{n,p}\), which costs a matrix decomposition per step. The solvers here instead dissolve the constraint: they work in the ambient space \(\mathbb{R}^{n \times p}\) and rely on a cheap map that pulls a drifting iterate back towards the manifold.
That map is
The first branch is the second-order expansion of \(X(X^\top X)^{-1/2}\) about a feasible point and costs only matrix products. The second branch is exact and needs a Cholesky factorization of order \(p\), which is cheap because \(p \ll n\) in the problems of interest. Both branches fix the manifold: \(\mathcal{A}(X) = X\) whenever \(X^\top X = I_p\).
Ingredients¶
Writing \(\Phi(M) = (M + M^\top)/2\) for the symmetrizing operator, the solvers are built from
| Map | Definition | Role |
|---|---|---|
| \(C(X)\) | \(X^\top X - I_p\) | constraint violation |
| \(\mathcal{J}_C(X)[\Lambda]\) | \(X\,\Phi(\Lambda)\) | constraint Jacobian |
| \(\mathcal{J}_C^\ast(X)[D]\) | \(\Phi(X^\top D)\) | its adjoint |
| \(\mathcal{J}_A(X)[G]\) | \(G - X\,\Phi(X^\top G)\) | projected gradient |
| \(\mathcal{A}(X)\) | above | feasibility restoration |
Feasibility is measured by \(\|C(X)\|_F\) throughout.
The solvers¶
SLPG¶
The SLPG family takes a Barzilai-Borwein step along \(\mathcal{J}_A(X)[\nabla f(X)]\), applies the proximal operator of \(r\), and restores feasibility with \(\mathcal{A}\). The step size uses one of the two BB formulas
with \(S_k = X_k - X_{k-1}\) and \(Y_k\) the corresponding change in the search direction. The first few iterations use a conservative \(c/L\) instead, where \(L\) is estimated from the gradient at the starting point.
Three drivers are provided:
slpg_smoothfor \(r \equiv 0\).slpgfor a general \(r\), whose constraint multiplier \(\Lambda\) is tracked by an inner Arrow-Hurwicz iteration so that no penalty parameter has to be tuned.-
slpg_l21for \(r(X) = \gamma\|X\|_{2,1}\), where both the prox and the multiplier\[ \Lambda(X) = -\gamma\, X^\top \operatorname{diag}\!\left(\frac{1}{\|X_{i,:}\|_2}\right) X \]are available in closed form, so no inner iteration is needed. The \(\ell_{2,1}\) norm sums the Euclidean norms of the rows of \(X\) and therefore drives whole rows to zero, which is how sparse principal component analysis and related models select variables.
pencf¶
pencf adds an explicit penalty to the search direction,
and restores feasibility only once \(\|C(X)\|_F\) exceeds \(10^{-1}\), capping \(\|X\|_F\) at \(1.001\sqrt{p}\) to keep the iteration bounded. The default \(\beta\) is \(0.1\|\nabla f(X_0)\|_F\).
Post-processing¶
Because the iterates are only approximately feasible, every solver optionally rounds the final point onto the manifold with the orthogonal polar factor
which is the nearest point of \(\mathcal{S}_{n,p}\) in the Frobenius norm.
smopt computes it from a Jacobi eigendecomposition of the \(p \times p\)
matrix \(X^\top X\) rather than from a singular value decomposition of
\(X\), which keeps the cost at \(O(np^2 + p^3)\).
Implementation¶
Every formula on this page is evaluated in Fortran 77, including the solver loops themselves. Python supplies the objective through a callback, and the linear algebra — matrix products, Cholesky, the Jacobi eigensolver, modified Gram-Schmidt — is hand-written in the same sources, so the extension links against nothing but the Fortran runtime.