PRAgMaTIc  master
Colour.h
Go to the documentation of this file.
1 /* Copyright (C) 2010 Imperial College London and others.
2  *
3  * Please see the AUTHORS file in the main source directory for a
4  * full list of copyright holders.
5  *
6  * Gerard Gorman
7  * Applied Modelling and Computation Group
8  * Department of Earth Science and Engineering
9  * Imperial College London
10  *
11  * g.gorman@imperial.ac.uk
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above
19  * copyright notice, this list of conditions and the following
20  * disclaimer in the documentation and/or other materials provided
21  * with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
34  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef COLOUR_H
39 #define COLOUR_H
40 
41 #include <limits>
42 #include <vector>
43 #include <algorithm>
44 #include <random>
45 
46 #include "HaloExchange.h"
47 
48 #include "PragmaticTypes.h"
49 #include "PragmaticMinis.h"
50 
53 class Colour{
54  public:
59  static void greedy(size_t NNodes, const std::vector< std::vector<index_t> > &NNList, std::vector<char> &colour){
60  char max_colour=64;
61 
62  // Colour first active node.
63  size_t node;
64  for(node=0;node<NNodes;node++){
65  if(NNList[node].size()>0){
66  colour[node] = 0;
67  break;
68  }
69  }
70 
71  // Colour remaining active nodes.
72  for(;node<NNodes;node++){
73  if(NNList[node].size()==0)
74  continue;
75 
76  std::vector<bool> used_colours(max_colour, false);;
77  for(std::vector<index_t>::const_iterator it=NNList[node].begin();it!=NNList[node].end();++it){
78  if(*it<(int)node){
79  if(colour[*it]>=max_colour){
80  max_colour*=2;
81  used_colours.resize(max_colour, false);
82  }
83 
84  used_colours[colour[*it]] = true;
85  }
86  }
87 
88  for(char i=0;;i++)
89  if(!used_colours[i]){
90  colour[node] = i;
91  break;
92  }
93  }
94  }
95 
102  static void GebremedhinManne(size_t NNodes, const std::vector< std::vector<index_t> > &NNList, std::vector<char> &colour){
103 
104  int max_iterations = 128;
105  std::vector<bool> conflicts_exist(max_iterations, false);;
106  std::vector<bool> conflict(NNodes);
107 
108 #pragma omp parallel firstprivate(NNodes)
109  {
110  // Initialize.
111  std::default_random_engine generator;
112  std::uniform_int_distribution<int> distribution(0,6);
113 
114 #pragma omp for
115  for(size_t i=0;i<NNodes;i++){
116  colour[i] = distribution(generator);
117  conflict[i] = true;
118  }
119 
120  for(int k=0;k<max_iterations;k++){
121  // Phase 1: pseudo-colouring. Note - assuming graph can be colored with fewer than 64 colours.
122 #pragma omp for schedule(guided)
123  for(size_t i=0;i<NNodes;i++){
124  if(!conflict[i])
125  continue;
126 
127  // Reset
128  conflict[i] = false;
129 
130  unsigned long colours = 0;
131  char c;
132  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
133  c = colour[*it];
134  colours = colours | 1<<c;
135  }
136  colours = ~colours;
137 
138  for(size_t j=0;j<64;j++){
139  if(colours&(1<<j)){
140  colour[i] = j;
141  break;
142  }
143  }
144  }
145 
146  // Phase 2: find conflicts
147 #pragma omp for
148  for(size_t i=0;i<NNodes;i++){
149  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
150  if(colour[i]==colour[*it] && i<(size_t)*it){
151  conflict[i] = true;
152  conflicts_exist[k] = true;
153  }
154  }
155  }
156  if(!conflicts_exist[k])
157  break;
158  }
159  }
160  }
161 
162  static void GebremedhinManne(MPI_Comm comm, size_t NNodes,
163  const std::vector< std::vector<index_t> > &NNList,
164  const std::vector< std::vector<index_t> > &send,
165  const std::vector< std::vector<index_t> > &recv,
166  const std::vector<int> &node_owner,
167  std::vector<char> &colour){
168  int max_iterations = 4096;
169  std::vector<int> conflicts_exist(max_iterations, 0);
170  std::vector<bool> conflict(NNodes);
171 
172  int rank;
173  MPI_Comm_rank(comm, &rank);
174 
175  char K=15; // Initialise guess at number
176 
177  assert(NNodes==colour.size());
178 
179  bool serialize=false;
180 
181 #pragma omp parallel firstprivate(NNodes)
182  {
183  // Initialize.
184  std::default_random_engine generator;
185  std::uniform_int_distribution<int> distribution(0,K);
186 
187 #pragma omp for
188  for(size_t i=0;i<NNodes;i++){
189  colour[i] = distribution(generator);
190  conflict[i] = true;
191  }
192 
193 #pragma omp single
194  {
195  halo_update<char, 1>(comm, send, recv, colour);
196  }
197 
198  for(int k=0;k<max_iterations;k++){
199  if(K==64){
200  serialize = true;
201  break;
202  }
203 
204  std::vector<char> colour_deck;
205  for(int i=0;i<K;i++)
206  colour_deck.push_back(i);
207  std::random_shuffle(colour_deck.begin(), colour_deck.end());
208 
209  // Phase 1: pseudo-colouring. Note - assuming graph can be colored with fewer than 64 colours.
210 #pragma omp for schedule(static)
211  for(size_t i=0;i<NNodes;i++){
212  if(i%1000 == 0)
213  std::random_shuffle(colour_deck.begin(), colour_deck.end());
214 
215  if(!conflict[i] || node_owner[i]!=rank)
216  continue;
217 
218  // Reset
219  conflict[i] = false;
220 
221  char c;
222  unsigned long colours = 0;
223  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
224  c = colour[*it];
225  colours = colours | 1<<c;
226  }
227  colours = ~colours;
228 
229  bool exhausted=true;
230  for(std::vector<char>::const_iterator it=colour_deck.begin();it!=colour_deck.end();++it){
231  if(colours&(1<<*it)){
232  colour[i] = *it;
233  exhausted=false;
234  break;
235  }
236  }
237  if(exhausted){
238  for(size_t j=K;j<64;j++){
239  if(colours&(1<<j)){
240  colour[i] = j;
241  break;
242  }
243  }
244  }
245  }
246 #pragma omp single
247  {
248  halo_update<char, 1>(comm, send, recv, colour);
249  }
250 
251  // Phase 2: find conflicts
252 #pragma omp for schedule(static)
253  for(index_t i=0;i<(index_t)NNodes;i++){
254  if(node_owner[i]!=rank)
255  continue;
256 
257  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
258  if(colour[i]==colour[*it] && i<*it){
259  conflict[i] = true;
260  conflicts_exist[k]++;
261  break;
262  }
263  }
264  }
265 
266 #pragma omp single
267  {
268  MPI_Allreduce(MPI_IN_PLACE, &(conflicts_exist[k]), 1, MPI_INT, MPI_SUM, comm);
269 
270  // If progress has stagnated then lift the limit on chromatic number.
271  if(k>=K && conflicts_exist[k-K]==conflicts_exist[k]){
272  for(int i=0;i<k;i++)
273  conflicts_exist[i] = -1;
274  K++;
275  }
276  }
277 
278  if(conflicts_exist[k]==0)
279  break;
280  }
281  }
282 
283  //
284  if(serialize){
285  int nranks;
286  MPI_Comm_size(comm, &nranks);
287 
288  halo_update<char, 1>(comm, send, recv, colour);
289 
290  for(int i=0;i<nranks;i++)
291  repair(NNodes, NNList, colour);
292  }
293  }
294 
301  static void repair(size_t NNodes, const std::vector< std::vector<index_t> > &NNList, std::vector<char> &colour){
302  // Phase 2: find conflicts
303  std::vector<size_t> conflicts;
304 #pragma omp for schedule(static, 64)
305  for(size_t i=0;i<NNodes;i++){
306  char c = colour[i];
307  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
308  char k = colour[*it];
309  if(c==k){
310  conflicts.push_back(i);
311  break;
312  }
313  }
314  }
315 
316  // Phase 3: serial resolution of conflicts
317  int tid = pragmatic_thread_id();
318  int nthreads = pragmatic_nthreads();
319  for(int i=0;i<nthreads;i++){
320  if(tid==i){
321  for(std::vector<size_t>::const_iterator it=conflicts.begin();it!=conflicts.end();++it){
322  unsigned long colours = 0;
323  for(std::vector<index_t>::const_iterator jt=NNList[*it].begin();jt!=NNList[*it].end();++jt){
324  colours = colours | 1<<(colour[*jt]);
325  }
326  colours = ~colours;
327 
328  for(size_t j=0;j<64;j++){
329  if(colours&(1<<j)){
330  colour[*it] = j;
331  break;
332  }
333  }
334  }
335  }
336 #pragma omp barrier
337  }
338  }
339 
340  static void repair(size_t NNodes, std::vector< std::vector<index_t> > &NNList, std::vector< std::set<index_t> > &marked_edges, std::vector<char> &colour){
341  // Phase 2: find conflicts
342  std::vector<size_t> conflicts;
343 #pragma omp for schedule(static, 64)
344  for(size_t i=0;i<NNodes;i++){
345  if(marked_edges[i].empty())
346  continue;
347 
348  char c = colour[i];
349  for(std::vector<index_t>::const_iterator it=NNList[i].begin();it!=NNList[i].end();++it){
350  char k = colour[*it];
351  if(c==k){
352  conflicts.push_back(i);
353  break;
354  }
355  }
356  }
357 
358  // Phase 3: serial resolution of conflicts
359  int tid = pragmatic_thread_id();
360  int nthreads = pragmatic_nthreads();
361  for(int i=0;i<nthreads;i++){
362  if(tid==i){
363  for(std::vector<size_t>::const_iterator it=conflicts.begin();it!=conflicts.end();++it){
364  unsigned long colours = 0;
365  for(std::vector<index_t>::const_iterator jt=NNList[*it].begin();jt!=NNList[*it].end();++jt){
366  colours = colours | 1<<(colour[*jt]);
367  }
368  colours = ~colours;
369 
370  for(size_t j=0;j<64;j++){
371  if(colours&(1<<j)){
372  colour[*it] = j;
373  break;
374  }
375  }
376  }
377  }
378 #pragma omp barrier
379  }
380  }
381 
382  static void RokosGorman(std::vector< std::vector<index_t>* > NNList, size_t NNodes,
383  int node_colour[], std::vector< std::vector< std::vector<index_t> > >& ind_sets,
384  int max_colour, size_t* worklist[], size_t worklist_size[], int tid){
385 #pragma omp single nowait
386  {
387  for(int i=0; i<3; ++i)
388  worklist_size[i] = 0;
389  }
390 
391  // Thread-private array of forbidden colours
392  std::vector<index_t> forbiddenColours(max_colour, std::numeric_limits<index_t>::max());
393 
394  // Phase 1: pseudo-colouring.
395 #pragma omp for schedule(guided)
396  for(size_t i=0; i<NNodes; ++i){
397  index_t n = *NNList[i]->begin();
398  for(typename std::vector<index_t>::const_iterator it=NNList[i]->begin()+1; it!=NNList[i]->end(); ++it){
399  int c = node_colour[*it];
400  if(c>=0)
401  forbiddenColours[c] = n;
402  }
403 
404  for(size_t j=0; j<forbiddenColours.size(); ++j){
405  if(forbiddenColours[j] != n){
406  node_colour[n] = j;
407  break;
408  }
409  }
410  }
411 
412  // Phase 2: find conflicts and create new worklist
413  std::vector<size_t> conflicts;
414 
415 #pragma omp for schedule(guided) nowait
416  for(size_t i=0; i<NNodes; ++i){
417  bool defective = false;
418  index_t n = *NNList[i]->begin();
419  for(typename std::vector<index_t>::const_iterator it=NNList[i]->begin()+1; it!=NNList[i]->end(); ++it){
420  if(node_colour[n] == node_colour[*it]){
421  // No need to mark both vertices as defectively coloured.
422  // Just mark the one with the lesser ID.
423  if(n < *it){
424  defective = true;
425  break;
426  }
427  }
428  }
429 
430  if(defective){
431  conflicts.push_back(i);
432 
433  for(typename std::vector<index_t>::const_iterator it=NNList[i]->begin()+1; it!=NNList[i]->end(); ++it){
434  int c = node_colour[*it];
435  forbiddenColours[c] = n;
436  }
437 
438  for(size_t j=0; j<forbiddenColours.size(); j++){
439  if(forbiddenColours[j] != n){
440  node_colour[n] = j;
441  break;
442  }
443  }
444  }else{
445  ind_sets[tid][node_colour[n]].push_back(n);
446  }
447  }
448 
449  size_t pos;
450  pos = pragmatic_omp_atomic_capture(&worklist_size[0], conflicts.size());
451 
452  memcpy(&worklist[0][pos], &conflicts[0], conflicts.size() * sizeof(size_t));
453 
454  conflicts.clear();
455 #pragma omp barrier
456 
457  // Private variable indicating which worklist we are currently using.
458  int wl = 0;
459 
460  while(worklist_size[wl]){
461 #pragma omp for schedule(guided) nowait
462  for(size_t item=0; item<worklist_size[wl]; ++item){
463  size_t i = worklist[wl][item];
464  bool defective = false;
465  index_t n = *NNList[i]->begin();
466  for(typename std::vector<index_t>::const_iterator it=NNList[i]->begin()+1; it!=NNList[i]->end(); ++it){
467  if(node_colour[n] == node_colour[*it]){
468  // No need to mark both vertices as defectively coloured.
469  // Just mark the one with the lesser ID.
470  if(n < *it){
471  defective = true;
472  break;
473  }
474  }
475  }
476 
477  if(defective){
478  conflicts.push_back(i);
479 
480  for(typename std::vector<index_t>::const_iterator it=NNList[i]->begin()+1; it!=NNList[i]->end(); ++it){
481  int c = node_colour[*it];
482  forbiddenColours[c] = n;
483  }
484 
485  for(size_t j=0; j<forbiddenColours.size(); j++){
486  if(forbiddenColours[j] != n){
487  node_colour[n] = j;
488  break;
489  }
490  }
491  }else{
492  ind_sets[tid][node_colour[n]].push_back(n);
493  }
494  }
495 
496  // Switch worklist
497  wl = (wl+1)%3;
498 
499  pos = pragmatic_omp_atomic_capture(&worklist_size[wl], conflicts.size());
500 
501  memcpy(&worklist[wl][pos], &conflicts[0], conflicts.size() * sizeof(size_t));
502 
503  conflicts.clear();
504 
505  // Clear the next worklist
506 #pragma omp single
507  {
508  worklist_size[(wl+1)%3] = 0;
509  }
510  }
511  }
512 };
513 #endif
static void GebremedhinManne(MPI_Comm comm, size_t NNodes, const std::vector< std::vector< index_t > > &NNList, const std::vector< std::vector< index_t > > &send, const std::vector< std::vector< index_t > > &recv, const std::vector< int > &node_owner, std::vector< char > &colour)
Definition: Colour.h:162
static void repair(size_t NNodes, const std::vector< std::vector< index_t > > &NNList, std::vector< char > &colour)
Definition: Colour.h:301
int index_t
int pragmatic_nthreads()
static void GebremedhinManne(size_t NNodes, const std::vector< std::vector< index_t > > &NNList, std::vector< char > &colour)
Definition: Colour.h:102
Performs a simple first breath greedy graph colouring of a local undirected graph.
Definition: Colour.h:53
static void greedy(size_t NNodes, const std::vector< std::vector< index_t > > &NNList, std::vector< char > &colour)
Definition: Colour.h:59
static void RokosGorman(std::vector< std::vector< index_t > * > NNList, size_t NNodes, int node_colour[], std::vector< std::vector< std::vector< index_t > > > &ind_sets, int max_colour, size_t *worklist[], size_t worklist_size[], int tid)
Definition: Colour.h:382
int pragmatic_thread_id()
static void repair(size_t NNodes, std::vector< std::vector< index_t > > &NNList, std::vector< std::set< index_t > > &marked_edges, std::vector< char > &colour)
Definition: Colour.h:340
size_t pragmatic_omp_atomic_capture(size_t *shared, size_t inc)