First, this is meant to be a discussion. So please, if you have an opinion or question or anything else, please respond.
This is stemming from recent discussions on the stan-dev/math repo. There are discussions about removing error checking of functions for speed. The latest discussion is here: https://github.com/stan-dev/math/issues/1468.
I realized that we haven’t really written out some of the guiding principles for the Math library. And what may have held in the past may not hold now.
I think it helps to think about the preconditions and postconditions of all the Stan Math functions. (This is design by contract terminology used in software development.) For each of the Math functions, we’ve been operating this way:
- None. Well… to be more specific:
- Input must match the function signature. This is given to us for free by C++.
- If using autodiff variables, they must be properly constructed and still on the autodiff stack.
- If the function did not throw an exception, the result is valid and can be trusted.
- If the function threw an exception, the autodiff stack isn’t meant to be in a good state.
And some general principles:
- since the main client is Stan, propagate an error message as soon as we can with a sensible message so the user knows what’s going on
- when there’s an error, we’re already in an exceptional case and it’s worth taking the time to write a reasonable message
- be as fast as we can.
In the past, we’ve tried to track down which error checks took a long time and we couldn’t find simple ones that do in a practical use case. Once we’re using the autodiff stack and doing anything real, the error checks don’t matter so much in execution time, but they really help from a user’s point of view.
So… that’s where we’re at now. If a function doesn’t throw, you can assume it works. There are a couple edge cases to this: the standard template library doesn’t throw and returns NaN.
There’s been talk about removing some of the checks for the sake of speed. My take:
- I don’t know how much speed we’ll gain. But we should time it and see. If it’s significant, we should consider.
- I don’t think we should leave the Math library in an inconsistent state. I think once we start removing checks, we should just state that the postcondition is that it if it doesn’t throw, it’s not guaranteed to have been the correct result and that still needs to be checked. And while we’re doing that, we could change the precondition to only allow things within the domain of the function.
Anyone with thoughts on where we want to go with the Math library? And how to stage it properly so we can keep the contract to something sensible?