-
-
Notifications
You must be signed in to change notification settings - Fork 188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved numerical stability of binomial_coefficient_log #1614
Improved numerical stability of binomial_coefficient_log #1614
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, that makes sense.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some style comments. As @bbbales2 is actually reviewing this, consider my comments optional unless Ben says otherwise.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be ops_partials.edge1_.partials_[0] = k_dbl == 0 ? 0 : NEGATIVE_INFTY
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
} | ||
} | ||
if (!is_constant_all<T_k>::value) { | ||
if (k_ == 0 && n_ == -1.0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah, that makes sense.
Summary
Reimplemented
binomial_coefficient_log
. Previously, this was done via calls tolgamma
or normal approximation to the binomial coefficient. The current version delegates tolbeta
for 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 tolbeta
in #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.lbeta
against 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