Scippy

SCIP

Solving Constraint Integer Programs

presol_symbreak.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_symbreak.c
17  * @brief presolver for adding symmetry breaking constraints
18  * @author Marc Pfetsch
19  * @author Thomas Rehn
20  * @author Christopher Hojny
21  *
22  * This presolver adds the following symmetry breaking constraints:
23  *
24  * - minimal cover inequalities for symresacks via a constraint handler
25  *
26  * @note It is important to control the order of presolvers within SCIP in order to avoid contraditions. First, one needs
27  * to take care of presolvers that have an effect on symmetry, e.g., presol_domcol. If symmetry is computed on the
28  * original formulation, we perform this presolver at the very beginning. Otherwise, we try to compute symmetry as late
29  * as possible and then add constraints based on this information.
30  *
31  * @note Currently, we only allocate memory for pointers to symresack constraints for group generators. If further
32  * constraints are considered, we have to reallocate memory.
33  */
34 
35 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
36 
37 #include "blockmemshell/memory.h"
38 #include "scip/cons_orbitope.h"
39 #include "scip/cons_symresack.h"
40 #include "scip/misc.h"
41 #include "scip/presol_symbreak.h"
42 #include "scip/presol_symmetry.h"
43 #include "scip/pub_cons.h"
44 #include "scip/pub_message.h"
45 #include "scip/pub_misc.h"
46 #include "scip/pub_presol.h"
47 #include "scip/pub_var.h"
48 #include "scip/scip_cons.h"
49 #include "scip/scip_general.h"
50 #include "scip/scip_mem.h"
51 #include "scip/scip_message.h"
52 #include "scip/scip_param.h"
53 #include "scip/scip_presol.h"
54 #include "scip/scip_prob.h"
55 #include <string.h>
56 
57 /* presolver properties */
58 #define PRESOL_NAME "symbreak"
59 #define PRESOL_DESC "presolver for adding symmetry breaking constraints"
60 #define PRESOL_PRIORITY -10000000 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers) */
61 #define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
62 #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /**< timing for presolving */
63 
64 /* default parameters */
65 #define DEFAULT_CONSSADDLP TRUE /**< Should the symmetry breaking constraints be added to the LP? */
66 #define DEFAULT_ADDSYMRESACKS TRUE /**< Add inequalities for symresacks for each generator? */
67 #define DEFAULT_COMPUTEORBITS FALSE /**< Should the orbits of the symmetry group be computed? */
68 #define DEFAULT_DETECTORBITOPES FALSE /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
69 #define DEFAULT_ADDCONSSTIMING 2 /**< timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) */
70 
71 /*
72  * Data structures
73  */
74 
75 /** presolver data */
76 struct SCIP_PresolData
77 {
78  int** perms; /**< list of permutations */
79  int nperms; /**< number of permutations in perms */
80  int npermvars; /**< number of variables affected by permutations */
81  SCIP_Real log10groupsize; /**< log10 of group size */
82  SCIP_Bool binvaraffected; /**< whether binary variables are affected */
83  SCIP_VAR** permvars; /**< array of variables on which permutations act */
84  SCIP_Bool addedconss; /**< whether we already added symmetry breaking constraints */
85  SCIP_Bool computedsymmetry; /**< whether symmetry has been computed already */
86  SCIP_Bool conssaddlp; /**< Should the symmetry breaking constraints be added to the LP? */
87  SCIP_Bool addsymresacks; /**< Add symresack constraints for each generator? */
88  SCIP_Bool enabled; /**< run presolver? */
89  int addconsstiming; /**< timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) */
90  SCIP_CONS** genconss; /**< list of generated constraints */
91  int ngenconss; /**< number of generated constraints */
92  int nsymresacks; /**< number of symresack constraints */
93  SCIP_Bool detectorbitopes; /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
94  int norbitopes; /**< number of orbitope constraints */
95  int norbits; /**< number of non-trivial orbits of permutation group */
96  SCIP_Bool computeorbits; /**< whether the orbits of the symmetry group should be computed */
97  int* orbits; /**< array containing the indices of variables sorted by orbits */
98  int* orbitbegins; /**< array containing in i-th position the first position of orbit i in orbits array */
99  int ncomponents; /**< number of components of symmetry group */
100  int* npermsincomponent; /**< array containing number of permutations per component */
101  int** components; /**< array containing for each components the corresponding permutations */
102  SCIP_Shortbool* componentblocked; /**< array to store whether a component is blocked to be considered by symmetry handling techniques */
103 };
104 
105 
106 
107 /*
108  * Local methods
109  */
110 
111 
112 /** compute components of symmetry group */
113 static
115  SCIP* scip, /**< SCIP instance */
116  SCIP_PRESOLDATA* presoldata /**< data of symmetry breaking presolver */
117  )
118 {
119  SCIP_DISJOINTSET* componentstoperm = NULL;
120  SCIP_Bool startchanged;
121  int** perms;
122  int npermsincomponent;
123  int curcomponent;
124  int ncomponents;
125  int componentcnt;
126  int npermvars;
127  int nperms;
128  int newstart;
129  int start;
130  int i;
131  int j;
132  int k;
133 
134  assert( scip != NULL );
135  assert( presoldata != NULL );
136 
137  nperms = presoldata->nperms;
138 
139  presoldata->ncomponents = -1;
140 
141  if ( nperms <= 0 )
142  return SCIP_OKAY;
143 
144  /* at this point, we have at least one non-trivial permutation */
145  npermvars = presoldata->npermvars;
146  perms = presoldata->perms;
147 
148  /* if there exists only one orbit, we have exactly one component */
149  if ( presoldata->norbits == 1 )
150  ncomponents = 1;
151  else
152  {
153  /* init array that assigns to each permutation its component of the group */
154  SCIP_CALL( SCIPdisjointsetCreate(&componentstoperm, SCIPblkmem(scip), nperms) );
155  ncomponents = nperms;
156 
157  /* check whether two permutations belong to the same component */
158  for (i = 0; i < nperms && ncomponents > 1; ++i)
159  {
160  for (j = i + 1; j < nperms && ncomponents > 1; ++j)
161  {
162  int componentI;
163  int componentJ;
164  int* permI;
165  int* permJ;
166 
167  componentI = SCIPdisjointsetFind(componentstoperm, i);
168  componentJ = SCIPdisjointsetFind(componentstoperm, j);
169  if ( componentI == componentJ )
170  continue;
171 
172  permI = perms[i];
173  permJ = perms[j];
174 
175  /* Do perms[i] and perms[j] belong to the same component? */
176  for (k = 0; k < npermvars; ++k)
177  {
178  /* both permutations belong to the same component */
179  if ( permI[k] != k && permJ[k] != k )
180  {
181  /* Keep the smallest identifier to keep track of where a new component starts.
182  * Note that it is necessary to store the smaller identifier to be able to iterate
183  * over all ordered pairs (i,j), i < j, of permutations, instead of all unordered
184  * pairs {i,j}. */
185  if ( componentI < componentJ )
186  SCIPdisjointsetUnion(componentstoperm, i, j, TRUE);
187  else
188  SCIPdisjointsetUnion(componentstoperm, j, i, TRUE);
189 
190  --ncomponents;
191  break;
192  }
193  }
194  }
195  }
196  assert( ncomponents > 0 );
197  }
198 
199  /* store data */
200  presoldata->ncomponents = ncomponents;
201 
202  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &presoldata->npermsincomponent, ncomponents) );
203  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &presoldata->components, ncomponents) );
204 
205  /* copy componentstoperm adequatly to components and npermsincomponent */
206  if ( ncomponents == 1 )
207  {
208  presoldata->npermsincomponent[0] = nperms;
209 
210  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(presoldata->components[0]), nperms) );
211  for (i = 0; i < nperms; ++i)
212  presoldata->components[0][i] = i;
213  }
214  else
215  {
216  componentcnt = 0;
217  start = 0;
218  newstart = 0;
219  startchanged = TRUE;
220  while ( startchanged )
221  {
222  startchanged = FALSE;
223  npermsincomponent = 0;
224  assert( componentstoperm != NULL );
225  curcomponent = SCIPdisjointsetFind(componentstoperm, start);
226 
227  /* find number of permutations in current component and detect first perm in another permutation */
228  for (i = start; i < nperms; ++i)
229  {
230  if ( SCIPdisjointsetFind(componentstoperm, i) == curcomponent )
231  ++npermsincomponent;
232  /* only store other component if it has the same identifier as its node (mark that new component starts) */
233  else if ( ! startchanged && SCIPdisjointsetFind(componentstoperm, i) == i )
234  {
235  newstart = i;
236  startchanged = TRUE;
237  }
238  }
239 
240  /* store number of permutations and permutation per component */
241  presoldata->npermsincomponent[componentcnt] = npermsincomponent;
242 
243  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(presoldata->components[componentcnt]), npermsincomponent) );
244 
245  j = 0;
246  for (i = start; i < nperms; ++i)
247  {
248  if ( SCIPdisjointsetFind(componentstoperm, i) == curcomponent )
249  presoldata->components[componentcnt][j++] = i;
250  }
251 
252  ++componentcnt;
253  start = newstart;
254  }
255  }
256 
257  if ( presoldata->norbits != 1 )
258  SCIPdisjointsetFree(&componentstoperm, SCIPblkmem(scip));
259 
260  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(presoldata->componentblocked), ncomponents) );
261  for (i = 0; i < ncomponents; ++i)
262  presoldata->componentblocked[i] = FALSE;
263 
264 #ifdef SCIP_OUTPUT
265  printf("\n\n\nTESTS\n\n");
266  printf("Number of components:\t\t%d\n", presoldata->ncomponents);
267  for (i = 0; i < presoldata->ncomponents; ++i)
268  {
269  printf("Component %d: Number of perms: %d\n", i, presoldata->npermsincomponent[i]);
270  for (j = 0; j < presoldata->npermsincomponent[i]; ++j)
271  {
272  printf("%d ", presoldata->components[i][j]);
273  }
274  printf("\n");
275  }
276 
277  printf("GENERATORS:");
278  for (i = 0; i < presoldata->nperms; ++i)
279  {
280  printf("generator %d:\n", i);
281  for (j = 0; j < presoldata->npermvars; ++j)
282  {
283  if ( presoldata->perms[i][j] != j )
284  printf("%d ", presoldata->perms[i][j]);
285  }
286  printf("\n");
287  }
288 #endif
289 
290  return SCIP_OKAY;
291 }
292 
293 
294 /** check whether a permutation is a composition of 2-cycles of binary variables and in this case determines the number of 2-cycles */
295 static
297  int* perm, /**< permutation */
298  SCIP_VAR** vars, /**< array of variables perm is acting on */
299  int nvars, /**< number of variables */
300  SCIP_Bool* iscompoftwocycles, /**< pointer to store whether permutation is a composition of 2-cycles */
301  int* ntwocyclesperm, /**< pointer to store number of 2-cycles */
302  SCIP_Bool* allvarsbinary /**< pointer to store whether perm is acting on binary variables only */
303  )
304 {
305  int ntwocycles = 0;
306  int i;
307 
308  assert( perm != NULL );
309  assert( iscompoftwocycles != NULL );
310  assert( ntwocyclesperm != NULL );
311  assert( allvarsbinary != NULL );
312 
313  *iscompoftwocycles = FALSE;
314  *ntwocyclesperm = 0;
315  *allvarsbinary = TRUE;
316  for (i = 0; i < nvars; ++i)
317  {
318  /* skip fixed points and avoid treating the same 2-cycle twice */
319  if ( perm[i] <= i )
320  continue;
321 
322  if ( perm[perm[i]] == i )
323  {
324  if ( SCIPvarIsBinary(vars[i]) && SCIPvarIsBinary(vars[perm[i]]) )
325  ++ntwocycles;
326  else
327  {
328  /* at least one variable is not binary */
329  *allvarsbinary = FALSE;
330  return SCIP_OKAY;
331  }
332  }
333  else
334  {
335  /* we do not have a 2-cycle */
336  return SCIP_OKAY;
337  }
338  }
339 
340  /* at this point the permutation is a composition of 2-cycles on binary variables */
341  *iscompoftwocycles = TRUE;
342  *ntwocyclesperm = ntwocycles;
343 
344  return SCIP_OKAY;
345 }
346 
347 
348 /** Given a matrix with nrows and \#perms + 1 columns whose first nfilledcols columns contain entries of variables, this routine
349  * checks whether the 2-cycles of perm intersect each row of column coltoextend in exactly one position. In this case,
350  * we add one column to the suborbitope of the first nfilledcols columns.
351  *
352  * @pre Every non-trivial cycle of perm is a 2-cycle.
353  * @pre perm has nrows many 2-cycles
354  */
355 static
357  int** suborbitope, /**< matrix containing suborbitope entries */
358  int nrows, /**< number of rows of suborbitope */
359  int nfilledcols, /**< number of columns of suborbitope which are filled with entries */
360  int coltoextend, /**< index of column that should be extended by perm */
361  int* perm, /**< permutation */
362  SCIP_Bool leftextension, /**< whether we extend the suborbitope to the left */
363  int** nusedelems, /**< pointer to array storing how often an element was used in the orbitope */
364  SCIP_Bool* success, /**< pointer to store whether extension was successful */
365  SCIP_Bool* infeasible /**< pointer to store if the number of intersecting cycles is too small */
366  )
367 {
368  int nintersections = 0;
369  int row;
370  int idx1;
371  int idx2;
372 
373  assert( suborbitope != NULL );
374  assert( nfilledcols > 0 );
375  assert( perm != NULL );
376  assert( nusedelems != NULL );
377  assert( success != NULL );
378  assert( infeasible != NULL );
379 
380  *success = FALSE;
381  *infeasible = FALSE;
382 
383  /* if we try to extend the first orbitope generator by another one,
384  * we can change the order of entries in each row of suborbitope */
385  if ( nfilledcols == 2 )
386  {
387  /* check whether each cycle of perm intersects with a row of suborbitope */
388  for (row = 0; row < nrows; ++row)
389  {
390  idx1 = suborbitope[row][0];
391  idx2 = suborbitope[row][1];
392 
393  /* if idx1 or idx2 is affected by perm, we can extend the row of the orbitope */
394  if ( idx1 != perm[idx1] )
395  {
396  /* change order of idx1 and idx2 for right extensions */
397  if ( ! leftextension )
398  {
399  suborbitope[row][0] = idx2;
400  suborbitope[row][1] = idx1;
401  }
402  suborbitope[row][2] = perm[idx1];
403  ++nintersections;
404 
405  (*nusedelems)[idx1] += 1;
406  (*nusedelems)[perm[idx1]] += 1;
407 
408  /* if an element appears too often in the orbitope matrix */
409  if ( (*nusedelems)[idx1] > 2 || (*nusedelems)[perm[idx1]] > 2 )
410  {
411  *infeasible = TRUE;
412  break;
413  }
414  }
415  else if ( idx2 != perm[idx2] )
416  {
417  /* change order of idx1 and idx2 for left extensions */
418  if ( leftextension )
419  {
420  suborbitope[row][0] = idx2;
421  suborbitope[row][1] = idx1;
422  }
423  suborbitope[row][2] = perm[idx2];
424  ++nintersections;
425 
426  (*nusedelems)[idx2] += 1;
427  (*nusedelems)[perm[idx2]] += 1;
428 
429  /* if an element appears too often in the orbitope matrix */
430  if ( (*nusedelems)[idx2] > 2 || (*nusedelems)[perm[idx2]] > 2 )
431  {
432  *infeasible = TRUE;
433  break;
434  }
435  }
436  }
437  }
438  else
439  {
440  /* check whether each cycle of perm intersects with a row of suborbitope */
441  for (row = 0; row < nrows; ++row)
442  {
443  idx1 = suborbitope[row][coltoextend];
444 
445  /* if idx1 is affected by perm, we can extend the row of the orbitope */
446  if ( idx1 != perm[idx1] )
447  {
448  suborbitope[row][nfilledcols] = perm[idx1];
449  ++nintersections;
450 
451  (*nusedelems)[idx1] += 1;
452  (*nusedelems)[perm[idx1]] += 1;
453 
454  /* if an element appears to often in the orbitope matrix */
455  if ( (*nusedelems)[idx1] > 2 || (*nusedelems)[perm[idx1]] > 2 )
456  {
457  *infeasible = TRUE;
458  break;
459  }
460  }
461  }
462  }
463 
464  /* if there are too few intersection, this is not an orbitope */
465  if ( nintersections > 0 && nintersections < nrows )
466  *infeasible = TRUE;
467  else if ( nintersections == nrows )
468  *success = TRUE;
469 
470  return SCIP_OKAY;
471 }
472 
473 
474 /** generate variable matrix for orbitope constraint handler */
475 static
477  SCIP_VAR**** vars, /**< pointer to matrix of orbitope variables */
478  int nrows, /**< number of rows of orbitope */
479  int ncols, /**< number of columns of orbitope */
480  SCIP_VAR** permvars, /**< superset of variables that are contained in orbitope */
481  int npermvars, /**< number of variables in permvars array */
482  int** orbitopevaridx, /**< permuted index table of variables in permvars that are contained in orbitope */
483  int* columnorder, /**< permutation to reorder column of orbitopevaridx */
484  int* nusedelems, /**< array storing how often an element was used in the orbitope */
485  SCIP_Bool* infeasible /**< pointer to store whether the potential orbitope is not an orbitope */
486  )
487 {
488  int nfilledcols = 0;
489  int curcolumn;
490  int i;
491 
492  assert( vars != NULL );
493  assert( nrows > 0 );
494  assert( ncols > 0 );
495  assert( permvars != NULL );
496  assert( npermvars > 0 );
497  assert( orbitopevaridx != NULL );
498  assert( columnorder != NULL );
499  assert( nusedelems != NULL );
500  assert( infeasible != NULL );
501 
502  curcolumn = ncols - 1;
503 
504  /* start filling vars matrix with the right-most column w.r.t. columnorder */
505  while ( curcolumn >= 0 && columnorder[curcolumn] >= 0 )
506  {
507  for (i = 0; i < nrows; ++i)
508  {
509  assert( orbitopevaridx[i][curcolumn] < npermvars );
510  assert( SCIPvarIsBinary(permvars[orbitopevaridx[i][curcolumn]]) );
511 
512  /* elements in first column of orbitope have to appear exactly once in the orbitope */
513  if ( nusedelems[orbitopevaridx[i][curcolumn]] > 1 )
514  {
515  *infeasible = TRUE;
516  break;
517  }
518 
519  (*vars)[i][nfilledcols] = permvars[orbitopevaridx[i][curcolumn]];
520  }
521  --curcolumn;
522  ++nfilledcols;
523  }
524 
525  /* There are three possibilities for the structure of columnorder:
526  * 1) [0, 1, -1, -1, ..., -1]
527  * 2) [0, 1, 1, 1, ..., 1]
528  * 3) [0, 1, -1, -1, ...., -1, 1, 1, ..., 1]
529  */
530  /* Either we are in case 1) or case 3), or all columns should have been added to vars in case 2) */
531  assert( curcolumn > 1 || (curcolumn < 0 && nfilledcols == ncols) );
532 
533  if ( curcolumn > 1 )
534  {
535  /* add column with columnorder 1 to vars */
536  for (i = 0; i < nrows; ++i)
537  {
538  assert( orbitopevaridx[i][1] < npermvars );
539  assert( SCIPvarIsBinary(permvars[orbitopevaridx[i][1]]) );
540 
541  (*vars)[i][nfilledcols] = permvars[orbitopevaridx[i][1]];
542  }
543  ++nfilledcols;
544 
545  /* add column with columnorder 0 to vars */
546  for (i = 0; i < nrows; ++i)
547  {
548  assert( orbitopevaridx[i][0] < npermvars );
549  assert( SCIPvarIsBinary(permvars[orbitopevaridx[i][0]]) );
550 
551  (*vars)[i][nfilledcols] = permvars[orbitopevaridx[i][0]];
552  }
553  ++nfilledcols;
554 
555  /* add columns with a negative column order to vars */
556  if ( nfilledcols < ncols )
557  {
558  assert( ncols > 2 );
559 
560  curcolumn = 2;
561  while ( nfilledcols < ncols )
562  {
563  assert( columnorder[curcolumn] < 0 );
564 
565  for (i = 0; i < nrows; ++i)
566  {
567  assert( orbitopevaridx[i][curcolumn] < npermvars );
568  assert( SCIPvarIsBinary(permvars[orbitopevaridx[i][curcolumn]]) );
569 
570  /* elements in last column of orbitope have to appear exactly once in the orbitope */
571  if ( nfilledcols == ncols - 1 && nusedelems[orbitopevaridx[i][curcolumn]] > 1 )
572  {
573  *infeasible = TRUE;
574  break;
575  }
576 
577  (*vars)[i][nfilledcols] = permvars[orbitopevaridx[i][curcolumn]];
578  }
579  ++curcolumn;
580  ++nfilledcols;
581  }
582  }
583  }
584 
585  return SCIP_OKAY;
586 }
587 
588 
589 /** check whether components of the symmetry group can be completely handled by orbitopes */
590 static
592  SCIP* scip, /**< SCIP instance */
593  SCIP_PRESOLDATA* presoldata /**< pointer to data of symbreak presolver */
594  )
595 {
596  SCIP_VAR** permvars;
597  int** components;
598  int** perms;
599  int* npermsincomponent;
600  int ncomponents;
601  int npermvars;
602  int i;
603 
604  assert( scip != NULL );
605  assert( presoldata != NULL );
606 
607  /* exit if no symmetry is present */
608  if ( presoldata->nperms == 0 )
609  return SCIP_OKAY;
610 
611  /* compute components if not done yet */
612  if ( presoldata->ncomponents == -1 )
613  {
614  SCIP_CALL( computeComponents(scip, presoldata) );
615  }
616  assert( presoldata->nperms > 0 );
617  assert( presoldata->perms != NULL );
618  assert( presoldata->npermvars > 0 );
619  assert( presoldata->permvars != NULL );
620  assert( presoldata->ncomponents > 0 );
621  assert( presoldata->components != NULL );
622  assert( presoldata->npermsincomponent != NULL );
623 
624  perms = presoldata->perms;
625  npermvars = presoldata->npermvars;
626  permvars = presoldata->permvars;
627  ncomponents = presoldata->ncomponents;
628  npermsincomponent = presoldata->npermsincomponent;
629  components = presoldata->components;
630 
631  /* iterate over components */
632  for (i = 0; i < ncomponents; ++i)
633  {
634  SCIP_VAR*** vars;
635  SCIP_CONS* cons;
636  SCIP_Bool isorbitope = TRUE;
637  SCIP_Bool* usedperm;
638  int** orbitopevaridx;
639  int* columnorder;
640  int ntwocyclescomp = INT_MAX;
641  int nfilledcols;
642  int nusedperms;
643  int* nusedelems;
644  int coltoextend;
645  int j;
646  int row;
647  SCIP_Bool infeasibleorbitope;
648 
649  /* get properties of permutations */
650  assert( npermsincomponent[i] > 0 );
651  for (j = 0; j < npermsincomponent[i]; ++j)
652  {
653  SCIP_Bool iscompoftwocycles = FALSE;
654  SCIP_Bool allvarsbinary = TRUE;
655  int ntwocyclesperm = 0;
656 
657  SCIP_CALL( getPermProperties(perms[components[i][j]], permvars, npermvars, &iscompoftwocycles, &ntwocyclesperm, &allvarsbinary) );
658 
659  /* if we are checking the first permutation */
660  if ( ntwocyclescomp == INT_MAX )
661  ntwocyclescomp = ntwocyclesperm;
662 
663  /* no or different number of 2-cycles or not all vars binary: permutations cannot generate orbitope */
664  if ( ntwocyclescomp == 0 || ntwocyclescomp != ntwocyclesperm || ! allvarsbinary )
665  {
666  isorbitope = FALSE;
667  break;
668  }
669  }
670 
671  /* if no orbitope was detected or orbitope has too few rows */
672  if ( ! isorbitope )
673  continue;
674  assert( ntwocyclescomp > 0 );
675  assert( ntwocyclescomp < INT_MAX );
676 
677  /* iterate over permutations and check whether for each permutation there exists
678  * another permutation whose 2-cycles intersect pairwise in exactly one element */
679 
680  /* whether a permutation was considered to contribute to orbitope */
681  SCIP_CALL( SCIPallocBufferArray(scip, &usedperm, npermsincomponent[i]) );
682  for (j = 0; j < npermsincomponent[i]; ++j)
683  usedperm[j] = FALSE;
684  nusedperms = 0;
685 
686  /* orbitope matrix for indices of variables in permvars array */
687  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, ntwocyclescomp) );
688  for (j = 0; j < ntwocyclescomp; ++j)
689  {
690  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[j], npermsincomponent[i] + 1) ); /*lint !e866*/
691  }
692 
693  /* order of columns of orbitopevaridx */
694  SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, npermsincomponent[i] + 1) );
695  for (j = 0; j < npermsincomponent[i] + 1; ++j)
696  columnorder[j] = npermsincomponent[i] + 2;
697 
698  /* count how often an element was used in the potential orbitope */
699  SCIP_CALL( SCIPallocBufferArray(scip, &nusedelems, npermvars) );
700  for (j = 0; j < npermvars; ++j)
701  nusedelems[j] = 0;
702 
703  /* fill first two columns of orbitopevaridx matrix */
704  row = 0;
705  for (j = 0; j < npermvars; ++j)
706  {
707  int permidx = components[i][0];
708 
709  /* avoid adding the same 2-cycle twice */
710  if ( perms[permidx][j] > j )
711  {
712  orbitopevaridx[row][0] = j;
713  orbitopevaridx[row++][1] = perms[permidx][j];
714  nusedelems[j] += 1;
715  nusedelems[perms[permidx][j]] += 1;
716  }
717 
718  if ( row == ntwocyclescomp )
719  break;
720  }
721  assert( row == ntwocyclescomp );
722 
723  usedperm[0] = TRUE;
724  ++nusedperms;
725  columnorder[0] = 0;
726  columnorder[1] = 1;
727  nfilledcols = 2;
728 
729  /* extend orbitopevaridx matrix to the left, i.e., iteratively find new permutations that
730  * intersect the last added left column in each row in exactly one entry, starting with
731  * column 0 */
732  coltoextend = 0;
733  for (j = 0; j < npermsincomponent[i]; ++j)
734  { /* lint --e{850} */
735  SCIP_Bool success = FALSE;
736  SCIP_Bool infeasible = FALSE;
737 
738  if ( nusedperms == npermsincomponent[i] )
739  break;
740 
741  if ( usedperm[j] )
742  continue;
743 
744  SCIP_CALL( extendSubOrbitope(orbitopevaridx, ntwocyclescomp, nfilledcols, coltoextend,
745  perms[components[i][j]], TRUE, &nusedelems, &success, &infeasible) );
746 
747  if ( infeasible )
748  {
749  isorbitope = FALSE;
750  break;
751  }
752  else if ( success )
753  {
754  usedperm[j] = TRUE;
755  ++nusedperms;
756  coltoextend = nfilledcols;
757  columnorder[nfilledcols++] = -1; /* mark column to be filled from the left */
758  j = 0; /* reset j since previous permutations can now intersect with the latest added column */
759  }
760  }
761 
762  if ( ! isorbitope ) /*lint !e850*/
763  goto FREEDATASTRUCTURES;
764 
765  coltoextend = 1;
766  for (j = 0; j < npermsincomponent[i]; ++j)
767  { /*lint --e(850)*/
768  SCIP_Bool success = FALSE;
769  SCIP_Bool infeasible = FALSE;
770 
771  if ( nusedperms == npermsincomponent[i] )
772  break;
773 
774  if ( usedperm[j] )
775  continue;
776 
777  SCIP_CALL( extendSubOrbitope(orbitopevaridx, ntwocyclescomp, nfilledcols, coltoextend,
778  perms[components[i][j]], FALSE, &nusedelems, &success, &infeasible) );
779 
780  if ( infeasible )
781  {
782  isorbitope = FALSE;
783  break;
784  }
785  else if ( success )
786  {
787  usedperm[j] = TRUE;
788  ++nusedperms;
789  coltoextend = nfilledcols;
790  columnorder[nfilledcols] = 1; /* mark column to be filled from the right */
791  ++nfilledcols;
792  j = 0; /* reset j since previous permutations can now intersect with the latest added column */
793  }
794  }
795 
796  if ( nusedperms < npermsincomponent[i] ) /*lint !e850*/
797  isorbitope = FALSE;
798 
799  if ( ! isorbitope )
800  goto FREEDATASTRUCTURES;
801 
802  /* we have found a potential orbitope, prepare data for orbitope conshdlr */
803  SCIP_CALL( SCIPallocBufferArray(scip, &vars, ntwocyclescomp) );
804  for (j = 0; j < ntwocyclescomp; ++j)
805  {
806  SCIP_CALL( SCIPallocBufferArray(scip, &vars[j], npermsincomponent[i] + 1) ); /*lint !e866*/
807  }
808 
809  /* prepare variable matrix (reorder columns of orbitopevaridx) */
810  infeasibleorbitope = FALSE;
811  SCIP_CALL( generateOrbitopeVarsMatrix(&vars, ntwocyclescomp, npermsincomponent[i] + 1, permvars, npermvars,
812  orbitopevaridx, columnorder, nusedelems, &infeasibleorbitope) );
813 
814  if ( ! infeasibleorbitope )
815  {
816  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, "orbitope", vars, SCIP_ORBITOPETYPE_FULL, ntwocyclescomp, npermsincomponent[i] + 1, TRUE,
817  presoldata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
818 
819  SCIP_CALL( SCIPaddCons(scip, cons) );
820 
821  /* do not release constraint here - will be done later */
822  presoldata->genconss[presoldata->ngenconss] = cons;
823  ++presoldata->ngenconss;
824  ++presoldata->norbitopes;
825  presoldata->componentblocked[i] = TRUE;
826  presoldata->addedconss = TRUE;
827  }
828 
829  /* free data structures */
830  for (j = 0; j < ntwocyclescomp; ++j)
831  SCIPfreeBufferArray(scip, &vars[j]);
832  SCIPfreeBufferArray(scip, &vars);
833 
834  FREEDATASTRUCTURES:
835  SCIPfreeBufferArray(scip, &nusedelems);
836  SCIPfreeBufferArray(scip, &columnorder);
837  for (j = 0; j < ntwocyclescomp; ++j)
838  SCIPfreeBufferArray(scip, &orbitopevaridx[j]);
839  SCIPfreeBufferArray(scip, &orbitopevaridx);
840  SCIPfreeBufferArray(scip, &usedperm);
841  }
842 
843  return SCIP_OKAY;
844 }
845 
846 
847 /** add symresack constraints */
848 static
850  SCIP* scip, /**< SCIP instance */
851  SCIP_PRESOL* presol /**< symmetry breaking presolver */
852  )
853 {
854  SCIP_PRESOLDATA* presoldata;
855  SCIP_VAR** permvars;
856  SCIP_Bool conssaddlp;
857  int** perms;
858  int nperms;
859  int nsymresackcons = 0;
860  int npermvars;
861  int ncomponents;
862  int i;
863  int p;
864 
865  assert( scip != NULL );
866  assert( presol != NULL );
867 
868  presoldata = SCIPpresolGetData(presol);
869  assert( presoldata != NULL );
870 
871  perms = presoldata->perms;
872  nperms = presoldata->nperms;
873  permvars = presoldata->permvars;
874  npermvars = presoldata->npermvars;
875  conssaddlp = presoldata->conssaddlp;
876  ncomponents = presoldata->ncomponents;
877 
878  assert( nperms <= 0 || perms != NULL );
879  assert( permvars != NULL );
880  assert( npermvars > 0 );
881 
882  /* if we use different approaches for components of symmetry group */
883  if ( ncomponents > 0 )
884  {
885  /* loop through components */
886  for (i = 0; i < ncomponents; ++i)
887  {
888  /* skip components that were treated by different symemtry handling techniques */
889  if ( presoldata->componentblocked[i] )
890  continue;
891 
892  /* loop through perms in component i and add symresack constraints */
893  for (p = 0; p < presoldata->npermsincomponent[i]; ++p)
894  {
895  SCIP_CONS* cons;
896  int permidx = presoldata->components[i][p];
897  char name[SCIP_MAXSTRLEN];
898 
899  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_component%d_perm%d", i, p);
900  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[permidx], permvars, npermvars,
901  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
902 
903  SCIP_CALL( SCIPaddCons(scip, cons) );
904 
905  /* do not release constraint here - will be done later */
906  presoldata->genconss[presoldata->ngenconss] = cons;
907  ++presoldata->ngenconss;
908  ++presoldata->nsymresacks;
909  ++nsymresackcons;
910  SCIPdebugMsg(scip, "Added symresack constraint: %d.\n", nsymresackcons);
911  }
912  }
913  }
914  else
915  {
916  /* loop through perms and add symresack constraints */
917  for (p = 0; p < nperms; ++p)
918  {
919  SCIP_CONS* cons;
920  char name[SCIP_MAXSTRLEN];
921 
922  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_perm%d", p);
923  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[p], permvars, npermvars,
924  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
925 
926  SCIP_CALL( SCIPaddCons(scip, cons) );
927 
928  /* do not release constraint here - will be done later */
929  presoldata->genconss[presoldata->ngenconss] = cons;
930  ++presoldata->ngenconss;
931  ++presoldata->nsymresacks;
932  ++nsymresackcons;
933  SCIPdebugMsg(scip, "Added symresack constraint: %d.\n", nsymresackcons);
934  }
935  }
936 
937  if ( nsymresackcons > 0 )
938  presoldata->addedconss = TRUE;
939 
940  return SCIP_OKAY;
941 }
942 
943 
944 /** analyze generators and add symmetry breaking constraints */
945 static
947  SCIP* scip, /**< SCIP instance */
948  SCIP_PRESOL* presol /**< presolver */
949  )
950 {
951  SCIP_PRESOLDATA* presoldata;
952 
953  assert( scip != NULL );
954  assert( presol != NULL );
955 
956  presoldata = SCIPpresolGetData(presol);
957  assert( presoldata != NULL );
958 
959  /* exit if no or only trivial symmetry group is available */
960  if ( presoldata->nperms < 1 || ! presoldata->binvaraffected )
961  return SCIP_OKAY;
962 
963  if ( presoldata->addsymresacks )
964  {
965  SCIP_CALL( addSymresackConss(scip, presol) );
966  }
967 
968  return SCIP_OKAY;
969 }
970 
971 
972 /** find problem symmetries */
973 static
975  SCIP* scip, /**< SCIP instance */
976  SCIP_PRESOL* presol /**< data of presolver */
977  )
978 {
979  SCIP_PRESOLDATA* presoldata;
980 
981  assert( presol != NULL );
982  assert( scip != NULL );
983 
984  presoldata = SCIPpresolGetData(presol);
985  assert( presoldata != NULL );
986 
987  /* symmetries have already been computed */
988  if ( presoldata->addedconss )
989  {
990  assert( presoldata->nperms > 0 );
991 
992  return SCIP_OKAY;
993  }
994 
995  /* get symmetry information, if not already computed */
996  if ( ! presoldata->computedsymmetry )
997  {
998  SCIPdebugMsg(scip, "Symmetry breaking presolver: computing symmetry ...\n");
999  assert( presoldata->nperms < 0 );
1000 
1001  /* get symmetries */
1003  &(presoldata->npermvars), &(presoldata->permvars), &(presoldata->nperms), &(presoldata->perms),
1004  &(presoldata->log10groupsize), &(presoldata->binvaraffected)) );
1005 
1006  presoldata->computedsymmetry = TRUE;
1007 
1008  if ( presoldata->nperms <= 0 || ! presoldata->binvaraffected )
1009  {
1010  SCIPdebugMsg(scip, "Symmetry breaking presolver: no symmetry on binary variables has been found, turning presolver off.\n");
1011  presoldata->enabled = FALSE;
1012  return SCIP_OKAY;
1013  }
1014  else
1015  {
1016  assert( presoldata->nperms > 0 );
1017 
1018  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(presoldata->genconss), presoldata->nperms) );
1019 
1020  if ( presoldata->computeorbits )
1021  {
1022  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &presoldata->orbits, presoldata->npermvars) );
1023  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &presoldata->orbitbegins, presoldata->npermvars) );
1024 
1025  SCIP_CALL( SCIPcomputeGroupOrbitsSymbreak(scip, presoldata->permvars, presoldata->npermvars, presoldata->perms, presoldata->nperms, NULL,
1026  presoldata->orbits, presoldata->orbitbegins, &presoldata->norbits) );
1027  }
1028 
1029  if ( presoldata->detectorbitopes )
1030  {
1031  SCIP_CALL( detectOrbitopes(scip, presoldata) );
1032  }
1033  }
1034  }
1035  else if ( presoldata->nperms <= 0 || ! presoldata->binvaraffected )
1036  return SCIP_OKAY;
1037 
1038  /* at this point, the symmetry group should be computed and nontrivial */
1039  assert( presoldata->nperms > 0 );
1040 
1041  /* possibly stop */
1042  if ( SCIPisStopped(scip) )
1043  return SCIP_OKAY;
1044 
1045  /* add symmetry breaking constraints */
1046  assert( ! presoldata->addedconss || presoldata->norbitopes > 0 );
1047 
1048  SCIP_CALL( addSymmetryBreakingConstraints(scip, presol) );
1049 
1050  return SCIP_OKAY;
1051 }
1052 
1053 
1054 /*
1055  * Callback methods of presolver
1056  */
1057 
1058 
1059 /** destructor of presolver to free user data (called when SCIP is exiting) */
1060 static
1061 SCIP_DECL_PRESOLFREE(presolFreeSymbreak)
1062 { /*lint --e{715}*/
1063  SCIP_PRESOLDATA* presoldata;
1064 
1065  assert( scip != NULL );
1066  assert( presol != NULL );
1067 
1068  presoldata = SCIPpresolGetData(presol);
1069  assert( presoldata != NULL );
1070 
1071  SCIPfreeMemory(scip, &presoldata);
1072 
1073  return SCIP_OKAY;
1074 }
1075 
1076 
1077 /** initialization method of presolver (called after problem was transformed) */
1078 static
1079 SCIP_DECL_PRESOLINIT(presolInitSymbreak)
1080 {
1081  SCIP_PRESOLDATA* presoldata;
1082  int usesymmetry;
1083 
1084  assert( scip != NULL );
1085  assert( presol != NULL );
1086 
1087  SCIPdebugMsg(scip, "Init method of symmetry breaking presolver ...\n");
1088 
1089  presoldata = SCIPpresolGetData(presol);
1090  assert( presoldata != NULL );
1091 
1092  /* check whether we should run */
1093  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &usesymmetry) );
1094  if ( usesymmetry == (int) SYM_HANDLETYPE_SYMBREAK )
1095  presoldata->enabled = TRUE;
1096  else
1097  {
1098  presoldata->enabled = FALSE;
1099  }
1100 
1101  return SCIP_OKAY;
1102 }
1103 
1104 
1105 /** deinitialization method of presolver (called before transformed problem is freed) */
1106 static
1107 SCIP_DECL_PRESOLEXIT(presolExitSymbreak)
1108 {
1109  SCIP_PRESOLDATA* presoldata;
1110  SCIP_CONS** genconss;
1111  int ngenconss;
1112  int i;
1113 
1114  assert( scip != NULL );
1115  assert( presol != NULL );
1116 
1117  SCIPdebugMsg(scip, "Exit method of symmetry breaking presolver ...\n");
1118 
1119  presoldata = SCIPpresolGetData(presol);
1120  assert( presoldata != NULL );
1121 
1122  ngenconss = presoldata->ngenconss;
1123 
1124  /* release constraints */
1125  genconss = presoldata->genconss;
1126  for (i = 0; i < ngenconss; ++i)
1127  {
1128  assert( genconss[i] != NULL );
1129  SCIP_CALL( SCIPreleaseCons(scip, &genconss[i]) );
1130  }
1131 
1132  /* reset data (necessary if another problem is read in the SCIP shell) */
1133 
1134  /* free pointers to symmetry group and binary variables */
1135  SCIPfreeBlockMemoryArrayNull(scip, &presoldata->genconss, presoldata->nperms);
1136 
1137  presoldata->genconss = NULL;
1138  presoldata->ngenconss = 0;
1139 
1140  /* free orbit structures */
1141  if ( presoldata->norbits >= 0 )
1142  {
1143  SCIPfreeBlockMemoryArray(scip, &presoldata->orbitbegins, presoldata->npermvars);
1144  SCIPfreeBlockMemoryArray(scip, &presoldata->orbits, presoldata->npermvars);
1145  }
1146 
1147  presoldata->orbitbegins = NULL;
1148  presoldata->orbits = NULL;
1149  presoldata->norbits = -1;
1150 
1151  /* free components */
1152  if ( presoldata->ncomponents > 0 )
1153  {
1154  SCIPfreeBlockMemoryArray(scip, &presoldata->componentblocked, presoldata->ncomponents);
1155  for (i = 0; i < presoldata->ncomponents; ++i)
1156  SCIPfreeBlockMemoryArray(scip, &presoldata->components[i], presoldata->npermsincomponent[i]);
1157  SCIPfreeBlockMemoryArray(scip, &presoldata->components, presoldata->ncomponents);
1158  SCIPfreeBlockMemoryArray(scip, &presoldata->npermsincomponent, presoldata->ncomponents);
1159  }
1160 
1161  presoldata->componentblocked = NULL;
1162  presoldata->components = NULL;
1163  presoldata->npermsincomponent = NULL;
1164  presoldata->ncomponents = -1;
1165 
1166  /* reset basic data */
1167  presoldata->addedconss = FALSE;
1168  presoldata->computedsymmetry = FALSE;
1169  presoldata->enabled = TRUE;
1170  presoldata->nperms = -1;
1171  presoldata->log10groupsize = -1.0;
1172  presoldata->binvaraffected = FALSE;
1173  presoldata->norbitopes = 0;
1174  presoldata->nsymresacks = 0;
1175 
1176  return SCIP_OKAY;
1177 }
1178 
1179 
1180 /** presolving initialization method of presolver (called when presolving is about to begin) */
1181 static
1182 SCIP_DECL_PRESOLINITPRE(presolInitpreSymbreak)
1183 { /*lint --e{715}*/
1184  SCIP_PRESOLDATA* presoldata;
1185  int usesymmetry;
1186 
1187  assert( scip != NULL );
1188  assert( presol != NULL );
1189 
1190  presoldata = SCIPpresolGetData(presol);
1191  assert( presoldata != NULL );
1192 
1193  /* check whether we should run */
1194  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &usesymmetry) );
1195  if ( usesymmetry == (int) SYM_HANDLETYPE_SYMBREAK )
1196  presoldata->enabled = TRUE;
1197  else
1198  {
1199  SCIP_CALL( SCIPsetIntParam(scip, "presolving/symbreak/maxrounds", 0) );
1200  presoldata->enabled = FALSE;
1201  }
1202 
1203  /* add symmetry handling constraints if required */
1204  if ( presoldata->enabled && presoldata->addconsstiming == 0 )
1205  {
1206  SCIPdebugMsg(scip, "Try to add symmetry handling constraints before presolving.");
1207 
1209  }
1210 
1211  return SCIP_OKAY;
1212 }
1213 
1214 
1215 /** presolving deinitialization method of presolver (called after presolving has been finished) */
1216 static
1217 SCIP_DECL_PRESOLEXITPRE(presolExitpreSymbreak)
1218 { /*lint --e{715}*/
1219  SCIP_PRESOLDATA* presoldata;
1220 
1221  assert( scip != NULL );
1222  assert( presol != NULL );
1223  assert( strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0 );
1224 
1225  SCIPdebugMsg(scip, "Exitpre method of symbreak presolver ...\n");
1226 
1227  presoldata = SCIPpresolGetData(presol);
1228  assert( presoldata != NULL );
1229 
1230  /* guarantee that symmetries are computed (and handled) even if presolving is disabled */
1231  if ( presoldata->enabled && ! presoldata->addedconss )
1232  {
1234  }
1235 
1236  return SCIP_OKAY;
1237 }
1238 
1239 
1240 /** execution method of presolver */
1241 static
1242 SCIP_DECL_PRESOLEXEC(presolExecSymbreak)
1243 { /*lint --e{715}*/
1244  SCIP_PRESOLDATA* presoldata;
1245  int noldngenconns;
1246  int i;
1247 
1248  assert( scip != NULL );
1249  assert( presol != NULL );
1250  assert( result != NULL );
1251  assert( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING );
1252 
1253  *result = SCIP_DIDNOTRUN;
1254 
1255  presoldata = SCIPpresolGetData(presol);
1256  assert( presoldata != NULL );
1257 
1258  /* possibly skip presolver */
1259  if ( ! presoldata->enabled || presoldata->addedconss )
1260  return SCIP_OKAY;
1261 
1262  /* skip presolving if we are not at the end if addconsstiming == 2 */
1263  if ( presoldata->addconsstiming > 1 && ! SCIPisPresolveFinished(scip) )
1264  return SCIP_OKAY;
1265 
1266  /* possibly stop */
1267  if ( SCIPisStopped(scip) )
1268  return SCIP_OKAY;
1269 
1270  noldngenconns = presoldata->ngenconss;
1271 
1273 
1274  /* terminate if symmetry handling constraints have already been added */
1275  if ( ! presoldata->addedconss )
1276  return SCIP_OKAY;
1277 
1278  /* at this point, the symmetry group should be computed and nontrivial */
1279  assert( presoldata->nperms > 0 );
1280  assert( presoldata->ngenconss > 0 );
1281 
1282  *result = SCIP_DIDNOTFIND;
1283 
1284  *naddconss += presoldata->ngenconss - noldngenconns;
1285  SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", presoldata->ngenconss - noldngenconns);
1286 
1287  /* if constraints have been added, loop through generated constraints and presolve each */
1288  for (i = 0; i < presoldata->ngenconss; ++i)
1289  {
1290  SCIP_CALL( SCIPpresolCons(scip, presoldata->genconss[i], nrounds, SCIP_PRESOLTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
1291  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
1292  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
1293 
1294  /* exit if cutoff or unboundedness has been detected */
1295  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
1296  {
1297  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(presoldata->genconss[i]));
1298  return SCIP_OKAY;
1299  }
1300  }
1301  SCIPdebugMsg(scip, "Presolved %d constraints generated by symbreak.\n", presoldata->ngenconss);
1302 
1303  *result = SCIP_SUCCESS;
1304 
1305  return SCIP_OKAY;
1306 }
1307 
1308 
1309 /*
1310  * presolver specific interface methods
1311  */
1312 
1313 /** creates the symbreak presolver and includes it in SCIP */
1315  SCIP* scip /**< SCIP data structure */
1316  )
1317 {
1318  SCIP_PRESOLDATA* presoldata = NULL;
1319  SCIP_PRESOL* presol;
1320 
1321  /* create presolver data */
1322  SCIP_CALL( SCIPallocMemory(scip, &presoldata) );
1323 
1324  /* we must call the constructor explictly, because memory was m'alloced and not new'ed */
1325  presoldata->addedconss = FALSE;
1326  presoldata->computedsymmetry = FALSE;
1327  presoldata->enabled = TRUE;
1328  presoldata->nsymresacks = 0;
1329  presoldata->norbitopes = 0;
1330  presoldata->ngenconss = 0;
1331  presoldata->genconss = NULL;
1332  presoldata->nperms = -1;
1333  presoldata->log10groupsize = -1.0;
1334  presoldata->binvaraffected = FALSE;
1335  presoldata->norbits = -1;
1336  presoldata->orbits = NULL;
1337  presoldata->orbitbegins = NULL;
1338  presoldata->ncomponents = -1;
1339  presoldata->npermsincomponent = NULL;
1340  presoldata->components = NULL;
1341  presoldata->componentblocked = NULL;
1342 
1343  /* include presolver */
1345  presolExecSymbreak, presoldata) );
1346 
1347  /* set additional callbacks */
1348  SCIP_CALL( SCIPsetPresolFree(scip, presol, presolFreeSymbreak) );
1349  SCIP_CALL( SCIPsetPresolInit(scip, presol, presolInitSymbreak) );
1350  SCIP_CALL( SCIPsetPresolExit(scip, presol, presolExitSymbreak) );
1351  SCIP_CALL( SCIPsetPresolInitpre(scip, presol, presolInitpreSymbreak) );
1352  SCIP_CALL( SCIPsetPresolExitpre(scip, presol, presolExitpreSymbreak) );
1353 
1354  /* add symmetry breaking presolver parameters */
1356  "presolving/" PRESOL_NAME "/conssaddlp",
1357  "Should the symmetry breaking constraints be added to the LP?",
1358  &presoldata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
1359 
1361  "presolving/" PRESOL_NAME "/addsymresacks",
1362  "Add inequalities for symresacks for each generator?",
1363  &presoldata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
1364 
1366  "presolving/" PRESOL_NAME "/computeorbits",
1367  "Should the orbits of the symmetry group be computed?",
1368  &presoldata->computeorbits, TRUE, DEFAULT_COMPUTEORBITS, NULL, NULL) );
1369 
1371  "presolving/" PRESOL_NAME "/detectorbitopes",
1372  "Should we check whether the components of the symmetry group can be handled by orbitopes?",
1373  &presoldata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
1374 
1375  SCIP_CALL( SCIPaddIntParam(scip,
1376  "presolving/" PRESOL_NAME "/addconsstiming",
1377  "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving)",
1378  &presoldata->addconsstiming, TRUE, DEFAULT_ADDCONSSTIMING, 0, 2, NULL, NULL) );
1379 
1380  return SCIP_OKAY;
1381 }
1382 
1383 
1384 /** compute non-trivial orbits of symmetry group
1385  *
1386  * The non-tivial orbits of the group action are stored in the array orbits of length npermvars. This array contains
1387  * the indices of variables from the permvars array such that variables that are contained in the same orbit appear
1388  * consecutively in the orbits array. The variables of the i-th orbit have indices
1389  * orbits[orbitbegins[i]], ... , orbits[orbitbegins[i + 1] - 1].
1390  * Note that the description of the orbits ends at orbitbegins[norbits] - 1.
1391  */
1393  SCIP* scip, /**< SCIP instance */
1394  SCIP_VAR** permvars, /**< variables considered by symbreak presolver */
1395  int npermvars, /**< length of a permutation array */
1396  int** perms, /**< matrix containing in each row a permutation of the symmetry group */
1397  int nperms, /**< number of permutations encoded in perms */
1398  SCIP_Shortbool* activeperms, /**< array for marking active permutations (or NULL) */
1399  int* orbits, /**< array of non-trivial orbits */
1400  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
1401  int* norbits /**< pointer to number of orbits currently stored in orbits */
1402  )
1403 {
1404  SCIP_Shortbool* varadded;
1405  int* curorbit;
1406  int i;
1407  int curorbitsize;
1408  int beginneworbit = 0;
1409 
1410  assert( scip != NULL );
1411  assert( permvars != NULL );
1412  assert( perms != NULL );
1413  assert( nperms > 0 );
1414  assert( npermvars > 0 );
1415  assert( orbits != NULL );
1416  assert( norbits != NULL );
1417 
1418  /* init data structures*/
1419  SCIP_CALL( SCIPallocBufferArray(scip, &curorbit, npermvars) );
1420  SCIP_CALL( SCIPallocBufferArray(scip, &varadded, npermvars) );
1421 
1422  /* initially, every variable is contained in no orbit */
1423  for (i = 0; i < npermvars; ++i)
1424  varadded[i] = FALSE;
1425 
1426  /* find variable orbits */
1427  *norbits = 0;
1428  for (i = 0; i < npermvars; ++i)
1429  {
1430  /* if variable is not contained in an orbit of a previous variable */
1431  if ( ! varadded[i] )
1432  {
1433  int j;
1434  int p;
1435  int curelem;
1436  int image;
1437 
1438  /* compute and store orbit if it is non-trivial */
1439  curorbit[0] = i;
1440  curorbitsize = 1;
1441  varadded[i] = TRUE;
1442 
1443  /* iterate over variables in curorbit and compute their images */
1444  for (j = 0; j < curorbitsize; ++j)
1445  {
1446  curelem = curorbit[j];
1447 
1448  for (p = 0; p < nperms; ++p)
1449  {
1450  if ( activeperms == NULL || activeperms[p] )
1451  {
1452  image = perms[p][curelem];
1453 
1454  /* found new element of the orbit of i */
1455  if ( ! varadded[image] )
1456  {
1457  curorbit[curorbitsize++] = image;
1458  varadded[image] = TRUE;
1459  }
1460  }
1461  }
1462  }
1463 
1464  if ( curorbitsize > 1 )
1465  {
1466  orbitbegins[*norbits] = beginneworbit;
1467  for (j = 0; j < curorbitsize; ++j)
1468  orbits[beginneworbit++] = curorbit[j];
1469 
1470  ++(*norbits);
1471  }
1472  }
1473  }
1474 
1475  /* store end in "last" orbitbegins entry */
1476  assert( *norbits < npermvars );
1477  orbitbegins[*norbits] = beginneworbit;
1478 
1479 #ifdef SCIP_OUTPUT
1480  printf("Orbits (total number: %d):\n", *norbits);
1481  for (i = 0; i < *norbits; ++i)
1482  {
1483  int j;
1484 
1485  printf("%d: ", i);
1486  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
1487  printf("%d ", orbits[j]);
1488  printf("\n");
1489  }
1490 #endif
1491 
1492  /* free memory */
1493  SCIPfreeBufferArray(scip, &varadded);
1494  SCIPfreeBufferArray(scip, &curorbit);
1495 
1496  return SCIP_OKAY;
1497 }
SCIP_RETCODE SCIPcreateConsOrbitope(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR ***vars, SCIP_ORBITOPETYPE orbitopetype, int nspcons, int nblocks, SCIP_Bool resolveprop, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:116
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_presol.c:174
#define PRESOL_MAXROUNDS
struct SCIP_PresolData SCIP_PRESOLDATA
Definition: type_presol.h:37
#define NULL
Definition: def.h:239
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:99
SCIP_RETCODE SCIPsetPresolFree(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLFREE((*presolfree)))
Definition: scip_presol.c:225
#define PRESOL_DESC
public methods for SCIP parameter handling
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:412
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition: misc.c:10437
SCIP_RETCODE SCIPcreateSymbreakCons(SCIP *scip, SCIP_CONS **cons, const char *name, int *perm, SCIP_VAR **vars, int nvars, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
public methods for memory management
SCIP_RETCODE SCIPsetPresolExit(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLEXIT((*presolexit)))
Definition: scip_presol.c:257
static SCIP_DECL_PRESOLEXIT(presolExitSymbreak)
static SCIP_RETCODE tryAddSymmetryHandlingConss(SCIP *scip, SCIP_PRESOL *presol)
#define PRESOL_TIMING
#define SCIP_MAXSTRLEN
Definition: def.h:260
SCIP_Bool SCIPvarIsBinary(SCIP_VAR *var)
Definition: var.c:16909
static SCIP_DECL_PRESOLINIT(presolInitSymbreak)
#define FALSE
Definition: def.h:65
public methods for presolving plugins
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:10017
#define TRUE
Definition: def.h:64
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:53
static SCIP_RETCODE getPermProperties(int *perm, SCIP_VAR **vars, int nvars, SCIP_Bool *iscompoftwocycles, int *ntwocyclesperm, SCIP_Bool *allvarsbinary)
#define DEFAULT_ADDCONSSTIMING
#define SYM_HANDLETYPE_SYMBREAK
Definition: type_symmetry.h:53
SCIP_PRESOLDATA * SCIPpresolGetData(SCIP_PRESOL *presol)
Definition: presol.c:502
SCIP_RETCODE SCIPcomputeGroupOrbitsSymbreak(SCIP *scip, SCIP_VAR **permvars, int npermvars, int **perms, int nperms, SCIP_Shortbool *activeperms, int *orbits, int *orbitbegins, int *norbits)
public methods for problem variables
presolver for adding symmetry breaking constraints
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:142
#define SCIPdebugMsg
Definition: scip_message.h:88
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_param.c:155
static SCIP_RETCODE detectOrbitopes(SCIP *scip, SCIP_PRESOLDATA *presoldata)
constraint handler for (partitioning/packing/full) orbitope constraints w.r.t. the full symmetric gro...
constraint handler for symresack constraints
#define SYM_SPEC_BINARY
Definition: type_symmetry.h:34
public methods for managing constraints
SCIP_Bool SCIPisPresolveFinished(SCIP *scip)
Definition: scip_general.c:648
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:2822
void SCIPdisjointsetFree(SCIP_DISJOINTSET **djset, BMS_BLKMEM *blkmem)
Definition: misc.c:10488
#define PRESOL_NAME
#define SYM_SPEC_INTEGER
Definition: type_symmetry.h:33
SCIP_RETCODE SCIPpresolCons(SCIP *scip, SCIP_CONS *cons, int nrounds, SCIP_PRESOLTIMING presoltiming, int nnewfixedvars, int nnewaggrvars, int nnewchgvartypes, int nnewchgbds, int nnewholes, int nnewdelconss, int nnewaddconss, int nnewupgdconss, int nnewchgcoefs, int nnewchgsides, int *nfixedvars, int *naggrvars, int *nchgvartypes, int *nchgbds, int *naddholes, int *ndelconss, int *naddconss, int *nupgdconss, int *nchgcoefs, int *nchgsides, SCIP_RESULT *result)
Definition: scip_cons.c:2420
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip_mem.c:128
static SCIP_DECL_PRESOLEXEC(presolExecSymbreak)
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition: cons.c:8076
SCIP_RETCODE SCIPsetPresolInit(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLINIT((*presolinit)))
Definition: scip_presol.c:241
SCIP_RETCODE SCIPgetGeneratorsSymmetry(SCIP *scip, SYM_SPEC symspecrequire, SYM_SPEC symspecrequirefixed, SCIP_Bool recompute, int *npermvars, SCIP_VAR ***permvars, int *nperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Bool *binvaraffected)
#define PRESOL_PRIORITY
internal miscellaneous methods
#define SCIP_Shortbool
Definition: def.h:70
static SCIP_RETCODE generateOrbitopeVarsMatrix(SCIP_VAR ****vars, int nrows, int ncols, SCIP_VAR **permvars, int npermvars, int **orbitopevaridx, int *columnorder, int *nusedelems, SCIP_Bool *infeasible)
SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
Definition: scip_param.c:341
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition: misc.c:10410
#define SCIP_CALL(x)
Definition: def.h:351
static SCIP_DECL_PRESOLFREE(presolFreeSymbreak)
SCIP_RETCODE SCIPsetPresolInitpre(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLINITPRE((*presolinitpre)))
Definition: scip_presol.c:273
#define DEFAULT_CONSSADDLP
public methods for constraint handler plugins and constraints
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:130
static SCIP_DECL_PRESOLINITPRE(presolInitpreSymbreak)
public data structures and miscellaneous methods
static SCIP_RETCODE extendSubOrbitope(int **suborbitope, int nrows, int nfilledcols, int coltoextend, int *perm, SCIP_Bool leftextension, int **nusedelems, SCIP_Bool *success, SCIP_Bool *infeasible)
#define SCIP_Bool
Definition: def.h:62
#define DEFAULT_ADDSYMRESACKS
SCIP_RETCODE SCIPsetIntParam(SCIP *scip, const char *name, int value)
Definition: scip_param.c:578
static SCIP_RETCODE addSymmetryBreakingConstraints(SCIP *scip, SCIP_PRESOL *presol)
const char * SCIPpresolGetName(SCIP_PRESOL *presol)
Definition: presol.c:589
static SCIP_RETCODE computeComponents(SCIP *scip, SCIP_PRESOLDATA *presoldata)
static SCIP_RETCODE addSymresackConss(SCIP *scip, SCIP_PRESOL *presol)
#define SCIP_PRESOLTIMING_ALWAYS
Definition: type_timing.h:49
static SCIP_DECL_PRESOLEXITPRE(presolExitpreSymbreak)
#define SCIPfreeMemory(scip, ptr)
Definition: scip_mem.h:86
public methods for presolvers
general public methods
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip_cons.c:1187
presolver for storing symmetry information about current problem
public methods for message output
#define SYM_SPEC_REAL
Definition: type_symmetry.h:35
#define SCIP_Real
Definition: def.h:150
SCIP_Bool SCIPisStopped(SCIP *scip)
Definition: scip_general.c:739
#define DEFAULT_DETECTORBITOPES
public methods for message handling
SCIP_RETCODE SCIPdisjointsetCreate(SCIP_DISJOINTSET **djset, BMS_BLKMEM *blkmem, int ncomponents)
Definition: misc.c:10370
#define SCIPallocMemory(scip, ptr)
Definition: scip_mem.h:70
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip_mem.h:117
#define DEFAULT_COMPUTEORBITS
public methods for global and local (sub)problems
SCIP_RETCODE SCIPincludePresolSymbreak(SCIP *scip)
SCIP_RETCODE SCIPsetPresolExitpre(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLEXITPRE((*presolexitpre)))
Definition: scip_presol.c:289
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_param.c:129
memory allocation routines