-
-
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
Feature/issue 2966 add 7 parameter ddm cdf and ccdf #3042
base: develop
Are you sure you want to change the base?
Feature/issue 2966 add 7 parameter ddm cdf and ccdf #3042
Conversation
…-7-parameter-DDM-CDF-and-CCDF merge PDF branch
merge develop with wiener_lpdf
if (v > 0) { | ||
const auto exponent = -2.0 * v * a * w; | ||
prob_grad_w | ||
= exp(LOG_TWO + exponent + log(fabs(v)) + log(a) - log1m_exp(exponent)); | ||
} else if (v < 0) { | ||
const auto exponent = 2.0 * v * a * w; | ||
prob_grad_w = exp(LOG_TWO + log(fabs(v)) + log(a) - log1m_exp(exponent)); | ||
} |
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.
Can this just be rewritten as
if (v > 0) { | |
const auto exponent = -2.0 * v * a * w; | |
prob_grad_w | |
= exp(LOG_TWO + exponent + log(fabs(v)) + log(a) - log1m_exp(exponent)); | |
} else if (v < 0) { | |
const auto exponent = 2.0 * v * a * w; | |
prob_grad_w = exp(LOG_TWO + log(fabs(v)) + log(a) - log1m_exp(exponent)); | |
} | |
const bool exp_sign = (v > 0) ? -1 : 1; | |
const auto exponent = exp_sign * 2.0 * v * a * w; | |
prob_grad_w = exp(LOG_TWO + log(fabs(v)) + log(a) - log1m_exp(exponent)); | |
if (exp_sign == -1) { | |
prob_grad_w *= exp(exponent) | |
} |
Also for places with if statements like this can you make a comment on why this split has to happen?
stan/math/prim/prob/wiener4_lcdf.hpp
Outdated
template <typename T_x> | ||
inline auto rexp(T_x&& x) noexcept { | ||
return (x <= 700) ? exp(x) : exp(700); | ||
} |
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.
Delete this. If the user is going to rollover a double they can rescale their problem
stan/math/prim/prob/wiener4_lcdf.hpp
Outdated
if (x > 1.0e5) { | ||
return -log(x); | ||
} |
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 is this cutoff point here?
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.
When I delete all the rexp
function calls and run the test
./runTests.py -j4 test/unit/math/prim/prob/wiener_full_lcdf_test.cpp
then an error occurs that does not occur when the function is in use:
terminate called after throwing an instance of 'std::domain_error'
what(): inv_Phi: Probability variable is -27.5065, but must be in the interval [0, 1]
Aborted
test/unit/math/prim/prob/wiener_full_lcdf_test --gtest_output="xml:test/unit/math/prim/prob/wiener_full_lcdf_test.xml" failed
exit now (09/27/24 11:26:08 CEST)
134
Shall we really then delete this function?
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 is this cutoff point here?
This is a safety mechanism but can be deleted when Stan handles such cases internally. I will delete it with the next commit.
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.
Shall we really then delete this function?
Yes we should figure out where thats happening and either fix those spots or throw errors or nans when they happen
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 we should figure out where thats happening and either fix those spots
Ok, I deleted this function and changed all spots to the following:
min(exp(ARGUMENT), std::numeric_limits<ret_t>::max());
The function was called 23 times. Now, we have 23 times this form.
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.
min(exp(ARGUMENT), std::numeric_limits<ret_t>::max());
Delete these. It should just be exp(argument)
. If there are parameter sets in the test that cause rollover like that we should see which. We can then try to rephrase those calculations so this doesn't happen. If the pdf is just not well defined for some ranges of parameters and we know that range then we can also throw errors (or return -inf if appropriate)
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.
In the cases where we use the rexp
function, or now this min-construct, we want to make sure that the expression exp(x)
does not become infinity for a large input x
. Therefore, we chose the threshold of 700 for x
as this not yet evaluates to infinity but larger numbers do.
This has nothing to do with the parameter ranges of the input parameters. We are already checking the allowed parameter ranges in the beginning of the function call. How would you rephrase the calculations?
stan/math/prim/prob/wiener4_lcdf.hpp
Outdated
inline auto log_probability_distribution(const T_a& a, const T_v& v, | ||
const T_w& w) noexcept { | ||
using ret_t = return_type_t<T_a, T_w, T_v>; | ||
auto nearly_one = ret_t(1.0 - 1.0e-6); |
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.
Can you just use std::numeric_limits<ret_t>::min()
here instead of 1e-6?
stan/math/prim/prob/wiener4_lcdf.hpp
Outdated
auto minus_two_va_one_minus_w = (-2.0 * v * a * (1.0 - w)); | ||
ret_t prob; | ||
if (minus_two_va_one_minus_w < 0) { | ||
const auto exp_arg = exp(minus_two_va_one_minus_w); | ||
if (exp_arg >= nearly_one) { | ||
return ret_t(log1p(-w)); | ||
} | ||
auto two_vaw = 2 * v * a * w; | ||
if (two_vaw > minus_two_va_one_minus_w) { | ||
prob = log1p(-exp_arg) - log_diff_exp(two_vaw, minus_two_va_one_minus_w); | ||
} else if (two_vaw < minus_two_va_one_minus_w) { | ||
prob = log1p(-exp_arg) - log_diff_exp(minus_two_va_one_minus_w, two_vaw); | ||
} else { | ||
prob = log1p(-exp_arg) - NEGATIVE_INFTY; | ||
} | ||
} else { | ||
const auto exp_arg = exp(-minus_two_va_one_minus_w); | ||
if (exp_arg >= nearly_one) | ||
return ret_t(log1p(-w)); | ||
prob = log1p(-exp_arg) - log1p(-exp(2 * v * a)); | ||
} | ||
return prob; |
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.
Early returns are preferred to show when a branch has finished
auto minus_two_va_one_minus_w = (-2.0 * v * a * (1.0 - w)); | |
ret_t prob; | |
if (minus_two_va_one_minus_w < 0) { | |
const auto exp_arg = exp(minus_two_va_one_minus_w); | |
if (exp_arg >= nearly_one) { | |
return ret_t(log1p(-w)); | |
} | |
auto two_vaw = 2 * v * a * w; | |
if (two_vaw > minus_two_va_one_minus_w) { | |
prob = log1p(-exp_arg) - log_diff_exp(two_vaw, minus_two_va_one_minus_w); | |
} else if (two_vaw < minus_two_va_one_minus_w) { | |
prob = log1p(-exp_arg) - log_diff_exp(minus_two_va_one_minus_w, two_vaw); | |
} else { | |
prob = log1p(-exp_arg) - NEGATIVE_INFTY; | |
} | |
} else { | |
const auto exp_arg = exp(-minus_two_va_one_minus_w); | |
if (exp_arg >= nearly_one) | |
return ret_t(log1p(-w)); | |
prob = log1p(-exp_arg) - log1p(-exp(2 * v * a)); | |
} | |
return prob; | |
auto minus_two_va_one_minus_w = (-2.0 * v * a * (1.0 - w)); | |
if (minus_two_va_one_minus_w < 0) { | |
const auto exp_arg = exp(minus_two_va_one_minus_w); | |
if (two_vaw > minus_two_va_one_minus_w) { | |
return ret_t(log1p(-exp_arg) - log_diff_exp(two_vaw, minus_two_va_one_minus_w)); | |
} else if (two_vaw < minus_two_va_one_minus_w) { | |
return ret_t(log1p(-exp_arg) - log_diff_exp(minus_two_va_one_minus_w, two_vaw)); | |
} else { | |
return ret_t(log1p(-exp_arg) - NEGATIVE_INFTY); | |
} | |
} else { | |
const auto exp_arg = exp(-minus_two_va_one_minus_w); | |
return ret_t(log1p(-exp_arg) - log1p(-exp(2 * v * a))); | |
} |
I also removed exp_arg >= nearly_one
. Can you show me an example where removing that causes the wrong answer?
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.
Can you show me an example where removing that causes the wrong answer?
In this case, all tests run successfully when I remove nearly_one
. This is not the case for the other nealry_one
below.
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.
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.
Does this function give the smallest positive number representable or does it give a negative number? std::numeric_limits<ret_t>::min()
We would need a number near to zero.
So that the function log1p(x)
does not become infinity
when the argument is too near to 1. Instead, if our argument is very near to 1, we want to return -w
.
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.
https://godbolt.org/z/jnrb5Ev8z
It gives back the smallest representable number that is greater than 0
stan/math/prim/prob/wiener4_lcdf.hpp
Outdated
if (fabs(v) == 0.0) { | ||
return ret_t(-w); | ||
} | ||
nearly_one = ret_t(1.0 - 1.1 * 1.0e-5); |
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.
Make a test case that shows where this would fail if nearly_one
was removed
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.
Did commit early returns some ifs pass the tests? Or did that one fail from removing |
I checked out e5f5048 and added the test values from the prim wiener_lcdf tests here to the mix tests. All the mix tests that check against finite difference differentiation passed, but the prim test that checks the gradients you calculated do not pass. Where did you get the values of the true gradient values you use in the test in prim? It looks like the only code that changed in that commit were related to the gradient calculations so if they were incorrect I would expect the ad test suite to fail |
tbc what I made a branch to show what I did git pull
git checkout wiener_lpdf/test-vals
## These tests pass
python3 ./runTests.py -j20 ./test/unit/math/mix/prob/ -f wiener_full_lcdf
## Some of these tests fail
python3 ./runTests.py -j20 ./test/unit/math/prim/prob/ -f wiener_full_lc |
Only your suggested changes above for I changed the lines in
Now, all tests should pass. Let's see. Then, you can say, what I shall change next. |
Jenkins Console Log Machine informationNo LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focalCPU: G++: Clang: |
Hey Steve, now the errors from before should be fixed and the wildcards are deleted. What next? |
Jenkins Console Log Machine informationNo LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focalCPU: G++: Clang: |
Summary
With this PR the CDF and the CCDF of the 7-parameter diffuion model are added.
See issue #2966
Relates to issue #2822
Tests
We implemented analogous tests as for the PDF
Side Effects
no
Release notes
CDF and CCDF for the 7-parameter diffusion model. Allows modeling truncated and censored data.
Checklist
Copyright holder: Franziska Henrich, Christoph Klauer
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