Scippy

SCIP

Solving Constraint Integer Programs

compute_symmetry_bliss.cpp
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP 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 SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file compute_symmetry_bliss.cpp
17  * @brief interface for symmetry computations to bliss
18  * @author Marc Pfetsch
19  * @author Thomas Rehn
20  */
21 
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23 
24 #include "compute_symmetry.h"
25 
26 /* include bliss graph */
27 #include <bliss/defs.hh>
28 #include <bliss/graph.hh>
29 
30 #include <vector>
31 #include <list>
32 #include <math.h>
33 
34 /** struct for bliss callback */
35 struct BLISS_Data
36 {
37  SCIP* scip; /**< SCIP pointer */
38  int npermvars; /**< number of variables for permutations */
39  int nperms; /**< number of permutations */
40  int** perms; /**< permutation generators as (nperms x npermvars) matrix */
41  int nmaxperms; /**< maximal number of permutations */
42  int maxgenerators; /**< maximal number of generators constructed (= 0 if unlimited) */
43 };
44 
45 
46 /** callback function for bliss */
47 static
48 void blisshook(
49  void* user_param, /**< parameter supplied at call to bliss */
50  unsigned int n, /**< size of aut vector */
51  const unsigned int* aut /**< automorphism */
52  )
53 {
54  assert( aut != NULL );
55  assert( user_param != NULL );
56 
57  BLISS_Data* data = (BLISS_Data*) user_param;
58  assert( data->scip != NULL );
59  assert( data->npermvars < (int) n );
60  assert( data->maxgenerators >= 0);
61 
62  /* make sure we do not generate more that maxgenerators many permutations, if the limit in bliss is not available */
63  if ( data->maxgenerators != 0 && data->nperms >= data->maxgenerators )
64  return;
65 
66  /* copy first part of automorphism */
67  bool isIdentity = true;
68  int* p = 0;
69  if ( SCIPallocBlockMemoryArray(data->scip, &p, data->npermvars) != SCIP_OKAY )
70  return;
71 
72  for (int j = 0; j < data->npermvars; ++j)
73  {
74  /* convert index of variable-level 0-nodes to variable indices */
75  p[j] = (int) aut[j];
76  if ( p[j] != j )
77  isIdentity = false;
78  }
79 
80  /* ignore trivial generators, i.e. generators that only permute the constraints */
81  if ( isIdentity )
82  {
83  SCIPfreeBlockMemoryArray(data->scip, &p, data->npermvars);
84  return;
85  }
86 
87  /* check whether we should allocate space for perms */
88  if ( data->nmaxperms <= 0 )
89  {
90  if ( data->maxgenerators == 0 )
91  data->nmaxperms = 100; /* seems to cover many cases */
92  else
93  data->nmaxperms = data->maxgenerators;
94 
95  if ( SCIPallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms) != SCIP_OKAY )
96  return;
97  }
98  else if ( data->nperms >= data->nmaxperms ) /* check whether we need to resize */
99  {
100  int newsize = SCIPcalcMemGrowSize(data->scip, data->nperms + 1);
101  assert( newsize >= data->nperms );
102  assert( data->maxgenerators == 0 );
103 
104  if ( SCIPreallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms, newsize) != SCIP_OKAY )
105  return;
106 
107  data->nmaxperms = newsize;
108  }
109 
110  data->perms[data->nperms++] = p;
111 }
112 
113 
114 /** Construct colored graph for symmetry computations
115  *
116  * Construct bipartite graph:
117  * - Each variable gets a different node.
118  * - Each constraint gets a different node.
119  * - Each matrix coefficient gets a different node that is conntected to the two nodes
120  * corresponding to the constraint and variable.
121  *
122  * Each different variable, rhs, and matrix coefficient type gets a different color that is
123  * attached to the corresponding entries.
124  */
125 static
127  SCIP* scip, /**< SCIP instance */
128  bliss::Graph* G, /**< Graph to be constructed */
129  SYM_MATRIXDATA* matrixdata, /**< data for MIP matrix */
130  int& nnodes, /**< number of nodes in graph */
131  int& nedges, /**< number of edges in graph */
132  SCIP_Bool& success /**< whether the construction was successful */
133  )
134 {
135  SCIPdebugMsg(scip, "Building graph with colored coefficient nodes.\n");
136 
137  nnodes = 0;
138  nedges = 0;
139  success = FALSE;
140 
141  /* add nodes for variables */
142  for (int v = 0; v < matrixdata->npermvars; ++v)
143  {
144  const int color = matrixdata->permvarcolors[v];
145  assert( 0 <= color && color < matrixdata->nuniquevars );
146 
147 #ifndef NDEBUG
148  int node = (int) G->add_vertex((unsigned) color);
149  assert( node == v );
150 #else
151  (void) G->add_vertex((unsigned) color);
152 #endif
153 
154  ++nnodes;
155  }
156  assert( (int) G->get_nof_vertices() == matrixdata->npermvars );
157 
158  /* add nodes for rhs of constraints */
159  for (int c = 0; c < matrixdata->nrhscoef; ++c)
160  {
161  const int color = matrixdata->rhscoefcolors[c];
162  assert( 0 <= color && color < matrixdata->nuniquerhs );
163 
164 #ifndef NDEBUG
165  int node = (int) G->add_vertex((unsigned) (matrixdata->nuniquevars + color));
166  assert( node == matrixdata->npermvars + c );
167 #else
168  (void) G->add_vertex((unsigned) (matrixdata->nuniquevars + color));
169 #endif
170 
171  ++nnodes;
172  }
173  assert( (int) G->get_nof_vertices() == matrixdata->npermvars + matrixdata->nrhscoef );
174 
175  /* Grouping of nodes depends on the number of nodes in the bipartite graph class.
176  * If there are more variables than constraints, we group by constraints.
177  * That is, given several variable nodes which are incident to one constraint node by the same color,
178  * we join these variable nodes to the constaint node by only one intermediate node.
179  */
180  const bool groupByConstraints = matrixdata->nrhscoef < matrixdata->npermvars;
181  if ( groupByConstraints )
182  SCIPdebugMsg(scip, "Group intermediate nodes by constraints.\n");
183  else
184  SCIPdebugMsg(scip, "Group intermediate nodes by variables.\n");
185 
186  /* "colored" edges based on all matrix coefficients - loop through ordered matrix coefficients */
187  int nusedcolors = matrixdata->nuniquevars + matrixdata->nuniquerhs;
188 
189  int ninternodes;
190  if ( groupByConstraints )
191  ninternodes = matrixdata->nrhscoef;
192  else
193  ninternodes = matrixdata->npermvars;
194 
195  int* internodes = NULL;
196  SCIP_CALL( SCIPallocBufferArray(scip, &internodes, ninternodes) ); /*lint !e530*/
197  for (int l = 0; l < ninternodes; ++l)
198  internodes[l] = -1;
199 
200  /* We pass through the matrix coeficients, grouped by color, i.e., different coefficients. If the coeffients appear
201  * in the same row or column, it suffices to only generate a single node (depending on groupByConstraints). We store
202  * this node in the array internodes. In order to avoid reinitialization, we store the node number with increasing
203  * numbers for each color. The smallest number for the current color is stored in firstcolornodenumber. */
204  int oldcolor = -1;
205 #ifndef NDEBUG
206  SCIP_Real oldcoef = SCIP_INVALID;
207 #endif
208  int firstcolornodenumber = -1;
209  for (int j = 0; j < matrixdata->nmatcoef; ++j)
210  {
211  int idx = matrixdata->matidx[j];
212  assert( 0 <= idx && idx < matrixdata->nmatcoef );
213 
214  /* find color corresponding to matrix coefficient */
215  const int color = matrixdata->matcoefcolors[idx];
216  assert( 0 <= color && color < matrixdata->nuniquemat );
217 
218  assert( 0 <= matrixdata->matrhsidx[idx] && matrixdata->matrhsidx[idx] < matrixdata->nrhscoef );
219  assert( 0 <= matrixdata->matvaridx[idx] && matrixdata->matvaridx[idx] < matrixdata->npermvars );
220 
221  const int rhsnode = matrixdata->npermvars + matrixdata->matrhsidx[idx];
222  const int varnode = matrixdata->matvaridx[idx];
223  assert( matrixdata->npermvars <= rhsnode && rhsnode < matrixdata->npermvars + matrixdata->nrhscoef );
224  assert( rhsnode < (int) G->get_nof_vertices() );
225  assert( varnode < (int) G->get_nof_vertices() );
226 
227  /* if we have only one color, we do not need intermediate nodes */
228  if ( matrixdata->nuniquemat == 1 )
229  {
230  G->add_edge((unsigned) varnode, (unsigned) rhsnode);
231  ++nedges;
232  }
233  else
234  {
235  /* if new group of coefficients has been reached */
236  if ( color != oldcolor )
237  {
238  assert( ! SCIPisEQ(scip, oldcoef, matrixdata->matcoef[idx]) );
239  oldcolor = color;
240  firstcolornodenumber = nnodes;
241 #ifndef NDEBUG
242  oldcoef = matrixdata->matcoef[idx];
243 #endif
244  }
245  else
246  assert( SCIPisEQ(scip, oldcoef, matrixdata->matcoef[idx]) );
247 
248  int varrhsidx;
249  if ( groupByConstraints )
250  varrhsidx = matrixdata->matrhsidx[idx];
251  else
252  varrhsidx = matrixdata->matvaridx[idx];
253  assert( 0 <= varrhsidx && varrhsidx < ninternodes );
254 
255  if ( internodes[varrhsidx] < firstcolornodenumber )
256  {
257  internodes[varrhsidx] = (int) G->add_vertex((unsigned) (nusedcolors + color));
258  ++nnodes;
259  }
260  assert( internodes[varrhsidx] >= matrixdata->npermvars + matrixdata->nrhscoef );
261  assert( internodes[varrhsidx] >= firstcolornodenumber );
262 
263  /* determine whether graph would be too large for bliss (can only handle int) */
264  if ( nnodes >= INT_MAX/2 )
265  {
266  SCIPfreeBufferArray(scip, &internodes);
267  return SCIP_OKAY;
268  }
269 
270  G->add_edge((unsigned) varnode, (unsigned) internodes[varrhsidx]);
271  G->add_edge((unsigned) rhsnode, (unsigned) internodes[varrhsidx]);
272  nedges += 2;
273  }
274  }
275  SCIPfreeBufferArray(scip, &internodes);
276 
277  success = TRUE; /*lint !e838*/
278 
279  return SCIP_OKAY;
280 }
281 
282 
283 /** return whether symmetry can be computed */
285 {
286  return TRUE;
287 }
288 
289 /** static variable for holding the name of bliss */
290 static char blissname[100];
291 
292 /** return name of external program used to compute generators */
293 const char* SYMsymmetryGetName(void)
294 {
295 #ifdef BLISS_PATCH_PRESENT
296  (void) snprintf(blissname, 100, "bliss %sp", bliss::version);
297 #else
298  (void) snprintf(blissname, 100, "bliss %s", bliss::version);
299 #endif
300  return blissname;
301 }
302 
303 /** return description of external program used to compute generators */
304 const char* SYMsymmetryGetDesc(void)
305 {
306  return "Computing Graph Automorphism Groups by T. Junttila and P. Kaski (http://www.tcs.hut.fi/Software/bliss/)";
307 }
308 
309 /** compute generators of symmetry group */
311  SCIP* scip, /**< SCIP pointer */
312  int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
313  SYM_MATRIXDATA* matrixdata, /**< data for MIP matrix */
314  int* nperms, /**< pointer to store number of permutations */
315  int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
316  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
317  SCIP_Real* log10groupsize /**< pointer to store size of group */
318  )
319 {
320  assert( scip != NULL );
321  assert( matrixdata != NULL );
322  assert( nperms != NULL );
323  assert( nmaxperms != NULL );
324  assert( perms != NULL );
325  assert( log10groupsize != NULL );
326  assert( maxgenerators >= 0 );
327 
328  /* init */
329  *nperms = 0;
330  *nmaxperms = 0;
331  *perms = NULL;
332  *log10groupsize = 0;
333 
334  int nnodes = 0;
335  int nedges = 0;
336 
337  /* create bliss graph */
338  bliss::Graph G(0);
339 
340  SCIP_Bool success = FALSE;
341  SCIP_CALL( fillGraphByColoredCoefficients(scip, &G, matrixdata, nnodes, nedges, success) );
342  if ( ! success )
343  {
344  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, 0, "Graph construction failed.\n");
345  return SCIP_OKAY;
346  }
347 
348 #ifdef SCIP_OUTPUT
349  G.write_dot("debug.dot");
350 #endif
351 
352  SCIPdebugMsg(scip, "Symmetry detection graph has %u nodes.\n", G.get_nof_vertices());
353 
354  /* compute automorphisms */
355  bliss::Stats stats;
356  BLISS_Data data;
357  data.scip = scip;
358  data.npermvars = matrixdata->npermvars;
359  data.nperms = 0;
360  data.nmaxperms = 0;
362  data.perms = NULL;
363 
364  /* Prefer splitting partition cells corresponding to variables over those corresponding
365  * to inequalities. This is because we are only interested in the action
366  * of the automorphism group on the variables, and we need a base for this action */
367  G.set_splitting_heuristic(bliss::Graph::shs_f);
368  /* disable component recursion as advised by Tommi Junttila from bliss */
369  G.set_component_recursion(false);
370 
371  /* do not use a node limit, but set generator limit */
372 #ifdef BLISS_PATCH_PRESENT
373  G.set_search_limits(0, (unsigned) maxgenerators);
374 #endif
375 
376  /* start search */
377  G.find_automorphisms(stats, blisshook, (void*) &data);
378 #ifdef SCIP_OUTPUT
379  (void) stats.print(stdout);
380 #endif
381 
382  /* prepare return values */
383  if ( data.nperms > 0 )
384  {
385  *perms = data.perms;
386  *nperms = data.nperms;
387  *nmaxperms = data.nmaxperms;
388  }
389  else
390  {
391  assert( data.perms == NULL );
392  assert( data.nmaxperms == 0 );
393  }
394 
395  /* determine log10 of symmetry group size */
396  *log10groupsize = (SCIP_Real) log10l(stats.get_group_size_approx());
397 
398  return SCIP_OKAY;
399 }
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:97
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:86
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:80
SCIP_Real * matcoef
const char * SYMsymmetryGetName(void)
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:216
#define FALSE
Definition: def.h:73
#define TRUE
Definition: def.h:72
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:54
const char * SYMsymmetryGetDesc(void)
SCIP_Bool SYMcanComputeSymmetry(void)
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:123
#define SCIPdebugMsg
Definition: scip_message.h:69
interface for symmetry computations
static void blisshook(void *user_param, unsigned int n, const unsigned int *aut)
static char blissname[100]
#define NULL
Definition: lpi_spx1.cpp:155
#define SCIP_CALL(x)
Definition: def.h:370
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:111
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip_mem.c:130
#define SCIP_Bool
Definition: def.h:70
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_MATRIXDATA *matrixdata, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize)
static SCIP_RETCODE fillGraphByColoredCoefficients(SCIP *scip, bliss::Graph *G, SYM_MATRIXDATA *matrixdata, int &nnodes, int &nedges, SCIP_Bool &success)
#define SCIP_Real
Definition: def.h:163
#define SCIP_INVALID
Definition: def.h:183
#define nnodes
Definition: gastrans.c:65