Reputation: 26256
I used to take the sum of a symmetric (Hermitian) matrix (the matrix is in an std::vector
), which is a huge waste because the imaginary part will always add to zero (I'm talking about huge matrices with side-length of n>1000
, so I think it makes a difference.
So now I only add the upper-triangular part of the matrix. But I want to optimize this further and avoid adding the complex part because I don't need it.
What I currently use is:
std::real(std::accumulate(myMatrix.begin(), myMatrix.end(), std::complex<Real>(0,0)));
This adds all the element of myMatrix
to std::complex<Real>(0,0)
, resulting in the sum I need.
But this will add real and imaginary components of my vector, which is a waste! How can I write the most optimized version of this that adds only the real part of this matrix?
UPDATE:
While I accepted the answer that works, I discovered that it's slower than summing the real and imaginary parts of my matrix. It's slower by 5%-10% for a matrix with side-length of 128. This is surprising. Any other faster suggestions are highly appreciated.
Please ask if you require additional information.
Upvotes: 4
Views: 1559
Reputation: 62975
Real real_sum = std::accumulate(
myMatrix.cbegin(), myMatrix.cend(), Real{},
[](Real const acc, std::complex<Real> const& c) { return acc + std::real(c); }
);
Upvotes: 8
Reputation: 10770
Is using std::accumulate
absolutely necessary?
Real acc = 0;
for(auto c : myMatrix)
acc += real(c);
Don't get me wrong, I'm in favour of using standard algorithms when they suit, but it seems hard to beat this loop in readability.
This is as compared to the implementation that came with my g++-4.8.4 install:
template<typename _InputIterator, typename _Tp>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__init = __init + *__first;
return __init;
}
So you can see they're pretty much doing the same thing.
Upvotes: 1
Reputation: 302852
std::accumulate
has two overloads, one of which takes an operator:
template< class InputIt, class T, class BinaryOperation >
T accumulate( InputIt first, InputIt last, T init,
BinaryOperation op );
Just provide your own instead of defaulting to +
:
std::accumulate(myMatrix.begin(), myMatrix.end(), Real{0},
[](Real const& sum, std::complex<Real> const& next){
return sum + std::real(next);
});
Alternatively, you could do something fun like use boost::transform_iterator
:
auto make_real = [](std::complex<Real> const& c) {
return std::real(c);
};
std::accumulate(
boost::make_transform_iterator(myMatrix.begin(), make_real),
boost::make_transform_iterator(myMatrix.end(), make_real),
Real{0});
Or with range-v3:
accumulate(myMatrix,
Real{0},
std::plus<>{},
[](std::complex<Real> const& c) { return c.real(); }
);
Both of the above would be much nicer if real
didn't have overloads and you could just provide std::real<Real>
in the boost example and &std::complex<Real>::real
in the second.
Upvotes: 4