Cyclops Tensor Framework
parallel arithmetic on multidimensional arrays
bitonic_sort.cxx
Go to the documentation of this file.
1 
8 #include <ctf.hpp>
9 #include <float.h>
10 using namespace CTF;
11 
12 void bitonic_sort(Vector<> & v, int logn, World & dw){
13  int64_t np;
14  int64_t * inds;
15  double * data;
16  int rank = dw.rank;
17 
18  int lens[logn];
19  std::fill(lens, lens+logn, 2);
20 
21  // min, * semiring
22  Semiring<> smin(DBL_MAX/2,
23  [](double a, double b){ return a <= b ? a : b; },
24  MPI_MIN,
25  1.,
26  [](double a, double b){ return a*b; });
27 
28  // represent vector to sort as 2-by-...-by-2 tensor
29  Tensor<> V(logn, lens, dw, smin);
30 
31  v.get_local_data(&np, &inds, &data);
32  V.write(np, inds, data);
33 
34  free(inds);
35  delete [] data;
36 
37 
38  // 2-by-2-by-2 tensor X, consisting of matrices
39  // | 1 1 | and | -1 -1 |
40  // | -1 -1 | | 1 1 |
41  // where the former matrix when applied to vector [a,b] computes
42  // [min(a,b),-max(a,b)] and the latter, [-max(a,b),min(a,b)]
43  int lens2[] = {2, 2, 2};
44  Tensor<> swap_up_down(3, lens2, dw, smin);
45  if (rank == 0){
46  double vals[] = {1.,1.,-1.,-1.,-1.,-1.,1.,1.};
47  int64_t inds[] = {0,1,2,3,4,5,6,7};
48  swap_up_down.write(8, inds, vals);
49  } else { swap_up_down.write(0, NULL, NULL); }
50 
51  // matrix used to correct the sign factor on the max values
52  Matrix<> fix_sign_up_down(2, 2, dw, smin);
53  if (rank == 0){
54  double vals[] = {1.,-1.,-1.,1.};
55  int64_t inds[] = {0,1,2,3};
56  fix_sign_up_down.write(4, inds, vals);
57  } else { fix_sign_up_down.write(0, NULL, NULL); }
58 
59  // see first matrix in definition of swap_up_down
60  Matrix<> swap_up(2, 2, dw, smin);
61  if (rank == 0){
62  double vals[] = {1.,1.,-1.,-1.};
63  int64_t inds[] = {0, 1, 2, 3};
64  swap_up.write(4, inds, vals);
65  } else { swap_up.write(0, NULL, NULL); }
66 
67  // corrects sign when everything is being sorted 'upwards'
68  Vector<> fix_sign_up(2, dw, smin);
69  if (rank == 0){
70  double vals[] = {1.,-1.};
71  int64_t inds[] = {0,1};
72  fix_sign_up.write(2, inds, vals);
73  } else { fix_sign_up.write(0, NULL, NULL); }
74 
75  char idx[logn];
76  char idx_z[logn];
77  for (int i=0; i<logn; i++){
78  idx[i] = 'a'+i;
79  idx_z[i] = idx[i];
80  }
81 
82  // lowest logn-1 recursive levels of bitonic sort
83  for (int i=0; i<logn-1; i++){
84  // every other subsequence of size 2^(i+1) needs to be ordered downwards
85  char up_down_idx = 'a'+i+1;
86  // i recursive levels for bitonic merge
87  for (int j=i; j>=0; j--){
88  idx_z[j] = 'z';
89  char swap_idx[] = {'z', idx[j], up_down_idx};
90  char fix_sign[] = {idx[j], up_down_idx};
91  V[idx] = swap_up_down[swap_idx]*V[idx_z];
92  V[idx] = fix_sign_up_down[fix_sign]*V[idx];
93  idx_z[j] = idx[j];
94  }
95  }
96 
97  // top recursive level of bitonic sort, only one sequence, so no need to order anything downwards
98  for (int j=logn-1; j>=0; j--){
99  idx_z[j] = 'z';
100  char swap_idx[] = {'z', idx[j]};
101  V[idx] = swap_up[swap_idx]*V[idx_z];
102  V[idx] = fix_sign_up[&(idx[j])]*V[idx];
103  idx_z[j] = idx[j];
104  }
105 
106 
107  // put the data from the tensor back into the vector
108  V.get_local_data(&np, &inds, &data);
109  v.write(np, inds, data);
110 
111  free(inds);
112  delete [] data;
113 }
114 
115 int bitonic(int logn,
116  World & dw){
117 
118  Vector<> v(1<<logn, dw);
119 
120  srand48(dw.rank*27);
121  v.fill_random(0.0, 1.0);
122 
123  bitonic_sort(v, logn, dw);
124 
125  double data[1<<logn];
126 
127  v.read_all(data);
128 
129  int pass = 1;
130  for (int i=1; i<1<<logn; i++){
131  if (data[i] < data[i-1]) pass = 0;
132  }
133  if (dw.rank == 0){
134  if (pass)
135  printf("{ bitonic sort via tensor contractions } passed \n");
136  else
137  printf("{ bitonic sort via tensor contractions } failed \n");
138  }
139  return pass;
140 }
141 
142 
143 #ifndef TEST_SUITE
144 char* getCmdOption(char ** begin,
145  char ** end,
146  const std::string & option){
147  char ** itr = std::find(begin, end, option);
148  if (itr != end && ++itr != end){
149  return *itr;
150  }
151  return 0;
152 }
153 
154 
155 int main(int argc, char ** argv){
156  int rank, np, logn, pass;
157  int const in_num = argc;
158  char ** input_str = argv;
159 
160  MPI_Init(&argc, &argv);
161  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
162  MPI_Comm_size(MPI_COMM_WORLD, &np);
163 
164  if (getCmdOption(input_str, input_str+in_num, "-logn")){
165  logn = atoi(getCmdOption(input_str, input_str+in_num, "-logn"));
166  if (logn < 0) logn = 4;
167  } else logn = 4;
168 
169 
170  {
171  World dw(argc, argv);
172 
173  if (rank == 0){
174  printf("Running bitonic sort on random dimension %d vector\n",1<<logn);
175  }
176  pass = bitonic(logn, dw);
177  assert(pass);
178  }
179 
180  MPI_Finalize();
181  return 0;
182 }
188 #endif
Matrix class which encapsulates a 2D tensor.
Definition: matrix.h:18
def rank(self)
Definition: core.pyx:312
Semiring is a Monoid with an addition multiplicaton function addition must have an identity and be as...
Definition: semiring.h:359
Vector class which encapsulates a 1D tensor.
Definition: vector.h:14
void read_all(int64_t *npair, dtype **data, bool unpack=false)
collects the entire tensor data on each process (not memory scalable)
Definition: tensor.cxx:377
an instance of the CTF library (world) on a MPI communicator
Definition: world.h:19
string
Definition: core.pyx:456
void fill_random(dtype rmin, dtype rmax)
fills local unique tensor elements to random values in the range [min,max] works only for dtype in {f...
Definition: tensor.cxx:928
int rank
rank of local processor
Definition: world.h:24
int bitonic(int logn, World &dw)
void get_local_data(int64_t *npair, int64_t **global_idx, dtype **data, bool nonzeros_only=false, bool unpack_sym=false) const
Gives the global indices and values associated with the local data.
Definition: tensor.cxx:159
int main(int argc, char **argv)
Definition: apsp.cxx:17
void bitonic_sort(Vector<> &v, int logn, World &dw)
an instance of a tensor within a CTF world
Definition: tensor.h:74
char * getCmdOption(char **begin, char **end, const std::string &option)
void write(int64_t npair, int64_t const *global_idx, dtype const *data)
writes in values associated with any set of indices The sparse data is defined in coordinate format...
Definition: tensor.cxx:264
def np(self)
Definition: core.pyx:315