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

DM-14740: Stop using ndarray::EigenView indirectly in C++ code #20

Merged
merged 1 commit into from Jun 18, 2018

Conversation

r-owen
Copy link
Contributor

@r-owen r-owen commented Jun 8, 2018

Replace ndarray::Array::asEigen, which returns an ndarray::EigenView,
with ndarray::asEigen, which returns an Eigen::Map

@r-owen r-owen force-pushed the tickets/DM-14740 branch 4 times, most recently from 2bf77d7 to aae840e Compare June 13, 2018 13:53
Copy link
Member

@kfindeisen kfindeisen left a comment

Choose a reason for hiding this comment

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

Looks good, a few minor suggestions.

@@ -52,9 +52,9 @@ double buildModelsImpl(
ndarray::Array<T,2,-2> matrix = matrixT.transpose();
double result = 0.0;
for (ndarray::Array<double const,2>::Iterator i = parameters.begin(); i != parameters.end(); ++i) {
ellipse.setParameterVector(i->asEigen());
ellipse.setParameterVector(ndarray::asEigenMatrix(*i));
Copy link
Member

Choose a reason for hiding this comment

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

You could avoid the awkward *i with a foreach loop.

Copy link
Member

@TallJimbo TallJimbo Jun 18, 2018

Choose a reason for hiding this comment

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

I would hold off on this for now, or at least be very careful testing that change. This just reminded me that https://github.com/ndarray/ndarray/blob/master/include/ndarray/detail/NestedIterator.h#L43 might need to be changed to the InputIterator concept equivalent, and not doing so might get in the way of higher-dimensionsal ndarray iterators working properly in range-based for.

Long version of the story is that proxy iterators in C++ aren't allowed to be RandomAccessIterators (or even ForwardIterators), due to a really annoying edge case in the standard. Once upon a time, Boost.Iterator and it's not-quite-the-same-as-the-standard's iterator categories were seen as the way towards a future version of the standard without those (and other) problems and that's why I used them here. AFAIK, that idea has gone by the wayside - I haven't seen anything about fixing proxy iterators in the latest standards (though I haven't paid super close attention) - and I think we just need to stop promising that these are RandomAccessIterators, even though they can do 99% of the things a RandomAccessIterator can do.

Sorry to dump that on you right before I disappear for a week, and I'm even more sorry I didn't fix this earlier - it is a serious problem, but I keep remembering it right when I can't fix it immediately. But for now, stay away from range-based for on 2-d or higher ndarray iterators.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I don't understand: as far as I can tell for-each loops should work just fine on input iterators, since they only need inequality, increment, and dereference capability. They certainly don't need random access.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll revert the change.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, for-each will work on either kind of iterator, but the compiler is allowed to do different things under the hood in the two cases, and the code it generates for random-access iterators may yield incorrect behavior if the iterator really only models input iterator. I have never really nailed down what is going on here (doing that is part of why I never got around to fixing it, though I really should just do it and investigate later), but I have seen at least one case (in an early version of afw::geom::SpanSet) where having an incorrect category for a proxy iterator led to some weird behavior in a range-based for, and that weird behavior went away if we either fixed the category or switched to a regular for loop (obviously the former is the right fix, but it always bothered me that I couldn't figure out what the range-based for was doing to cause trouble).

@@ -479,7 +479,7 @@ class RemappedShapeletImpl : public ShapeletImpl<T> {
_ellipse.scale(_radius);
_lhs.setZero();
ShapeletImpl<T>::buildMatrix(_lhs, _ellipse);
output.asEigen() += _lhs.matrix() * _remapMatrix.transpose(); // undo the transpose in the ctor
ndarray::asEigenMatrix(output) += _lhs.matrix() * _remapMatrix.transpose(); // undo the transpose in ctor
Copy link
Member

Choose a reason for hiding this comment

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

Consider moving the comment to above this line instead of abbreviating it.

result.getComponents().back().getCoefficients().asEigen()
= i->getMatrix().asEigen() * coefficients.asEigen();
ndarray::asEigenMatrix(result.getComponents().back().getCoefficients())
= ndarray::asEigenMatrix(i->getMatrix()) * ndarray::asEigenMatrix(coefficients);
Copy link
Member

Choose a reason for hiding this comment

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

I realize this is outside the scope of the ticket, but do you even need Eigen here?

Copy link
Contributor Author

@r-owen r-owen Jun 18, 2018

Choose a reason for hiding this comment

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

I think the eigen matrix map is being used for the matrix multiplication

@@ -169,13 +169,14 @@ ShapeletFunctionEvaluator::ShapeletFunctionEvaluator(
ShapeletFunction ShapeletFunction::convolve(ShapeletFunction const & other) const {
GaussHermiteConvolution convolution(getOrder(), other);
afw::geom::ellipses::Ellipse newEllipse(_ellipse);
auto matrix = convolution.evaluate(newEllipse).asEigen();
auto matrixNdArray = convolution.evaluate(newEllipse);
auto matrix = ndarray::asEigenMatrix(matrixNdArray);
Copy link
Member

Choose a reason for hiding this comment

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

Consider adding a comment saying that matrixNdArray must be a local variable, to prevent accidental refactoring. Though since matrix is only used at one point, you could also just do the evaluation and conversion there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I'll eliminate the matrix temporary.

Replace ndarray::Array::asEigen, which returns an ndarray::EigenView,
with ndarray::asEigen, which  returns an Eigen::Map
@r-owen r-owen merged commit b532a05 into master Jun 18, 2018
@ktlim ktlim deleted the tickets/DM-14740 branch August 25, 2018 05:56
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.

None yet

3 participants