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

Issue 123 #643

Merged
merged 9 commits into from
Feb 8, 2018
Merged

Issue 123 #643

merged 9 commits into from
Feb 8, 2018

Conversation

bgoodri
Copy link
Contributor

@bgoodri bgoodri commented Oct 8, 2017

Submission Checklist

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

Summary:

Define some kinda safe var constructors when the input is std::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 a var from the real part. For the case where the input is std::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:

@seantalts
Copy link
Member

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?

@bgoodri
Copy link
Contributor Author

bgoodri commented Oct 13, 2017 via email

Copy link
Contributor

@bob-carpenter bob-carpenter left a 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.

*
* @param x Value of the variable.
*/
explicit var(std::complex<double> x) {
Copy link
Contributor

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).

Copy link
Contributor Author

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>’

Copy link
Contributor Author

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.

* @param x Value of the variable.
*/
explicit var(std::complex<double> x) {
assert(imag(x) == 0);
Copy link
Contributor

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);
}

* @param x Value of the variable.
*/
explicit var(std::complex<float> x) {
assert(imag(x) == 0);
Copy link
Contributor

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

* @param x Value of the variable.
*/
explicit var(std::complex<long double> x) {
assert(imag(x) == 0);
Copy link
Contributor

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

* @param x Value of the variable.
*/
explicit var(std::complex<var> x) {
assert(false);
Copy link
Contributor

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);

Copy link
Contributor Author

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.

Copy link
Contributor

@bob-carpenter bob-carpenter Oct 14, 2017

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.

@@ -7,6 +7,9 @@
#include <boost/math/tools/config.hpp>
#include <ostream>
#include <vector>
#include <complex>
#include <cassert>
#include <limits>
Copy link
Contributor

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.

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.

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>
Copy link
Member

Choose a reason for hiding this comment

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

Need to include stdexcept.

@syclik
Copy link
Member

syclik commented Oct 20, 2017

@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.

@bob-carpenter
Copy link
Contributor

@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 vari by calling other functions that can throw exceptions.

Is there something I should be worried about here?

@syclik
Copy link
Member

syclik commented Oct 21, 2017

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!

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!

@bob-carpenter
Copy link
Contributor

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.

@seantalts
Copy link
Member

@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.

@seantalts
Copy link
Member

Woops, my bad - #622 and #533 also have conflicts from the test formatting. Still, they should be easy enough to fix and one of those had been sitting there for a while.

@seantalts
Copy link
Member

Jenkins, retest this please.

@seantalts
Copy link
Member

Should we merge this?

@bgoodri
Copy link
Contributor Author

bgoodri commented Feb 8, 2018

I think it does not hurt anything, but it does not help all that much.

@seantalts
Copy link
Member

Okay, I'll merge it now that the tests are passing. No sense in keeping it around in limbo.

@seantalts seantalts merged commit 5ea9efa into develop Feb 8, 2018
@seantalts seantalts deleted the issue-123 branch February 8, 2018 20:18
@bob-carpenter
Copy link
Contributor

bob-carpenter commented Mar 5, 2018

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?

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