Search code examples
cudauniquethrust

thrust::unique on float3 tuple


I'm trying to use thrust::unique over float3 tuples. However, it seems that it is not returning correct results. Here is a full example:

#include <iostream>
#include <thrust/tuple.h>
#include <thrust/device_vector.h>
#include <thrust/unique.h>

// --- Equality between two float3's
__host__ __device__ __forceinline__ bool operator==(const float3 &a, const float3 &b) { 
    return ((a.x == b.x) && (a.y == b.y) && (a.z == b.z)); }

// --- Binary predicate for a tuple pair
typedef thrust::tuple<float3, float3> tuple_t;
struct tupleEqual
{
    __host__ __device__
        bool operator()(tuple_t x, tuple_t y)
    {
        return ((x.get<0>() == y.get<0>()) && (x.get<1>() == y.get<1>()));
    }
};

/********/
/* MAIN */
/********/
int main(void)
{
    const int N = 6;

    thrust::device_vector<float3> v(N), d(N);
    thrust::device_vector<tuple_t> vd(N);

    v[0] = make_float3(2.f, 5.f, 9.f);      d[0] = make_float3(2.f, 3.f, 10.f);
    v[1] = make_float3(3.f, 2.f, 1.f);      d[1] = make_float3(2.f, 5.f, 9.f);
    v[2] = make_float3(2.f, 5.f, 9.f);      d[2] = make_float3(2.f, 3.f, 10.f);
    v[3] = make_float3(2.f, 3.f, 10.f);     d[3] = make_float3(2.f, 5.f, 9.f);
    v[4] = make_float3(2.f, 3.f, 10.f);     d[4] = make_float3(1.f, 1.f, 1.f);
    v[5] = make_float3(2.f, 5.f, 9.f);      d[5] = make_float3(2.f, 3.f, 10.f);

    vd[0] = thrust::make_tuple(v[0], d[0]);
    vd[1] = thrust::make_tuple(v[1], d[1]);
    vd[2] = thrust::make_tuple(v[2], d[2]);
    vd[3] = thrust::make_tuple(v[3], d[3]);
    vd[4] = thrust::make_tuple(v[4], d[4]);
    vd[5] = thrust::make_tuple(v[5], d[5]);

    auto new_end = thrust::unique(vd.begin(), vd.end(), tupleEqual());

    const size_t Nnew = new_end - vd.begin();

    printf("Nnew = %d\n", Nnew);
    for (int k = 0; k < Nnew; k++) {
        tuple_t temp = vd[k];
        float3 vtemp = thrust::get<0>(temp);
        float3 dtemp = thrust::get<1>(temp);
        printf("%d %f %f %f %f %f %f\n", k, vtemp.x, vtemp.y, vtemp.z, dtemp.x, dtemp.y, dtemp.z);
    }

    return 0;
}

The result I'm obtaining is

Nnew = 6
0 2.000000 5.000000 9.000000 2.000000 3.000000 10.000000
1 3.000000 2.000000 1.000000 2.000000 5.000000 9.000000
2 2.000000 5.000000 9.000000 2.000000 3.000000 10.000000
3 2.000000 3.000000 10.000000 2.000000 5.000000 9.000000
4 2.000000 3.000000 10.000000 1.000000 1.000000 1.000000
5 2.000000 5.000000 9.000000 2.000000 3.000000 10.000000

which is exactly the input without any duplicate removal.

I'm compiling with Windows 10, Visual Studio 2015, using either CUDA 8.0 or CUDA 9.1 (the result is the same).

My question is: what I'm doing wrong?


Solution

  • The reason that you don't see any change in the output is that your inputs do no contain any duplicate sequences. If I modify the input in your code:

    #include <iostream>
    #include <thrust/tuple.h>
    #include <thrust/device_vector.h>
    #include <thrust/unique.h>
    
    __host__ __device__ __forceinline__ bool operator==(const float3 &a, const float3 &b) { 
        return ((a.x == b.x) && (a.y == b.y) && (a.z == b.z)); }
    
    typedef thrust::tuple<float3, float3> tuple_t;
    struct tupleEqual
    {
        __host__ __device__
            bool operator()(tuple_t x, tuple_t y)
        {
            return ((x.get<0>() == y.get<0>()) && (x.get<1>() == y.get<1>()));
        }
    };
    
    int main(void)
    {
        const int N = 6;
    
        thrust::device_vector<float3> v(N), d(N);
        thrust::device_vector<tuple_t> vd(N);
    
        v[0] = make_float3(2.f, 5.f, 9.f);      d[0] = make_float3(2.f, 3.f, 10.f);
        v[1] = make_float3(2.f, 5.f, 9.f);      d[1] = make_float3(2.f, 3.f, 10.f);
        v[2] = make_float3(2.f, 3.f, 10.f);     d[2] = make_float3(2.f, 5.f, 9.f);
        v[3] = make_float3(2.f, 3.f, 10.f);     d[3] = make_float3(2.f, 5.f, 9.f);
        v[4] = make_float3(2.f, 3.f, 10.f);     d[4] = make_float3(2.f, 5.f, 9.f);
        v[5] = make_float3(2.f, 3.f, 10.f);     d[5] = make_float3(2.f, 5.f, 9.f);
    
        vd[0] = thrust::make_tuple(v[0], d[0]);
        vd[1] = thrust::make_tuple(v[1], d[1]);
        vd[2] = thrust::make_tuple(v[2], d[2]);
        vd[3] = thrust::make_tuple(v[3], d[3]);
        vd[4] = thrust::make_tuple(v[4], d[4]);
        vd[5] = thrust::make_tuple(v[5], d[5]);
    
        auto new_end = thrust::unique(vd.begin(), vd.end(), tupleEqual());
        const size_t Nnew = new_end - vd.begin();
    
        printf("Nnew = %zu\n", Nnew);
        for (int k = 0; k < Nnew; k++) {
            tuple_t temp = vd[k];
            float3 vtemp = thrust::get<0>(temp);
            float3 dtemp = thrust::get<1>(temp);
            printf("%d %f %f %f %f %f %f\n", k, vtemp.x, vtemp.y, vtemp.z, dtemp.x, dtemp.y, dtemp.z);
        }
    
        return 0;
    }
    

    so that it contains sequences of identical inputs, then the removal works as expected:

    $ nvcc -arch=sm_52 -std=c++11 -o float3 float3.cu
    $ ./float3
    Nnew = 2
    0 2.000000 5.000000 9.000000 2.000000 3.000000 10.000000
    1 2.000000 3.000000 10.000000 2.000000 5.000000 9.000000
    

    thrust::unique only removes repetitions of identical sequences in the input iterators. It does not sort. Quoting from the documentation:

    For each group of consecutive elements in the range [first, last) with the same value, unique removes all but the first element of the group.

    Emphasis mine. Your only error here is one of understanding of the operation the function performs. The code you have written is correct and works as expected.