Repository navigation
Conversation
inv_logit and log_inv_logit_diff at large arguments
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.762 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 |
|
I reviwed this with help from Claude
The auto x_arena = to_arena(std::forward<T>(x));
const Eigen::Index n = x_arena.size();
arena_t<Eigen::ArrayXd> d(n);
arena_t<T> ret(n);
for (Eigen::Index i = 0; i < n; ++i) {
const double xi = x_arena.coeff(i).val();
const double e = std::exp(-std::fabs(xi));
ret.coeffRef(i) = xi < 0 ? (xi < LOG_EPSILON ? e : e / (1.0 + e)) : 1.0 / (1.0 + e);
d.coeffRef(i) = e / ((1.0 + e) * (1.0 + e));
}
reverse_pass_callback([x_arena, ret, d]() mutable {
x_arena.adj().array() += ret.adj().array() * d;
});For
|
|
@avehtari Do I understand your review correctly that it's just commentary without a request for changes? |
|
Sorry it was not clear that they are not just comments, but suggestions for changes
They are not show-stopper requests |
The Eigen::Matrix<var> overload computed exp(-|x|) with a vectorized pass over a heap copy of the values, stored it, and divided again in the reverse pass: 1.07-1.30 of develop on glibc. Now one loop computes the value and the derivative e / (1 + e)^2 per element and stores the derivative, as suggested in the review of stan-dev#3446: 0.94-1.11 on glibc and 1.00-1.07 on MinGW, within the noise of identical code; values within 2 ulp of develop, +8 arena bytes per element as before.
|
@avehtari I see. Sometimes the LLM comments are challenging to parse properly, but I'm very guilty of producing that myself too 😅 Anyway, thanks for the review. I checked that the case 1 is valid and then adopted it. Adjusted the PR text based on notes in 2, 3. |
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: 43 bits physical, 48 bits virtual CPU(s): 256 On-line CPU(s) list: 0-255 Thread(s) per core: 2 Core(s) per socket: 64 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 23 Model: 49 Model name: AMD EPYC 7742 64-Core Processor Stepping: 0 Frequency boost: enabled CPU MHz: 1496.184 CPU max MHz: 3416.0681 CPU min MHz: 1500.0000 BogoMIPS: 4491.85 Virtualization: AMD-V L1d cache: 4 MiB L1i cache: 4 MiB L2 cache: 64 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-63,128-191 NUMA node1 CPU(s): 64-127,192-255 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: Mitigation; untrained return thunk; SMT enabled with STIBP protection 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; Retpolines; IBPB conditional; STIBP always-on; RSB filling; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Not affected 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 nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 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 ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip rdpid overflow_recov succor smca sev sev_es 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 |
AI use disclosure: This was developed with the help of Claude Code (Claude Opus 5.5, Claude Fable 5.1)
Summary
The derivative of
inv_logitis computed asval * (1 - val), which subtracts a number close to 1: it has a relative error of 3.6e-8 at x = 20 and is exactly 0 for x ≥ 37 (true value 8.5e-17). The first-order gradient of a model is accurate in absolute terms, but everything that differentiates it is not: the second derivatives oflog1p_exp,log1m_inv_logitand the −W oflaplace_marginal_bernoulli_logit_lpmfare 0 there (and those oflog_inv_logitfor x ≤ −37), and so are the forward-mode derivatives of every function whosefvaroverload goes throughinv_logit(Phi_approx,lub_constrain, the logistic cdf family). The x partial oflog_inv_logit_diffcancels the same way (0 at (40, 0) instead of 8.5e-18).The rev, fwd and OpenCL overloads now use these forms (rev with the value's exponential):
The rev
inv_logitis also rewritten in the current callback style, with one overload forvarandvar_value<Matrix>and one forEigen::Matrix<var>. The latter computes the value and the derivative in one scalar pass over the elements and stores the derivative for the reverse pass.It also carries a forward-mode tail test of
lub_constrain, whosextangent and second derivative go through thefvarinv_logitondevelop. Thelbtangent (1 - σ(x)) and the second derivative of the log Jacobian at x = 0 are separate defects ofprim/constraint/lub_constrain.hpp, fixed in #3447 (commit 456e257), which also evaluatesinv_logitonly at non-positive arguments: #3447 does not depend on this PR, and with it merged the test here is a guard.lub_constrain's reverse-mode fix is also in #3447.Phi_approxis fixed in forward mode the same way; its forward-mode test is onbugfix/phi-approx-stable-derivatives, which is stacked on this branch.Tests
References are mpmath values (50–60 digits, see
test/unit/math/mix/fun/inv_logit_tail_refs.hpp); tolerances are 1e-13 relative, floored at the smallest normal double. Every new test fails ondevelopunless marked.test/unit/math/mix/fun/inv_logit_test.cpp,log1p_exp_test.cpp,log_inv_logit_diff_test.cpp: first and second derivatives forvar,var_value,fvar<double>,fvar<fvar<double>>andfvar<var>at x from −800 to 800;...EqualArgsoflog_inv_logit_diffpins the behaviour change below.inv_logit:log1mInvLogitFwdDerivativeTails,logInvLogitFwdDerivativeTails(mix/fun/log1m_inv_logit_test.cpp,log_inv_logit_test.cpp) andlub_constrain_fwd_derivative_tails(mix/constraint/lub_constrain_1_test.cpp).test/unit/math/opencl/rev/inv_logit_test.cpp,log_inv_logit_diff_test.cpp):rev_derivative_large_args,rev_gradient_large_argsagainst the same references, andrev_non_const_lvalue, which does not compile ondevelop.Run locally and passing:
O=0: at 549b4b5, before the one-pass rewrite of theMatrix<var>overload, the sixmixtest files above; after the rewrite,mix/fun/inv_logit_test.cpp,mix/prob/student_t_qf_test.cpp(the one other test that instantiates the rewritten overload),rev/prob/ordered_logistic_test.cpp, the expression tests ofinv_logit, the header check ofrev/fun/inv_logit.hpp,runChecks.py, cpplint and clang-format 10.bugfix/numerically-stable-derivativesat a3f1aaa): MSYS2 g++ 15.2-O1prim, rev, fwd, mix and Laplace tests of the changed functions and their callers; the expression tests; the OpenCL tests on the Intel OpenCL CPU runtime and on an AMD gfx1035 GPU; clang-format 10 and cpplint.Not run:
make test-headers,make doxygen, the full unit suite. CI passed on the previous head (549b4b5).Side Effects
Benchmarked against
developin one process,-O3 -DNDEBUG, on MinGW (MSYS2 g++ 15.2) and glibc (WSL g++ 12.2); theMatrix<var>row after the one-pass rewrite of that overload, the other rows on the earlier head, whose code on those paths is unchanged. Time, this branch / develop:inv_logitofMatrix<var>, n = 10 to 10⁴inv_logitof a scalarvarlub_constrainfvar<var>Hessian, through forward-modeinv_logitMemory:
inv_logitof a scalarvar32 → 40 arena bytes per call, containers +8 bytes per element; thefvar<var>Hessian oflog1p_exp+4%. Values within 3 ulp of develop. OpenCL was not timed.Behaviour changes:
log_inv_logit_diff(x, y)at x = y: ∂x is +∞ (the limit from x > y) instead of −∞.inv_logitof a non-const lvaluevar_value<matrix_cl<double>>now compiles.Release notes
Fixed the derivatives of
inv_logitandlog_inv_logit_diff, inaccurate from |x| ≈ 20 and zero beyond 37.Checklist
Copyright holder: Jáchym Barvínek jachymb@gmail.com
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