|
| 1 | +# Journal de développement — 2-3 juillet 2026 |
| 2 | + |
| 3 | +Branche : `optim-plnpca` |
| 4 | + |
| 5 | +## Contexte |
| 6 | + |
| 7 | +Exploration de méthodes d'accélération pour les procédures d'optimisation de PLNmodels, |
| 8 | +puis développement d'un nouvel optimiseur pour PLNPCA. Deux volets : |
| 9 | +1. Évaluation de SQUAREM (accélération de point-fixe EM) sur ZIPLN et PLNmixture. |
| 10 | +2. Conception, implémentation et intégration d'un **Newton profilé à région de confiance** |
| 11 | + pour PLNPCA, qui remplace l'ancien backend `builtin`. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## 1. Évaluation SQUAREM (ZIPLN, PLNmixture) — négatif |
| 16 | + |
| 17 | +SQUAREM (Varadhan & Roland 2008) accélère une carte de point-fixe EM lente. Prototypes |
| 18 | +non-invasifs (scratchpad) reconstruisant les cartes EM de ZIPLN (`ZIPLNfit$optimize`) et |
| 19 | +PLNmixture (`PLNmixturefit$optimize`). |
| 20 | + |
| 21 | +**Verdict** : pas de gain de vitesse. |
| 22 | +- ZIPLN / PLNmixture : SQUAREM coupe ~1.5–2× les *cycles externes*, mais chaque cycle coûte |
| 23 | + ~2–3 évaluations de la carte (VE-step / fits de composantes), et la garde de monotonie |
| 24 | + ELBO rejette l'extrapolation → net plus lent (25–40 %). Léger mieux d'ELBO (échappe des |
| 25 | + plateaux) mais pas de vitesse. |
| 26 | +- PLN / PLNPCA de base : hors-scope (solveur conjoint en C++, pas de carte EM séparable lente). |
| 27 | + |
| 28 | +Conclusion : SQUAREM = éventuelle option de robustesse, jamais un défaut. Le vrai levier pour |
| 29 | +PLNPCA est ailleurs (cf. §2). |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## 2. PLNPCA — Newton profilé à région de confiance (nouveau backend) |
| 34 | + |
| 35 | +### 2.1 Diagnostic de départ |
| 36 | + |
| 37 | +À q fixé, l'optim PLNPCA (nlopt CCSAQ ou l'ancien builtin spectral BB) faisait des **milliers** |
| 38 | +d'itérations premier-ordre. L'ascension par blocs (VE-step Newton pour (M,S), puis (B,C)) |
| 39 | +stagne sur des **points-selles** du paysage non-convexe des loadings ; c'est pourquoi les |
| 40 | +méthodes conjointes premier-ordre payent tant d'itérations (prix de la robustesse aux selles). |
| 41 | + |
| 42 | +### 2.2 L'idée |
| 43 | + |
| 44 | +Profiler les paramètres variationnels : `g(B,C) = max_{M,S} ELBO`. |
| 45 | +- Le bloc (M,S) est **concave** → VE-step Newton par observation (solve q×q), ~9 passes. |
| 46 | +- Le **théorème de l'enveloppe** donne `∇g` gratuitement (= ∂ELBO/∂(B,C) au (M,S) optimal). |
| 47 | +- Optimiser `g(B,C)` par un Newton **conscient des selles** (région de confiance + courbure |
| 48 | + négative), en utilisant le **complément de Schur** `H_θθ − H_θφ H_φφ⁻¹ H_φθ` où `H_φφ` est |
| 49 | + bloc-diagonal par observation (2q×2q) → produits Hessien-vecteur matrix-free bon marché. |
| 50 | + |
| 51 | +Validé en R d'abord : un pas le long de la courbure négative à la selle escalade vers un |
| 52 | +meilleur optimum que le package. Le Newton conscient des selles bat nlopt PARTOUT en ~10–40 |
| 53 | +itérations externes (vs 463–5406 CCSAQ). |
| 54 | + |
| 55 | +### 2.3 Implémentation C++ |
| 56 | + |
| 57 | +- `src/builtin_plnpca.h` : `ve_solve` (VE Newton), `inner_blocks_inv` (bloc interne analytique |
| 58 | + 2q×2q), `hess_dir` (Hessienne appliquée à une direction — dérivée analytiquement, `dA=A⊙dη` |
| 59 | + + règle du produit, cohérente terme à terme avec `rank_obj_grad`), `precond_diag` |
| 60 | + (préconditionneur de Jacobi = diagonale analytique de `L_θθ`, strictement positive). |
| 61 | +- `src/wrappers_builtin_optim_plnpca.cpp` : `builtin_optimize_rank` — gradient-enveloppe, |
| 62 | + produit Hessien-vecteur de Schur (2 appels `hess_dir` + solve du bloc interne), **Steihaug-CG |
| 63 | + préconditionné** (région de confiance en norme-P), boucle TR (test de ratio ρ, mise à jour |
| 64 | + du rayon Δ). |
| 65 | + |
| 66 | +**Pièges rencontrés** : |
| 67 | +- `PLNPCAfit$optimize` normalise les covariables ; la région de confiance / les produits |
| 68 | + Hessien-vecteur ne sont PAS invariants d'échelle → la normalisation DÉGRADE (oaks −1553 ll). |
| 69 | + Fix : sauter la normalisation pour ce backend (échelle naturelle). |
| 70 | +- Compile : `C2` doit être déclaré AVANT les lambdas `[&]`. |
| 71 | +- Les produits Hessien-vecteur coûtent *intrinsèquement* 2 évaluations directionnelles de |
| 72 | + gradient (une par direction θ/φ) — l'analytique gagne en propreté (pas d'ε FD), pas en vitesse. |
| 73 | + |
| 74 | +### 2.4 Préconditionnement |
| 75 | + |
| 76 | +Le Jacobi (diagonale de `L_θθ`) absorbe la disparité d'échelle B-vs-C → le Steihaug-CG converge |
| 77 | +en bien moins d'itérations (`cg_maxit` abaissé). Sur oaks (grand p), sans préconditionnement |
| 78 | +cg=6 donnait −2388 ll (inutilisable) ; avec, cg=8 → parité vitesse + meilleure qualité. |
| 79 | + |
| 80 | +### 2.5 builtin ← trnewton (renommage) |
| 81 | + |
| 82 | +Décision : garder torch (experimental), **supprimer l'ancien builtin spectral BB de PLNPCA** et |
| 83 | +exposer le Newton région-de-confiance sous le nom `builtin` (cohérent avec les autres variantes |
| 84 | +PLN où builtin = optimiseur maison). nlopt reste le défaut. Renommage complet |
| 85 | +(fichiers/namespace/fonction/tag), plus de backend "trnewton" user-facing. |
| 86 | + |
| 87 | +### 2.6 Comparaison des 4 backends |
| 88 | + |
| 89 | +Qualité (Δloglik vs meilleur de ligne, ranks 1:5) : **trnewton(builtin) > builtin-ancien > nlopt |
| 90 | +> torch** sur les 15 cas (trichoptera/barents/oaks). torch = pire partout ET le plus lent. |
| 91 | +L'ancien builtin spectral était même LE PLUS LENT à l'échelle (microcosm 351s vs nlopt 273s vs |
| 92 | +nouveau builtin 155s), sans niche → suppression justifiée. |
| 93 | + |
| 94 | +### 2.7 Robustesse à l'échelle |
| 95 | + |
| 96 | +Bug trouvé sur microcosm (n=880, p=259) : crash (`solve(): solution not found`) et non-convergence |
| 97 | +(gtol absolu jamais atteint car ‖g‖ scale en millions). Corrigés : |
| 98 | +- convergence **relative** `‖g‖ ≤ gtol·max(‖g₀‖,1)` ; |
| 99 | +- solves robustes (`arma::solve` non-levant + fallback ; ridge relatif + `pinv` sur le bloc) ; |
| 100 | +- défauts : `maxit_out 60→150`, `gtol 1e-4→1e-3` relatif (point idéal), `cg_maxit=8`. |
| 101 | + |
| 102 | +### 2.8 Gestion de la région de confiance |
| 103 | + |
| 104 | +Diagnostic (trace TR) : rayon initial surdimensionné (3 rejets) + dents de scie (30 rejets/120), |
| 105 | +chacun recalculant inutilement gradient/`inner_blocks_inv`/préconditionneur alors que le point ne |
| 106 | +bouge pas. Fix : **rejets bon marché** (quantités mises en cache, recalculées seulement à |
| 107 | +l'acceptation), seed Δ ÷4, rétrécissement `Δ=0.25·‖s‖`. Résultat : q1/q3 convergent maintenant |
| 108 | +(status 3), + rapides + meilleure qualité ; petit/moyen non régressé. |
| 109 | + |
| 110 | +### 2.9 Convergence lente à grand n — cause fondamentale (levier A épuisé) |
| 111 | + |
| 112 | +Instrumentation du Steihaug-CG (exit reason + résidu, dans le trace) : sur microcosm q3, le CG |
| 113 | +**ne converge JAMAIS** (0/120), il bute sur la frontière TR (83) ou le cap avec **résidu qui |
| 114 | +GROSSIT** (26). **Cause racine : le Hessien réduit est INDÉFINI à l'échelle** (paysage riche en |
| 115 | +selles) ET `g(B,C)` est très **non-quadratique aux grands pas** (le profilage de (M,S) est une |
| 116 | +carte non-linéaire violente) → pas limités par le rayon TR. Mais les pas restent **précis (ρ≈1)** |
| 117 | +et réduisent bien f : la rampe (~100 itér × ~2000 d'amélioration) est **fondamentale**. |
| 118 | +- Garde-fou "stopper CG si résidu grossit" → EMPIRE (les itérations errantes produisent quand |
| 119 | + même de bons pas). Retiré. |
| 120 | +- Préconditionneur diagonal impuissant face à l'indéfini / directions plates non-alignées. |
| 121 | +- Seul levier théorique restant = solveur TR indéfini exact (GLTR/Lanczos), ~150 lignes, ROI |
| 122 | + incertain (pas déjà exact, balle TR petite par non-quadraticité) → non retenu. |
| 123 | + |
| 124 | +### 2.10 Leviers pragmatiques — pas de gain gratuit |
| 125 | + |
| 126 | +- **Warm-start des rangs** (sequential=TRUE) : NET NÉGATIF pour le TR Newton (oaks 0.55× + pire). |
| 127 | + Contrairement à un 1er-ordre, le coût par rang est dans la rampe (paysage indéfini), pas dans la |
| 128 | + distance à l'init ; partir du rang q−1 biaise le bassin. Écarté. |
| 129 | +- **maxit_out** : pur cadran temps↔qualité (les hauts rangs rampent productivement : q5 |
| 130 | + +5.8k→+175k de maxit 150 à 350). Défaut 150 = compromis ; documenté que l'utilisateur peut |
| 131 | + relever `maxit_out` pour la qualité à l'échelle (ajout doc `@param backend`). |
| 132 | + |
| 133 | +### 2.11 État final |
| 134 | + |
| 135 | +builtin (TR Newton) = meilleure qualité petit/moyen partout ; à l'échelle plus rapide (1.8×) et |
| 136 | +meilleur en agrégat, dominant sur tous les rangs si `maxit_out` relevé. La lenteur de convergence |
| 137 | +à grand n est comprise et fondamentale. 85 tests PLNPCA passent, nlopt reste défaut. |
| 138 | + |
| 139 | +**Commits** : `0880c681` (backend trnewton) · `98c6c35d` (robustesse échelle) · `a791a163` |
| 140 | +(trnewton→builtin) · `c9f38bec` (gestion TR) · `e641fe13` (diagnostics CG). Point d'arrêt naturel. |
0 commit comments