1 #include <stdlib.h>
2 #include <stdio.h>
3 #include "k-partitioning.h"
4 #include "tm_mt.h"
5 #include "tm_verbose.h"
6
7 void memory_allocation(PriorityQueue ** Q, PriorityQueue ** Qinst, double *** D, int n, int k);
8 void initialization(int * const part, double ** const matrice, PriorityQueue * const Qpart, PriorityQueue * const Q, PriorityQueue * const Qinst, double ** const D, int n, int k, int * const deficit, int * const surplus);
9 void algo(int * const part, double ** const matrice, PriorityQueue * const Qpart, PriorityQueue * const Q, PriorityQueue * const Qinst, double ** const D, int n, int * const deficit, int * const surplus);
10 double nextGain(PriorityQueue * const Qpart, PriorityQueue * const Q, int * const deficit, int * const surplus);
11 void balancing(int n, int deficit, int surplus, double ** const D, int * const part);
12 void destruction(PriorityQueue * Qpart, PriorityQueue * Q, PriorityQueue * Qinst, double ** D, int n, int k);
13
14 void allocate_vertex2(int u, int *res, double **comm, int n, int *size, int max_size);
15 double eval_cost2(int *,int,double **);
16 int *kpartition_greedy2(int k, double **comm, int n, int nb_try_max, int *constraints, int nb_constraints);
17 int* build_p_vector(double **comm, int n, int k, int greedy_trials, int * constraints, int nb_constraints);
18
kPartitioning(double ** comm,int n,int k,int * constraints,int nb_constraints,int greedy_trials)19 int* kPartitioning(double ** comm, int n, int k, int * constraints, int nb_constraints, int greedy_trials)
20 {
21 /* ##### declarations & allocations ##### */
22
23 PriorityQueue Qpart, *Q = NULL, *Qinst = NULL;
24 double **D = NULL;
25 int deficit, surplus, *part = NULL;
26 int real_n = n-nb_constraints;
27
28 part = build_p_vector(comm, n, k, greedy_trials, constraints, nb_constraints);
29
30 memory_allocation(&Q, &Qinst, &D, real_n, k);
31
32 /* ##### Initialization ##### */
33
34 initialization(part, comm, &Qpart, Q, Qinst, D, real_n, k, &deficit, &surplus);
35
36 /* ##### Main loop ##### */
37 while((nextGain(&Qpart, Q, &deficit, &surplus))>0)
38 {
39 algo(part, comm, &Qpart, Q, Qinst, D, real_n, &deficit, &surplus);
40 }
41
42 /* ##### Balancing the partition ##### */
43 balancing(real_n, deficit, surplus, D, part); /*if partition isn't balanced we have to make one last move*/
44
45 /* ##### Memory deallocation ##### */
46 destruction(&Qpart, Q, Qinst, D, real_n, k);
47
48 return part;
49 }
50
memory_allocation(PriorityQueue ** Q,PriorityQueue ** Qinst,double *** D,int n,int k)51 void memory_allocation(PriorityQueue ** Q, PriorityQueue ** Qinst, double *** D, int n, int k)
52 {
53 int i;
54 *Q = calloc(k, sizeof(PriorityQueue)); /*one Q for each partition*/
55 *Qinst = calloc(n, sizeof(PriorityQueue)); /*one Qinst for each vertex*/
56 *D = malloc(sizeof(double *) * n); /*D's size is n * k*/
57 for(i=0; i < n; ++i)
58 (*D)[i] = calloc(k, sizeof(double));
59 }
60
initialization(int * const part,double ** const matrice,PriorityQueue * const Qpart,PriorityQueue * const Q,PriorityQueue * const Qinst,double ** const D,int n,int k,int * const deficit,int * const surplus)61 void initialization(int * const part, double ** const matrice, PriorityQueue * const Qpart, PriorityQueue * const Q, PriorityQueue * const Qinst, double ** const D, int n, int k, int * const deficit, int * const surplus)
62 {
63 int i,j;
64
65 /* ##### PriorityQueue initializations ##### */
66 /* We initialize Qpart with a size of k because it contains the subsets's indexes. */
67 PQ_init(Qpart, k);
68
69 /* We initialize each Q[i] with a size of n because each vertex is in one of these queue at any time. */
70 /* However we could set a size of (n/k)+1 as this is the maximum size of a subset when the partition is not balanced. */
71 for(i=0; i<k; ++i)
72 PQ_init(&Q[i], n);
73
74 /* We initialize each Qinst[i] with a size of k because fo each vertex i, Qinst[i] contains the D(i,j) values for j = 0...(k-1) */
75 for(i=0; i<n; ++i)
76 PQ_init(&Qinst[i], k);
77
78 /* ##### Computing the D(i,j) values ##### */
79 for(i=0; i < n; ++i) /*for each vertex i*/
80 {
81 for(j=0; j < n; ++j) /*and for each vertex j*/
82 {
83 D[i][part[j]] += matrice[i][j];
84 }
85 }
86
87 /* ##### Filling up the queues ##### */
88 /* ### Qinst ### */
89 for(i=0; i < n; ++i) /*for each vertex i*/
90 for(j=0; j < k; ++j) /*and for each subset j*/
91 PQ_insert(&Qinst[i], j, D[i][j]); /*we insert the corresponding D(i,j) value in Qinst[i]*/
92
93 /* ### Q ### */
94 for(i=0; i<n; ++i) /*for each vertex i*/
95 PQ_insert(&Q[part[i]], i, PQ_findMaxKey(&Qinst[i])-D[i][part[i]]); /*we insert in Q[part[i]] the vertex i with its highest possible gain*/
96
97 /* ### Qpart ### */
98 for(i=0; i < k; ++i) /*for each subset i*/
99 PQ_insert(Qpart, i, PQ_findMaxKey(&Q[i])); /*we insert it in Qpart with the highest possible gain by one of its vertex as key*/
100
101
102 /* ##### Initialization of deficit/surplus ##### */
103 *surplus = *deficit = 0;
104 }
105
algo(int * const part,double ** const matrice,PriorityQueue * const Qpart,PriorityQueue * const Q,PriorityQueue * const Qinst,double ** const D,int n,int * const deficit,int * const surplus)106 void algo(int * const part, double ** const matrice, PriorityQueue * const Qpart, PriorityQueue * const Q, PriorityQueue * const Qinst, double ** const D, int n, int * const deficit, int * const surplus)
107 {
108 int p,u,v,j;
109 double d;
110 if(*deficit == *surplus) /*if the current partition is balanced*/
111 {
112 p = PQ_deleteMax(Qpart); /*we get the subset with the highest possible gain in p and remove it from Qpart*/
113 u = PQ_deleteMax(&Q[p]); /*then we get the vertex with this highest possible gain in u and remove it from Q[p] */
114 *deficit = part[u]; /*p becomes the deficit */
115 }
116 else /*the current partition is not balanced*/
117 {
118 u = PQ_deleteMax(&Q[*surplus]); /*we get the vertex with the highest possible gain in surplus and remove it from Q[surplus] */
119 PQ_delete(Qpart, part[u]); /*then we remove surplus from Qpart (note that u is from surplus so part[u] is surplus) */
120 }
121 d = PQ_findMaxKey(&Q[part[u]]); /*we get the next highest possible gain in part[u] (without taking u in account as we already removed it from Q[part[u])*/
122 PQ_insert(Qpart, part[u], d); /*we put part[u] back in Qpart with its new highest possible gain*/
123 j = PQ_deleteMax(&Qinst[u]); /*we get from Qinst[u] the subset in which we have to move u to get the highest gain.*/
124 if ( j < 0){
125 if(tm_get_verbose_level() >= CRITICAL)
126 fprintf(stderr,"Error Max element in priority queue negative!\n");
127 exit(-1);
128 }
129 *surplus = j; /*this subset becomes surplus*/
130
131 for(v=0; v < n; ++v) /*we scan though all edges (u,v) */
132 {
133 j = part[u]; /*we set j to the starting subset */
134 D[v][j]= D[v][j] - matrice[u][v]; /*we compute the new D[v, i] (here j has the value of the starting subset of u, that's why we say i) */
135 PQ_adjustKey(&Qinst[v], j, D[v][j]); /*we update this gain in Qinst[v]*/
136 j = *surplus; /*we put back the arrival subset in j*/
137 D[v][j] = D[v][j] + matrice[u][v]; /*matrice[u][v]; we compute the new D[v, j]*/
138 PQ_adjustKey(&Qinst[v], j, D[v][j]);/*we update this gain in Qinst[v]*/
139 d = PQ_findMaxKey(&Qinst[v]) - D[v][part[v]]; /*we compute v's new highest possible gain*/
140 PQ_adjustKey(&Q[part[v]], v, d); /*we update it in Q[p[v]]*/
141 d = PQ_findMaxKey(&Q[part[v]]); /*we get the highest possible gain in v's subset*/
142 PQ_adjustKey(Qpart, part[v], d); /*we update it in Qpart*/
143 }
144 part[u] = *surplus; /*we move u from i to j (here surplus has the value of j the arrival subset)*/
145
146 d = PQ_findMaxKey(&Qinst[u]) - D[u][part[u]]; /*we compute the new u's highest possible gain*/
147 if(!PQ_isEmpty(&Qinst[u])) /*if at least one more move of u is possible*/
148 PQ_insert(&Q[part[u]], u, d); /*we insert u in the Q queue of its new subset*/
149 PQ_adjustKey(Qpart, part[u], d); /*we update the new highest possible gain in u's subset*/
150 }
151
nextGain(PriorityQueue * const Qpart,PriorityQueue * const Q,int * const deficit,int * const surplus)152 double nextGain(PriorityQueue * const Qpart, PriorityQueue * const Q, int * const deficit, int * const surplus)
153 {
154 double res;
155 if(*deficit == *surplus) /*if the current partition is balanced*/
156 res = PQ_findMaxKey(Qpart); /*we get the highest possible gain*/
157 else /*the current partition is not balanced*/
158 res = PQ_findMaxKey(&Q[*surplus]); /*we get the highest possible gain from surplus*/
159 return res;
160 }
161
balancing(int n,int deficit,int surplus,double ** const D,int * const part)162 void balancing(int n, int deficit, int surplus, double ** const D, int * const part)
163 {
164 if(surplus != deficit) /*if the current partition is not balanced*/
165 {
166 int i;
167 PriorityQueue moves; /*we use a queue to store the possible moves from surplus to deficit*/
168 PQ_init(&moves, n);
169 for(i=0; i<n; ++i) /*for each vertex*/
170 {
171 if(part[i] == surplus) /*if i is from surplus*/
172 PQ_insert(&moves, i, D[i][deficit]-D[i][surplus]); /*we insert i in moves with the gain we get from moving i from surplus to deficit as key */
173 }
174 part[PQ_deleteMax(&moves)] = deficit; /*we put the i from moves with the highest gain in deficit*/
175 PQ_exit(&moves);
176 }
177 }
178
destruction(PriorityQueue * Qpart,PriorityQueue * Q,PriorityQueue * Qinst,double ** D,int n,int k)179 void destruction(PriorityQueue * Qpart, PriorityQueue * Q, PriorityQueue * Qinst, double ** D, int n, int k)
180 {
181 int i;
182 PQ_exit(Qpart);
183 for(i=0; i<k; ++i)
184 PQ_exit(&Q[i]);
185 free(Q);
186 for(i=0; i<n; ++i)
187 {
188 PQ_exit(&Qinst[i]);
189 }
190 free(Qinst);
191
192 for(i=0; i<n; ++i)
193 free(D[i]);
194 free(D);
195 }
196
197
kpartition_greedy2(int k,double ** comm,int n,int nb_try_max,int * constraints,int nb_constraints)198 int *kpartition_greedy2(int k, double **comm, int n, int nb_try_max, int *constraints, int nb_constraints)
199 {
200 int *res = NULL, *best_res=NULL, *size = NULL;
201 int i,j,nb_trials;
202 int max_size;
203 double cost, best_cost = -1;
204
205 for( nb_trials = 0 ; nb_trials < nb_try_max ; nb_trials++ ){
206 res = (int *)malloc(sizeof(int)*n);
207 for ( i = 0 ; i < n ; ++i )
208 res[i] = -1;
209
210 size = (int *)calloc(k,sizeof(int));
211 max_size = n/k;
212
213 /* put "dumb" vertices in the correct partition if there are any*/
214 if (nb_constraints){ /*if there are at least one constraint*/
215 int nb_real_nodes = n-nb_constraints; /*this is the number of "real" nodes by opposition to the dumb ones*/
216 for(i=0; i<nb_constraints; ++i) /*for each constraint*/
217 {
218 int i_part = constraints[i]/max_size; /*we compute its partition*/
219 res[nb_real_nodes+i] = i_part; /*and we set it in partition vector*/
220 size[i_part]++; /*we update the partition's size*/
221 }
222 }
223
224 /* choose k initial "true" vertices at random and put them in a different partition */
225 for ( i = 0 ; i < k ; ++i ){
226 /* if the partition is full of dumb vertices go to next partition*/
227 if(size[i] >= max_size)
228 continue;
229 /* find a vertex not already partitionned*/
230 do{
231 /* call the mersenne twister PRNG of tm_mt.c*/
232 j = genrand_int32() % n;
233 } while ( res[j] != -1 );
234 /* allocate and update size of partition*/
235 res[j] = i;
236 /* printf("random: %d -> %d\n",j,i); */
237 size[i]++;
238 }
239
240 /* allocate each unallocated vertices in the partition that maximize the communication*/
241 for( i = 0 ; i < n ; ++i )
242 if( res[i] == -1)
243 allocate_vertex2(i, res, comm, n-nb_constraints, size, max_size);
244
245 cost = eval_cost2(res,n-nb_constraints,comm);
246 /*print_1D_tab(res,n);
247 printf("cost=%.2f\n",cost);*/
248 if((cost<best_cost) || (best_cost == -1)){
249 best_cost=cost;
250 free(best_res);
251 best_res=res;
252 }else
253 free(res);
254
255 free(size);
256 }
257
258 /*print_1D_tab(best_res,n);
259 printf("best_cost=%.2f\n",best_cost);
260 */
261 return best_res;
262 }
263
allocate_vertex2(int u,int * res,double ** comm,int n,int * size,int max_size)264 void allocate_vertex2(int u, int *res, double **comm, int n, int *size, int max_size)
265 {
266 int i,best_part = -1;
267 double cost, best_cost = -1;
268
269 /*printf("\n");
270 print_1D_tab(res,n);*/
271 for( i = 0 ; i < n ; ++i){
272 if (( res[i] != -1 ) && ( size[res[i]] < max_size )){
273 cost = comm[u][i];
274 if (( cost > best_cost)){
275 best_cost = cost;
276 best_part = res[i];
277 }
278 }
279 }
280
281 /* printf("size[%d]: %d\n",best_part, size[best_part]);*/
282 /* printf("putting(%.2f): %d -> %d\n",best_cost, u, best_part); */
283
284 res[u] = best_part;
285 size[best_part]++;
286 }
287
eval_cost2(int * partition,int n,double ** comm)288 double eval_cost2(int *partition, int n, double **comm)
289 {
290 double cost = 0;
291 int i,j;
292
293 for( i = 0 ; i < n ; ++i )
294 for( j = i+1 ; j < n ; ++j )
295 if(partition[i] != partition[j])
296 cost += comm[i][j];
297
298 return cost;
299 }
300
build_p_vector(double ** comm,int n,int k,int greedy_trials,int * constraints,int nb_constraints)301 int* build_p_vector(double **comm, int n, int k, int greedy_trials, int * constraints, int nb_constraints)
302 {
303 int * part = NULL;
304 if(greedy_trials>0) /*if greedy_trials > 0 then we use kpartition_greedy with greedy_trials trials*/
305 {
306 part = kpartition_greedy2(k, comm, n, greedy_trials, constraints, nb_constraints);
307 }
308 else
309 {
310 int * size = calloc(k, sizeof(int));
311 int i,j;
312 int nodes_per_part = n/k;
313 int nb_real_nodes = n-nb_constraints;
314 part = malloc(sizeof(int) * n);
315 for(i=0; i<nb_constraints; i++) /*for each constraints*/
316 {
317 int i_part = constraints[i]/nodes_per_part; /*we compute the partition where we have to put this constraint*/
318 part[nb_real_nodes+i] = i_part;
319 size[i_part]++;
320 }
321 j=0;
322 /* now we have to fill the partitions with the "real" nodes */
323 for(i=0; i<nb_real_nodes; i++) /*for each node*/
324 {
325 if(size[j] < nodes_per_part) /*if j partition isn't full*/
326 {
327 size[j]++;
328 part[i] = j; /*then we put the node in this part*/
329 }
330 else /*otherwise we decrement i to get the same node in the next loop*/
331 {
332 i--;
333 }
334 j = (j+1)%k; /*and we change j to the next partition*/
335 }
336 free(size);
337 }
338 return part;
339 }
340