Это можно сделать с помощью одной операции sort_by_key с последующей «перестановкой» отсортированных значений.
«Ключи» (вещи, по которым мы сортируем) будут представлять собой zip_iterator, объединяющий ваши фактические значения для сортировки и индикатор строки. Функтор сортировки будет упорядочен для сортировки сначала по строке, а затем по значению в строке. «Значения» (вещи, которые перемещаются вместе с ключами) будут индексом столбца в каждой строке.
Эти "значения" после сортировки могут быть переставлены в наш показатель "ранга" в строке.
Мы можем использовать числовой пример после 3-й строки вашей матрицы:
до сортировки:
keys: [2, 4, 2, 0]
values: [0, 1, 2, 3]
после сортировки:
keys: [0, 2, 2, 4]
values: [3, 0, 2, 1]
до перегруппировки:
destination map: [3, 0, 2, 1]
values: [0, 1, 2, 3]
после перегруппировки:
destination: [1, 3, 2, 0]
(как это бывает, для первых двух строк в вашем примере, нет никаких изменений между отсортированными значениями и целевыми значениями)
Вот рабочий пример:
$ cat t1633.cu
#include <iostream>
#include <thrust/sort.h>
#include <thrust/device_vector.h>
#include <thrust/iterator/zip_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/permutation_iterator.h>
#include <thrust/copy.h>
typedef int dtype;
// creates row indices:
// 0 0 0 0 ...
// 1 1 1 1 ...
// 2 2 2 2 ...
struct row_f : public thrust::unary_function<int, int>
{
int ncols;
row_f(int _nc) : ncols(_nc) {};
__host__ __device__
int operator()(int i){
return i/ncols;}
};
// creates column indices:
// 0 1 2 3 ...
// 0 1 2 3 ...
// 0 1 2 3 ...
struct col_f : public thrust::unary_function<int, int>
{
int ncols;
col_f(int _nc) : ncols(_nc) {};
__host__ __device__
int operator()(int i){
return i%ncols;}
};
struct map_f : public thrust::unary_function<thrust::tuple<int, int>, int>
{
int ncols;
map_f(int _nc) : ncols(_nc) {};
__host__ __device__
int operator()(thrust::tuple<int, int> t){
return thrust::get<0>(t) + ncols*thrust::get<1>(t);}
};
struct sort_f
{
template <typename T1, typename T2>
__host__ __device__
bool operator()(T1 k1, T2 k2){
// sort by row first
if (thrust::get<1>(k1) < thrust::get<1>(k2)) return true;
if (thrust::get<1>(k1) > thrust::get<1>(k2)) return false;
// then sort within the row
if (thrust::get<0>(k1) < thrust::get<0>(k2)) return true;
return false;}
};
int main(){
// example data setup
dtype keys[] = {5, 4, 1, 9, 1, 4, 3, 2, 2, 4, 2, 0};
int nrows = 3;
int ds = sizeof(keys)/sizeof(keys[0]);
int ncols = ds/nrows;
thrust::device_vector<dtype> d_keys(keys, keys+ds);
// create values to be moved which is effectively index within row, i.e. column indices
thrust::device_vector<int> d_vals(nrows*ncols);
thrust::sequence(d_vals.begin(), d_vals.end());
thrust::transform(d_vals.begin(), d_vals.end(), d_vals.begin(), col_f(ncols));
// sort
thrust::sort_by_key(thrust::make_zip_iterator(thrust::make_tuple(d_keys.begin(), thrust::make_transform_iterator(thrust::counting_iterator<int>(0), row_f(ncols)))), thrust::make_zip_iterator(thrust::make_tuple(d_keys.end(), thrust::make_transform_iterator(thrust::counting_iterator<int>(nrows*ncols), row_f(ncols)))), d_vals.begin(), sort_f());
// rearrange
thrust::device_vector<int> d_rank(nrows*ncols);
thrust::copy_n(thrust::make_transform_iterator(thrust::counting_iterator<int>(0), col_f(ncols)), nrows*ncols, thrust::make_permutation_iterator(d_rank.begin(), thrust::make_transform_iterator(thrust::make_zip_iterator(thrust::make_tuple(d_vals.begin(), thrust::make_transform_iterator(thrust::counting_iterator<int>(0), row_f(ncols)))), map_f(ncols))));
// print results
thrust::copy_n(d_rank.begin(), ncols*nrows, std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
$ nvcc -o t1633 t1633.cu
$ ./t1633
2,1,0,3,0,3,2,1,1,3,2,0,
$