Improved numerical stability of binomial_coefficient_log #1614
Improved numerical stability of binomial_coefficient_log #1614bbbales2 merged 37 commits intostan-dev:developfrom
Conversation
…omial_coefficient_log
…s' into bugfix/1592-binomial_coefficient_log
…gs/RELEASE_500/final)
…s' into bugfix/1592-binomial_coefficient_log
…gs/RELEASE_500/final)
…mial_coefficient_log
…omial_coefficient_log
…omial_coefficient_log
…mial_coefficient_log
…gs/RELEASE_500/final)
…stable/2017-11-14)
…_log' into bugfix/1592-binomial_coefficient_log
…stable/2017-11-14)
|
Sure yeah. Did we do a pull related to this for the last release? I went to the closed pull reqs. and didn't find it. |
|
Found it: #1612 |
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
bbbales2
left a comment
There was a problem hiding this comment.
Looks good, just a couple explanations so I can double check yah on the last couple bits.
| } | ||
| } | ||
| if (!is_constant_all<T_k>::value) { | ||
| if (k_ == 0 && n_ == -1.0) { |
There was a problem hiding this comment.
Could you explain the gradient logic for k here?
I think I kinda followed the ones for n but these ones confused me.
There was a problem hiding this comment.
Not sure what to add. The only fact I use is that lim x-> 0 digamma(x) from above is negative infinity. Mentioned that in the comments, if that is still confusing, let me know (it is also possible I am missing some of the edge cases).
|
Just for clarity just explain here on Github then we can throw them in the code if that seems worth the 8 hours of testing or just staple it on the next pull. |
| if (k_ == 0) { | ||
| ops_partials.edge1_.partials_[0] = 0; | ||
| } else { | ||
| ops_partials.edge1_.partials_[0] = stan::math::NEGATIVE_INFTY; |
There was a problem hiding this comment.
This could be ops_partials.edge1_.partials_[0] = k_dbl == 0 ? 0 : NEGATIVE_INFTY.
There was a problem hiding this comment.
Yes, I just thought the ternary operator is discouraged. But if that is the Stan style, I can happily use it.
There was a problem hiding this comment.
I don't think it's discouraged, I've been suggested to use it in similar cases. It's a matter of preference, if you find your version clearer, don't change it. :) For me, given that there are a series of nested ifs, it helped reducing the number of things I had to keep in mind while reading.
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
| } | ||
| } | ||
| if (!is_constant_all<T_k>::value) { | ||
| if (k_ == 0 && n_ == -1.0) { |
Summary
Reimplemented
binomial_coefficient_log. Previously, this was done via calls tolgammaor normal approximation to the binomial coefficient. The current version delegates tolbetafor most cases, using the fact thatbinomial_coefficient_log(N,n) == -lbeta(N - n + 1, n + 1) - log(N + 1). This in turn relies on the stability improvements made tolbetain #1612 (this PR depends on #1612 - the branch has been merged here as otherwise the tests don't past).Additionally, better handling of edge cases has been improved. The function is now valid to call whenever calling
lgamma(N + 1) - lgamma(n + 1) - lgamma(N + 1 - n)would be, i.e. forn >= -1, N >= -1, N + 1 >= n. With the cases when equality holds return 0 or infinity with correct sign.Tests
1e-7, but I think it is not a big deal.binomial_coefficient_log(n, k) == binomial_coefficient_log(n - 1, k - 1) + log(n) - log(k)). The code could be improved once [WIP] Tools for testing identities #1677 is done.lbetaagainst a few identities. It is a bit weaker than it should be, because I triggerred a bug Derivatives of log_sum_exp are wrong for large arguments #1679 which causes false positives in the test. The code can be simplified once [WIP] Tools for testing identities #1677 is done.Side Effects
I don't think so, although the code might slightly change performance in either direction.
Checklist
Math issue binomial_coefficient_log produces wrong results when N >> n #1592
Copyright holder: Institute of Microbiology of the Czech Academy of Sciences
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 doxygen)make cpplint)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested