Scippy

SCIP

Solving Constraint Integer Programs

presol_symmetry.c
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-2018 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 email to scip@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file presol_symmetry.c
17  * @brief presolver for storing symmetry information about current problem
18  * @author Marc Pfetsch
19  * @author Thomas Rehn
20  *
21  * This presolver computes symmetries of the problem and stores this information in adequate form. It does not
22  * perform additional actions. The symmetry information can be accessed through external functions. However, the user
23  * has to declare the type of symmetry that is needed before execution, see SYMsetSpecRequirement().
24  *
25  * @note We treat implict integer variables as if they were continuous/real variables. The reason is that there is
26  * currently no distinction between implicit integer and implicit binary. Moreover, currently implicit integer variables
27  * hurt our code more than continuous/real variables (we basically do not handle integral variables at all).
28  *
29  * @note We do not copy symmetry information, since it is not clear how this information transfers. Moreover, copying
30  * symmetry might inhibit heuristics. But note that solving the a sub-SCIP might then happen without symmetry
31  * information!
32  */
33 
34 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35 
36 #include <scip/cons_linear.h>
37 #include <scip/cons_knapsack.h>
38 #include <scip/cons_varbound.h>
39 #include <scip/cons_setppc.h>
40 #include <scip/cons_and.h>
41 #include <scip/cons_logicor.h>
42 #include <scip/cons_or.h>
43 #include <scip/cons_xor.h>
44 
45 #include <scip/presol_symmetry.h>
47 
48 #include <string.h>
49 
50 /* presolver properties */
51 #define PRESOL_NAME "symmetry"
52 #define PRESOL_DESC "presolver for computing and storing symmetry information about current problem"
53 #define PRESOL_PRIORITY 0 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers) */
54 #define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
55 #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */
56 
57 /* default parameter values */
58 #define DEFAULT_MAXGENERATORS 1500 /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
59 #define DEFAULT_COMPUTEPRESOLVED TRUE /**< Should the symmetry be computed after presolving (otherwise before presol)? */
60 #define DEFAULT_CHECKSYMMETRIES FALSE /**< Should all symmetries be checked after computation? */
61 #define DEFAULT_DISPLAYNORBITVARS FALSE /**< Should the number of variables affected by some symmetry be displayed? */
62 
63 /* other defines */
64 #define MAXGENNUMERATOR 64000000 /**< determine maximal number of generators by dividing this number by the number of variables */
65 
66 
67 /** presolver data */
68 struct SCIP_PresolData
69 {
70  SCIP_Bool computepresolved; /**< Should the symmetry be computed afer presolving (otherwise before presol)? */
71  int maxgenerators; /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
72  SCIP_Bool checksymmetries; /**< Should all symmetries be checked after computation? */
73  SCIP_Bool displaynorbitvars; /**< Whether the number of variables in non-trivial orbits shall be computed */
74  SYM_HANDLETYPE symtype; /**< type of symmetry of registered calling function */
75  SYM_SPEC symspecrequire; /**< symmetry specification for which we need to compute symmetries */
76  SYM_SPEC symspecrequirefixed;/**< symmetry specification of variables which must be fixed by symmetries */
77  int npermvars; /**< number of variables for permutations */
78  SCIP_VAR** permvars; /**< variables on which permutations act */
79  SCIP_Real* permvarsobj; /**< objective values of permuted variables (for debugging) */
80  int nperms; /**< number of permutations */
81  int nmaxperms; /**< maximal number of permutations (needed for freeing storage) */
82  int** perms; /**< permutation generators as (nperms x npermvars) matrix */
83  SCIP_Real log10groupsize; /**< log10 of size of symmetry group */
84  int norbitvars; /**< number of vars that are contained in a non-trivial orbit */
85  SCIP_Bool computedsym; /**< Have we already tried to compute symmetries? */
86  SCIP_Bool successful; /**< Was the computation of symmetries successful? */
87  int oldmaxpreroundscomponents; /**< original value of parameter constraints/components/maxprerounds */
88  int oldmaxroundsdomcol; /**< original value of parameter presolving/maxrounds/domcol */
89  int oldmaxpreroundsdualfix; /**< original value of parameter propagating/dualfix/maxprerounds */
90  int oldfreqdualfix; /**< original value of parameter propagating/dualfix/freq */
91  SCIP_Bool changeddefaultparams; /**< whether default parameters were changed */
92 };
93 
94 
95 /*
96  * local data structures
97  */
98 
99 /* ------------------- map for variable types ------------------- */
100 
101 /** gets the key of the given element */
102 static
103 SCIP_DECL_HASHGETKEY(SYMhashGetKeyVartype)
104 { /*lint --e{715}*/
105  return elem;
106 }
107 
108 /** returns TRUE iff both keys are equal
109  *
110  * Compare the types of two variables according to objective, lower and upper bound, and variable type.
111  */
112 static
113 SCIP_DECL_HASHKEYEQ(SYMhashKeyEQVartype)
114 {
115  SCIP* scip;
116  SYM_VARTYPE* k1;
117  SYM_VARTYPE* k2;
118 
119  scip = (SCIP*) userptr;
120  k1 = (SYM_VARTYPE*) key1;
121  k2 = (SYM_VARTYPE*) key2;
122 
123  /* first check objective coefficients */
124  if ( ! SCIPisEQ(scip, k1->obj, k2->obj) )
125  return FALSE;
126 
127  /* if still undecided, take lower bound */
128  if ( ! SCIPisEQ(scip, k1->lb, k2->lb) )
129  return FALSE;
130 
131  /* if still undecided, take upper bound */
132  if ( ! SCIPisEQ(scip, k1->ub, k2->ub) )
133  return FALSE;
134 
135  /* if still undecided, take variable type */
136  if ( k1->type != k2->type )
137  return FALSE;
138 
139  return TRUE;
140 }
141 
142 /** returns the hash value of the key */
143 static
144 SCIP_DECL_HASHKEYVAL(SYMhashKeyValVartype)
145 { /*lint --e{715}*/
146  SYM_VARTYPE* k;
147 
148  k = (SYM_VARTYPE*) key;
149 
151 }
152 
153 
154 /* ------------------- sorting function for rhs types ------------------- */
155 
156 /** data struct to store arrays used for sorting rhs types */
158 {
159  SCIP_Real* vals; /**< array of values */
160  SYM_RHSSENSE* senses; /**< array of senses of rhs */
161  int nrhscoef; /**< size of arrays (for debugging) */
162 };
164 
165 /** sort rhs types - first by sense, then by value
166  *
167  * Due to numerical issues, we first sort by sense, then by value.
168  *
169  * result:
170  * < 0: ind1 comes before (is better than) ind2
171  * = 0: both indices have the same value
172  * > 0: ind2 comes after (is worse than) ind2
173  */
174 static
175 SCIP_DECL_SORTINDCOMP(SYMsortRhsTypes)
176 {
177  SYM_SORTRHSTYPE* data;
178  SCIP_Real diffvals;
179 
180  data = (SYM_SORTRHSTYPE*) dataptr;
181  assert( 0 <= ind1 && ind1 < data->nrhscoef );
182  assert( 0 <= ind2 && ind2 < data->nrhscoef );
183 
184  /* first sort by senses */
185  if ( data->senses[ind1] < data->senses[ind2] )
186  return -1;
187  else if ( data->senses[ind1] > data->senses[ind2] )
188  return 1;
189 
190  /* senses are equal, use values */
191  diffvals = data->vals[ind1] - data->vals[ind2];
192 
193  if ( diffvals < 0.0 )
194  return -1;
195  else if ( diffvals > 0.0 )
196  return 1;
197 
198  return 0;
199 }
200 
201 /** sort matrix coefficients
202  *
203  * result:
204  * < 0: ind1 comes before (is better than) ind2
205  * = 0: both indices have the same value
206  * > 0: ind2 comes after (is worse than) ind2
207  */
208 static
209 SCIP_DECL_SORTINDCOMP(SYMsortMatCoef)
210 {
211  SCIP_Real diffvals;
212  SCIP_Real* vals;
213 
214  vals = (SCIP_Real*) dataptr;
215  diffvals = vals[ind1] - vals[ind2];
216 
217  if ( diffvals < 0.0 )
218  return -1;
219  else if ( diffvals > 0.0 )
220  return 1;
221 
222  return 0;
223 }
224 
225 
226 /*
227  * Local methods
228  */
229 
230 /** determines whether variable should be fixed by permutations */
231 static
233  SYM_SPEC fixedtype, /**< bitset of variable types that should be fixed */
234  SCIP_VAR* var /**< variable to be considered */
235  )
236 {
237  if ( (fixedtype & SYM_SPEC_INTEGER) && SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER )
238  return TRUE;
239  if ( (fixedtype & SYM_SPEC_BINARY) && SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
240  return TRUE;
241  if ( (fixedtype & SYM_SPEC_REAL) &&
243  return TRUE;
244  return FALSE;
245 }
246 
247 
248 /** Transforms given variables, scalars, and constant to the corresponding active variables, scalars, and constant.
249  *
250  * @note @p constant needs to be initialized!
251  */
252 static
254  SCIP* scip, /**< SCIP data structure */
255  SCIP_VAR*** vars, /**< pointer to vars array to get active variables for */
256  SCIP_Real** scalars, /**< pointer to scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
257  int* nvars, /**< pointer to number of variables and values in vars and vals array */
258  SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
259  SCIP_Bool transformed /**< transformed constraint? */
260  )
261 {
262  int requiredsize;
263  int v;
264 
265  assert( scip != NULL );
266  assert( vars != NULL );
267  assert( scalars != NULL );
268  assert( *vars != NULL );
269  assert( *scalars != NULL );
270  assert( nvars != NULL );
271  assert( constant != NULL );
272 
273  if ( transformed )
274  {
275  SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, *nvars, constant, &requiredsize, TRUE) );
276 
277  if ( requiredsize > *nvars )
278  {
279  SCIP_CALL( SCIPreallocBufferArray(scip, vars, requiredsize) );
280  SCIP_CALL( SCIPreallocBufferArray(scip, scalars, requiredsize) );
281 
282  SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, requiredsize, constant, &requiredsize, TRUE) );
283  assert( requiredsize <= *nvars );
284  }
285  }
286  else
287  {
288  for (v = 0; v < *nvars; ++v)
289  {
290  SCIP_CALL( SCIPvarGetOrigvarSum(&(*vars)[v], &(*scalars)[v], constant) );
291  }
292  }
293  return SCIP_OKAY;
294 }
295 
296 
297 /** fill in matrix elements into coefficient arrays */
298 static
300  SCIP* scip, /**< SCIP data structure */
301  SCIP_VAR** linvars, /**< array of linear variables */
302  SCIP_Real* linvals, /**< array of linear coefficients values (or NULL if all linear coefficient values are 1) */
303  int nlinvars, /**< number of linear variables */
304  SCIP_Real lhs, /**< left hand side */
305  SCIP_Real rhs, /**< right hand side */
306  SCIP_Bool istransformed, /**< whether the constraint is transformed */
307  SYM_RHSSENSE rhssense, /**< identifier of constraint type */
308  SYM_MATRIXDATA* matrixdata /**< matrix data to be filled in */
309  )
310 {
311  SCIP_VAR** vars;
312  SCIP_Real* vals;
313  SCIP_Real constant = 0.0;
314  int nrhscoef;
315  int nmatcoef;
316  int nvars;
317  int j;
318 
319  assert( scip != NULL );
320  assert( nlinvars == 0 || linvars != NULL );
321  assert( lhs <= rhs );
322 
323  /* do nothing if constraint is empty */
324  if ( nlinvars == 0 )
325  return SCIP_OKAY;
326 
327  /* ignore redundant constraints */
328  if ( SCIPisInfinity(scip, -lhs) && SCIPisInfinity(scip, rhs) )
329  return SCIP_OKAY;
330 
331  /* duplicate variable and value array */
332  nvars = nlinvars;
333  SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, linvars, nvars) );
334  if ( linvals != NULL )
335  {
336  SCIP_CALL( SCIPduplicateBufferArray(scip, &vals, linvals, nvars) );
337  }
338  else
339  {
340  SCIP_CALL( SCIPallocBufferArray(scip, &vals, nvars) );
341  for (j = 0; j < nvars; ++j)
342  vals[j] = 1.0;
343  }
344  assert( vars != NULL );
345  assert( vals != NULL );
346 
347  /* get active variables */
348  SCIP_CALL( getActiveVariables(scip, &vars, &vals, &nvars, &constant, istransformed) );
349 
350  /* check whether constraint is empty after transformation to active variables */
351  if ( nvars <= 0 )
352  {
353  SCIPfreeBufferArray(scip, &vals);
354  SCIPfreeBufferArray(scip, &vars);
355  return SCIP_OKAY;
356  }
357 
358  /* handle constant */
359  if ( ! SCIPisInfinity(scip, -lhs) )
360  lhs -= constant;
361  if ( ! SCIPisInfinity(scip, rhs) )
362  rhs -= constant;
363 
364  /* check whether we have to resize; note that we have to add 2 * nvars since two inequalities may be added */
365  if ( matrixdata->nmatcoef + 2 * nvars > matrixdata->nmaxmatcoef )
366  {
367  int newsize;
368 
369  newsize = SCIPcalcMemGrowSize(scip, matrixdata->nmatcoef + 2 * nvars);
370  assert( newsize >= 0 );
371  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matidx), matrixdata->nmaxmatcoef, newsize) );
372  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matrhsidx), matrixdata->nmaxmatcoef, newsize) );
373  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matvaridx), matrixdata->nmaxmatcoef, newsize) );
374  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matcoef), matrixdata->nmaxmatcoef, newsize) );
375  SCIPdebugMsg(scip, "Resized matrix coefficients from %u to %d.\n", matrixdata->nmaxmatcoef, newsize);
376  matrixdata->nmaxmatcoef = newsize;
377  }
378 
379  nrhscoef = matrixdata->nrhscoef;
380  nmatcoef = matrixdata->nmatcoef;
381 
382  /* check lhs/rhs */
383  if ( SCIPisEQ(scip, lhs, rhs) )
384  {
385  assert( ! SCIPisInfinity(scip, rhs) );
386 
387  /* equality constraint */
388  matrixdata->rhscoef[nrhscoef] = rhs;
389  /* if we deal with special constraints */
390  if ( (int) rhssense >= 3 )
391  matrixdata->rhssense[nrhscoef] = rhssense;
392  else
393  matrixdata->rhssense[nrhscoef] = SYM_SENSE_EQUATION;
394  matrixdata->rhsidx[nrhscoef] = nrhscoef;
395 
396  for (j = 0; j < nvars; ++j)
397  {
398  assert( nmatcoef < matrixdata->nmaxmatcoef );
399 
400  matrixdata->matidx[nmatcoef] = nmatcoef;
401  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
402 
403  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
404 
405  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
406  matrixdata->matcoef[nmatcoef++] = vals[j];
407  }
408  nrhscoef++;
409  }
410  else
411  {
412  if ( ! SCIPisInfinity(scip, -lhs) )
413  {
414  matrixdata->rhscoef[nrhscoef] = -lhs;
415  matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
416  matrixdata->rhsidx[nrhscoef] = nrhscoef;
417 
418  for (j = 0; j < nvars; ++j)
419  {
420  assert( nmatcoef < matrixdata->nmaxmatcoef );
421  matrixdata->matidx[nmatcoef] = nmatcoef;
422  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
423  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
424 
425  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
426 
427  matrixdata->matcoef[nmatcoef++] = -vals[j];
428  }
429  nrhscoef++;
430  }
431 
432  if ( ! SCIPisInfinity(scip, rhs) )
433  {
434  matrixdata->rhscoef[nrhscoef] = rhs;
435  matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
436  matrixdata->rhsidx[nrhscoef] = nrhscoef;
437 
438  for (j = 0; j < nvars; ++j)
439  {
440  assert( nmatcoef < matrixdata->nmaxmatcoef );
441  matrixdata->matidx[nmatcoef] = nmatcoef;
442  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
443 
444  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
445 
446  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
447  matrixdata->matcoef[nmatcoef++] = vals[j];
448  }
449  nrhscoef++;
450  }
451  }
452  matrixdata->nrhscoef = nrhscoef;
453  matrixdata->nmatcoef = nmatcoef;
454 
455  SCIPfreeBufferArray(scip, &vals);
456  SCIPfreeBufferArray(scip, &vars);
457 
458  return SCIP_OKAY;
459 }
460 
461 
462 /** checks whether given permutations form a symmetry of a MIP
463  *
464  * We need the matrix and rhs in the original order in order to speed up the comparison process. The matrix is needed
465  * in the right order to easily check rows. The rhs is used because of cache effects.
466  */
467 static
469  SCIP* scip, /**< SCIP data structure */
470  SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
471  SYM_MATRIXDATA* matrixdata, /**< matrix data */
472  int nperms, /**< number of permutations */
473  int** perms /**< permutations */
474  )
475 {
476  SCIP_Real* permrow = 0;
477  int* rhsmatbeg = 0;
478  int oldrhs;
479  int j;
480  int p;
481 
482  SCIPdebugMsg(scip, "Checking whether symmetries are symmetries (generators: %u).\n", nperms);
483 
484  /* set up dense arrow for permuted row */
485  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &permrow, matrixdata->npermvars) );
486 
487  /* set up map between rows and first entry in matcoef array */
488  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &rhsmatbeg, matrixdata->nrhscoef) );
489  for (j = 0; j < matrixdata->nrhscoef; ++j)
490  rhsmatbeg[j] = -1;
491 
492  /* build map from rhs into matrix */
493  oldrhs = -1;
494  for (j = 0; j < matrixdata->nmatcoef; ++j)
495  {
496  int rhs;
497 
498  rhs = matrixdata->matrhsidx[j];
499  if ( rhs != oldrhs )
500  {
501  assert( 0 <= rhs && rhs < matrixdata->nrhscoef );
502  rhsmatbeg[rhs] = j;
503  oldrhs = rhs;
504  }
505  }
506 
507  /* create row */
508  for (j = 0; j < matrixdata->npermvars; ++j)
509  permrow[j] = 0.0;
510 
511  /* check all generators */
512  for (p = 0; p < nperms; ++p)
513  {
514  int* P;
515  int r1;
516  int r2;
517 
518  SCIPdebugMsg(scip, "Verifying automorphism group generator #%d ...\n", p);
519  P = perms[p];
520  assert( P != NULL );
521 
522  for (j = 0; j < matrixdata->npermvars; ++j)
523  {
524  if ( SymmetryFixVar(fixedtype, matrixdata->permvars[j]) && P[j] != j )
525  {
526  SCIPdebugMsg(scip, "Permutation does not fix types %u, moving variable %d.\n", fixedtype, j);
527  return SCIP_ERROR;
528  }
529  }
530 
531  /* check all constraints == rhs */
532  for (r1 = 0; r1 < matrixdata->nrhscoef; ++r1)
533  {
534  int npermuted = 0;
535 
536  /* fill row into permrow (dense) */
537  j = rhsmatbeg[r1];
538  assert( 0 <= j && j < matrixdata->nmatcoef );
539  assert( matrixdata->matrhsidx[j] == r1 ); /* note: row cannot be empty by construction */
540 
541  /* loop through row */
542  while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
543  {
544  int varidx;
545 
546  assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
547  varidx = P[matrixdata->matvaridx[j]];
548  assert( 0 <= varidx && varidx < matrixdata->npermvars );
549  if ( varidx != matrixdata->matvaridx[j] )
550  ++npermuted;
551  assert( SCIPisZero(scip, permrow[varidx]) );
552  permrow[varidx] = matrixdata->matcoef[j];
553  ++j;
554  }
555 
556  /* if row is not affected by permutation, we do not have to check it */
557  if ( npermuted > 0 )
558  {
559  /* check other rows (sparse) */
560  SCIP_Bool found = FALSE;
561  for (r2 = 0; r2 < matrixdata->nrhscoef; ++r2)
562  {
563  /* a permutation must map constraints of the same type and respect rhs coefficients */
564  if ( matrixdata->rhssense[r1] == matrixdata->rhssense[r2] && SCIPisEQ(scip, matrixdata->rhscoef[r1], matrixdata->rhscoef[r2]) )
565  {
566  j = rhsmatbeg[r2];
567  assert( 0 <= j && j < matrixdata->nmatcoef );
568  assert( matrixdata->matrhsidx[j] == r2 );
569  assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
570 
571  /* loop through row r2 and check whether it is equal to permuted row r */
572  while (j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r2 && SCIPisEQ(scip, permrow[matrixdata->matvaridx[j]], matrixdata->matcoef[j]) )
573  ++j;
574 
575  /* check whether rows are completely equal */
576  if ( j >= matrixdata->nmatcoef || matrixdata->matrhsidx[j] != r2 )
577  {
578  /* perm[p] is indeed a symmetry */
579  found = TRUE;
580  break;
581  }
582  }
583  }
584 
585  assert( found );
586  if ( ! found ) /*lint !e774*/
587  {
588  SCIPerrorMessage("Found permutation that is not a symmetry.\n");
589  return SCIP_ERROR;
590  }
591  }
592 
593  /* reset permrow */
594  j = rhsmatbeg[r1];
595  while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
596  {
597  int varidx;
598  varidx = P[matrixdata->matvaridx[j]];
599  permrow[varidx] = 0.0;
600  ++j;
601  }
602  }
603  }
604 
605  SCIPfreeBlockMemoryArray(scip, &rhsmatbeg, matrixdata->nrhscoef);
606  SCIPfreeBlockMemoryArray(scip, &permrow, matrixdata->npermvars);
607 
608  return SCIP_OKAY;
609 }
610 
611 
612 /** returns the number of active constraints that can be handled by symmetry */
613 static
615  SCIP* scip /**< SCIP instance */
616  )
617 {
618  SCIP_CONSHDLR* conshdlr;
619  int nhandleconss = 0;
620 
621  assert( scip != NULL );
622 
623  conshdlr = SCIPfindConshdlr(scip, "linear");
624  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
625  conshdlr = SCIPfindConshdlr(scip, "setppc");
626  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
627  conshdlr = SCIPfindConshdlr(scip, "xor");
628  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
629  conshdlr = SCIPfindConshdlr(scip, "and");
630  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
631  conshdlr = SCIPfindConshdlr(scip, "or");
632  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
633  conshdlr = SCIPfindConshdlr(scip, "logicor");
634  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
635  conshdlr = SCIPfindConshdlr(scip, "knapsack");
636  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
637  conshdlr = SCIPfindConshdlr(scip, "varbound");
638  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
639  conshdlr = SCIPfindConshdlr(scip, "bounddisjunction");
640  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
641 
642  return nhandleconss;
643 }
644 
645 /** compute symmetry group of MIP */
646 static
648  SCIP* scip, /**< SCIP pointer */
649  int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
650  SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
651  SCIP_Bool local, /**< Use local variable bounds? */
652  SCIP_Bool checksymmetries, /**< Should all symmetries be checked after computation? */
653  int* npermvars, /**< pointer to store number of variables for permutations */
654  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
655  SCIP_Real** permvarsobj, /**< objective values of permuted variables */
656  int* nperms, /**< pointer to store number of permutations */
657  int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
658  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
659  SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
660  SCIP_Bool* success /**< pointer to store whether symmetry computation was successful */
661  )
662 {
663  SCIP_CONSHDLR* conshdlr;
664  SYM_MATRIXDATA matrixdata;
665  SCIP_HASHTABLE* vartypemap;
666  SCIP_VAR** consvars;
667  SCIP_Real* consvals;
668  SCIP_CONS** conss;
669  SCIP_VAR** vars;
670  SYM_VARTYPE* uniquevararray;
671  SYM_RHSSENSE oldsense = SYM_SENSE_UNKOWN;
672  SYM_SORTRHSTYPE sortrhstype;
673  SCIP_Real oldcoef = SCIP_INVALID;
674  SCIP_Real val;
675  int nuniquevararray = 0;
676  int nhandleconss;
677  int nactiveconss;
678  int nconss;
679  int nvars;
680  int nallvars;
681  int c;
682  int j;
683 
684  assert( scip != NULL );
685  assert( npermvars != NULL );
686  assert( permvars != NULL );
687  assert( permvarsobj != NULL );
688  assert( nperms != NULL );
689  assert( nmaxperms != NULL );
690  assert( perms != NULL );
691  assert( log10groupsize != NULL );
692  assert( success != NULL );
693 
694  /* init */
695  *npermvars = 0;
696  *permvars = NULL;
697  *permvarsobj = NULL;
698  *nperms = 0;
699  *nmaxperms = 0;
700  *perms = NULL;
701  *log10groupsize = 0;
702  *success = FALSE;
703 
704  /* skip if no symmetry can be computed */
705  if ( ! SYMcanComputeSymmetry() )
706  return SCIP_OKAY;
707 
708  nconss = SCIPgetNConss(scip);
709  nvars = SCIPgetNVars(scip);
710 
711  /* exit if no constraints or no variables are available */
712  if ( nconss == 0 || nvars == 0 )
713  {
714  *success = TRUE;
715  return SCIP_OKAY;
716  }
717 
718  conss = SCIPgetConss(scip);
719  assert( conss != NULL );
720 
721  /* compute the number of active constraints */
722  nactiveconss = SCIPgetNActiveConss(scip);
723 
724  /* exit if no active constraints are available */
725  if ( nactiveconss == 0 )
726  {
727  *success = TRUE;
728  return SCIP_OKAY;
729  }
730 
731  /* before we set up the matrix, check whether we can handle all constraints */
732  nhandleconss = getNSymhandableConss(scip);
733  assert( nhandleconss <= nactiveconss );
734  if ( nhandleconss < nactiveconss )
735  {
736  /* In this case we found unkown constraints and we exit, since we cannot handle them. */
737  *success = FALSE;
738  return SCIP_OKAY;
739  }
740 
741  SCIPdebugMsg(scip, "Detecting %ssymmetry on %d variables and %d constraints.\n", local ? "local " : "", nvars, nactiveconss);
742 
743  /* copy variables */
744  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &vars, SCIPgetVars(scip), nvars) ); /*lint !e666*/
745  assert( vars != NULL );
746 
747  /* fill matrixdata */
748  matrixdata.nmaxmatcoef = 100 * nvars;
749  matrixdata.nmatcoef = 0;
750  matrixdata.nrhscoef = 0;
751  matrixdata.nuniquemat = 0;
752  matrixdata.nuniquevars = 0;
753  matrixdata.nuniquerhs = 0;
754  matrixdata.npermvars = nvars;
755  matrixdata.permvars = vars;
756  matrixdata.permvarcolors = NULL;
757  matrixdata.matcoefcolors = NULL;
758  matrixdata.rhscoefcolors = NULL;
759 
760  /* prepare matrix data (use block memory, since this can become large) */
761  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef) );
762  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef) );
763  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef) );
764  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef) );
765  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoef, 2 * nactiveconss) );
766  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhssense, 2 * nactiveconss) );
767  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhsidx, 2 * nactiveconss) );
768 
769  /* prepare temporary constraint data (use block memory, since this can become large);
770  * also allocate memory for fixed vars since some vars might have been deactivated meanwhile */
771  nallvars = nvars + SCIPgetNFixedVars(scip);
772  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consvars, nallvars) );
773  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consvals, nallvars) );
774 
775  /* loop through all constraints */
776  for (c = 0; c < nconss; ++c)
777  {
778  const char* conshdlrname;
779  SCIP_CONS* cons;
780  SCIP_VAR** linvars;
781  int nconsvars;
782 
783  /* get constraint */
784  cons = conss[c];
785  assert( cons != NULL );
786 
787  /* skip non-active constraints */
788  if ( ! SCIPconsIsActive(cons) )
789  continue;
790 
791  /* get constraint handler */
792  conshdlr = SCIPconsGetHdlr(cons);
793  assert( conshdlr != NULL );
794 
795  conshdlrname = SCIPconshdlrGetName(conshdlr);
796  assert( conshdlrname != NULL );
797 
798  /* check type of constraint */
799  if ( strcmp(conshdlrname, "linear") == 0 )
800  {
801  SCIP_CALL( collectCoefficients(scip, SCIPgetVarsLinear(scip, cons), SCIPgetValsLinear(scip, cons),
802  SCIPgetNVarsLinear(scip, cons), SCIPgetLhsLinear(scip, cons), SCIPgetRhsLinear(scip, cons),
803  SCIPconsIsTransformed(cons), SYM_SENSE_UNKOWN, &matrixdata) );
804  }
805  else if ( strcmp(conshdlrname, "setppc") == 0 )
806  {
807  linvars = SCIPgetVarsSetppc(scip, cons);
808  nconsvars = SCIPgetNVarsSetppc(scip, cons);
809 
810  switch ( SCIPgetTypeSetppc(scip, cons) )
811  {
813  SCIP_CALL( collectCoefficients(scip, linvars, 0, nconsvars, 1.0, 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_EQUATION, &matrixdata) );
814  break;
816  SCIP_CALL( collectCoefficients(scip, linvars, 0, nconsvars, -SCIPinfinity(scip), 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata) );
817  break;
819  SCIP_CALL( collectCoefficients(scip, linvars, 0, nconsvars, 1.0, SCIPinfinity(scip), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata) );
820  break;
821  default:
822  SCIPerrorMessage("Unknown setppc type %d.\n", SCIPgetTypeSetppc(scip, cons));
823  return SCIP_ERROR;
824  }
825  }
826  else if ( strcmp(conshdlrname, "xor") == 0 )
827  {
828  SCIP_VAR** curconsvars;
829  SCIP_VAR* var;
830 
831  /* get number of variables of XOR constraint (without integer variable) */
832  nconsvars = SCIPgetNVarsXor(scip, cons);
833 
834  /* get variables of XOR constraint */
835  curconsvars = SCIPgetVarsXor(scip, cons);
836  for (j = 0; j < nconsvars; ++j)
837  {
838  assert( curconsvars[j] != NULL );
839  consvars[j] = curconsvars[j];
840  consvals[j] = 1.0;
841  }
842 
843  /* intVar of xor constraint might have been removed */
844  var = SCIPgetIntVarXor(scip, cons);
845  if ( var != NULL )
846  {
847  consvars[nconsvars] = var;
848  consvals[nconsvars++] = 2.0;
849  }
850  assert( nconsvars <= nallvars );
851 
852  SCIP_CALL( collectCoefficients(scip, consvars, consvals, nconsvars, (SCIP_Real) SCIPgetRhsXor(scip, cons),
853  (SCIP_Real) SCIPgetRhsXor(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_XOR, &matrixdata) );
854  }
855  else if ( strcmp(conshdlrname, "and") == 0 )
856  {
857  SCIP_VAR** curconsvars;
858 
859  /* get number of variables of AND constraint (without resultant) */
860  nconsvars = SCIPgetNVarsAnd(scip, cons);
861 
862  /* get variables of AND constraint */
863  curconsvars = SCIPgetVarsAnd(scip, cons);
864 
865  for (j = 0; j < nconsvars; ++j)
866  {
867  assert( curconsvars[j] != NULL );
868  consvars[j] = curconsvars[j];
869  consvals[j] = 1.0;
870  }
871 
872  assert( SCIPgetResultantAnd(scip, cons) != NULL );
873  consvars[nconsvars] = SCIPgetResultantAnd(scip, cons);
874  consvals[nconsvars++] = 2.0;
875  assert( nconsvars <= nallvars );
876 
877  SCIP_CALL( collectCoefficients(scip, consvars, consvals, nconsvars, 0.0, 0.0,
878  SCIPconsIsTransformed(cons), SYM_SENSE_AND, &matrixdata) );
879  }
880  else if ( strcmp(conshdlrname, "or") == 0 )
881  {
882  SCIP_VAR** curconsvars;
883 
884  /* get number of variables of OR constraint (without resultant) */
885  nconsvars = SCIPgetNVarsOr(scip, cons);
886 
887  /* get variables of OR constraint */
888  curconsvars = SCIPgetVarsOr(scip, cons);
889 
890  for (j = 0; j < nconsvars; ++j)
891  {
892  assert( curconsvars[j] != NULL );
893  consvars[j] = curconsvars[j];
894  consvals[j] = 1.0;
895  }
896 
897  assert( SCIPgetResultantOr(scip, cons) != NULL );
898  consvars[nconsvars] = SCIPgetResultantOr(scip, cons);
899  consvals[nconsvars++] = 2.0;
900  assert( nconsvars <= nallvars );
901 
902  SCIP_CALL( collectCoefficients(scip, consvars, consvals, nconsvars, 0.0, 0.0,
903  SCIPconsIsTransformed(cons), SYM_SENSE_OR, &matrixdata) );
904  }
905  else if ( strcmp(conshdlrname, "logicor") == 0 )
906  {
907  SCIP_CALL( collectCoefficients(scip, SCIPgetVarsLogicor(scip, cons), 0, SCIPgetNVarsLogicor(scip, cons),
908  1.0, SCIPinfinity(scip), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata) );
909  }
910  else if ( strcmp(conshdlrname, "knapsack") == 0 )
911  {
912  SCIP_Longint* weights;
913 
914  nconsvars = SCIPgetNVarsKnapsack(scip, cons);
915 
916  /* copy Longint array to SCIP_Real array and get active variables of constraint */
917  weights = SCIPgetWeightsKnapsack(scip, cons);
918  for (j = 0; j < nconsvars; ++j)
919  consvals[j] = (SCIP_Real) weights[j];
920  assert( nconsvars <= nallvars );
921 
922  SCIP_CALL( collectCoefficients(scip, SCIPgetVarsKnapsack(scip, cons), consvals, nconsvars, -SCIPinfinity(scip),
923  (SCIP_Real) SCIPgetCapacityKnapsack(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata) );
924  }
925  else if ( strcmp(conshdlrname, "varbound") == 0 )
926  {
927  consvars[0] = SCIPgetVarVarbound(scip, cons);
928  consvals[0] = 1.0;
929 
930  consvars[1] = SCIPgetVbdvarVarbound(scip, cons);
931  consvals[1] = SCIPgetVbdcoefVarbound(scip, cons);
932 
933  SCIP_CALL( collectCoefficients(scip, consvars, consvals, 2, SCIPgetLhsVarbound(scip, cons),
934  SCIPgetRhsVarbound(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata) );
935  }
936  else if ( strcmp(conshdlrname, "bounddisjunction") == 0 )
937  {
938  /* currently assume bound disjunctions are o.k. for non local symmetry groups */
939  if ( local )
940  {
941  /* @todo we need to handle bounddisjunctions if local symmetry groups are considered */
942  SCIPerrorMessage("Cannot determine symmetries for constraint <%s> of constraint handler <%s>.\n",
943  SCIPconsGetName(cons), SCIPconshdlrGetName(conshdlr) );
944  return SCIP_ERROR;
945  }
946  }
947  else
948  {
949  SCIPerrorMessage("Cannot determine symmetries for constraint <%s> of constraint handler <%s>.\n",
950  SCIPconsGetName(cons), SCIPconshdlrGetName(conshdlr) );
951  return SCIP_ERROR;
952  }
953  }
954  assert( matrixdata.nrhscoef <= 2 * nactiveconss );
955  assert( matrixdata.nrhscoef > 0 ); /* cannot have empty rows! */
956 
957  SCIPfreeBlockMemoryArray(scip, &consvals, nallvars);
958  SCIPfreeBlockMemoryArray(scip, &consvars, nallvars);
959 
960  /* sort matrix coefficients (leave matrix array intact) */
961  SCIPsort(matrixdata.matidx, SYMsortMatCoef, (void*) matrixdata.matcoef, matrixdata.nmatcoef);
962 
963  /* sort rhs types (first by sense, then by value, leave rhscoef intact) */
964  sortrhstype.vals = matrixdata.rhscoef;
965  sortrhstype.senses = matrixdata.rhssense;
966  sortrhstype.nrhscoef = matrixdata.nrhscoef;
967  SCIPsort(matrixdata.rhsidx, SYMsortRhsTypes, (void*) &sortrhstype, matrixdata.nrhscoef);
968 
969  /* create map for variables to indices */
970  SCIP_CALL( SCIPhashtableCreate(&vartypemap, SCIPblkmem(scip), 5 * nvars, SYMhashGetKeyVartype, SYMhashKeyEQVartype, SYMhashKeyValVartype, (void*) scip) );
971  assert( vartypemap != NULL );
972 
973  /* allocate space for mappings to colors */
974  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.permvarcolors, nvars) );
975  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef) );
976  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef) );
977  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &uniquevararray, nvars) );
978 
979  /* determine number of different coefficents */
980 
981  /* find non-equivalent variables: same objective, lower and upper bounds, and variable type */
982  for (j = 0; j < nvars; ++j)
983  {
984  SCIP_VAR* var;
985 
986  var = vars[j];
987  assert( var != NULL );
988 
989  /* if the variable type should be fixed just increase the color */
990  if ( SymmetryFixVar(fixedtype, var) )
991  {
992  matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
993 #ifdef SCIP_OUTPUT
994  SCIPdebugMsg(scip, "Detected variable <%s> of fixed type %d - color %d.\n", SCIPvarGetName(var), SCIPvarGetType(var), matrixdata.nuniquevars - 1);
995 #endif
996  }
997  else
998  {
999  SYM_VARTYPE* vt;
1000 
1001  vt = &uniquevararray[nuniquevararray];
1002  assert( nuniquevararray <= matrixdata.nuniquevars );
1003 
1004  vt->obj = SCIPvarGetObj(var);
1005  if ( local )
1006  {
1007  vt->lb = SCIPvarGetLbLocal(var);
1008  vt->ub = SCIPvarGetUbLocal(var);
1009  }
1010  else
1011  {
1012  vt->lb = SCIPvarGetLbGlobal(var);
1013  vt->ub = SCIPvarGetUbGlobal(var);
1014  }
1015  vt->type = SCIPvarGetType(var);
1016 
1017  if ( ! SCIPhashtableExists(vartypemap, (void*) vt) )
1018  {
1019  SCIP_CALL( SCIPhashtableInsert(vartypemap, (void*) vt) );
1020  vt->color = matrixdata.nuniquevars;
1021  matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
1022  ++nuniquevararray;
1023 #ifdef SCIP_OUTPUT
1024  SCIPdebugMsg(scip, "Detected variable <%s> of new type (probindex: %d, obj: %g, lb: %g, ub: %g, type: %d) - color %d.\n",
1025  SCIPvarGetName(var), SCIPvarGetProbindex(var), vt->obj, vt->lb, vt->ub, vt->type, matrixdata.nuniquevars - 1);
1026 #endif
1027  }
1028  else
1029  {
1030  SYM_VARTYPE* vtr;
1031 
1032  vtr = (SYM_VARTYPE*) SCIPhashtableRetrieve(vartypemap, (void*) vt);
1033  matrixdata.permvarcolors[j] = vtr->color;
1034  }
1035  }
1036  }
1037 
1038  /* find non-equivalent matrix entries (use sorting to avoid too many map calls) */
1039  for (j = 0; j < matrixdata.nmatcoef; ++j)
1040  {
1041  int idx;
1042 
1043  idx = matrixdata.matidx[j];
1044  assert( 0 <= idx && idx < matrixdata.nmatcoef );
1045 
1046  val = matrixdata.matcoef[idx];
1047  assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
1048 
1049  if ( ! SCIPisEQ(scip, val, oldcoef) )
1050  {
1051 #ifdef SCIP_OUTPUT
1052  SCIPdebugMsg(scip, "Detected new matrix entry type %f - color: %d\n.", val, matrixdata.nuniquemat);
1053 #endif
1054  matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat++;
1055  oldcoef = val;
1056  }
1057  else
1058  {
1059  assert( matrixdata.nuniquemat > 0 );
1060  matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat - 1;
1061  }
1062  }
1063 
1064  /* find non-equivalent rhs */
1065  oldcoef = SCIP_INVALID;
1066  for (j = 0; j < matrixdata.nrhscoef; ++j)
1067  {
1068  SYM_RHSSENSE sense;
1069  int idx;
1070 
1071  idx = matrixdata.rhsidx[j];
1072  assert( 0 <= idx && idx < matrixdata.nrhscoef );
1073  sense = matrixdata.rhssense[idx];
1074  val = matrixdata.rhscoef[idx];
1075 
1076  /* make sure that new senses are treated with new color */
1077  if ( sense != oldsense )
1078  oldcoef = SCIP_INVALID;
1079  oldsense = sense;
1080  assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
1081 
1082  /* assign new color to new type */
1083  if ( ! SCIPisEQ(scip, val, oldcoef) )
1084  {
1085 #ifdef SCIP_OUTPUT
1086  SCIPdebugMsg(scip, "Detected new rhs type %f, type: %u - color: %d\n", val, sense, matrixdata.nuniquerhs);
1087 #endif
1088  matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs++;
1089  oldcoef = val;
1090  }
1091  else
1092  {
1093  assert( matrixdata.nuniquerhs > 0 );
1094  matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs - 1;
1095  }
1096  }
1097  assert( 0 < matrixdata.nuniquevars && matrixdata.nuniquevars <= nvars );
1098  assert( 0 < matrixdata.nuniquerhs && matrixdata.nuniquerhs <= matrixdata.nrhscoef );
1099  assert( 0 < matrixdata.nuniquemat && matrixdata.nuniquemat <= matrixdata.nmatcoef );
1100 
1101  SCIPdebugMsg(scip, "Number of detected different variables: %d (total: %d).\n", matrixdata.nuniquevars, nvars);
1102  SCIPdebugMsg(scip, "Number of detected different rhs types: %d (total: %d).\n", matrixdata.nuniquerhs, matrixdata.nrhscoef);
1103  SCIPdebugMsg(scip, "Number of detected different matrix coefficients: %d (total: %d).\n", matrixdata.nuniquemat, matrixdata.nmatcoef);
1104 
1105  /* do not compute symmetry if all variables are non-equivalent (unique) or if all matrix coefficients are different */
1106  if ( matrixdata.nuniquevars < nvars && matrixdata.nuniquemat < matrixdata.nmatcoef )
1107  {
1108  /* determine generators */
1109  SCIP_CALL( SYMcomputeSymmetryGenerators(scip, maxgenerators, &matrixdata, nperms, nmaxperms, perms, log10groupsize) );
1110 
1111  if ( ! SCIPisStopped(scip) && checksymmetries )
1112  {
1113  SCIP_CALL( checkSymmetriesAreSymmetries(scip, fixedtype, &matrixdata, *nperms, *perms) );
1114  }
1115  }
1116  *success = TRUE;
1117 
1118  /* free matrix data */
1119  SCIPfreeBlockMemoryArray(scip, &uniquevararray, nvars);
1120 
1121  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef);
1122  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef);
1123  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.permvarcolors, nvars);
1124  SCIPhashtableFree(&vartypemap);
1125 
1126  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
1127  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
1128  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
1129  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
1130  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
1131  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef);
1132  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef);
1133 
1134  /* copy variables */
1135  *permvars = vars;
1136  *npermvars = nvars;
1137 
1138  /* symmetric variables are not allowed to be multi-aggregated */
1139  for (j = 0; j < nvars; ++j)
1140  {
1141  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, vars[j]) );
1142  }
1143 
1144 #ifndef NDEBUG
1145  SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvarsobj, nvars) );
1146  for (j = 0; j < nvars; ++j)
1147  (*permvarsobj)[j] = SCIPvarGetObj(vars[j]);
1148 #endif
1149 
1150  return SCIP_OKAY;
1151 }
1152 
1153 
1154 /* compute number of variables that are contained in a non-trivial orbit */
1155 static
1157  SCIP* scip, /**< SCIP instance */
1158  SCIP_PRESOLDATA* presoldata /**< presolver data */
1159  )
1160 {
1161  int** perms;
1162  int nperms;
1163  int nvars;
1164  SCIP_Shortbool* affected;
1165  int i;
1166  int p;
1167  int naffected = 0;
1168 
1169  assert( scip != NULL );
1170  assert( presoldata != NULL );
1171  assert( presoldata->perms != NULL );
1172  assert( presoldata->nperms > 0 );
1173  assert( presoldata->npermvars > 0 );
1174 
1175  perms = presoldata->perms;
1176  nperms = presoldata->nperms;
1177  nvars = presoldata->npermvars;
1178 
1179  SCIP_CALL( SCIPallocClearBufferArray(scip, &affected, nvars) );
1180 
1181  /* iterate over permutations and check which variables are affected by some symmetry */
1182  for (p = 0; p < nperms; ++p)
1183  {
1184  for (i = 0; i < nvars; ++i)
1185  {
1186  if ( affected[i] )
1187  continue;
1188 
1189  if ( perms[p][i] != i )
1190  {
1191  affected[i] = TRUE;
1192  ++naffected;
1193  }
1194  }
1195  }
1196 
1197  presoldata->norbitvars = naffected;
1198 
1199  SCIPfreeBufferArray(scip, &affected);
1200 
1201  return SCIP_OKAY;
1202 }
1203 
1204 
1205 /** determine symmetry */
1206 static
1208  SCIP* scip, /**< SCIP instance */
1209  SCIP_PRESOLDATA* presoldata /**< presolver data */
1210  )
1211 {
1212  int maxgenerators;
1213  int type = 0;
1214  int nvars;
1215 
1216  assert( scip != NULL );
1217  assert( presoldata != NULL );
1218 
1219  assert( ! presoldata->computedsym );
1220  assert( presoldata->npermvars == 0 );
1221  assert( presoldata->permvars == NULL );
1222  assert( presoldata->permvarsobj == NULL );
1223  assert( presoldata->nperms == 0 );
1224  assert( presoldata->nmaxperms == 0 );
1225  assert( presoldata->perms == NULL );
1226 
1227  presoldata->computedsym = TRUE;
1228 
1229 #ifndef NDEBUG
1230  {
1231  int usesymmetry;
1232  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &usesymmetry) );
1233  assert( usesymmetry );
1234  }
1235 #endif
1236 
1237  /* do not compute symmetry if there are active pricers */
1238  if ( SCIPgetNActivePricers(scip) > 0 )
1239  return SCIP_OKAY;
1240 
1241  /* avoid trivial cases */
1242  nvars = SCIPgetNVars(scip);
1243  if ( nvars <= 0 )
1244  return SCIP_OKAY;
1245 
1246  /* determine symmetry specification */
1247  if ( SCIPgetNBinVars(scip) > 0 )
1248  type |= (int) SYM_SPEC_BINARY;
1249  if ( SCIPgetNIntVars(scip) > 0 )
1250  type |= (int) SYM_SPEC_INTEGER;
1251  /* count implicit integer variables as real variables, since we cannot currently handle integral variables well */
1252  if ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
1253  type |= (int) SYM_SPEC_REAL;
1254 
1255  /* skip symmetry computation if no graph automorphism code was linked */
1256  if ( ! SYMcanComputeSymmetry() )
1257  {
1258  int nconss = SCIPgetNActiveConss(scip);
1259  int nhandleconss = getNSymhandableConss(scip);
1260 
1261  /* print verbMessage only if problem consists of symmetry handable constraints */
1262  assert( nhandleconss <= nconss );
1263  if ( nhandleconss < nconss )
1264  return SCIP_OKAY;
1265 
1267  " Deactivated symmetry handling methods, since SCIP was built without symmetry detector (SYM=none).\n");
1268  return SCIP_OKAY;
1269  }
1270  /* skip symmetry computation if required variables are not present */
1271  else if ( ! (type & presoldata->symspecrequire) )
1272  {
1274  " (%.1fs) symmetry computation skipped: type (bin %c, int %c, cont %c) does not match requirements (bin %c, int %c, cont %c)\n",
1275  SCIPgetSolvingTime(scip),
1276  SCIPgetNBinVars(scip) > 0 ? '+' : '-',
1277  SCIPgetNIntVars(scip) > 0 ? '+' : '-',
1278  SCIPgetNContVars(scip) + SCIPgetNImplVars(scip) > 0 ? '+' : '-',
1279  (presoldata->symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
1280  (presoldata->symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
1281  (presoldata->symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
1282  return SCIP_OKAY;
1283  }
1284  /* skip symmetry computation if there are constraints that cannot be handled by symmetry */
1285  else if ( getNSymhandableConss(scip) < SCIPgetNActiveConss(scip) )
1286  {
1288  " (%.1fs) symmetry computation skipped: there exist constraints that cannot be handled by symmetry methods\n",
1289  SCIPgetSolvingTime(scip));
1290  return SCIP_OKAY;
1291  }
1292 
1294  " (%.1fs) symmetry computation started: requiring (bin %c, int %c, cont %c), (fixed: bin %c, int %c, cont %c)\n",
1295  SCIPgetSolvingTime(scip),
1296  (presoldata->symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
1297  (presoldata->symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
1298  (presoldata->symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-',
1299  (presoldata->symspecrequirefixed & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
1300  (presoldata->symspecrequirefixed & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
1301  (presoldata->symspecrequirefixed & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
1302 
1303  if ( presoldata->symspecrequire & presoldata->symspecrequirefixed )
1304  SCIPwarningMessage(scip, "Warning: some required symmetries must be fixed.\n");
1305 
1306  /* actually compute (global) symmetry */
1307  /* determine maximal number of generators depending on the number of variables */
1308  maxgenerators = presoldata->maxgenerators;
1309  maxgenerators = MIN(maxgenerators, MAXGENNUMERATOR / nvars);
1310 
1311  SCIP_CALL( computeSymmetryGroup(scip, maxgenerators, presoldata->symspecrequirefixed, FALSE, presoldata->checksymmetries,
1312  &presoldata->npermvars, &presoldata->permvars, &presoldata->permvarsobj, &presoldata->nperms, &presoldata->nmaxperms, &presoldata->perms,
1313  &presoldata->log10groupsize, &presoldata->successful) );
1314 
1315  /* output statistics */
1316  if ( ! presoldata->successful )
1317  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) could not compute symmetry\n", SCIPgetSolvingTime(scip));
1318  else if ( presoldata->nperms == 0 )
1319  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry present\n", SCIPgetSolvingTime(scip));
1320  else
1321  {
1322  assert( presoldata->nperms > 0 );
1323 
1324  if ( presoldata->displaynorbitvars )
1325  {
1326  SCIP_CALL( computeNOrbitVars(scip, presoldata) );
1327  }
1328 
1329  /* display statistics: number of generators */
1331  " (%.1fs) symmetry computation finished: %d generators found (max: ",
1332  SCIPgetSolvingTime(scip), presoldata->nperms);
1333 
1334  /* display statistics: maximum number of generators*/
1335  if ( maxgenerators == 0 )
1336  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "-");
1337  else
1338  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%u", maxgenerators);
1339 
1340  /* display statistics: log10 group size, number of affected vars*/
1341  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", log10 of symmetry group size: %.1f", presoldata->log10groupsize);
1342 
1343  /* display statistics: number of affected vars*/
1344  if ( presoldata->displaynorbitvars )
1345  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", number of affected variables: %d)\n", presoldata->norbitvars);
1346  else
1347  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ")\n");
1348 
1349  /* turn off some other presolving methods in order to be sure that they do not destroy symmetry afterwards */
1351  " (%.1fs) turning off presolver <domcol>, constraint handler <components>, and propagator <dualfix> for remaining computations in order to avoid conflicts\n",
1352  SCIPgetSolvingTime(scip));
1353 
1354  /* domcol avoids S_2-symmetries and may not be compatible with other symmetry handling methods */
1355  SCIP_CALL( SCIPsetIntParam(scip, "presolving/domcol/maxrounds", 0) );
1356 
1357  /* components creates sub-SCIPs on which no symmetry handling is installed, thus turn this off */
1358  SCIP_CALL( SCIPsetIntParam(scip, "constraints/components/maxprerounds", 0) );
1359 
1360  /* dual fixing might interfere with symmetry handling methods, thus turn this off */
1361  SCIP_CALL( SCIPsetIntParam(scip, "propagating/dualfix/maxprerounds", 0) );
1362  SCIP_CALL( SCIPsetIntParam(scip, "propagating/dualfix/freq", 0) );
1363 
1364  presoldata->changeddefaultparams = TRUE;
1365  }
1366 
1367  return SCIP_OKAY;
1368 }
1369 
1370 
1371 /*
1372  * Callback methods of presolver
1373  */
1374 
1375 /** initialization method of presolver (called after problem was transformed) */
1376 static
1377 SCIP_DECL_PRESOLINIT(presolInitSymmetry)
1378 { /*lint --e{715}*/
1379  SCIP_PRESOLDATA* presoldata;
1380 
1381  assert( scip != NULL );
1382  assert( presol != NULL );
1383  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1384 
1385  presoldata = SCIPpresolGetData(presol);
1386 
1387  /* initialize original values of changed parameters in case we do not enter determineSymmetry() */
1388  SCIP_CALL( SCIPgetIntParam(scip, "presolving/domcol/maxrounds", &(presoldata->oldmaxroundsdomcol)) );
1389  SCIP_CALL( SCIPgetIntParam(scip, "constraints/components/maxprerounds", &(presoldata->oldmaxpreroundscomponents)) );
1390  SCIP_CALL( SCIPgetIntParam(scip, "propagating/dualfix/maxprerounds", &(presoldata->oldmaxpreroundsdualfix)) );
1391  SCIP_CALL( SCIPgetIntParam(scip, "propagating/dualfix/freq", &(presoldata->oldfreqdualfix)) );
1392 
1393  return SCIP_OKAY;
1394 }
1395 
1396 
1397 /** presolving initialization method of presolver (called when presolving is about to begin) */
1398 static
1399 SCIP_DECL_PRESOLINITPRE(presolInitpreSymmetry)
1400 {
1401  SCIP_PRESOLDATA* presoldata;
1402 
1403  assert( scip != NULL );
1404  assert( presol != NULL );
1405  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1406 
1407  presoldata = SCIPpresolGetData(presol);
1408  assert( presoldata != NULL );
1409 
1410  SCIPdebugMsg(scip, "Initialization of symmetry presolver.\n");
1411 
1412  /* compute symmetries if not requested during presolving */
1413  if ( ! presoldata->computepresolved && ! presoldata->computedsym && presoldata->symtype != 0 )
1414  {
1415  /* determine symmetry here in initpre, since other plugins specify their problem type in init() */
1416  SCIP_CALL( determineSymmetry(scip, presoldata) );
1417  }
1418 
1419  return SCIP_OKAY;
1420 }
1421 
1422 
1423 /** deinitialization method of presolver (called before transformed problem is freed) */
1424 static
1425 SCIP_DECL_PRESOLEXIT(presolExitSymmetry)
1426 {
1427  SCIP_PRESOLDATA* presoldata;
1428  int i;
1429 
1430  assert( scip != NULL );
1431  assert( presol != NULL );
1432  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1433 
1434  SCIPdebugMsg(scip, "Exiting symmetry presolver.\n");
1435 
1436  presoldata = SCIPpresolGetData(presol);
1437  assert( presoldata != NULL );
1438 
1439  SCIPfreeBlockMemoryArrayNull(scip, &presoldata->permvars, presoldata->npermvars);
1440  SCIPfreeBlockMemoryArrayNull(scip, &presoldata->permvarsobj, presoldata->npermvars);
1441  for (i = 0; i < presoldata->nperms; ++i)
1442  {
1443  SCIPfreeBlockMemoryArray(scip, &presoldata->perms[i], presoldata->npermvars);
1444  }
1445  SCIPfreeBlockMemoryArrayNull(scip, &presoldata->perms, presoldata->nmaxperms);
1446 
1447  /* reset settings */
1448  presoldata->symtype = 0;
1449  presoldata->symspecrequire = 0;
1450  presoldata->symspecrequirefixed = 0;
1451  presoldata->npermvars = 0;
1452  presoldata->nperms = 0;
1453  presoldata->nmaxperms = 0;
1454  presoldata->norbitvars = 0;
1455  presoldata->computedsym = FALSE;
1456  presoldata->successful = FALSE;
1457 
1458  /* reset changed parameters */
1459  if ( presoldata->changeddefaultparams )
1460  {
1461  SCIP_CALL( SCIPsetIntParam(scip, "presolving/domcol/maxrounds", presoldata->oldmaxroundsdomcol) );
1462  SCIP_CALL( SCIPsetIntParam(scip, "constraints/components/maxprerounds", presoldata->oldmaxpreroundscomponents) );
1463  SCIP_CALL( SCIPsetIntParam(scip, "propagating/dualfix/maxprerounds", presoldata->oldmaxpreroundsdualfix) );
1464  SCIP_CALL( SCIPsetIntParam(scip, "propagating/dualfix/maxprerounds", presoldata->oldfreqdualfix) );
1465 
1466  presoldata->changeddefaultparams = FALSE;
1467  }
1468 
1469  return SCIP_OKAY;
1470 }
1471 
1472 
1473 /** destructor of presolver to free user data (called when SCIP is exiting) */
1474 static
1475 SCIP_DECL_PRESOLFREE(presolFreeSymmetry)
1476 { /*lint --e{715}*/
1477  SCIP_PRESOLDATA* presoldata;
1478 
1479  assert( scip != NULL );
1480  assert( presol != NULL );
1481  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1482 
1483  SCIPdebugMsg(scip, "Freeing symmetry presolver.\n");
1484 
1485  presoldata = SCIPpresolGetData(presol);
1486  assert( presoldata != NULL );
1487 
1488  SCIPfreeBlockMemory(scip, &presoldata);
1489 
1490  return SCIP_OKAY;
1491 }
1492 
1493 
1494 /** execution method of presolver */
1495 static
1496 SCIP_DECL_PRESOLEXEC(presolExecSymmetry)
1497 { /*lint --e{715}*/
1498  assert( scip != NULL );
1499  assert( presol != NULL );
1500  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1501  assert( result != NULL );
1502 
1503  /* do nothing */
1504  *result = SCIP_DIDNOTRUN;
1505 
1506  return SCIP_OKAY;
1507 }
1508 
1509 
1510 /** presolving deinitialization method of presolver (called after presolving has been finished) */
1511 static
1512 SCIP_DECL_PRESOLEXITPRE(presolExitpreSymmetry)
1513 {
1514  SCIP_PRESOLDATA* presoldata;
1515 
1516  assert( scip != NULL );
1517  assert( presol != NULL );
1518  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1519 
1520  /* skip if we are in a restart */
1521  if ( SCIPgetNRuns(scip) > 1 )
1522  return SCIP_OKAY;
1523 
1524  /* skip if we already terminated */
1526  return SCIP_OKAY;
1527 
1528  /* skip if we are exiting */
1529  if ( SCIPisStopped(scip) )
1530  return SCIP_OKAY;
1531 
1532  SCIPdebugMsg(scip, "Exitpre method of symmetry presolver ...\n");
1533 
1534  presoldata = SCIPpresolGetData(presol);
1535  assert( presoldata != NULL );
1536 
1537  /* compute symmetries if requested during presolving */
1538  if ( presoldata->computepresolved && ! presoldata->computedsym && presoldata->symtype != 0 )
1539  {
1540  SCIP_CALL( determineSymmetry(scip, presoldata) );
1541  }
1542 
1543  return SCIP_OKAY;
1544 }
1545 
1546 
1547 
1548 /*
1549  * External methods
1550  */
1551 
1552 /** include symmetry constraint handler */
1554  SCIP* scip /**< SCIP data structure */
1555  )
1556 {
1557  SCIP_PRESOL* presol = NULL;
1558  SCIP_PRESOLDATA* presoldata = NULL;
1559 
1560  SCIP_CALL( SCIPallocBlockMemory(scip, &presoldata) );
1561  assert( presoldata != NULL );
1562 
1563  presoldata->symtype = 0;
1564  presoldata->symspecrequire = 0;
1565  presoldata->symspecrequirefixed = 0;
1566  presoldata->npermvars = 0;
1567  presoldata->permvars = NULL;
1568  presoldata->permvarsobj = NULL;
1569  presoldata->perms = NULL;
1570  presoldata->nperms = 0;
1571  presoldata->nmaxperms = 0;
1572  presoldata->norbitvars = 0;
1573  presoldata->computedsym = FALSE;
1574  presoldata->successful = FALSE;
1575  presoldata->changeddefaultparams = FALSE;
1576 
1577  /* include constraint handler */
1579  PRESOL_PRIORITY, PRESOL_MAXROUNDS, PRESOL_TIMING, presolExecSymmetry, presoldata) );
1580  assert( presol != NULL );
1581 
1582  SCIP_CALL( SCIPsetPresolFree(scip, presol, presolFreeSymmetry) );
1583  SCIP_CALL( SCIPsetPresolInit(scip, presol, presolInitSymmetry) );
1584  SCIP_CALL( SCIPsetPresolExit(scip, presol, presolExitSymmetry) );
1585  SCIP_CALL( SCIPsetPresolInitpre(scip, presol, presolInitpreSymmetry) );
1586  SCIP_CALL( SCIPsetPresolExitpre(scip, presol, presolExitpreSymmetry) );
1587 
1588  /* add parameters */
1590  "presolving/" PRESOL_NAME "/computepresolved",
1591  "Should the symmetry be computed after presolving (otherwise before presol)?",
1592  &presoldata->computepresolved, TRUE, DEFAULT_COMPUTEPRESOLVED, NULL, NULL) );
1593 
1594  SCIP_CALL( SCIPaddIntParam(scip,
1595  "presolving/" PRESOL_NAME "/maxgenerators",
1596  "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
1597  &presoldata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
1598 
1600  "presolving/" PRESOL_NAME "/checksymmetries",
1601  "Should all symmetries be checked after computation?",
1602  &presoldata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
1603 
1605  "presolving/" PRESOL_NAME "/displaynorbitvars",
1606  "Should the number of variables affected by some symmetry be displayed?",
1607  &presoldata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
1608 
1609  /* possibly add description */
1610  if ( SYMcanComputeSymmetry() )
1611  {
1613  }
1614 
1615  return SCIP_OKAY;
1616 }
1617 
1618 
1619 /** return symmetry group generators */
1621  SCIP* scip, /**< SCIP data structure */
1622  int* npermvars, /**< pointer to store number of variables for permutations */
1623  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
1624  int* nperms, /**< pointer to store number of permutations */
1625  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
1626  SCIP_Real* log10groupsize /**< pointer to store log10 of group size (or NULL) */
1627  )
1628 {
1629  SCIP_PRESOLDATA* presoldata;
1630  SCIP_PRESOL* presol;
1631 
1632  assert( scip != NULL );
1633  assert( npermvars != NULL );
1634  assert( permvars != NULL );
1635  assert( nperms != NULL );
1636  assert( perms != NULL );
1637 
1638  /* find symmetry presolver */
1639  presol = SCIPfindPresol(scip, "symmetry");
1640  if ( presol == NULL )
1641  {
1642  SCIPerrorMessage("Could not find symmetry presolver.\n");
1643  return SCIP_PLUGINNOTFOUND;
1644  }
1645  assert( presol != NULL );
1646  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1647 
1648  presoldata = SCIPpresolGetData(presol);
1649  assert( presoldata != NULL );
1650 
1651  if ( ! presoldata->computedsym )
1652  {
1655  {
1656  SCIPerrorMessage("Cannot call symmetry detection outside of presolving.\n");
1657  return SCIP_INVALIDCALL;
1658  }
1659 
1660  /* determine symmetry here */
1661  SCIP_CALL( determineSymmetry(scip, presoldata) );
1662  }
1663 
1664  *npermvars = presoldata->npermvars;
1665  *permvars = presoldata->permvars;
1666  *nperms = presoldata->nperms;
1667  *perms = presoldata->perms;
1668  if ( log10groupsize != NULL )
1669  *log10groupsize = presoldata->log10groupsize;
1670 
1671  return SCIP_OKAY;
1672 }
1673 
1674 
1675 /** return objective coefficients of permuted variables at time of symmetry computation */
1677  SCIP* scip, /**< SCIP data structure */
1678  SCIP_Real** permvarsobj /**< pointer to store objective coefficients of permuted variables (NULL if not available) */
1679  )
1680 {
1681  SCIP_PRESOLDATA* presoldata;
1682  SCIP_PRESOL* presol;
1683 
1684  assert( scip != NULL );
1685  assert( permvarsobj != NULL );
1686 
1687  /* find symmetry presolver */
1688  presol = SCIPfindPresol(scip, "symmetry");
1689  if ( presol == NULL )
1690  {
1691  SCIPerrorMessage("Could not find symmetry presolver.\n");
1692  return SCIP_PLUGINNOTFOUND;
1693  }
1694  assert( presol != NULL );
1695  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1696 
1697  presoldata = SCIPpresolGetData(presol);
1698  assert( presoldata != NULL );
1699 
1700  *permvarsobj = presoldata->permvarsobj;
1701 
1702  return SCIP_OKAY;
1703 }
1704 
1705 
1706 /** register that a specific symmetry is needed */
1708  SCIP* scip, /**< SCIP data structure */
1709  SYM_HANDLETYPE symtype, /**< type of symmetry handling of callee */
1710  SYM_SPEC type, /**< variable types the callee is interested in */
1711  SYM_SPEC fixedtype /**< variable types that callee wants to have fixed */
1712  )
1713 {
1714  SCIP_PRESOLDATA* presoldata;
1715  SCIP_PRESOL* presol;
1716 
1717  assert( scip != NULL );
1718 
1719  /* find symmetry presolver */
1720  presol = SCIPfindPresol(scip, "symmetry");
1721  if ( presol == NULL )
1722  {
1723  SCIPerrorMessage("Could not find symmetry presolver.\n");
1724  return SCIP_PLUGINNOTFOUND;
1725  }
1726  assert( presol != NULL );
1727  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1728 
1729  presoldata = SCIPpresolGetData(presol);
1730  assert( presoldata != NULL );
1731 
1732  /* check if there are conflicting symmetry handling methods */
1733  if ( ( presoldata->symtype & ~ symtype ) != 0 )
1734  {
1735  SCIPerrorMessage("Conflicting symmetry handling methods are activated.\n");
1736  return SCIP_INVALIDDATA;
1737  }
1738 
1739  presoldata->symtype |= symtype;
1740  presoldata->symspecrequire |= type;
1741  presoldata->symspecrequirefixed |= fixedtype;
1742 
1743  return SCIP_OKAY;
1744 }
1745 
1746 
1747 /** return at what time symmetry is computed (before or after presolving) */
1749  SCIP* scip, /**< SCIP data structure */
1750  SCIP_Bool* afterpresolve /**< pointer to store whether symmetry is computed in stage initpre or exitpre */
1751  )
1752 {
1753  SCIP_PRESOLDATA* presoldata;
1754  SCIP_PRESOL* presol;
1755 
1756  assert( scip != NULL );
1757  assert( afterpresolve != NULL );
1758 
1759  /* find symmetry presolver */
1760  presol = SCIPfindPresol(scip, "symmetry");
1761  if ( presol == NULL )
1762  {
1763  SCIPerrorMessage("Could not find symmetry presolver.\n");
1764  return SCIP_PLUGINNOTFOUND;
1765  }
1766  assert( presol != NULL );
1767  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1768 
1769  presoldata = SCIPpresolGetData(presol);
1770  assert( presoldata != NULL );
1771 
1772  *afterpresolve = presoldata->computepresolved;
1773 
1774  return SCIP_OKAY;
1775 }
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip.h:22604
SCIP_RETCODE SCIPincludePresolBasic(SCIP *scip, SCIP_PRESOL **presolptr, const char *name, const char *desc, int priority, int maxrounds, SCIP_PRESOLTIMING timing, SCIP_DECL_PRESOLEXEC((*presolexec)), SCIP_PRESOLDATA *presoldata)
Definition: scip.c:6917
int SCIPgetNIntVars(SCIP *scip)
Definition: scip.c:11902
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip.h:22593
SCIP_Real SCIPgetSolvingTime(SCIP *scip)
Definition: scip.c:46306
struct SCIP_PresolData SCIP_PRESOLDATA
Definition: type_presol.h:37
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip.h:22587
SCIP_Real * matcoef
SCIP_RETCODE SCIPsetPresolFree(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLFREE((*presolfree)))
Definition: scip.c:6968
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip.c:821
Constraint handler for variable bound constraints .
#define PRESOL_DESC
SCIP_RETCODE SCIPhashtableInsert(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2265
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip.c:6604
SCIP_RETCODE SCIPsetPresolExit(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLEXIT((*presolexit)))
Definition: scip.c:7000
int SCIPgetNVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9230
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition: scip.h:22622
static SCIP_RETCODE checkSymmetriesAreSymmetries(SCIP *scip, SYM_SPEC fixedtype, SYM_MATRIXDATA *matrixdata, int nperms, int **perms)
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:17276
int SCIPgetNVarsLogicor(SCIP *scip, SCIP_CONS *cons)
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip.c:46813
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:17332
#define DEFAULT_DISPLAYNORBITVARS
static int getNSymhandableConss(SCIP *scip)
#define FALSE
Definition: def.h:64
int SCIPgetNActivePricers(SCIP *scip)
Definition: scip.c:5718
SCIP_Real SCIPinfinity(SCIP *scip)
Definition: scip.c:47028
#define TRUE
Definition: def.h:63
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:53
SCIP_PRESOLDATA * SCIPpresolGetData(SCIP_PRESOL *presol)
Definition: presol.c:466
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition: var.c:16969
static SCIP_RETCODE collectCoefficients(SCIP *scip, SCIP_VAR **linvars, SCIP_Real *linvals, int nlinvars, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool istransformed, SYM_RHSSENSE rhssense, SYM_MATRIXDATA *matrixdata)
SCIP_Bool SCIPconsIsTransformed(SCIP_CONS *cons)
Definition: cons.c:8295
int SCIPgetNActiveConss(SCIP *scip)
Definition: scip.c:43180
#define SCIPfreeBlockMemory(scip, ptr)
Definition: scip.h:22602
SCIP_VAR ** SCIPgetVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
#define PRESOL_PRIORITY
Constraint handler for AND constraints, .
#define SCIPduplicateBufferArray(scip, ptr, source, num)
Definition: scip.h:22628
SCIP_CONS ** SCIPgetConss(SCIP *scip)
Definition: scip.c:12908
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition: scip.c:46963
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip.h:22632
Constraint handler for the set partitioning / packing / covering constraints .
#define SCIPallocBlockMemory(scip, ptr)
Definition: scip.h:22585
SCIP_VAR ** permvars
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
Definition: scip.c:1267
#define SCIPdebugMsg
Definition: scip.h:455
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip.c:4265
SCIP_Real SCIPgetRhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPconsIsActive(SCIP_CONS *cons)
Definition: cons.c:8047
int SCIPgetNContVars(SCIP *scip)
Definition: scip.c:11992
SCIP_RETCODE SCIPhashtableCreate(SCIP_HASHTABLE **hashtable, BMS_BLKMEM *blkmem, int tablesize, SCIP_DECL_HASHGETKEY((*hashgetkey)), SCIP_DECL_HASHKEYEQ((*hashkeyeq)), SCIP_DECL_HASHKEYVAL((*hashkeyval)), void *userptr)
Definition: misc.c:2014
interface for symmetry computations
static SCIP_DECL_PRESOLEXEC(presolExecSymmetry)
#define DEFAULT_MAXGENERATORS
int SCIPgetNFixedVars(SCIP *scip)
Definition: scip.c:12129
Constraint handler for "or" constraints, .
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:17286
#define SYM_SPEC_BINARY
Definition: type_symmetry.h:34
#define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
Definition: scip.h:22599
Constraint handler for knapsack constraints of the form , x binary and .
SCIP_Real ub
SCIP_VAR ** SCIPgetVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5121
#define PRESOL_MAXROUNDS
SCIP_VAR * SCIPgetVarVarbound(SCIP *scip, SCIP_CONS *cons)
SYM_RHSSENSE * rhssense
#define SCIPerrorMessage
Definition: pub_message.h:45
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4113
SCIP_VAR * SCIPgetResultantAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5146
SCIP_Real SCIPgetRhsVarbound(SCIP *scip, SCIP_CONS *cons)
#define SYM_SPEC_INTEGER
Definition: type_symmetry.h:33
Constraint handler for logicor constraints (equivalent to set covering, but algorithms are suited fo...
SCIP_Real SCIPgetVbdcoefVarbound(SCIP *scip, SCIP_CONS *cons)
static INLINE uint32_t SCIPrealHashCode(double x)
Definition: pub_misc.h:489
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip.c:928
#define DEFAULT_COMPUTEPRESOLVED
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip.c:46731
SYM_RHSSENSE * senses
SCIP_RETCODE SCIPgetTimingSymmetry(SCIP *scip, SCIP_Bool *afterpresolve)
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition: cons.c:7986
#define MAXGENNUMERATOR
SCIP_RETCODE SCIPsetPresolInit(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLINIT((*presolinit)))
Definition: scip.c:6984
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP *scip, SCIP_VAR *var)
Definition: scip.c:25932
SCIP_VAR ** SCIPgetVarsLogicor(SCIP *scip, SCIP_CONS *cons)
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:16662
SCIP_PRESOL * SCIPfindPresol(SCIP *scip, const char *name)
Definition: scip.c:7048
#define SCIP_Shortbool
Definition: def.h:69
SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
Definition: scip.c:4451
SCIP_VAR * SCIPgetVbdvarVarbound(SCIP *scip, SCIP_CONS *cons)
#define SCIP_CALL(x)
Definition: def.h:350
#define SCIPhashTwo(a, b)
Definition: pub_misc.h:473
SCIP_RETCODE SCIPgetProbvarLinearSum(SCIP *scip, SCIP_VAR **vars, SCIP_Real *scalars, int *nvars, int varssize, SCIP_Real *constant, int *requiredsize, SCIP_Bool mergemultiples)
Definition: scip.c:19255
SCIP_VAR ** SCIPgetVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2167
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip.c:1360
SCIP_Real * vals
SCIP_RETCODE SCIPsetPresolInitpre(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLINITPRE((*presolinitpre)))
Definition: scip.c:7016
SCIP_RETCODE SCIPregisterSymmetry(SCIP *scip, SYM_HANDLETYPE symtype, SYM_SPEC type, SYM_SPEC fixedtype)
static SCIP_DECL_PRESOLINITPRE(presolInitpreSymmetry)
SCIP_Longint SCIPgetCapacityKnapsack(SCIP *scip, SCIP_CONS *cons)
static SCIP_RETCODE computeNOrbitVars(SCIP *scip, SCIP_PRESOLDATA *presoldata)
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip.h:22620
#define SCIP_Bool
Definition: def.h:61
int SCIPgetNImplVars(SCIP *scip)
Definition: scip.c:11947
uint32_t SYM_SPEC
Definition: type_symmetry.h:37
int SCIPgetNVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2146
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9272
SCIP_Bool SYMcanComputeSymmetry(void)
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition: cons.c:8006
SCIP_RETCODE SCIPsetIntParam(SCIP *scip, const char *name, int value)
Definition: scip.c:4688
SCIP_Real lb
const char * SCIPpresolGetName(SCIP_PRESOL *presol)
Definition: presol.c:553
SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
Definition: var.c:17124
static SCIP_DECL_HASHKEYVAL(SYMhashKeyValVartype)
SCIP_VARTYPE type
int SCIPgetNRuns(SCIP *scip)
Definition: scip.c:42050
Constraint handler for linear constraints in their most general form, .
void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
Definition: misc.c:2326
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
Definition: scip.c:47039
SCIP_Bool SCIPgetRhsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5767
static SCIP_DECL_PRESOLEXITPRE(presolExitpreSymmetry)
static SCIP_RETCODE determineSymmetry(SCIP *scip, SCIP_PRESOLDATA *presoldata)
SCIP_RETCODE SCIPvarGetOrigvarSum(SCIP_VAR **var, SCIP_Real *scalar, SCIP_Real *constant)
Definition: var.c:12184
static SCIP_DECL_HASHKEYEQ(SYMhashKeyEQVartype)
int SCIPgetNBinVars(SCIP *scip)
Definition: scip.c:11857
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4549
static SCIP_RETCODE computeSymmetryGroup(SCIP *scip, int maxgenerators, SYM_SPEC fixedtype, SCIP_Bool local, SCIP_Bool checksymmetries, int *npermvars, SCIP_VAR ***permvars, SCIP_Real **permvarsobj, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Bool *success)
SCIP_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9251
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition: misc.c:2064
int SCIPgetNVars(SCIP *scip)
Definition: scip.c:11812
SCIP_VAR ** SCIPgetVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5725
enum SYM_Rhssense SYM_RHSSENSE
Definition: type_symmetry.h:49
Constraint handler for XOR constraints, .
int SCIPgetNVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5704
void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
Definition: misc.c:5081
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_MATRIXDATA *matrixdata, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize)
SCIP_RETCODE SCIPgetPermvarsObjSymmetry(SCIP *scip, SCIP_Real **permvarsobj)
static const SCIP_Real scalars[]
Definition: lp.c:5618
SCIP_VAR ** SCIPgetVarsLinear(SCIP *scip, SCIP_CONS *cons)
static SCIP_DECL_SORTINDCOMP(SYMsortRhsTypes)
const char * SYMsymmetryGetName(void)
SCIP_VAR * SCIPgetResultantOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2188
int SCIPgetNConss(SCIP *scip)
Definition: scip.c:12862
#define PRESOL_NAME
static SCIP_DECL_PRESOLINIT(presolInitSymmetry)
presolver for storing symmetry information about current problem
static SCIP_DECL_PRESOLEXIT(presolExitSymmetry)
#define SYM_SPEC_REAL
Definition: type_symmetry.h:35
SCIP_VAR ** SCIPgetVars(SCIP *scip)
Definition: scip.c:11767
#define SCIP_Real
Definition: def.h:149
SCIP_Bool SCIPisStopped(SCIP *scip)
Definition: scip.c:1145
int SCIPgetNVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5097
uint32_t SYM_HANDLETYPE
Definition: type_symmetry.h:56
int SCIPgetNVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
#define SCIP_INVALID
Definition: def.h:169
#define PRESOL_TIMING
#define SCIP_Longint
Definition: def.h:134
const char * SYMsymmetryGetDesc(void)
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition: var.c:16827
SCIP_Bool SCIPhashtableExists(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2377
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
Definition: scip.c:9628
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
Definition: scip.c:47076
SCIP_Real * SCIPgetValsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:17342
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip.h:22605
static SCIP_RETCODE getActiveVariables(SCIP *scip, SCIP_VAR ***vars, SCIP_Real **scalars, int *nvars, SCIP_Real *constant, SCIP_Bool transformed)
static SCIP_DECL_HASHGETKEY(SYMhashGetKeyVartype)
SCIP_Longint * SCIPgetWeightsKnapsack(SCIP *scip, SCIP_CONS *cons)
#define SCIPcombineTwoInt(a, b)
Definition: pub_misc.h:479
SCIP_Real SCIPgetLhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_Real obj
static SCIP_DECL_PRESOLFREE(presolFreeSymmetry)
SCIP_Real * rhscoef
static SCIP_Bool SymmetryFixVar(SYM_SPEC fixedtype, SCIP_VAR *var)
int SCIPgetNVarsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetLhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR * SCIPgetIntVarXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5746
SCIP_RETCODE SCIPgetGeneratorsSymmetry(SCIP *scip, int *npermvars, SCIP_VAR ***permvars, int *nperms, int ***perms, SCIP_Real *log10groupsize)
SCIP_RETCODE SCIPsetPresolExitpre(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLEXITPRE((*presolexitpre)))
Definition: scip.c:7032
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip.c:4239
SCIP_RETCODE SCIPincludePresolSymmetry(SCIP *scip)
#define SCIPreallocBufferArray(scip, ptr, num)
Definition: scip.h:22624
#define DEFAULT_CHECKSYMMETRIES