Skip to content
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

Check input conditions for log1m() before delegating to log1p(). #725

Merged
merged 12 commits into from
Feb 3, 2018

Conversation

mcol
Copy link
Contributor

@mcol mcol commented Jan 19, 2018

Submission Checklist

  • Run unit tests: ./runTests.py test/unit
  • Run cpplint: make cpplint
  • Declare copyright holder and open-source license: see below

Summary:

This attempts to fix issue #681. I went with using Stan's domain_error() function, but alternatively I could have used Boost's raise_domain_error() as in

  if (x > 1)
    return boost::math::policies::raise_domain_error<double>(
         "boost::math::log1p<%1%>(%1%)",
         "log1m(x) requires x < 1, but got x = %1%.", x, boost_policy_t());

which would have produced a message closer to the current one. I decided against it because the message would still have to reference boost::math::log1p, which may be confusing.

Intended Effect:

Trying stan::math::log1m(2.0f) will print the following error:

terminate called after throwing an instance of 'std::domain_error'
  what():  Error in function log1m(double): log1m(x) requires x < 1, but got x = 2

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):
Marco Colombo, University of Edinburgh

By submitting this pull request, the copyright holder is agreeing to license the submitted work under the following licenses:

Copy link
Member

@syclik syclik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting a pull request! We really appreciate it, especially when it's actually fixing the doc too!!!

A couple changes:

  1. please use check_less_or_equal() for the check. It'll give a more consistent error message (and gives the ability to really refactor chunks at once).
  2. Add a test in the log1m_test.cpp file that checks that the error message correctly says log1m instead of log1p (since that's the purpose of this pull request). One (easier) way to do this is to use the EXPECT_THROW_MSG() macro that's used in a test like this one: https://github.com/stan-dev/math/blob/develop/test/unit/math/rev/mat/err/check_nonzero_size_test.cpp#L34

inline double log1m(double x) { return stan::math::log1p(-x); }
inline double log1m(double x) {
if (x > 1)
domain_error("Error in function log1m(double)", "log1m(x)", x,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@syclik
Copy link
Member

syclik commented Jan 19, 2018

Also, do you know if the code is has dual copyright ownership?

@mcol
Copy link
Contributor Author

mcol commented Jan 19, 2018

Thanks for the very quick review! I've now used check_less_or_equal, but now this throws an exception also for NaNs, so one of the pre-existing tests now fails.

@bob-carpenter
Copy link
Contributor

I've set the tests up very carefully to verify that the autodiff versions throw exceptions under the exact same conditions as the double versions. Because log1p is a C++ library function, we don't have it throw exceptions.

One option would be to change the double version in stan::math. We now have implementations in that namespace to avoid all the ambiguity with integer promotion that otherwise arises (you can construct an autodiff variable or a double out of an integer and we have no way to say to prefer a double).

@syclik
Copy link
Member

syclik commented Jan 20, 2018

@bob-carpenter, I think changing the double version in stan::math seems like the right thing to do to maintain consistency. I'm thinking this way for a couple reasons:

  1. most of the clients of the math library are coming from the Stan language. It makes sense to have the same behavior of a function (for exceptions) when the variable is declared in any block.
  2. I don't think it's worth our effort maintaining consistency with the C++ library functions any more. We really can't deal with NaN elegantly in Stan and that's our main user. I think throwing is appropriate.

Agree? If so, I can help add a double version (if it doesn't exist) and get this into the math library.

*/
inline double log1m(double x) { return stan::math::log1p(-x); }
inline double log1m(double x) {
check_less_or_equal("log1m(x)", "x", x, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace "log1m(x)" with `"log1m". You'll need to update the test to match. (The rest of our error messages just have the function name followed by colon, no arguments in the function)

@bob-carpenter
Copy link
Contributor

@sycklik Do you want to do this just here or for all the C++ built-in functions? We have the same behavior with log, exp, all the trig functions, etc.---there are a couple dozen built-in functions, I think.

Most of them have double and int versions which is where these tests would go. Then the double tests would need to be updated. Then the automatic autodiff tests should still pass if the autodiff version always delegates to the non-autodiff version. Otherwise, all the autodiff versions will need to be updated.

If you think it's worth doing just for this one function, then go ahead, but please leave an issue to fix the rest.

This would also be a good time to add stan::math::sqrt declarations and definitions, which seem to be missing.

@mcol
Copy link
Contributor Author

mcol commented Jan 20, 2018

Here it is. I wish I could follow your discussion, but I'm finding it impenetrable at this stage. When you reach an agreement, if you think it's worth spending some time to guide me through, I'll be happy to give it a go. :)

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 20, 2018 via email

@wds15
Copy link
Contributor

wds15 commented Jan 20, 2018

You should put this on the wiki if you have some spare time. Helpful read.

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 21, 2018 via email

@syclik
Copy link
Member

syclik commented Jan 22, 2018

@bob-carpenter: sorry about flip flopping. After giving it thought, I think we should hold off on changing behavior until our next major version (or some other major version).

@mcol, I'm going to submit a pull request to help fix the behavior on the check. If we want to keep the current behavior the same, we want to only use that check function when the input is not nan.

@syclik
Copy link
Member

syclik commented Jan 22, 2018

I opened up a PR on @mcol's fork. When that gets merged, it should update this PR.

@mcol
Copy link
Contributor Author

mcol commented Jan 22, 2018

That should be it, I believe.

@syclik
Copy link
Member

syclik commented Jan 22, 2018

Thanks, @mcol. When it passes tests, we'll merge.

@bob-carpenter
Copy link
Contributor

bob-carpenter commented Jan 23, 2018 via email

@mcol
Copy link
Contributor Author

mcol commented Feb 2, 2018

Having fixed the missing header issue, can this be merged now (the jenkins failure seems to be unrelated to this)?

@bob-carpenter
Copy link
Contributor

Sorry, but we want to fix tests before merging. Changing something like the boundary conditions on functions could affect the distribution tests.

It looks like Jenkins hung up on something here. I no longer know offhand how to restart it and there's not an obvious button. I think @seantalts wrote some doc somewhere, but searching and the index on our top-level wiki leave me to this old page: https://github.com/stan-dev/stan/wiki/Jenkins

@seantalts
Copy link
Member

http://discourse.mc-stan.org/t/new-jenkins-jobs-tutorial/2383 though most of this is using the old UI, which you can get to with the exit button

I restarted the tests already.

@seantalts
Copy link
Member

Interesting - the distribution tests failed again for this PR. I haven't seen this disconnect error yet on develop or other PRs. Can anyone run the distribution tests locally? I'm using my cores for another simulation, haha.

I will also try running this again on Jenkins with lower N_TESTS and see if that helps.

@seantalts
Copy link
Member

So it passed with N_TESTS=500 instead of 1000, but I don't have a good answer for why this PR increased the load on the C++ compiler. Curiously, the distribution test run for this PR didn't even seem to take any longer than a regular run with N_TESTS=1000. Should we merge?

@bob-carpenter bob-carpenter merged commit 514dc1b into stan-dev:develop Feb 3, 2018
@mcol mcol deleted the fix_681 branch June 29, 2018 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants