Scippy

SCIP

Solving Constraint Integer Programs

tclique_coloring.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program */
4/* TCLIQUE --- Algorithm for Maximum Cliques */
5/* */
6/* Copyright (c) 1996-2024 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with TCLIQUE; see the file LICENSE. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file tclique_coloring.c
26 * @ingroup OTHER_CFILES
27 * @brief coloring part of algorithm for maximum cliques
28 * @author Tobias Achterberg
29 * @author Ralf Borndoerfer
30 * @author Zoltan Kormos
31 * @author Kati Wolter
32 */
33
34/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35
36#include <stdio.h>
37#include <assert.h>
38#include <stdlib.h>
39
40#include "tclique/tclique.h"
41#include "tclique/tclique_def.h"
44
45
46
47/** gets index of the uncolored node in a given array of nodes in V with maximum satdeg;
48 * in case of a tie choose node with maximum weight;
49 * if no uncolored node is found, -1 is returned
50 */
51static
53 int* V, /**< non-zero weighted nodes for branching */
54 int nV, /**< number of non-zero weighted nodes for branching */
55 NBC* gsd, /**< neighbor color information of all nodes */
56 TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */
57 const TCLIQUE_WEIGHT* weights /**< weight of nodes in grpah */
58 )
59{
60 TCLIQUE_WEIGHT maxweight;
61 int maxsatdeg;
62 int maxsatdegindex;
63 int i;
64
65 maxweight = -1;
66 maxsatdeg = -1;
67 maxsatdegindex = -1;
68
69 assert(gsd != NULL);
70 assert(iscolored != NULL);
71
72 for( i = 0; i < nV; i++ )
73 {
74 TCLIQUE_WEIGHT weight;
75 int satdeg;
76
77 /* check only uncolored nodes */
78 if( iscolored[i] )
79 continue;
80
81 weight = weights[V[i]];
82 assert(weight > 0);
83
84 satdeg = gsd[i].satdeg;
85 if( satdeg > maxsatdeg || (satdeg == maxsatdeg && weight > maxweight) )
86 {
87 maxsatdeg = satdeg;
88 maxweight = weight;
89 maxsatdegindex = i;
90 }
91 }
92
93 return maxsatdegindex;
94}
95
96/** gets index of the node in a given set of nodes with maximum weight */
97static
99 TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */
100 TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
101 TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
102 int* V, /**< non-zero weighted nodes for branching */
103 int nV /**< number of non-zero weighted nodes for branching */
104 )
105{
106 const TCLIQUE_WEIGHT* weights;
107 TCLIQUE_WEIGHT maxweight;
108 int maxweightindex;
109 int i;
110
111 assert(getnnodes != NULL);
112 assert(getweights != NULL);
113 assert(tcliquegraph != NULL);
114 assert(nV > 0);
115
116 weights = getweights(tcliquegraph);
117
118 maxweightindex = -1;
119 maxweight = 0;
120
121 /* try to improve maxweight */
122 for( i = 0 ; i < nV; i++ )
123 {
124 assert(0 <= V[i] && V[i] < getnnodes(tcliquegraph));
125 assert(weights[V[i]] > 0);
126 if( weights[V[i]] > maxweight)
127 {
128 /* node has larger weight */
129 maxweight = weights[V[i]];
130 maxweightindex = i;
131 }
132 }
133 assert(maxweightindex >= 0);
134
135 return maxweightindex;
136}
137
138/** updates the neighbor colors information of a node: updates the list of neighbor color intervals
139 * by making the union of the existing list and the given list of color intervals, and updates the saturation degree
140 */
141static
143 BMS_CHKMEM* mem, /**< block memory */
144 NBC* pgsd, /**< pointer to neighbor color information of node to update */
145 LIST_ITV* pnc /**< pointer to given list of color intervals */
146 )
147{
148 LIST_ITV head;
149 LIST_ITV* apciv;
150 LIST_ITV* pciv;
151 LIST_ITV* nciv;
152
153 /* save the pointer to the first element of the list */
154 head.next = pgsd->lcitv;
155 apciv = &head;
156 pciv = apciv->next;
157
158 /* construct the union of the two intervals */
159 while( (pnc != NULL) && (pciv != NULL) )
160 {
161 if( pnc->itv.inf < pciv->itv.inf )
162 {
163 ALLOC_ABORT( BMSallocChunkMemory(mem, &nciv) );
164 nciv->itv = pnc->itv;
165 nciv->next = pciv;
166 apciv->next = nciv;
167 apciv = nciv;
168
169 pnc = pnc->next;
170 }
171 else if( pnc->itv.inf <= pciv->itv.sup )
172 {
173 if( pnc->itv.sup > pciv->itv.sup )
174 pciv->itv.sup = pnc->itv.sup;
175 pnc = pnc->next;
176 }
177 else
178 {
179 apciv = pciv;
180 pciv = pciv->next;
181 }
182 }
183
184 while( pnc != NULL )
185 {
186 ALLOC_ABORT( BMSallocChunkMemory(mem, &nciv) );
187 nciv->itv = pnc->itv;
188 nciv->next = NULL;
189
190 apciv->next = nciv;
191 apciv = nciv;
192
193 pnc = pnc->next;
194 }
195
196 /* try to reduce the number of intervals */
197 pgsd->satdeg = 0;
198 apciv = head.next;
199 while( (pciv = apciv->next) != NULL ) /*lint !e838*/
200 {
201 if( apciv->itv.sup < (pciv->itv.inf - 1) )
202 {
203 pgsd->satdeg += apciv->itv.sup - apciv->itv.inf + 1;
204 apciv = apciv->next;
205 }
206 else
207 {
208 LIST_ITV* tmp;
209
210 if( apciv->itv.sup < pciv->itv.sup )
211 apciv->itv.sup = pciv->itv.sup;
212 apciv->next = pciv->next;
213
214 /* free data structure for created colorinterval */
215 tmp = pciv->next;
216 /* coverity[double_free] */
217 BMSfreeChunkMemory(mem, &pciv);
218 pciv = tmp;
219 }
220 }
221 pgsd->satdeg += apciv->itv.sup - apciv->itv.inf + 1;
222
223 /* updates the pointer to the first element of the list */
224 pgsd->lcitv = head.next;
225}
226
227/** colors the positive weighted nodes of a given set of nodes V with the lowest possible number of colors and
228 * finds a clique in the graph induced by V, an upper bound and an apriori bound for further branching steps
229 */
231 TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */
232 TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
233 TCLIQUE_SELECTADJNODES((*selectadjnodes)),/**< user function to select adjacent edges */
234 TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
235 BMS_CHKMEM* mem, /**< block memory */
236 int* buffer, /**< buffer of size nnodes */
237 int* V, /**< non-zero weighted nodes for branching */
238 int nV, /**< number of non-zero weighted nodes for branching */
239 NBC* gsd, /**< neighbor color information of all nodes */
240 TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */
241 TCLIQUE_WEIGHT* apbound, /**< pointer to store apriori bound of nodes for branching */
242 int* clique, /**< buffer for storing the clique */
243 int* nclique, /**< pointer to store number of nodes in the clique */
244 TCLIQUE_WEIGHT* weightclique /**< pointer to store the weight of the clique */
245 )
246{
247 const TCLIQUE_WEIGHT* weights;
248 TCLIQUE_WEIGHT maxsatdegree;
249 TCLIQUE_WEIGHT range;
250 TCLIQUE_Bool growclique;
251 int node;
252 int nodeVindex;
253 int i;
254 int j;
255 LIST_ITV* colorinterval;
256 LIST_ITV nwcitv;
257 LIST_ITV* pnc;
258 LIST_ITV* lcitv;
259 LIST_ITV* item;
260 LIST_ITV* tmpitem;
261 int* workclique;
262 int* currentclique;
263 int ncurrentclique;
264 int weightcurrentclique;
265 int* Vadj;
266 int nVadj;
267 int adjidx;
268
269 assert(getnnodes != NULL);
270 assert(getweights != NULL);
271 assert(selectadjnodes != NULL);
272 assert(buffer != NULL);
273 assert(V != NULL);
274 assert(nV > 0);
275 assert(clique != NULL);
276 assert(nclique != NULL);
277 assert(weightclique != NULL);
278 assert(gsd != NULL);
279 assert(iscolored != NULL);
280
281 weights = getweights(tcliquegraph);
282 assert(weights != NULL);
283
284 /* initialize maximum weight clique found so far */
285 growclique = TRUE;
286 *nclique = 0;
287 *weightclique = 0;
288
289 /* get node of V with maximum weight */
290 nodeVindex = getMaxWeightIndex(getnnodes, getweights, tcliquegraph, V, nV);
291 node = V[nodeVindex];
292 assert(0 <= node && node < getnnodes(tcliquegraph));
293 range = weights[node];
294 assert(range > 0);
295
296 /* set up data structures for coloring */
297 BMSclearMemoryArray(iscolored, nV); /* new-memory */
298 BMSclearMemoryArray(gsd, nV); /* new-memory */
299 iscolored[nodeVindex] = TRUE;
300
301 /* color the first node */
302 debugMessage("---------------coloring-----------------\n");
303 debugMessage("1. node choosen: vindex=%d, vertex=%d, satdeg=%d, range=%d)\n",
304 nodeVindex, node, gsd[nodeVindex].satdeg, range);
305
306 /* set apriori bound: apbound(v_i) = satdeg(v_i) + weight(v_i) */
307 apbound[nodeVindex] = range;
308 assert(apbound[nodeVindex] > 0);
309
310 /* update maximum saturation degree: maxsatdeg = max { satdeg(v_i) + weight(v_i) | v_i in V } */
311 maxsatdegree = range;
312
313 debugMessage("-> updated neighbors:\n");
314
315 /* set neighbor color of the adjacent nodes of node */
316 Vadj = buffer;
317 nVadj = selectadjnodes(tcliquegraph, node, V, nV, Vadj);
318 for( i = 0, adjidx = 0; i < nV && adjidx < nVadj; ++i )
319 {
320 assert(V[i] <= Vadj[adjidx]); /* Vadj is a subset of V */
321 if( V[i] == Vadj[adjidx] )
322 {
323 /* node is adjacent to itself, but we do not need to color it again */
324 if( i == nodeVindex )
325 {
326 /* go to the next node in Vadj */
327 adjidx++;
328 continue;
329 }
330
331 debugMessage(" nodeVindex=%d, node=%d, weight=%d, satdegold=%d -> ",
332 i, V[i], weights[V[i]], gsd[i].satdeg);
333
334 /* sets satdeg for adjacent node */
335 gsd[i].satdeg = range;
336
337 /* creates new color interval [1,range] */
338 ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) );
339 colorinterval->next = NULL;
340 colorinterval->itv.inf = 1;
341 colorinterval->itv.sup = range;
342
343 /* colorinterval is the first added element of the list of neighborcolors of the adjacent node */
344 gsd[i].lcitv = colorinterval;
345
346 /* go to the next node in Vadj */
347 adjidx++;
348
349 debugPrintf("satdegnew=%d, nbc=[%d,%d]\n", gsd[i].satdeg, gsd[i].lcitv->itv.inf, gsd[i].lcitv->itv.sup);
350 }
351 }
352
353 /* set up data structures for the current clique */
354 ALLOC_ABORT( BMSallocMemoryArray(&currentclique, nV) );
355 workclique = clique;
356
357 /* add node to the current clique */
358 currentclique[0] = node;
359 ncurrentclique = 1;
360 weightcurrentclique = range;
361
362 /* color all other nodes of V */
363 for( i = 0 ; i < nV-1; i++ )
364 {
365 assert((workclique == clique) != (currentclique == clique));
366
367 /* selects the next uncolored node to color */
368 nodeVindex = getMaxSatdegIndex(V, nV, gsd, iscolored, weights);
369 if( nodeVindex == -1 ) /* no uncolored nodes left */
370 break;
371
372 node = V[nodeVindex];
373 assert(0 <= node && node < getnnodes(tcliquegraph));
374 range = weights[node];
375 assert(range > 0);
376 iscolored[nodeVindex] = TRUE;
377
378 debugMessage("%d. node choosen: vindex=%d, vertex=%d, satdeg=%d, range=%d, growclique=%u, weight=%d)\n",
379 i+2, nodeVindex, node, gsd[nodeVindex].satdeg, range, growclique, weightcurrentclique);
380
381 /* set apriori bound: apbound(v_i) = satdeg(v_i) + weight(v_i) */
382 apbound[nodeVindex] = gsd[nodeVindex].satdeg + range;
383 assert(apbound[nodeVindex] > 0);
384
385 /* update maximum saturation degree: maxsatdeg = max { satdeg(v_i) + weight(v_i) | v_i in V } */
386 if( maxsatdegree < apbound[nodeVindex] )
387 maxsatdegree = apbound[nodeVindex];
388
389 /* update clique */
390 if( gsd[nodeVindex].satdeg == 0 )
391 {
392 /* current node is not adjacent to nodes of current clique,
393 * i.e. current clique can not be increased
394 */
395 debugMessage("current node not adjacend to current clique (weight:%d) -> starting new clique\n",
396 weightcurrentclique);
397
398 /* check, if weight of current clique is larger than weight of maximum weight clique found so far */
399 if( weightcurrentclique > *weightclique )
400 {
401 int* tmp;
402
403 /* update maximum weight clique found so far */
404 assert((workclique == clique) != (currentclique == clique));
405 tmp = workclique;
406 *weightclique = weightcurrentclique;
407 *nclique = ncurrentclique;
408 workclique = currentclique;
409 currentclique = tmp;
410 assert((workclique == clique) != (currentclique == clique));
411 }
412 weightcurrentclique = 0;
413 ncurrentclique = 0;
414 growclique = TRUE;
415 }
416 if( growclique )
417 {
418 /* check, if the current node is still adjacent to all nodes in the clique */
419 if( gsd[nodeVindex].satdeg == weightcurrentclique )
420 {
421 assert(ncurrentclique < nV);
422 currentclique[ncurrentclique] = node;
423 ncurrentclique++;
424 weightcurrentclique += range;
425#ifdef TCLIQUE_DEBUG
426 {
427 int k;
428 debugMessage("current clique (size:%d, weight:%d):", ncurrentclique, weightcurrentclique);
429 for( k = 0; k < ncurrentclique; ++k )
430 debugPrintf(" %d", currentclique[k]);
431 debugPrintf("\n");
432 }
433#endif
434 }
435 else
436 {
437 debugMessage("node satdeg: %d, clique weight: %d -> stop growing clique\n",
438 gsd[nodeVindex].satdeg, weightcurrentclique);
439 growclique = FALSE;
440 }
441 }
442
443 /* search for fitting color intervals for current node */
444 pnc = &nwcitv;
445 if( gsd[nodeVindex].lcitv == NULL )
446 {
447 /* current node has no colored neighbors yet: create new color interval [1,range] */
448 ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) );
449 colorinterval->next = NULL;
450 colorinterval->itv.inf = 1;
451 colorinterval->itv.sup = range;
452
453 /* add the new colorinterval [1, range] to the list of chosen colorintervals for node */
454 pnc->next = colorinterval;
455 }
456 else
457 {
458 int tocolor;
459 int dif;
460
461 /* current node has colored neighbors */
462 tocolor = range;
463 lcitv = gsd[nodeVindex].lcitv;
464
465 /* check, if first neighbor color interval [inf, sup] has inf > 1 */
466 if( lcitv->itv.inf != 1 )
467 {
468 /* create new interval [1, min{range, inf}] */
469 dif = lcitv->itv.inf - 1 ;
470 if( dif > tocolor )
471 dif = tocolor;
472
473 ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) );
474 colorinterval->next = NULL;
475 colorinterval->itv.inf = 1;
476 colorinterval->itv.sup = dif;
477
478 tocolor -= dif;
479 pnc->next = colorinterval;
480 pnc = colorinterval;
481 }
482
483 /* as long as node is not colored with all colors, create new color interval by filling
484 * the gaps in the existing neighbor color intervals of the neighbors of node
485 */
486 while( tocolor > 0 )
487 {
488 dif = tocolor;
489
490 ALLOC_ABORT( BMSallocChunkMemory(mem, &colorinterval) );
491 colorinterval->next = NULL;
492 colorinterval->itv.inf = lcitv->itv.sup+1;
493 if( lcitv->next != NULL )
494 {
495 int min;
496
497 min = lcitv->next->itv.inf - lcitv->itv.sup - 1;
498
499 if( dif > min )
500 dif = min;
501 lcitv = lcitv->next;
502 }
503 colorinterval->itv.sup = colorinterval->itv.inf + dif - 1;
504
505 tocolor -= dif;
506 pnc->next = colorinterval;
507 pnc = colorinterval;
508 }
509 }
510
511 debugMessage("-> updated neighbors:\n");
512
513 /* update saturation degree and neighbor colorintervals of all neighbors of node */
514 Vadj = buffer;
515 nVadj = selectadjnodes(tcliquegraph, node, V, nV, Vadj);
516 for( j = 0, adjidx = 0; j < nV && adjidx < nVadj; ++j )
517 {
518 assert(V[j] <= Vadj[adjidx]); /* Vadj is a subset of V */
519 if( V[j] == Vadj[adjidx] )
520 {
521 if( !iscolored[j] )
522 {
523 debugMessage(" nodeVindex=%d, node=%d, weight=%d, satdegold=%d -> ",
524 j, V[j], weights[V[j]], gsd[j].satdeg);
525 updateNeighbor(mem, &gsd[j], nwcitv.next);
526 debugPrintf("satdegnew=%d, nbc=[%d,%d]\n", gsd[j].satdeg, gsd[j].lcitv->itv.inf, gsd[j].lcitv->itv.sup);
527 }
528
529 /* go to the next node in Vadj */
530 adjidx++;
531 }
532 }
533
534 /* free data structure of created colorintervals */
535 item = nwcitv.next;
536 while( item != NULL )
537 {
538 tmpitem = item->next;
539 /* coverity[double_free] */
540 BMSfreeChunkMemory(mem, &item);
541 item = tmpitem;
542 }
543
544 /* free data structure of neighbor colorinterval of node just colored */
545 item = gsd[nodeVindex].lcitv;
546 while( item != NULL )
547 {
548 tmpitem = item->next;
549 /* coverity[double_free] */
550 BMSfreeChunkMemory(mem, &item);
551 item = tmpitem;
552 }
553 }
554 assert((workclique == clique) != (currentclique == clique));
555
556 /* update maximum weight clique found so far */
557 if( weightcurrentclique > *weightclique )
558 {
559 int* tmp;
560
561 tmp = workclique;
562 *weightclique = weightcurrentclique;
563 *nclique = ncurrentclique;
564 workclique = currentclique;
565 currentclique = tmp;
566 }
567 assert((workclique == clique) != (currentclique == clique));
568
569 /* move the found clique to the provided clique pointer, if it is not the memory array */
570 if( workclique != clique )
571 {
572 assert(clique == currentclique);
573 assert(*nclique <= nV);
574 BMScopyMemoryArray(clique, workclique, *nclique);
575 currentclique = workclique;
576 }
577
578 /* free data structures */
579 BMSfreeMemoryArray(&currentclique);
580
581 /* clear chunk memory */
583
584 debugMessage("------------coloringend-----------------\n");
585
586 return maxsatdegree;
587}
#define NULL
Definition: def.h:266
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
memory allocation routines
#define BMSallocChunkMemory(mem, ptr)
Definition: memory.h:311
struct BMS_ChkMem BMS_CHKMEM
Definition: memory.h:302
#define BMSallocMemoryArray(ptr, num)
Definition: memory.h:123
#define BMSfreeMemoryArray(ptr)
Definition: memory.h:147
#define BMScopyMemoryArray(ptr, source, num)
Definition: memory.h:134
#define BMSfreeChunkMemory(mem, ptr)
Definition: memory.h:314
#define BMSclearMemoryArray(ptr, num)
Definition: memory.h:130
#define BMSclearChunkMemory(mem)
Definition: memory.h:308
tclique user interface
#define TCLIQUE_GETWEIGHTS(x)
Definition: tclique.h:105
#define TCLIQUE_GETNNODES(x)
Definition: tclique.h:97
#define TCLIQUE_SELECTADJNODES(x)
Definition: tclique.h:130
int TCLIQUE_WEIGHT
Definition: tclique.h:48
struct TCLIQUE_Graph TCLIQUE_GRAPH
Definition: tclique.h:49
#define TCLIQUE_Bool
Definition: tclique.h:53
static int getMaxWeightIndex(TCLIQUE_GETNNODES((*getnnodes)), TCLIQUE_GETWEIGHTS((*getweights)), TCLIQUE_GRAPH *tcliquegraph, int *V, int nV)
static void updateNeighbor(BMS_CHKMEM *mem, NBC *pgsd, LIST_ITV *pnc)
static int getMaxSatdegIndex(int *V, int nV, NBC *gsd, TCLIQUE_Bool *iscolored, const TCLIQUE_WEIGHT *weights)
TCLIQUE_WEIGHT tcliqueColoring(TCLIQUE_GETNNODES((*getnnodes)), TCLIQUE_GETWEIGHTS((*getweights)), TCLIQUE_SELECTADJNODES((*selectadjnodes)), TCLIQUE_GRAPH *tcliquegraph, BMS_CHKMEM *mem, int *buffer, int *V, int nV, NBC *gsd, TCLIQUE_Bool *iscolored, TCLIQUE_WEIGHT *apbound, int *clique, int *nclique, TCLIQUE_WEIGHT *weightclique)
coloring part of algorithm for maximum cliques
struct _LIST_ITV LIST_ITV
struct _NBC NBC
tclique defines
#define ALLOC_ABORT(x)
Definition: tclique_def.h:50
#define debugPrintf
Definition: tclique_def.h:81
#define debugMessage
Definition: tclique_def.h:80