Repository navigation
Return the final Newton iterate as the Laplace latent mean, and fix the solver-3 log determinant - #3438
Return the final Newton iterate as the Laplace latent mean, and fix the solver-3 log determinant#3438avehtari wants to merge 6 commits into
Conversation
2d8cec3 to
697dce2
Compare
…nt-mean-lu-logdet' into bugfix/laplace-latent-mean-lu-logdet
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 52 bits physical, 57 bits virtual CPU(s): 192 On-line CPU(s) list: 0-191 Thread(s) per core: 2 Core(s) per socket: 48 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 25 Model: 17 Model name: AMD EPYC 9474F 48-Core Processor Stepping: 1 Frequency boost: enabled CPU MHz: 1497.097 CPU max MHz: 4114.4229 CPU min MHz: 1500.0000 BogoMIPS: 7189.39 Virtualization: AMD-V L1d cache: 3 MiB L1i cache: 3 MiB L2 cache: 96 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-47,96-143 NUMA node1 CPU(s): 48-95,144-191 Vulnerability Gather data sampling: Not affected Vulnerability Indirect target selection: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Old microcode: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Not affected Vulnerability Spec rstack overflow: Mitigation; Safe RET Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; STIBP always-on; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Mitigation; Clear CPU buffers Vulnerability Tsx async abort: Not affected Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq la57 rdpid overflow_recov succor smca fsrm flush_l1d debug_swap G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
|
An update after #3469 (embedded Laplace returns the value at the initial guess) and stan-dev/stan#3442 (the Newton optimizer's Hessian is 1e-6 too small). I ran the example of #3469 with develop (e4745c8) and with this PR (3c5e80e), This PR does not fix part 1 of #3469: the μ = −30, α ≥ 1e5 rows still return the value at θ = 0. #3469 also reports a GP-model point where develop gives NaN and this PR gives a finite value, which is again the value at θ = 0. The log determinant is correct there (det(B) > 0 with LU pivots of both signs); the wrong value comes from part 1. Before this PR, the NaN made the samplers and optimizers reject such a point. So it would be good to fix part 1 soon after this PR.
|
…n_alpha >= 1, match the log-det message shared with stan-dev#3438
|
Just a heads up, #3473 fixes a somewhat related issue and should probably me merged only after this one. (no conflict) |
Summary
Two independent fixes in the embedded Laplace approximation.
This PR was Claude assisted
1. The latent mean is the final Newton iterate (
stan/math/mix/functor/laplace_base_rng.hpp).laplace_base_rng()computed the mean asmean_train = covariance_train * md_est.theta_grad, that isK * grad log p(y | theta_hat). At an exact mode this equalstheta_hat, but before exact convergence the two differ byK * grad log p(theta | y)attheta_hat. The stopping rule limits the change of the objective, not this difference, and the factorKmakes the difference large when the posterior is much narrower than the prior. In example A (tolerance 1e-6), the final iterate is 0.0012 posterior sd from the mode, but the returned mean was 0.53 posterior sd from it, at a log density 7.1 lower. The mean is nowmd_est.theta, the final iterate. This affectslaplace_latent_solve(),laplace_latent_solve_tol(), and the mean of the draws oflaplace_latent_rng(),laplace_latent_tol_rng()andlaplace_latent[_tol]_{bernoulli_logit,poisson_log,neg_binomial_2_log}_rng().2. The log determinant of solver 3 uses
|U_ii|and checks the sign (LUSolver::compute_log_determinant()instan/math/mix/functor/laplace_marginal_density_estimator.hpp). Solver 3 factorizes the non-symmetricB = I + K Wwith partial pivoting. The diagonal ofUcan then have negative entries also whendet(B) > 0, and the oldsum(log(diag(U)))was NaN:laplace_marginal*()returned NaN without a message, with solver 3 and after a fallthrough to solver 3. This happens also for a Gaussian likelihood (example B). The log determinant is nowsum(log(abs(diag(U)))), and the sign ofdet(B)is the sign of the permutation times the signs of theU_ii. Becausedet(B) = det(K) det(K^-1 + W)anddet(K) > 0, a non-positivedet(B)means thatK^-1 + Wis not positive definite at the final iterate, for example at a saddle point, so the Laplace approximation is not defined there. The function then throwsstd::domain_errorwith a message that says so. (A positivedet(B)does not prove positive definiteness: the check finds an odd number of negative eigenvalues.)compute_log_determinant()also runs forlaplace_latent_solve*()andlaplace_latent*_rng(). So at such a point (example C),laplace_marginal_tol()now throws instead of returning NaN, andlaplace_latent_solve_tol()throws this message instead ofcholesky_decompose: Matrix m is not positive definite.Examples
y ~ normal(m, sigma * m)withm(t) = bsld * (phi * exp(-d * t) + (1 - phi) * exp(g * t)), and 4 latent variables on the log or logit scale of(bsld, d, g, phi)with diagonalK. The posterior sd is 5.6 to 9.4 times smaller than the prior sd.laplace_latent_solve_tol()with solver 3. The reference mode comes fromoptim()with Newton polishing.y ~ normal(theta, s),y = (0.3, -0.2),s = sqrt(0.1),K = [1 1.5; 1.5 4].B = I + K Whasdet(B) = 226, but its LU diagonal is(15, -15.07)because the rows are swapped. The exact log marginal ismulti_normal_lpdf(y | 0, K + s^2 I).K = [4 1; 1 1];theta[1]has two Cauchy observationsy = (-3, 3),s = 0.3, andtheta[2]one Gaussian observationz = 0,sz = 0.1. The log posterior has two maxima and a saddle point at(0, 0). From the start(0.01, 3), the Newton iteration of solver 3 converges to the saddle point.developlaplace_latent_solve_tol()laplace_latent_solve_tol()laplace_marginal_tol(), solver 3laplace_marginal_tol(), solvers 1 and 2laplace_marginal_tol()det(I + Sigma * W) is not positive at the final iterate, ...laplace_latent_solve_tol()cholesky_decompose: Matrix m is not positive definitedet(I + Sigma * W) is not positive at the final iterate, ...The
developcolumn is from CmdStan 2.40.0; examples A and B give the same values withdevelop43bc568. The column "this PR" is from CmdStandevelopwith this branch.R script (cmdstanr; the Stan programs are inline)
Tests
New tests:
laplace_latent_solve.mean_is_final_iterate_tumor_size(intest/unit/math/laplace/laplace_latent_solve_test.cpp): example A. The reference is a solve with tolerance 1e-14. With tolerance 1e-6, the returned mean must be within 0.01 posterior sd of the reference.laplace_marginal_tol.all_solvers_exact_with_negative_lu_pivot(new filetest/unit/math/laplace/laplace_lu_log_determinant_test.cpp): example B. The test asserts that the LU factorization ofBhas a negative diagonal entry and thatdet(B) > 0. Solvers 1, 2 and 3 must return the exact log marginal.laplace_marginal_tol.solver_3_saddle_point_throws(same new file): example C. From(0.01, 3),laplace_marginal_tol()andlaplace_latent_solve_tol()must throwstd::domain_errorwith the new message. From(0.3, 3), solver 3 must return the maximum(2.9385, 0.0097).Without the changes in
stan/, all three tests fail: the returned mean is 0.53 posterior sd from the reference, solver 3 returns NaN, andlaplace_marginal_tol()does not throw at the saddle point.With the changes, the new tests pass, and so do the other test files in
test/unit/math/laplace/. I stopped the slowlaplace_disease_map_testsuite inlaplace_marginal_lpdf_test.cppafter its first case, which passed.clang-formatandcpplintpass on the changed files.Side Effects
laplace_latent_solve*()andlaplace_latent*_rng(): the mean moves fromK * grad log p(y | theta_hat)totheta_hat. The two agree up to the convergence error, so results change only where the iteration stopped before exact convergence, and they move toward the mode.laplace_marginal*()with solver 3, or after a fallthrough to solver 3: where it returned NaN because of a negative pivot, it now returns the log marginal. Wheredet(B) <= 0, it now throwsstd::domain_error; before, it returned NaN, or a finite value when the permutation was odd and all pivots were positive. In the model block, both a NaN and an exception reject the proposal; in generated quantities, the exception replaces a silent NaN or a wrong value.Release notes
The embedded Laplace functions
laplace_latent_solve(),laplace_latent_rng()and their variants now return the final Newton iterate as the mean, which is more accurate when the iteration stops before exact convergence.laplace_marginal()and its variants with solver 3 no longer return NaN when the LU factorization has a negative pivot, and they report an error when the Newton iteration ends at a point where the negative Hessian of the log density is not positive definite.Checklist
Copyright holder: Aalto University
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit)make test-headers)make test-math-dependencies)make doxygen)make cpplint)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested