Scippy

SCIP

Solving Constraint Integer Programs

tclique_branch.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-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* TCLIQUE is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with TCLIQUE; see the file COPYING. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file tclique_branch.c
17  * @brief branch and bound part of algorithm for maximum cliques
18  * @author Tobias Achterberg
19  * @author Ralf Borndoerfer
20  * @author Zoltan Kormos
21  * @author Kati Wolter
22  */
23 
24 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
25 
26 #include <stdio.h>
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <limits.h>
30 
31 #include "tclique/tclique.h"
32 #include "tclique/tclique_def.h"
34 #include "blockmemshell/memory.h"
35 
36 
37 
38 #define CHUNK_SIZE (64)
39 #define CLIQUEHASH_INITSIZE (1024)
40 
41 
42 
43 
44 /***************************
45  * clique hash table methods
46  ***************************/
47 
48 typedef struct clique CLIQUE; /**< single element of the clique hash table */
49 
50 /** single element of the clique hash table */
51 struct clique
52 {
53  int* nodes; /**< node number of the clique elements */
54  int nnodes; /**< length of the clique */
55 };
56 
57 typedef struct cliquehash CLIQUEHASH; /**< hash table for storing cliques */
58 
59 /** hash table for storing cliques */
60 struct cliquehash
61 {
62  CLIQUE** cliques; /**< elements of the table */
63  int cliquessize; /**< size of the table */
64  int ncliques; /**< number of cliques stored in the table */
65 };
66 
67 
68 /** creates a clique */
69 static
71  CLIQUE** clique, /**< pointer to the clique */
72  int* nodes, /**< nodes of the clique */
73  int nnodes /**< number of nodes in the clique */
74  )
75 {
76  int i;
77 
78  assert(clique != NULL);
79 
80  ALLOC_ABORT( BMSallocMemory(clique) );
81  ALLOC_ABORT( BMSallocMemoryArray(&(*clique)->nodes, nnodes) );
82 
83  /* sort the nodes into the clique's node array */
84  for( i = 0; i < nnodes; ++i )
85  {
86  int node;
87  int j;
88 
89  node = nodes[i];
90  for( j = i; j > 0 && node < (*clique)->nodes[j-1]; --j )
91  (*clique)->nodes[j] = (*clique)->nodes[j-1];
92  (*clique)->nodes[j] = node;
93  }
94  (*clique)->nnodes = nnodes;
95 }
96 
97 /** frees a clique */
98 static
100  CLIQUE** clique /**< pointer to the clique */
101  )
102 {
103  assert(clique != NULL);
104  assert(*clique != NULL);
105 
106  BMSfreeMemoryArray(&(*clique)->nodes);
107  BMSfreeMemory(clique);
108 }
109 
110 /** checks, whether clique1 is a subset of clique2 and returns the following value:
111  * == 0 if clique1 == clique2, or clique1 is contained in clique2,
112  * < 0 if clique1 < clique2, and clique1 is not contained in clique2,
113  * > 0 if clique1 > clique2, and clique1 is not contained in clique2
114  */
115 static
117  CLIQUE* clique1, /**< first clique to compare */
118  CLIQUE* clique2 /**< second clique to compare */
119  )
120 {
121  int pos1;
122  int pos2;
123  TCLIQUE_Bool clique2smaller;
124 
125  assert(clique1 != NULL);
126  assert(clique2 != NULL);
127 
128  pos1 = 0;
129  pos2 = 0;
130  clique2smaller = FALSE;
131  while( pos1 < clique1->nnodes && pos2 < clique2->nnodes )
132  {
133  if( clique1->nodes[pos1] < clique2->nodes[pos2] )
134  {
135  /* clique1 has an element not contained in clique2: clique1 is lex-smaller, if clique2 was not
136  * detected earlier to be lex-smaller
137  */
138  return (clique2smaller ? +1 : -1);
139  }
140  else if( clique1->nodes[pos1] > clique2->nodes[pos2] )
141  {
142  /* clique2 has an element not contained in clique1: clique2 is lex-smaller, but probably clique1 is
143  * contained in clique2
144  */
145  pos2++;
146  clique2smaller = TRUE;
147  }
148  else
149  {
150  pos1++;
151  pos2++;
152  }
153  }
154 
155  /* if clique1 has additional elements, it is not contained in clique2 */
156  if( pos1 < clique1->nnodes )
157  return (clique2smaller ? +1 : -1);
158 
159  /* clique1 is contained in clique2 */
160  return 0;
161 }
162 
163 #ifndef NDEBUG
164 /** performs an integrity check of the clique hash table */
165 static
167  CLIQUEHASH* cliquehash /**< clique hash table */
168  )
169 {
170  int i;
171 
172  assert(cliquehash != NULL);
173 
174  for( i = 0; i < cliquehash->ncliques-1; ++i )
175  assert(compSubcliques(cliquehash->cliques[i], cliquehash->cliques[i+1]) < 0);
176 }
177 #else
178 #define checkCliquehash(cliquehash) /**/
179 #endif
180 
181 /** creates a table for storing cliques */
182 static
184  CLIQUEHASH** cliquehash, /**< pointer to store the clique hash table */
185  int tablesize /**< initial size of the clique hash table */
186  )
187 {
188  assert(cliquehash != NULL);
189  assert(tablesize > 0);
190 
191  ALLOC_ABORT( BMSallocMemory(cliquehash) );
192  ALLOC_ABORT( BMSallocMemoryArray(&(*cliquehash)->cliques, tablesize) );
193  (*cliquehash)->cliquessize = tablesize;
194  (*cliquehash)->ncliques = 0;
195 }
196 
197 /** clears the clique hash table and frees all inserted cliques */
198 static
200  CLIQUEHASH* cliquehash /**< clique hash table */
201  )
202 {
203  int i;
204 
205  assert(cliquehash != NULL);
206 
207  /* free the cliques in the table */
208  for( i = 0; i < cliquehash->ncliques; ++i )
209  freeClique(&cliquehash->cliques[i]);
210 
211  cliquehash->ncliques = 0;
212 }
213 
214 /** frees the table for storing cliques and all inserted cliques */
215 static
217  CLIQUEHASH** cliquehash /**< pointer to the clique hash table */
218  )
219 {
220  assert(cliquehash != NULL);
221  assert(*cliquehash != NULL);
222 
223  /* free the cliques in the table */
224  clearCliquehash(*cliquehash);
225 
226  /* free the table data structure */
227  BMSfreeMemoryArray(&(*cliquehash)->cliques);
228  BMSfreeMemory(cliquehash);
229 }
230 
231 /** ensures, that the clique hash table is able to store at least the given number of cliques */
232 static
234  CLIQUEHASH* cliquehash, /**< clique hash table */
235  int num /**< minimal number of cliques to store */
236  )
237 {
238  assert(cliquehash != NULL);
239 
240  if( num > cliquehash->cliquessize )
241  {
242  int newsize;
243 
244  newsize = 2*cliquehash->cliquessize;
245  if( num > newsize )
246  newsize = num;
247 
248  ALLOC_ABORT( BMSreallocMemoryArray(&cliquehash->cliques, newsize) );
249  cliquehash->cliquessize = newsize;
250  }
251  assert(cliquehash->cliquessize >= num);
252 }
253 
254 #ifdef TCLIQUE_DEBUG
255 /** displayes clique hash table */
256 static
257 void printCliquehash(
258  CLIQUEHASH* cliquehash /**< clique hash table */
259  )
260 {
261  int i;
262 
263  assert(cliquehash != NULL);
264 
265  debugMessage("cliquehash (%d cliques):\n", cliquehash->ncliques);
266  for( i = 0; i < cliquehash->ncliques; ++i )
267  {
268  int j;
269 
270  debugPrintf("%d:", i);
271  for( j = 0; j < cliquehash->cliques[i]->nnodes; ++j )
272  debugPrintf(" %d", cliquehash->cliques[i]->nodes[j]);
273  debugPrintf("\n");
274  }
275 }
276 #endif
277 
278 /** searches the given clique in the clique hash table and returns whether it (or a stronger clique) exists */
279 static
281  CLIQUEHASH* cliquehash, /**< clique hash table */
282  CLIQUE* clique, /**< clique to search in the table */
283  int* insertpos /**< position where the clique should be inserted in the table */
284  )
285 {
286  int left;
287  int right;
288  int middle;
289  int cmp;
290 
291  assert(cliquehash != NULL);
292  assert(cliquehash->cliquessize > 0);
293  assert(clique != NULL);
294  assert(insertpos != NULL);
295 
296  /* perform a binary search on the clique hash table */
297  left = 0;
298  right = cliquehash->ncliques-1;
299  while( left <= right )
300  {
301  middle = (left+right)/2;
302  cmp = compSubcliques(clique, cliquehash->cliques[middle]);
303  if( cmp > 0 )
304  left = middle+1;
305  else if( cmp < 0 )
306  right = middle-1;
307  else
308  {
309  *insertpos = middle;
310  return TRUE;
311  }
312  }
313 
314  /* we found the correct insertion position for the clique, but it might be contained in a lex-smaller clique */
315  *insertpos = left;
316  for( middle = left-1; middle >= 0; --middle )
317  {
318  cmp = compSubcliques(clique, cliquehash->cliques[middle]);
319  assert(cmp >= 0);
320  if( cmp == 0 )
321  return TRUE;
322  }
323 
324  return FALSE;
325 }
326 
327 /** inserts clique into clique hash table */
328 static
330  CLIQUEHASH* cliquehash, /**< clique hash table */
331  CLIQUE* clique, /**< clique to search in the table */
332  int insertpos /**< position to insert clique into table (returned by inCliquehash()) */
333  )
334 {
335  int i;
336 
337  assert(cliquehash != NULL);
338  assert(clique != NULL);
339  assert(0 <= insertpos && insertpos <= cliquehash->ncliques);
340 
341  /* allocate memory */
342  ensureCliquehashSize(cliquehash, cliquehash->ncliques+1);
343 
344  /* insert clique into table */
345  for( i = cliquehash->ncliques; i > insertpos; --i )
346  cliquehash->cliques[i] = cliquehash->cliques[i-1];
347  cliquehash->cliques[insertpos] = clique;
348  cliquehash->ncliques++;
349 
350  /* check, whether the clique hash table is still sorted */
351  checkCliquehash(cliquehash);
352 
353  debug(printCliquehash(cliquehash));
354 }
355 
356 
357 
358 
359 /****************************
360  * clique calculation methods
361  ****************************/
362 
363 /** extends given clique by additional zero-weight nodes of the given node set */
364 static
366  TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */
367  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
368  int* buffer, /**< buffer of size nnodes */
369  int* Vzero, /**< zero weighted nodes */
370  int nVzero, /**< number of zero weighted nodes */
371  int maxnzeroextensions, /**< maximal number of zero-valued variables extending the clique */
372  int* curcliquenodes, /**< nodes of the clique */
373  int* ncurcliquenodes /**< pointer to store number of nodes in the clique */
374  )
375 {
376  int i;
377  int* zerocands;
378  int nzerocands;
379  int nzeroextensions;
380 
381  assert(selectadjnodes != NULL);
382  assert(buffer != NULL);
383  assert(Vzero != NULL);
384  assert(curcliquenodes != NULL);
385  assert(ncurcliquenodes != NULL);
386 
387  debugMessage("extending temporary clique (size %d) with zero-weighted nodes (nVzero=%d)\n", *ncurcliquenodes, nVzero);
388 
389  if( maxnzeroextensions == 0 )
390  return;
391 
392  /* initialize the zero-weighted candidates for clique extension */
393  zerocands = buffer;
394  BMScopyMemoryArray(zerocands, Vzero, nVzero);
395  nzerocands = nVzero;
396 
397  /* for each node in the clique, remove non-adjacent nodes from the zero extension candidates */
398  for( i = 0; i < *ncurcliquenodes && nzerocands > 0; ++i )
399  {
400  nzerocands = selectadjnodes(tcliquegraph, curcliquenodes[i], zerocands, nzerocands, zerocands);
401  }
402 
403  /* put zero-weighted candidates into the clique, and remove non-adjacent nodes from the candidate set */
404  nzeroextensions = 0;
405  while( nzerocands > 0 )
406  {
407  /* put first candidate into the clique */
408  curcliquenodes[*ncurcliquenodes] = zerocands[0];
409  (*ncurcliquenodes)++;
410  nzerocands--;
411  zerocands++;
412  nzeroextensions++;
413  if( nzeroextensions >= maxnzeroextensions )
414  break;
415 
416  /* remove candidates that are not adjacent to the inserted zero-weighted node */
417  nzerocands = selectadjnodes(tcliquegraph, curcliquenodes[(*ncurcliquenodes)-1], zerocands, nzerocands, zerocands);
418  }
419 }
420 
421 /** calls user callback after a new solution was found, that is better than the current incumbent;
422  * the callback decides, whether this solution should be accepted as new incumbent, and whether the solution process
423  * should be stopped
424  */
425 static
427  TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */
428  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
429  TCLIQUE_NEWSOL ((*newsol)), /**< user function to call on every new solution */
430  TCLIQUE_DATA* tcliquedata, /**< user data to pass to user callback function */
431  CLIQUEHASH* cliquehash, /**< clique hash table */
432  int* buffer, /**< buffer of size nnodes */
433  int* Vzero, /**< zero weighted nodes */
434  int nVzero, /**< number of zero weighted nodes */
435  int maxnzeroextensions, /**< maximal number of zero-valued variables extending the clique */
436  int* curcliquenodes, /**< nodes of the new clique */
437  int ncurcliquenodes, /**< number of nodes in the new clique */
438  TCLIQUE_WEIGHT curcliqueweight, /**< weight of the new clique */
439  int* maxcliquenodes, /**< pointer to store nodes of the maximum weight clique */
440  int* nmaxcliquenodes, /**< pointer to store number of nodes in the maximum weight clique */
441  TCLIQUE_WEIGHT* maxcliqueweight, /**< pointer to store weight of the maximum weight clique */
442  TCLIQUE_Bool* stopsolving /**< pointer to store whether the solving should be stopped */
443  )
444 {
445  CLIQUE* clique;
446  int insertpos;
447  TCLIQUE_Bool acceptsol;
448 
449  assert(curcliquenodes != NULL);
450  assert(maxcliquenodes != NULL);
451  assert(nmaxcliquenodes != NULL);
452  assert(maxcliqueweight != NULL);
453  assert(curcliqueweight > *maxcliqueweight);
454  assert(stopsolving != NULL);
455  assert(newsol == NULL || cliquehash != NULL);
456 
457  acceptsol = TRUE;
458  *stopsolving = FALSE;
459  clique = NULL;
460  insertpos = 0;
461 
462  if( newsol != NULL )
463  {
464  /* check whether the clique is already stored in the table */
465  if( cliquehash->ncliques > 0 )
466  {
467  createClique(&clique, curcliquenodes, ncurcliquenodes);
468  acceptsol = !inCliquehash(cliquehash, clique, &insertpos);
469  }
470  }
471 
472  /* check, if this is a new clique */
473  if( acceptsol )
474  {
475  /* extend the clique with the zero-weighted nodes */
476  extendCliqueZeroWeight(selectadjnodes, tcliquegraph, buffer, Vzero, nVzero, maxnzeroextensions,
477  curcliquenodes, &ncurcliquenodes);
478 
479  if( newsol != NULL )
480  {
481  /* call user callback method */
482  newsol(tcliquedata, curcliquenodes, ncurcliquenodes, curcliqueweight, maxcliqueweight, &acceptsol, stopsolving);
483 
484  /* if clique was accepted, clear the clique hash table; otherwise, insert it into the clique hash table, such that
485  * the same or a weaker clique is not presented to the user again
486  */
487  if( acceptsol )
488  clearCliquehash(cliquehash);
489  else
490  {
491  /* if the clique was not yet created, do it now */
492  if( clique == NULL )
493  {
494  assert(insertpos == 0);
495  assert(cliquehash->ncliques == 0);
496  createClique(&clique, curcliquenodes, ncurcliquenodes);
497  }
498 
499  /* insert clique into clique hash table */
500  insertClique(cliquehash, clique, insertpos);
501  clique = NULL; /* the clique now belongs to the table */
502  }
503  }
504  }
505 
506  /* free the clique, if it was created and not put into the clique hash table */
507  if( clique != NULL )
508  freeClique(&clique);
509 
510  if( acceptsol )
511  {
512  /* copy the solution to the incumbent */
513  BMScopyMemoryArray(maxcliquenodes, curcliquenodes, ncurcliquenodes);
514  *nmaxcliquenodes = ncurcliquenodes;
515  if( curcliqueweight > *maxcliqueweight )
516  *maxcliqueweight = curcliqueweight;
517  }
518 
519 #ifdef TCLIQUE_DEBUG
520  debugMessage(" -> clique %s (weight %d):", acceptsol ? "accepted" : "rejected", curcliqueweight);
521  {
522  int i;
523  for( i = 0; i < ncurcliquenodes; ++i )
524  debugPrintf(" %d", curcliquenodes[i]);
525  debugPrintf("\n");
526  }
527 #endif
528 }
529 
530 /** tries to find a clique, if V has only one or two nodes */
531 static
532 void reduced(
533  TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
534  TCLIQUE_ISEDGE ((*isedge)), /**< user function to check for existence of an edge */
535  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
536  int* V, /**< non-zero weighted nodes for branching */
537  int nV, /**< number of non-zero weighted nodes for branching */
538  TCLIQUE_WEIGHT* apbound, /**< apriori bound of nodes for branching */
539  int* tmpcliquenodes, /**< buffer for storing the temporary clique */
540  int* ntmpcliquenodes, /**< pointer to store number of nodes of the temporary clique */
541  TCLIQUE_WEIGHT* tmpcliqueweight /**< pointer to store weight of the temporary clique */
542  )
543 {
544  const TCLIQUE_WEIGHT* weights;
545 
546  assert(getweights != NULL);
547  assert(isedge != NULL);
548  assert(tcliquegraph != NULL);
549  assert(V != NULL);
550  assert(0 <= nV && nV <= 2);
551  assert(apbound != NULL);
552  assert(tmpcliquenodes != NULL);
553  assert(ntmpcliquenodes != NULL);
554  assert(tmpcliqueweight != NULL);
555 
556  weights = getweights(tcliquegraph);
557  assert(nV == 0 || weights[V[0]] > 0);
558  assert(nV <= 1 || weights[V[1]] > 0);
559 
560  if( nV >= 1 )
561  apbound[0] = weights[V[0]];
562  if( nV >= 2 )
563  apbound[1] = weights[V[1]];
564 
565  /* check if nodes are adjacent */
566  if( nV >= 2 && isedge(tcliquegraph, V[0], V[1]) )
567  {
568  assert(isedge(tcliquegraph, V[1], V[0]));
569 
570  /* put nodes into clique */
571  tmpcliquenodes[0] = V[0];
572  tmpcliquenodes[1] = V[1];
573  *ntmpcliquenodes = 2;
574  *tmpcliqueweight = weights[V[0]] + weights[V[1]];
575  apbound[0] += weights[V[1]];
576  }
577  else if( nV >= 2 && weights[V[1]] > weights[V[0]] )
578  {
579  /* put V[1] into clique */
580  tmpcliquenodes[0] = V[1];
581  *ntmpcliquenodes = 1;
582  *tmpcliqueweight = weights[V[1]];
583  }
584  else if( nV >= 1 )
585  {
586  /* put V[0] into clique */
587  tmpcliquenodes[0] = V[0];
588  *ntmpcliquenodes = 1;
589  *tmpcliqueweight = weights[V[0]];
590  }
591  else
592  {
593  *tmpcliqueweight = 0;
594  *ntmpcliquenodes = 0;
595  }
596 }
597 
598 /** calculates upper bound on remaining subgraph, and heuristically generates a clique */
599 static
601  TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */
602  TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
603  TCLIQUE_ISEDGE ((*isedge)), /**< user function to check for existence of an edge */
604  TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */
605  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
606  BMS_CHKMEM* mem, /**< block memory */
607  int* buffer, /**< buffer of size nnodes */
608  int* V, /**< non-zero weighted nodes for branching */
609  int nV, /**< number of non-zero weighted nodes for branching */
610  NBC* gsd, /**< neighbour color information of all nodes */
611  TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */
612  TCLIQUE_WEIGHT* apbound, /**< apriori bound of nodes for branching */
613  int* tmpcliquenodes, /**< buffer for storing the temporary clique */
614  int* ntmpcliquenodes, /**< pointer to store number of nodes of the temporary clique */
615  TCLIQUE_WEIGHT* tmpcliqueweight /**< pointer to store weight of the temporary clique */
616  )
617 {
618  assert(tmpcliqueweight != NULL);
619 
620  /* check if we are in an easy case with at most 2 nodes left */
621  if( nV <= 2 )
622  {
623  /* get 1- or 2-clique and bounds without coloring */
624  reduced(getweights, isedge, tcliquegraph, V, nV, apbound, tmpcliquenodes, ntmpcliquenodes, tmpcliqueweight);
625  return *tmpcliqueweight;
626  }
627  else
628  {
629  /* color the graph induces by nodes of V to get an upper bound for the remaining subgraph */
630  return tcliqueColoring(getnnodes, getweights, selectadjnodes, tcliquegraph,
631  mem, buffer, V, nV, gsd, iscolored, apbound,
632  tmpcliquenodes, ntmpcliquenodes, tmpcliqueweight);
633  }
634 }
635 
636 /** gets the index of the node of V with the maximum apriori bound; returns -1, if no node exists */
637 static
639  int nV, /**< number of nodes of V */
640  TCLIQUE_WEIGHT* apbound /**< apriori bound of nodes of V */
641  )
642 {
643  TCLIQUE_WEIGHT maxapbound;
644  int maxindex;
645  int i;
646 
647  assert(apbound != NULL);
648 
649  maxapbound = 0;
650  maxindex = -1;
651 
652  for( i = 0 ; i < nV; i++ )
653  {
654  assert(apbound[i] > 0);
655  if( apbound[i] >= maxapbound )
656  {
657  maxapbound = apbound[i];
658  maxindex = i;
659  }
660  }
661 
662  return maxindex;
663 }
664 
665 /** gets the index of the node of V with the maximum apriori bound, but ignores nodes with weights
666  * larger than the given maximal weight;
667  * returns -1 if no node with weight smaller or equal than maxweight is found
668  */
669 static
671  int* V, /**< non-zero weighted nodes for branching */
672  int nV, /**< number of non-zero weighted nodes for branching */
673  const TCLIQUE_WEIGHT* apbound, /**< apriori bound of nodes of V */
674  const TCLIQUE_WEIGHT* weights, /**< weights of nodes */
675  TCLIQUE_WEIGHT maxweight /**< maximal weight of node to be candidate for selection */
676  )
677 {
678  TCLIQUE_WEIGHT maxapbound;
679  int maxindex;
680  int i;
681 
682  assert(apbound != NULL);
683 
684  maxapbound = 0;
685  maxindex = -1;
686 
687  for( i = 0 ; i < nV; i++ )
688  {
689  assert(apbound[i] > 0);
690  assert(weights[V[i]] > 0);
691 
692  if( apbound[i] >= maxapbound && weights[V[i]] <= maxweight )
693  {
694  maxapbound = apbound[i];
695  maxindex = i;
696  }
697  }
698 
699  return maxindex;
700 }
701 
702 /** branches the searching tree, branching nodes are selected in decreasing order of their apriori bound,
703  * returns the level to which we should backtrack, or INT_MAX for continuing normally
704  */
705 static
706 int branch(
707  TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */
708  TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
709  TCLIQUE_ISEDGE ((*isedge)), /**< user function to check for existence of an edge */
710  TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */
711  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure */
712  TCLIQUE_NEWSOL ((*newsol)), /**< user function to call on every new solution */
713  TCLIQUE_DATA* tcliquedata, /**< user data to pass to user callback function */
714  BMS_CHKMEM* mem, /**< block memory */
715  CLIQUEHASH* cliquehash, /**< clique hash table */
716  int* buffer, /**< buffer of size nnodes */
717  int level, /**< level of b&b tree */
718  int* V, /**< non-zero weighted nodes for branching */
719  int nV, /**< number of non-zero weighted nodes for branching */
720  int* Vzero, /**< zero weighted nodes */
721  int nVzero, /**< number of zero weighted nodes */
722  NBC* gsd, /**< neighbour color information of all nodes */
723  TCLIQUE_Bool* iscolored, /**< coloring status of all nodes */
724  int* K, /**< nodes from the b&b tree */
725  TCLIQUE_WEIGHT weightK, /**< weight of the nodes from b&b tree */
726  int* maxcliquenodes, /**< pointer to store nodes of the maximum weight clique */
727  int* nmaxcliquenodes, /**< pointer to store number of nodes in the maximum weight clique */
728  TCLIQUE_WEIGHT* maxcliqueweight, /**< pointer to store weight of the maximum weight clique */
729  int* curcliquenodes, /**< pointer to store nodes of currenct clique */
730  int* ncurcliquenodes, /**< pointer to store number of nodes in current clique */
731  TCLIQUE_WEIGHT* curcliqueweight, /**< pointer to store weight of current clique */
732  int* tmpcliquenodes, /**< buffer for storing the temporary clique */
733  TCLIQUE_WEIGHT maxfirstnodeweight, /**< maximum weight of branching nodes in level 0; 0 if not used
734  ** (for cliques with at least one fractional node) */
735  int* ntreenodes, /**< pointer to store number of nodes of b&b tree */
736  int maxntreenodes, /**< maximal number of nodes of b&b tree */
737  int backtrackfreq, /**< frequency to backtrack to first level of tree (0: no premature backtracking) */
738  int maxnzeroextensions, /**< maximal number of zero-valued variables extending the clique */
739  int fixednode, /**< node that is forced to be in the clique, or -1; must have positive weight */
740  TCLIQUE_STATUS* status /**< pointer to store the status of the solving call */
741  )
742 {
743  TCLIQUE_Bool isleaf;
744  const TCLIQUE_WEIGHT* weights;
745  TCLIQUE_WEIGHT* apbound;
746  TCLIQUE_WEIGHT subgraphweight;
747  TCLIQUE_WEIGHT weightKold;
748  TCLIQUE_WEIGHT tmpcliqueweight;
749  int backtracklevel;
750  int ntmpcliquenodes;
751  int i;
752 
753  assert(getnnodes != NULL);
754  assert(getweights != NULL);
755  assert(selectadjnodes != NULL);
756  assert(mem != NULL);
757  assert(V != NULL);
758  assert(gsd != NULL);
759  assert(iscolored != NULL);
760  assert(K != NULL);
761  assert(maxcliqueweight != NULL);
762  assert(curcliquenodes != NULL);
763  assert(ncurcliquenodes != NULL);
764  assert(curcliqueweight != NULL);
765  assert(ntreenodes != NULL);
766  assert(maxfirstnodeweight >= 0);
767  assert(*ntreenodes >= 0);
768  assert(maxntreenodes >= 0);
769  assert(status != NULL);
770 
771  /* increase the number of nodes, and stop solving, if the node limit is exceeded */
772  (*ntreenodes)++;
773 #ifdef TCLIQUE_DEBUG
774  debugMessage("(level %d, treenode %d) maxclique = %d, curclique = %d [mem=%lld (%lld), cliques=%d]\n",
775  level, *ntreenodes, *maxcliqueweight, *curcliqueweight,
776  BMSgetChunkMemoryUsed(mem), BMSgetMemoryUsed(), cliquehash == NULL ? 0 : cliquehash->ncliques);
777 
778  debugMessage(" -> current branching (weight %d):", weightK);
779  for( i = 0; i < level; ++i )
780  debugPrintf(" %d", K[i]);
781  debugPrintf("\n");
782  debugMessage(" -> branching candidates:");
783  for( i = 0; i < nV; ++i )
784  debugPrintf(" %d", V[i]);
785  debugPrintf("\n");
786 #endif
787  if( *ntreenodes > maxntreenodes )
788  {
789  *status = TCLIQUE_NODELIMIT;
790  return TRUE;
791  }
792 
793  weights = getweights(tcliquegraph);
794  backtracklevel = INT_MAX;
795  isleaf = TRUE;
796 
797  /* allocate temporary memory for a priori bounds */
798  ALLOC_ABORT( BMSallocMemoryArray(&apbound, nV) );
799  BMSclearMemoryArray(apbound, nV);
800 
801  /* use coloring relaxation to generate an upper bound for the current subtree and a heuristic solution */
802  subgraphweight = boundSubgraph(getnnodes, getweights, isedge, selectadjnodes, tcliquegraph,
803  mem, buffer, V, nV, gsd, iscolored, apbound,
804  tmpcliquenodes, &ntmpcliquenodes, &tmpcliqueweight);
805 
806 #ifndef NDEBUG
807  /* check correctness of V and apbound arrays */
808  for( i = 0; i < nV; ++i )
809  {
810  assert(0 <= V[i] && V[i] < getnnodes(tcliquegraph));
811  assert(i == 0 || V[i-1] < V[i]);
812  assert(apbound[i] >= 0);
813  assert((apbound[i] == 0) == (weights[V[i]] == 0));
814  }
815 #endif
816 
817  /* check, whether the heuristic solution is better than the current subtree's solution;
818  * if the user wanted to have a fixed variable inside the clique and we are in level 0, we first have to
819  * fix this variable in this level (the current clique might not contain the fixed node)
820  */
821  if( weightK + tmpcliqueweight > *curcliqueweight && (level > 0 || fixednode == -1) )
822  {
823  /* install the newly generated clique as current clique */
824  for( i = 0; i < level; ++i )
825  curcliquenodes[i] = K[i];
826  for( i = 0; i < ntmpcliquenodes; ++i )
827  curcliquenodes[level+i] = tmpcliquenodes[i];
828  *ncurcliquenodes = level + ntmpcliquenodes;
829  *curcliqueweight = weightK + tmpcliqueweight;
830 
831 #ifdef TCLIQUE_DEBUG
832  debugMessage(" -> new current clique with weight %d at node %d in level %d:",
833  *curcliqueweight, *ntreenodes, level);
834  for( i = 0; i < *ncurcliquenodes; ++i )
835  debugPrintf(" %d", curcliquenodes[i]);
836  debugPrintf("\n");
837 #endif
838  }
839 
840  /* discard subtree, if the upper bound is not better than the weight of the currently best clique;
841  * if only 2 nodes are left, the maximal weighted clique was already calculated in boundSubgraph() and nothing
842  * more has to be done;
843  * however, if the user wanted to have a fixed node and we are in the first decision level, we have to continue
844  */
845  if( weightK + subgraphweight > *maxcliqueweight && (nV > 2 || (fixednode >= 0 && level == 0)) )
846  {
847  int* Vcurrent;
848  int nVcurrent;
849  int nValive;
850  int branchingnode;
851 
852  assert(nV > 0);
853 
854  /* process current subtree */
855  level++;
856 
857  /* set up data structures */
858  ALLOC_ABORT( BMSallocMemoryArray(&Vcurrent, nV-1) );
859 
860  nValive = nV;
861  weightKold = weightK;
862 
863  debugMessage("============================ branching level %d ===============================\n", level);
864 
865  /* branch on the nodes of V by decreasing order of their apriori bound */
866  while( backtracklevel >= level && nValive > 0 )
867  {
868  int branchidx;
869 
870  /* check if we meet the backtracking frequency - in this case abort the search until we have reached first level */
871  if( level > 1 && backtrackfreq > 0 && (*ntreenodes) % backtrackfreq == 0 )
872  {
873  backtracklevel = 1;
874  break;
875  }
876 
877  /* get next branching node */
878  if( level == 1 && fixednode >= 0 )
879  {
880  /* select the fixed node as first "branching" candidate */
881  for( branchidx = 0; branchidx < nValive && V[branchidx] != fixednode; branchidx++ )
882  {}
883  assert(branchidx < nValive);
884  assert(V[branchidx] == fixednode);
885  }
886  else if( level == 1 && maxfirstnodeweight > 0 )
887  branchidx = getMaxApBoundIndexNotMaxWeight(V, nValive, apbound, weights, maxfirstnodeweight);
888  else
889  branchidx = getMaxApBoundIndex(nValive, apbound);
890  if( branchidx < 0 )
891  break;
892  assert(0 <= branchidx && branchidx < nValive && nValive <= nV);
893  assert(apbound[branchidx] > 0);
894  assert(weights[V[branchidx]] > 0);
895 
896  /* test a priori bound */
897  if( (weightKold + apbound[branchidx]) <= *maxcliqueweight )
898  break;
899 
900  debugMessage("%d. branching in level %d (treenode %d): bidx=%d, node %d, weight %d, upperbound: %d+%d = %d, maxclique=%d\n",
901  nV-nValive+1, level, *ntreenodes, branchidx, V[branchidx], weights[V[branchidx]], weightKold,
902  apbound[branchidx], weightKold + apbound[branchidx], *maxcliqueweight);
903 
904  /* because we branch on this node, the node is no leaf in the tree */
905  isleaf = FALSE;
906 
907  /* update the set of nodes from the b&b tree
908  * K = K & {branchingnode}
909  */
910  branchingnode = V[branchidx];
911  K[level-1] = branchingnode;
912  weightK = weightKold + weights[branchingnode];
913 
914  /* update the set of nodes for branching
915  * V = V \ {branchingnode}
916  */
917  nValive--;
918  for( i = branchidx; i < nValive; ++i )
919  {
920  V[i] = V[i+1];
921  apbound[i] = apbound[i+1];
922  }
923 
924  /* set the nodes for the next level of b&b tree
925  * Vcurrent = nodes of V, that are adjacent to branchingnode
926  */
927  nVcurrent = selectadjnodes(tcliquegraph, branchingnode, V, nValive, Vcurrent);
928 
929  /* process the selected subtree */
930  backtracklevel = branch(getnnodes, getweights, isedge, selectadjnodes, tcliquegraph, newsol, tcliquedata,
931  mem, cliquehash, buffer,
932  level, Vcurrent, nVcurrent, Vzero, nVzero, gsd, iscolored, K, weightK,
933  maxcliquenodes, nmaxcliquenodes, maxcliqueweight,
934  curcliquenodes, ncurcliquenodes, curcliqueweight, tmpcliquenodes,
935  maxfirstnodeweight, ntreenodes, maxntreenodes, backtrackfreq, maxnzeroextensions, -1, status);
936 
937  /* if all other candidates stayed in the candidate list, the current branching was optimal and
938  * there is no need to try the remaining ones
939  */
940  if( nVcurrent == nValive )
941  {
942  debugMessage("branching on node %d was optimal - ignoring remaining candidates\n", branchingnode);
943  nValive = 0;
944  }
945 
946  /* if we had a fixed node, ignore all other nodes */
947  if( fixednode >= 0 )
948  nValive = 0;
949  }
950 
951  debugMessage("========================== branching level %d end =============================\n\n", level);
952 
953  /* free data structures */
954  BMSfreeMemoryArray(&Vcurrent);
955  }
956 
957  /* check, whether any branchings have been applied, or if this node is a leaf of the branching tree */
958  if( isleaf )
959  {
960  /* the current clique is the best clique found on the path to this leaf
961  * -> check, whether it is an improvement to the currently best clique
962  */
963  if( *curcliqueweight > *maxcliqueweight )
964  {
965  TCLIQUE_Bool stopsolving;
966 
967  debugMessage("found clique of weight %d at node %d in level %d\n", *curcliqueweight, *ntreenodes, level);
968  newSolution(selectadjnodes, tcliquegraph, newsol, tcliquedata, cliquehash, buffer, Vzero, nVzero,
969  maxnzeroextensions, curcliquenodes, *ncurcliquenodes, *curcliqueweight,
970  maxcliquenodes, nmaxcliquenodes, maxcliqueweight, &stopsolving);
971 
972  if( stopsolving )
973  {
974  debugMessage(" -> solving terminated by callback method\n");
975  backtracklevel = 0;
976  }
977  }
978 
979  /* discard the current clique */
980  *ncurcliquenodes = 0;
981  *curcliqueweight = 0;
982  }
983 
984 #ifdef TCLIQUE_DEBUG
985  if( level > backtracklevel )
986  {
987  debugMessage("premature backtracking after %d nodes - level %d\n", *ntreenodes, level);
988  }
989 #endif
990 
991  /* free data structures */
992  BMSfreeMemoryArray(&apbound);
993 
994  return backtracklevel;
995 }
996 
997 /** finds maximum weight clique */
999  TCLIQUE_GETNNODES((*getnnodes)), /**< user function to get the number of nodes */
1000  TCLIQUE_GETWEIGHTS((*getweights)), /**< user function to get the node weights */
1001  TCLIQUE_ISEDGE ((*isedge)), /**< user function to check for existence of an edge */
1002  TCLIQUE_SELECTADJNODES((*selectadjnodes)), /**< user function to select adjacent edges */
1003  TCLIQUE_GRAPH* tcliquegraph, /**< pointer to graph data structure that is passed to graph callbacks */
1004  TCLIQUE_NEWSOL ((*newsol)), /**< user function to call on every new solution */
1005  TCLIQUE_DATA* tcliquedata, /**< user data to pass to new solution callback function */
1006  int* maxcliquenodes, /**< pointer to store nodes of the maximum weight clique */
1007  int* nmaxcliquenodes, /**< pointer to store number of nodes in the maximum weight clique */
1008  TCLIQUE_WEIGHT* maxcliqueweight, /**< pointer to store weight of the maximum weight clique */
1009  TCLIQUE_WEIGHT maxfirstnodeweight, /**< maximum weight of branching nodes in level 0; 0 if not used
1010  * for cliques with at least one fractional node) */
1011  TCLIQUE_WEIGHT minweight, /**< lower bound for weight of generated cliques */
1012  int maxntreenodes, /**< maximal number of nodes of b&b tree */
1013  int backtrackfreq, /**< frequency to backtrack to first level of tree (0: no premature backtracking) */
1014  int maxnzeroextensions, /**< maximal number of zero-valued variables extending the clique */
1015  int fixednode, /**< node that is forced to be in the clique, or -1; must have positive weight */
1016  int* ntreenodes, /**< pointer to store the number of used tree nodes (or NULL) */
1017  TCLIQUE_STATUS* status /**< pointer to store the status of the solving call */
1018  )
1019 {
1020  CLIQUEHASH* cliquehash;
1021  const TCLIQUE_WEIGHT* weights;
1022  int* buffer;
1023  int* K;
1024  int* V;
1025  int* Vzero;
1026  int nnodes;
1027  int nV;
1028  int nVzero;
1029  int i;
1030  BMS_CHKMEM* mem;
1031  NBC* gsd;
1032  TCLIQUE_Bool* iscolored;
1033  int* curcliquenodes;
1034  int ncurcliquenodes;
1035  TCLIQUE_WEIGHT curcliqueweight;
1036  int* tmpcliquenodes;
1037  int nbbtreenodes;
1038  int backtracklevel;
1039 
1040  assert(maxcliquenodes != NULL);
1041  assert(nmaxcliquenodes != NULL);
1042  assert(maxcliqueweight != NULL);
1043  assert(maxntreenodes >= 0);
1044  assert(backtrackfreq >= 0);
1045  assert(maxnzeroextensions >= 0);
1046  assert(status != NULL);
1047 
1048  *status = TCLIQUE_OPTIMAL;
1049 
1050  /* use default graph callbacks, if NULL pointers are given */
1051  if( getnnodes == NULL )
1052  getnnodes = tcliqueGetNNodes;
1053  if( getweights == NULL )
1054  getweights = tcliqueGetWeights;
1055  if( isedge == NULL )
1056  isedge = tcliqueIsEdge;
1057  if( selectadjnodes == NULL )
1058  selectadjnodes = tcliqueSelectAdjnodes;
1059 
1060  /* get number of nodes */
1061  nnodes = getnnodes(tcliquegraph);
1062 
1063  debugMessage("calculating maximal weighted clique in graph (%d nodes)\n", nnodes);
1064 
1065  /* set up data structures */
1066  if( newsol != NULL )
1067  createCliquehash(&cliquehash, CLIQUEHASH_INITSIZE);
1068  else
1069  cliquehash = NULL;
1070  ALLOC_ABORT( BMSallocMemoryArray(&buffer, nnodes) );
1071  ALLOC_ABORT( BMSallocMemoryArray(&K, nnodes) );
1072  ALLOC_ABORT( BMSallocMemoryArray(&V, nnodes) );
1073  ALLOC_ABORT( BMSallocMemoryArray(&Vzero, nnodes) );
1074  ALLOC_ABORT( BMSallocMemoryArray(&gsd, nnodes) );
1075  ALLOC_ABORT( BMSallocMemoryArray(&iscolored, nnodes) );
1076  ALLOC_ABORT( BMSallocMemoryArray(&curcliquenodes, nnodes) );
1077  ALLOC_ABORT( BMSallocMemoryArray(&tmpcliquenodes, nnodes) );
1078 
1079  /* set weight and number of nodes of maximum weighted clique */
1080  *nmaxcliquenodes = 0;
1081  *maxcliqueweight = minweight-1;
1082  ncurcliquenodes = 0;
1083  curcliqueweight = 0;
1084  nbbtreenodes = 0;
1085 
1086  /* set up V and Vzero */
1087  weights = getweights(tcliquegraph);
1088  assert(weights != NULL);
1089  nV = 0;
1090  nVzero = 0;
1091  for( i = 0 ; i < nnodes; i++ )
1092  {
1093  if( weights[i] == 0 )
1094  {
1095  Vzero[nVzero] = i;
1096  nVzero++;
1097  }
1098  else
1099  {
1100  V[nV] = i;
1101  nV++;
1102  }
1103  }
1104 
1105  /* initialize own memory allocator for coloring */
1106  mem = BMScreateChunkMemory(sizeof(LIST_ITV), CHUNK_SIZE, -1);
1107 
1108  /* branch to find maximum weight clique */
1109  backtracklevel = branch(getnnodes, getweights, isedge, selectadjnodes, tcliquegraph, newsol, tcliquedata, mem,
1110  cliquehash, buffer, 0, V, nV, Vzero, nVzero, gsd, iscolored, K, 0,
1111  maxcliquenodes, nmaxcliquenodes, maxcliqueweight,
1112  curcliquenodes, &ncurcliquenodes, &curcliqueweight, tmpcliquenodes,
1113  maxfirstnodeweight, &nbbtreenodes, maxntreenodes, backtrackfreq, maxnzeroextensions, fixednode, status);
1114 
1115  if ( ntreenodes != NULL )
1116  *ntreenodes = nbbtreenodes;
1117 
1118  if( backtracklevel != INT_MAX && *status == TCLIQUE_OPTIMAL )
1119  *status = TCLIQUE_USERABORT;
1120 
1121  /* delete own memory allocator for coloring */
1122  BMSdestroyChunkMemory(&mem);
1123 
1124  /* free data structures */
1125  BMSfreeMemoryArray(&tmpcliquenodes);
1126  BMSfreeMemoryArray(&curcliquenodes);
1127  BMSfreeMemoryArray(&iscolored);
1128  BMSfreeMemoryArray(&gsd);
1129  BMSfreeMemoryArray(&Vzero);
1130  BMSfreeMemoryArray(&V);
1131  BMSfreeMemoryArray(&K);
1132  BMSfreeMemoryArray(&buffer);
1133  if( newsol != NULL )
1134  freeCliquehash(&cliquehash);
1135 }
1136