Skip to content

Optim plnpca - explore block Newton with preconditionning and trust-region#171

Merged
jchiquet merged 13 commits into
masterfrom
optim-plnpca
Jul 3, 2026
Merged

Optim plnpca - explore block Newton with preconditionning and trust-region#171
jchiquet merged 13 commits into
masterfrom
optim-plnpca

Conversation

@jchiquet

@jchiquet jchiquet commented Jul 3, 2026

Copy link
Copy Markdown
Member

No description provided.

jchiquet and others added 13 commits July 2, 2026 22:55
Profiles out the variational (M, S) block with a concave per-observation
Newton VE-step (the envelope theorem gives the reduced gradient of
g(B,C) = max_{M,S} ELBO for free), then optimises the loadings (B, C) with a
saddle-aware trust-region Newton: analytic Schur-complement Hessian-vector
products, a Jacobi preconditioner (analytic diagonal of L_theta_theta), and a
preconditioned Steihaug-CG with a P-norm trust region.

Unlike block-coordinate ascent, it escapes the saddle points of the
non-convex low-rank landscape, reaching a higher variational bound than the
nlopt CCSAQ backend on every tested dataset, while being faster on moderate p
(barents ~1.5x) and at parity on large p (oaks). Opt-in via
PLNPCA_param(backend = "trnewton"); the nlopt default is unchanged. Covariate
normalisation is skipped for this backend, whose trust region is not
scale-invariant. The 85 existing PLNPCA tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…olves

Fixes failures found on large data (microcosm n=880, p=259), where trnewton
crashed (solve(): solution not found) and never converged:

* Scale-invariant stopping: the gradient norm scales with n and the counts, so
  the absolute gtol was never reached on large problems. Use a relative rule
  ||g|| <= gtol * max(||g0||, 1).
* Robust linear solves for large counts: the per-observation M-Newton solve now
  uses the non-throwing arma::solve (dropping no_approx, falling back to a zero
  step), and the inner 2q x 2q block inverse uses a relative ridge with a pinv
  fallback instead of an absolute ridge that was negligible against large A.
* Defaults retuned: maxit_out 60 -> 150 (the cap was cutting convergence short),
  gtol 1e-4 -> 1e-3 relative (sweet spot: best loglik at ~parity speed on
  small/moderate p; 1e-2 underconverges, 1e-4 is needlessly slow).

trnewton now converges on microcosm (1.76x faster than nlopt, better bound in
aggregate). The 85 PLNPCA tests pass; the nlopt default is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the previous PLNPCA "builtin" optimizer (spectral Barzilai-Borwein) with
the profiled trust-region Newton, and expose it under the name "builtin" for
consistency with the other PLN variants (where "builtin" is the home-made
optimizer). No user-facing "trnewton" backend anymore; nlopt stays the default.

* Delete the old spectral builtin rank optimizer (builtin_optimize_rank and
  builtin_optimize_vestep_rank).
* Rename the trust-region Newton implementation to the "builtin" name:
  trnewton_plnpca.h -> builtin_plnpca.h (namespace trnewton -> builtin),
  wrappers_trnewton_optim_plnpca.cpp -> wrappers_builtin_optim_plnpca.cpp,
  trnewton_optimize_rank -> builtin_optimize_rank, backend tag "trnewton" ->
  "builtin".
* R wiring: drop the "trnewton" backend choice; PLNPCA_param(backend = "builtin")
  now carries the trust-region config (cg_maxit, maxit_out, ftol_out, gtol,
  delta0); the builtin prediction VE-step falls back to the nlopt VE-step (the
  Newton backend has no dedicated VE-step); update the backend documentation.

Rationale: across datasets the trust-region Newton reaches a higher variational
bound than both the old builtin and nlopt, and it is not slower than the old
builtin (which was in fact the slowest backend on large data). The 88 PLNPCA /
backend tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Improves the outer trust-region convergence of the builtin (trust-region Newton)
backend on large data, where it was crawling. A trace on microcosm showed the
iterations were wasted on (i) an oversized initial radius (3 rejections before it
settled) and (ii) a sawtooth radius with many rejected steps, each of which
needlessly recomputed the gradient, the per-observation inner-block inverses and
the preconditioner even though the iterate had not moved.

* On a rejected step the iterate is unchanged, so the gradient / inner-block
  Hessian / preconditioner are now cached and refreshed only on acceptance; a
  rejection costs just one extra Steihaug-CG solve at the reduced radius.
* Seed the initial radius 4x smaller and shrink to 0.25*||s|| (a fraction of the
  failed step) instead of 0.25*Delta.

On microcosm (n=880, p=259) ranks that previously hit the iteration cap now
converge (status 3), faster and at a higher variational bound. Small/moderate
data is unchanged or slightly better (oaks +241 vs nlopt at ~parity speed). The
85 PLNPCA tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds, under trace > 1, the CG iteration count, exit reason (converged /
negative curvature / trust-region boundary / iteration cap) and residual ratio
for each outer trust-region step. This is what pinned down the large-n
convergence behaviour: on microcosm the reduced Hessian is indefinite and the
Steihaug-CG never fully converges (it hits the trust-region boundary or the
iteration cap), so the outer loop is limited by the trust-region radius rather
than by an under-solved Newton system. No change to the optimizer's behaviour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… + DEVLOG

* Note in the backend documentation that, on large data, the builtin (trust-region
  Newton) backend is bounded by maxit_out rather than by the gradient tolerance —
  the higher ranks keep improving the bound and trade quality for time; users
  wanting the best bound should raise maxit_out.
* Add DEVLOG_2026-07-02-03.md summarising the session: SQUAREM evaluation
  (negative), the profiled trust-region Newton for PLNPCA, its promotion to the
  "builtin" backend, scale robustness, trust-region tuning, and the (understood,
  fundamental) large-n convergence limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers the previously untested builtin PLNPCA optimizer (the profiled
trust-region Newton) and its backend-specific R path, fixing the codecov/patch
failure: a family fit with backend = "builtin" checks it reaches a variational
bound at least as good as nlopt, behaves like a normal PLNPCAfit, and honours the
trust-region tuning keys (cg_maxit, maxit_out).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Quarto article (website-only, not built by R CMD check) documenting, for each
model, which optimizer is used and its default, and detailing the mathematical
principles of the home-made "builtin" backend: the joint (mean, variance) Newton
VE-step shared by PLN / PLNnetwork / ZIPLN, the closed-form and graphical-lasso
covariance M-steps, and the profiled saddle-aware trust-region Newton for PLNPCA
(envelope-theorem gradient, matrix-free Schur reduced Hessian, Jacobi-preconditioned
Steihaug-CG).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The singular "article/" (holding only the shared PLNreferences.bib) was easily
confused with the plural "articles/" (the pkgdown convention for website-only
articles, introduced for optimization.qmd). Rename to something unambiguous and
update the bibliography: path in the 8 vignettes that reference it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jchiquet jchiquet marked this pull request as ready for review July 3, 2026 15:50
@jchiquet jchiquet merged commit 6491dc8 into master Jul 3, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant