argsort in Thrust - c++

Is it legal to use thrust::sort_by_key like in following code?
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/sort.h>
#include <thrust/advance.h>
#include <thrust/copy.h>
#include <iterator>
#include <iostream>
int main()
{
int init[] = {2, 0, 1, 3, 4};
const thrust::device_vector< int > v{std::cbegin(init), std::cend(init)};
thrust::device_vector< std::intptr_t > index{v.size()};
thrust::sequence(index.begin(), index.end());
auto key =
thrust::make_permutation_iterator(
thrust::make_transform_iterator(
v.cbegin(),
thrust::identity< thrust::tuple< int > >{}),
index.cbegin());
thrust::sort_by_key(
key,
thrust::next(key, index.size()),
index.begin());
thrust::copy(
index.cbegin(), index.cend(),
std::ostream_iterator< std::intptr_t >(std::cout, ", "));
std::cout << std::endl;
}
Here index array points to v array of values. I want to have a "sorted view" of v.index after above sorting is the view, that is [v[i] for i in index] (pythonic pseudocode) is sorted.
The trick with identity transformation is crucial here: it transform values pointed to by index in v to one-element-tuple. thrust::tuple is a class and have operator =, which is not cv-ref-qualified for lvalues only and thus can be used on rvalues just returned as a result of dereferencing of the transform_iterator. thrust::tuple< int >(1) = 2; is a legal statement and effectively is a no-op, because left hand side value dropped right after the assignment. As a result key swaps in sort_by_key are all no-ops and real sorting occurs in "values" part of key-value sorting. Also not, that v is immutable here (result v.cbegin() is const iterator).
As I know, developers of Thrust generally assume, that all callables are idempotent. I believe the assumption is not violated here, because only the argument of the callable (thrust::identity) changed, not a state of the callable. But on the other hand any superposition of Thrust fancy iterators can be considered as a composition of a functions (say, permutation_iterator is a simple mapping).
In sort_by_key index is read and written. It can be prohibited by implicit rules of implementation. Is it correct code?

Related

Copy vector of vectors into 1D array

I have following C++ object
std::vector<std::vector<SomeClass>> someClassVectors(sizeOFOuter);
where I know the size of "outer" vector, but sizes of "inner" vectors varies. I need to copy the elements of this structure into 1D array like this:
SomeClass * someClassArray;
I have a solution where I use std::copy like this
int count = 0;
for (int i = 0; i < sizeOfOuter; i++)
{
std::copy(someClassVectors[i].begin(), someClassVectors[i].end(), &someClassArray[count]);
count += someClassVectors[i].size();
}
but the class includes large matrices which means I cannot have the "vectors" structure and 1D array allocated twice at the same time.
Any ideas?
Do you previously preallocate someClassArray to a given size? I'd suggest using 1D vector for getting rid of known problems with the plain array if possible.
what about something like this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::vector<std::vector<int>> someClassVectors {
{1,2,3},
{4,5,6},
{7,8,9}
};
std::vector<int> flat;
while (!someClassVectors.empty())
{
auto& last = someClassVectors.back();
std::move(std::rbegin(last), std::rend(last), std::back_inserter(flat));
someClassVectors.pop_back();
}
std::reverse(std::begin(flat), std::end(flat));
int * someClassArray = flat.data();
std::copy(someClassArray, someClassArray + flat.size(), std::ostream_iterator<int>(std::cout, " "));
}
The extra reverse operation doesn't have an effect on memory metrics - such an approach helps to avoid unneeded memory reallocations resulting from removing vector elements from beginning to end.
EDIT
Inspired by comments I changed copy to move semantics
Embrace Range-v3 (or whatever will be introduced in C++20) and write a solution in (almost) a single line:
auto flattenedRange = ranges::views::join(someClassVectors);
this gives you a range in flattenedRange, which you can loop over or copy somewhere else easily.
This is a possible use case:
#include <iostream>
#include <vector>
#include <range/v3/view/join.hpp>
int main()
{
std::vector<std::vector<int>> Ints2D = {
{1,2,3},
{4},
{5,6}
};
auto Ints1D = ranges::views::join(Ints2D);
// here, going from Ints1D to a C-style array is easy, and shown in the other answer already
for (auto const& Int : Ints1D) {
std::cout << Int << ' ';
}
std::cout << '\n';
// output is: 1 2 3 4 5 6
}
In case you want to get a true std::vector instead of a range, before writing it into a C-style array, you can include this other header
#include <range/v3/range/conversion.hpp>
and pipe join's output into a conversion function:
auto Ints1D = ranges::views::join(Ints2D) | ranges::to_vector;
// auto deduces std::vector<int>
In terms of standard and versions, it doesn't really require much. In this demo you can see that it compiles and runs just fine with
compiler GCC 7.3
library Range-v3 0.9.1
C++14 standard (option -std=c++14 to g++)
As regards the copies
ranges::views::join(Ints2D) is only creating a view on Ints2D, so no copy happens; if view doesn't make sense to you, you might want to give a look at Chapter 7 from Functional Programming in C++, which has a very clear explanation of ranges, with pictures and everything;¹
even assigning that output to a variable, auto Ints1D = ranges::views::join(Ints2D);, does not trigger a copy; Ints1D in this case is not a std::vector<int>, even though it behaves as one when we loop on it (behaves as a vector because it's a view on it);
converting it to a vector, e.g. via | ranges::to_vector, obviously triggers a copy, because you are no more requesting a view on a vector, but a true one;
passing the range to an algorithm which loops on its elements doesn't trigger a copy.
Here's an example code that you can try out:
// STL
#include <iostream>
#include <vector>
// Boost and Range-v3
#include <boost/range/algorithm/for_each.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/range/conversion.hpp>
struct A {
A() = default;
A(A const&) { std::cout << "copy ctor\n"; };
};
int main()
{
std::vector<std::vector<A>> Ints2D = {
{A{},A{}},
{A{},A{}}
};
using boost::range::for_each;
using ranges::to_vector;
using ranges::views::join;
std::cout << "no copy, because you're happy with the range\n";
auto Ints1Dview = join(Ints2D);
std::cout << "copy, because you want a true vector\n";
auto Ints1D = join(Ints2D) | to_vector;
std::cout << "copy, despite the refernce, because you need a true vector\n";
auto const& Ints1Dref = join(Ints2D) | to_vector;
std::cout << "no copy, because we movedd\n";
auto const& Ints1Dref_ = join(std::move(Ints2D)) | to_vector;
std::cout << "no copy\n";
for_each(join(Ints2D), [](auto const&){ std::cout << "hello\n"; });
}
¹ In an attempt to try giving a clue of what a range is, I would say that you can imagine it as a thing wrapping two iterators, one poiting to the end of the range, the other one pointing to the begin of the range, the latter being incrementable via operator++; this opearator will take care of the jumps in the correct way, for instance, after viewing the element 3 in Ints2D (which is in Ints2D[0][2]), operator++ will make the iterator jump to view the elment Ints[1][0].

C++17 multiplying a list using reduce in conjunction with multiplies doesn't work

I'd like to multiply the elements of a float list using reduce in conjunction with multiplies:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <numeric>
using namespace std;
int main () {
vector<float>series{2, 1.91421, 2.06538, 2.25, 2.43607};
float result = reduce(series.begin(), series.end(), 1, multiplies<float>() );
cout << "result: " << result << endl; // it's 29
// it must have been 43.340291222788287
}
How to do it correctly?
Please note that, I'd like to do it using specifically reduce in conjuction with the multiplies, not any other function such as transform_reduce or any other method, as much as possible.
Note that the interface of reduce() is:
template<class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp binary_op);
Notably, it deduces the type from the initial value and returns that type. You're passing in 1, which is an int. So this returns int and the internal accumulator is an int. That's... not going to work well for obvious reasons (doesn't matter that you're using multiplies<float>, the result gets stored in an int anyway).
You want:
float result = reduce(series.begin(), series.end(), 1.0f, multiplies<float>() );
You don't even really need to specify the type on multiplies, multiplies{} works fine.

Type agnostic abstraction to handle forward and reverse iterators and ranges using the same runtime interface?

By design forward and reverse iterators and ranges are fundamentally different types. This is nice in the compile time optimization that it allows for. Sometimes it would be nice to hide that type difference behind an abstraction that allows them to be passed to the same run-time interface.
Are there any adapters in boost or the stl that make this easy? (ideally but not strictly C++11)
The following code shows both the known/expected failure and the desired hypothetical:
#include <boost/range.hpp>
#include <vector>
using Ints = std::vector<int>;
void real(boost::iterator_range<Ints::iterator> range){}
void fake(boost::agnostic_range<Ints::iterator> range){} // imaginary desired
int main()
{
auto ints = Ints{1,2,3,4,5};
real(boost::make_iterator_range(ints.begin(), ints.end()));
real(boost::make_iterator_range(ints.rbegin(), ints.rend())); // Error
fake(boost::make_agnsotic_range(ints.begin(), ints.end())); // imaginary
fake(boost::make_agnsotic_range(ints.rbegin(), ints.rend())); // imaginary
return 0;
}
Yes! Boost::any_range type erases the iterated object type and exposes only the output type and the iterator access type.
Note that the type erasure here requires a call through a virtual function to dereference the iterator so there's a performance cost there, but as long as non-trivial operations are performed inside the loop, this cost will be likely irrelevant.
BUG WARNING: boost::range had a big bug between ~1.55ish until release 1.74 (2020-08) which would cause access to destroyed items being passed through any_range that would cause UB (undefined behavior/probably crash) The work around to this exists in the code below where you explicitly pass the so-called reference type though the template parameters as const which causes some of the internal machinery to avoid tripping over the mistake.
#include <boost/range/adaptor/type_erased.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/any_range.hpp>
#include <vector>
#include <list>
#include <iostream>
// note const int bug workaround
using GenericBiDirIntRange =
boost::any_range<int, boost::bidirectional_traversal_tag, const int>;
void possible(GenericBiDirIntRange const &inputRange) {
for(auto item: inputRange)
std::cout << item << "\n";
}
// note const int bug workaround
using type_erased_bi =
boost::adaptors::type_erased<int, boost::bidirectional_traversal_tag, const int>;
using reversed = boost::adaptors::reversed;
auto main() -> int {
auto intVec = std::vector<int>{1, 2, 3, 4};
auto intList = std::list<int>{1, 2, 3, 4};
possible(intVec | type_erased_bi());
possible(intList | reversed | type_erased_bi());
return 0;
}

How would one store the result of filtering a vector inside another vector without copying

In C++: Let's say I have a vector const std:vector<MyStruct> that (and its elements) won'tt be modified anymore. Now I want to filter this vector based on some predicate and store the result in some object, because I'd like to iterate over this subset of elements frequently.
Is there a good way to avoid copying MyStructs from the vector into the another vector and how would this be done?
This can be done even with plain STL, using few standard types, reference_wrapper being one particularly important:
#include <iostream>
#include <vector>
#include <functional>
#include <iterator>
#include <algorithm>
int main() {
std::vector<int> cv{0, 1, 2, 3, 4, 5};
std::vector<std::reference_wrapper<int>> fv;
std::copy_if(cv.begin(), cv.end(), std::back_inserter(fv)
, [](int x){ return x % 2; });
for(auto const &v: fv) std::cout << v << '\n';
std::cout << "-----\n";
cv[3] = 42;
for(auto const &v: fv) std::cout << v << '\n';
}
$ g++ meow.cpp && ./a.out
1
3
5
-----
1
42
5
Note how changes in cv reflect in fv. fv stores but references to the original elements, namely, to odd-valued elements of cv, so no copies are performed.

C++: how to find max_element using boost::range?

I am trying to return an iterator to the largest element in a filtered range. Here is what I have so far:
#include <boost/lambda/lambda.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>
#include <iostream>
using namespace boost::adaptors;
using namespace boost::lambda;
using namespace std;
int main ()
{
vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element(x | indexed(0) | filtered(_1>100)); /* problem here */
cout << it.index() << endl;
return 0;
}
I expected the code to print out the index in the vector x which has the largest element (ie 2), but unfortunately it does not compile (Linux 64bit, GCC 4.7.2), the problem being in the line indicated above. The first compilation error I get from the compiler (amongst others) is the following:
/boost/tuple/detail/tuple_basic.hpp:396:36: error: assignment of read-only member ‘boost::tuples::cons::head’
Any ideas what I am doing wrong? Or how else I can achieve what I am trying to do? Thanks in advance!
EDIT:
Changing the problematic line to :
auto it = boost::max_element<boost::return_found>(x | sliced(1,4) | filtered(boost::function<bool(double)>(_1>100)));
seems to return the iterator to the largest element. However, is there a way to check that the iterator is within the range? Comparing it with boost::end(x) gives me an error. The only thing I can think of is to return
auto another_range = boost::max_element<boost::return_found_end>(x | sliced(1,4) | filtered(boost::function<bool(double)>(_1>100)));
and check if boost::empty(another_range). Is this the only option? Thanks.
The specific error you've encountered appears because boost lambdas are not CopyAssignable. Here's a simpler way to achieve the same message:
auto f1 = _1 > 100;
auto f2 = f1;
f2 = f1; // same error
If you provide a CopyAssignable functor to filtered, boost.phoenix (which you should be using anyway, boost.lambda is on the road to deprecation in favor of phoenix), a hand-written struct, or the old faithful std::bind2nd(std::greater<double>(), 100), this line compiles with clang++:
bind2nd demo: http://liveworkspace.org/code/2xKZIf
phoenix demo: http://liveworkspace.org/code/18425g
It fails with gcc due to some boost.concept check, which is probably a bug, but it's a moot point because the result of filtered is boost::filtered_range, whose iterators don't have the .index() member function.
EDIT in response to comment:
comparing iterator into filtered_range with the iterator into the original vector wouldn't work. However, since you used vector, and since it's still accessible, you can compare addresses, since neither indexed nor filtered make copies
#include <vector>
#include <iostream>
#include <cassert>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/phoenix.hpp>
using namespace boost::adaptors;
using namespace boost::phoenix::placeholders;
int main ()
{
std::vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element( x | indexed(0) | filtered(arg1 < 110) );
assert(&x[0] <= &*it && &*it < &x[0] + x.size());
std::cout << "Element " << *it << " is at index " << &*it - &x[0] << '\n';
}
demo http://liveworkspace.org/code/1zBIJ9
Or, for a more general solution, you could transform your vector into a vector of pairs (when boost gets zip adaptor, it could be neatly zipped with counting_range), and carry the original sequence index along with the value through all the transformations.