-
-
Notifications
You must be signed in to change notification settings - Fork 183
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
Feature/log bessel i #640
Feature/log bessel i #640
Conversation
by using log_modified_bessel_first_kind
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.
Overall this looks great. The changes requested are some minor efficiency issues, some needless includes, and abit of doc on template parameters.
check_greater_or_equal("log_modified_bessel_first_kind", "v", v, -1); | ||
|
||
using std::log; | ||
using stan::math::log_sum_exp; |
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.
Symbols in the same namespace get brought in automatically---so you don't need the stan::math::
using statements. They won't hurt anything being there.
} | ||
|
||
T log_half_z = log(0.5 * z); | ||
T lgam = lgamma(v + 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.
Shouldn't lgam
be set inside the v > -1
branch? The other branch sets it to 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.
Boost's lgamma function stupidly throws an exception if v = -1
and thus lgamma
is evaluated at 0. It should be returning positive infinity, in which case the first term in the denominator of the Bessel I function is just zero. So, we avoid that, handle the m = 1
case manually, and start the infinity sum with the m = 2
case.
double m = 2; | ||
T lfac = 0; | ||
T old_out; | ||
do { |
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.
Why isn't the test just out != old_out
.
I always find the do-while convention takes me a while to get my head around, but I could get used to 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.
You are right. I forgot that NaN != NaN
is false
.
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.
Upon further review, I was right the first time: NaN != NaN
is true
because NaN == NaN
is false
. So, if out
and old_out
somehow both go NaN
then the loop would never terminate.
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.
Thanks for reviewing this. I somehow thought any comparisons involving NaN were automatically false, but you're right that !=
is an exception to this general rule and always returns true
.
@@ -67,7 +68,7 @@ namespace stan { | |||
kappa_dbl[i] = value_of(kappa_vec[i]); | |||
if (include_summand<propto, T_scale>::value) | |||
log_bessel0[i] | |||
= log(modified_bessel_first_kind(0, value_of(kappa_vec[i]))); | |||
= log_modified_bessel_first_kind(0, value_of(kappa_vec[i])); |
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.
Nice!
/* Log of the modified Bessel function of the first kind, | ||
* which is better known as the Bessel I function. See | ||
* modified_bessel_first_kind.hpp for the function definition. | ||
* |
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.
You want to add doc for the template parameters,
@tparam T1 type of the order (v)
@tparam T2 type of argument (z)
template <typename T1, typename T2> | ||
inline typename boost::math::tools::promote_args<T1, T2, double>::type | ||
log_modified_bessel_first_kind(const T1 v, const T2 z) { | ||
check_not_nan("log_modified_bessel_first_kind", "v", v); |
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.
These comments can't really use v
since that's not what the user is necessarily going to have typed into their program, so it gets confusing.
It's better if you can replace v
with first argument
and z
with second argument (order)
in the error messages.
if (stan::math::is_inf(z)) return z; | ||
if (z > 100) { | ||
// Boost does something like this in asymptotic_bessel_i_large_x | ||
T lim = (4 * square(v) + 10) / (8 * z); |
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 would be more efficient as (square(v) + 2.5) / (2 * z)
.
} | ||
} | ||
|
||
T log_half_z = log(0.5 * z); |
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.
The type can be tighter here as T2
. That way, if T2 == double
, this won't be overpromoted.
Same thing goes for the next one (lgam
) being just T2
. You only need the promoted T
when v
and z
are involved (directly or indirectly).
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 think you have to metaprogram here. If T1
or T2
is int
then we want the left-hand side to be a double
.
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.
Good point about integers; it'll need to be
typename boost::math::tools::promote_args<T1>::type log_half_z
num *= mu - 25; | ||
denom *= ex * 3; | ||
s -= num / denom; | ||
s = z - log(std::sqrt(2 * z * M_PI)) + log(s); |
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 needs to be using std::sqrt
and then just sqrt
here if z
is going to be anything other than double
or int
.
T out; | ||
if (v > -1) { | ||
out = log_sum_exp(v * log_half_z - lgam, lcons - lgamma(v + 2)); | ||
lgam += log(v + 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.
The righthand side can be log1p(v)
--- this may need an include and a using std::log1p
if there's not a primitive stan::math::log1p
.
works faster for v = 0 or v = 1
I think I responded to all of your comments, but in the latest push I also copied special cases from Boost when v = 0 or v = 1. |
The failing test is a failed instantiation of the log-Bessel for |
Thanks. Just the one issue with instantiation to sort out and this should be good to go. I also responded on the metaprogramming --- promote_args does what you need.
… On Oct 7, 2017, at 7:50 PM, bgoodri ***@***.***> wrote:
I think I responded to all of your comments, but in the latest push I also copied special cases from Boost when v = 0 or v = 1.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I don't understand the error message. It just says over and over again that it can't do the template instantiation. But it has |
I'll check it out and have a look. Probably just a missing include somewhere. |
…re/log_besselI
I fixed the issue with this and will push as soon as I generalize the testing a bit.
… On Oct 8, 2017, at 7:47 PM, bgoodri ***@***.***> wrote:
I don't understand the error message. It just says over and over again that it can't do the template instantiation. But it has T1 and T2, so I don't know what is stopping it from working in fwd mode.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I pushed a fix for the compilation problem. I then plugged in the general
testing framework with a test in:
test/unit/math/mix/scal/fun/log_modified_bessel_first_kind_test.cpp
I had to update the testing framework to deal with exceptions, but now there's an issue with the autodiff test failing. It's failing at all orders vs. finite diffs. Is this something you might expect to be way off?
I didn't see any other tests for gradients---should've caught that the first time!
|
It won't autodiff correctly at |
OK, let me write a finer test for this case, then, and see if that works. |
I'm pushing a version that passes tests locally for me. There are still some known issues and then a bunch of cases where the
|
Jenkins, retest this please. |
@seantalts --- any idea what's up? My request to Jenkins to retest this didn't kick off a new testing round. Looks like the failure's a Jenkins issue of some kind. |
…re/log_besselI
Thanks --- they will show that mustard-colored ball when it gets registered even before it starts. Just took a while to show up, which meant given that it's a computer, I figured it was broken.
|
Jenkins, retest this please. |
My bad. Jenkins, retest this please. |
@bob-carpenter, can you change your review to approved? @bgoodri went through and updated the pull request. |
Hey @bgoodri, looks like there's a compiler warning in develop on Stan during This wasn't caught because our current Math build doesn't test the full Stan test suite when it tests Stan Upstream. The new pipelines fix this but aren't in yet. Can you make a PR to fix this and we can roll forward? |
It looks like this is currently not exposed. I just wanted to mention that the log_bessel_k is used in other distributions beyond the von Misses, such as the Generalized Inverse Gaussian Distribution, and having log_bessel_i exposed could be helpful in implementing these. |
Yeah, we need to expose it on the Stan side.
…On Tue, Mar 27, 2018 at 9:23 PM, aaronjg ***@***.***> wrote:
It looks like this is currently not exposed. I just wanted to mention that
the log_bessel_k is used in other distributions beyond the von Misses, such
as the Generalized Inverse Gaussian Distribution, and having log_bessel_i
exposed could be helpful in implementing these.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#640 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADOrqkdiveMv2LaAnbw8WcJzwExTw8jTks5tiuYugaJpZM4Pxb6y>
.
|
Submission Checklist
./runTests.py test/unit
make cpplint
Summary:
Introduce
log_modified_bessel_first_kind(T1 v, T2 z)
function that useslog_sum_exp
to avoid overflow and can be evaluated for non-integerv
.Intended Effect:
Not overflowing while expanding the number of situations where the Bessel I function can be used.
How to Verify:
Run the unit tests
Side Effects:
Inconsistency with other Bessel functions, which currently are restricted to integer orders and do not have
log_
versions.Fixes #639
Documentation:
I am not sure
log_modified_bessel_first_kind
should necessarily be exposed in the Stan language since the only place it is used in Stan isvon_mises_lpdf
, which only calls it withv = 0
and double-precisionz
. Thus, I have not implementedrev
orfwd
specializations because the derivative with respect tov
is a lot of work for no known use case. It probably wouldn't hurt anything to expose it in the Stan language, but any derivatives would be calculated by autodiffing a truncated infinite sum.Copyright and Licensing
Please list the copyright holder for the work you are submitting (this will be you or your assignee, such as a university or company): Trustees of Columbia University
By submitting this pull request, the copyright holder is agreeing to license the submitted work under the following licenses: