-
-
Notifications
You must be signed in to change notification settings - Fork 187
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
Issue 123 #643
Conversation
It seems like we never use |
There is basically no danger of a complex number accidentally being created
by the user. The only danger is that Eigen, Boost, CVODES, etc. use complex
numbers for internal calculations, which would mess up the autodiff at a
minimum. Eigen is known to do this, so I wanted to catch it in the tests.
…On Thu, Oct 12, 2017 at 8:30 PM, seantalts ***@***.***> wrote:
It seems like we never use cassert's assert() in the math library - do we
normally use something else for this kind of error handling? It seems like
maybe this could happen at runtime for a user if we allowed complex
parameters - should we throw an exception with an error message?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#643 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADOrqsZgiffCSC5UMG5ReAT3qDS7WtrTks5srq8lgaJpZM4Pxzeb>
.
|
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.
Complex arguments should be passed by constant reference.
The asserts should be exceptions now that the construtor is in the math library. That will make sure we don't hit some new execution branch of Eigen at runtime and crash R or Python and will also make it safe for clients of the math library.
Otherwise, the functionality and doc looks great.
stan/math/rev/core/var.hpp
Outdated
* | ||
* @param x Value of the variable. | ||
*/ | ||
explicit var(std::complex<double> 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.
Instances of complex
should be passed by constant reference, var(const std::complex<double>& 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.
This change results in a compiler error
error: cannot bind non-const lvalue reference of type ‘std::complex<long double>&’ to an rvalue of type ‘std::complex<long 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.
Nevermind. I forgot the const
.
stan/math/rev/core/var.hpp
Outdated
* @param x Value of the variable. | ||
*/ | ||
explicit var(std::complex<double> x) { | ||
assert(imag(x) == 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.
This should be an exception. Although users of the Stan language won't explicitly call it, it's going to be a constructor in the math library.
I don't know how much printing for complex is built in, but maybe
if (imag(x) == 0) {
std::stringstream ss;
ss << "Imaginary part of std::complex used to construct var must be zero."
<< " Found argument = " << x << std::endl;
std::string msg = ss.str();
throw std::invalid_argument(msg);
}
stan/math/rev/core/var.hpp
Outdated
* @param x Value of the variable. | ||
*/ | ||
explicit var(std::complex<float> x) { | ||
assert(imag(x) == 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.
same comment as before on pass-by-reference and exception
stan/math/rev/core/var.hpp
Outdated
* @param x Value of the variable. | ||
*/ | ||
explicit var(std::complex<long double> x) { | ||
assert(imag(x) == 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.
same comment as previous on exception and pass by reference
stan/math/rev/core/var.hpp
Outdated
* @param x Value of the variable. | ||
*/ | ||
explicit var(std::complex<var> x) { | ||
assert(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.
I don't think you need this constructor at all. If you try to call var(complex<var>(2,0))
it won't have a constructor to match, so the compiler will catch it. If complex<T>
can be cast to T
(I'm not sure, but I'd be surprised), then you want to declare this constructor but not define it. That will prevent it from being instantiated at compile time.
explicit var(const std::complex<var>& 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.
This does not prevent the segfault in #123 but neither does filling in the body to only throw an exception. I don't know what to do but am pushing anyway.
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 segfault in #123 is from constructing std::complex<var>(var(1))
not var(std::complex(1,0))
. Are they related?
I don't think we can specialize the complex<T>
constructor to complex<var>
to fail.
stan/math/rev/core/var.hpp
Outdated
@@ -7,6 +7,9 @@ | |||
#include <boost/math/tools/config.hpp> | |||
#include <ostream> | |||
#include <vector> | |||
#include <complex> | |||
#include <cassert> | |||
#include <limits> |
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 didn't see anywhere <limits>
gets used.
You won't need <cassert>
after changing the asserts to exceptions.
still does not prevent segfault
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.
Just noticed that it's missing stdexcept
@@ -7,6 +7,8 @@ | |||
#include <boost/math/tools/config.hpp> | |||
#include <ostream> | |||
#include <vector> | |||
#include <complex> | |||
#include <string> |
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.
Need to include stdexcept
.
@bob-carpenter, Ben has walked through all the changes. Want to approve? I noticed that there's an exception being thrown in the constructor -- we good with that? I don't see us using this yet, but I just want to make sure that's the right pattern to be using for errors. |
@syclik I approved it and will let you take over the final review. I don't see a problem with throwing exceptions in constructors. Lots of our constructors already do that implicitly for Is there something I should be worried about here? |
Nope. I just wanted to make sure I hadn't considered something obvious with exceptions in constructors. @bgoodri, add the include and we're good! |
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!
I thought this and many of these others were merged. This is a direct conflict with the cpplint fix for tests and it's going to hit all 15 or so outstanding math pull requests as they changed lots of lines. |
@bob-carpenter Git is a lot better at merging than Subversion was - the only outstanding pull request that has a conflict due to the test folder reformatting is mine for removing row vector distribution tests. There are 2 others with unrelated conflicts. I don't think we want to try to coordinate PR merging to try to avoid conflicts - this has been a big waste of time in my professional life whenever it was attempted. |
Jenkins, retest this please. |
Should we merge this? |
I think it does not hurt anything, but it does not help all that much. |
Okay, I'll merge it now that the tests are passing. No sense in keeping it around in limbo. |
I deleted my last comment after talking to @seantalts and figuring out what's really going on. @ChrisChiasson is right in that this PR as merged doesn't directly address issue #123. I think in this case, the PR is as intended, whereas the issue was poorly specified. The one question I have about it is that it doesn't just throw, but actually lets you construct an autodiff variable from a complex number with zero imaginary component. That was not the feature request. Is this really the right fix? |
Submission Checklist
./runTests.py test/unit
make cpplint
Summary:
Define some kinda safe
var
constructors when the input isstd::complex
. Eigen uses complex numbers internally for some calculations, so we want to find out if we are accidentally using them.Intended Effect:
For the case where the input is
std::complex
but the imaginary part is zero, create avar
from the real part. For the case where the input isstd::complex<var>
, just fail.How to Verify:
There are some tests for the non-failing case. I don't know how to test the failing cases because they call
assert
.Side Effects:
Fixes #123 sort of.
R may complain, but at least on Linux the compiler seems to get rid of these constructors when they are not used.
Documentation:
None
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: