Scippy

SCIP

Solving Constraint Integer Programs

prop_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-2024 Zuse Institute Berlin (ZIB) */
7 /* */
8 /* Licensed under the Apache License, Version 2.0 (the "License"); */
9 /* you may not use this file except in compliance with the License. */
10 /* You may obtain a copy of the License at */
11 /* */
12 /* http://www.apache.org/licenses/LICENSE-2.0 */
13 /* */
14 /* Unless required by applicable law or agreed to in writing, software */
15 /* distributed under the License is distributed on an "AS IS" BASIS, */
16 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17 /* See the License for the specific language governing permissions and */
18 /* limitations under the License. */
19 /* */
20 /* You should have received a copy of the Apache-2.0 license */
21 /* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22 /* */
23 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24 
25 /**@file prop_symmetry.c
26  * @ingroup DEFPLUGINS_PROP
27  * @brief propagator for handling symmetries
28  * @author Marc Pfetsch
29  * @author Thomas Rehn
30  * @author Christopher Hojny
31  * @author Fabian Wegscheider
32  * @author Jasper van Doornmalen
33  *
34  * This propagator combines the following symmetry handling functionalities:
35  * - It allows to compute symmetries of the problem and to store this information in adequate form. The symmetry
36  * information can be accessed through external functions.
37  * - It implements various methods to handle the symmetries:
38  * - orbital reduction, which generalizes orbital fixing. See symmetry_orbital.c
39  * - (dynamic) orbitopal reduction, which generalizes (dynamic) orbital fixing. See symmetry_orbitopal.c
40  * - static orbitopal fixing (for binary variable domains) for full orbitopes. See cons_orbitope.c
41  * - static orbitopal fixing (for binary variable domains) for packing-partitioning orbitopes. See cons_orbitope.c
42  * - (dynamic) lexicographic reduction. See symmetry_lexred.c
43  * - static lexicographic fixing for binary variable domains (i.e., symresack propagation). See cons_symresack.c
44  * - static lexicographic fixing for binary variable domains on involutions (i.e., orbisacks). See cons_orbisack.c
45  * - Symmetry breaking inequalities based on the Schreier-Sims Table (i.e., SST cuts).
46  * - Strong and weak symmetry breaking inequalities.
47  *
48  *
49  * @section SYMCOMP Symmetry Computation
50  *
51  * The generic functionality of the compute_symmetry.h interface is used.
52  * We do not copy symmetry information, since it is not clear how this information transfers. Moreover, copying
53  * symmetry might inhibit heuristics. But note that solving a sub-SCIP might then happen without symmetry information!
54  *
55  *
56  * @section SYMBREAK Symmetry handling by the (unified) symmetry handling constraints
57  *
58  * Many common methods are captured by a framework that dynamifies symmetry handling constraints. The ideas are
59  * described in@n
60  * J. van Doornmalen, C. Hojny, "A Unified Framework for Symmetry Handling", preprint, 2023,
61  * https://doi.org/10.48550/arXiv.2211.01295.
62  *
63  * This paper shows that various symmetry handling methods are compatible under certain conditions, and provides
64  * generalizations to common symmetry handling constraints from binary variable domains to arbitrary variable domains.
65  * This includes symresack propagation, orbitopal fixing, and orbital fixing, that are generalized to
66  * lexicographic reduction, orbitopal reduction and orbital reduction, respectively. For a description and
67  * implementation, see symmetry_lexred.c, symmetry_orbitopal.c and symmetry_orbital.c, respectively.
68  * The static counterparts on binary variable domains are cons_symresack.c and cons_orbisack.c for lexicographic
69  * reduction (cf. symresack propagation), and cons_orbitope.c and cons_orbisack.c for orbitopal reduction
70  * (cf. orbitopal fixing). We refer to the description of tryAddSymmetryHandlingMethods for the order in which these
71  * methods are applied.
72  *
73  * @section SST Cuts derived from the Schreier Sims table
74  *
75  * SST cuts have been introduced by@n
76  * D. Salvagnin: Symmetry Breaking Inequalities from the Schreier-Sims table. CPAIOR 2018 Proceedings, 521-529, 2018.
77  *
78  * These inequalities are computed as follows. Throughout these procedure a set of so-called leaders is maintained.
79  * Initially the set of leaders is empty. In a first step, select a variable \f$x_i\f$ and compute its orbit w.r.t.
80  * the symmetry group of the mixed-integer program. For each variable \f$x_j\f$ in the orbit of \f$x_i\f$, the
81  * inequality \f$x_i \geq x_j\f$ is a valid symmetry handling inequality, which can be added to the mixed-integer
82  * program. We call \f$x_i\f$ the leader of this inequality. Add the leader \f$x_i\f$ to the set of leaders and
83  * compute the pointwise stabilizer of the leader set. In the next step, select a new variable, compute its orbit
84  * w.r.t. the stabilizer group of the leaders, add the inequalities based on this orbit, and add the new leader
85  * to the set of leaders. This procedure is iterated until the pointwise stabilizer group of the leaders has become
86  * trivial.
87  *
88  * @todo Possibly turn off propagator in subtrees.
89  * @todo Check application of conflict resolution.
90  * @todo Check whether one should switch the role of 0 and 1
91  * @todo Implement stabilizer computation?
92  * @todo Implement isomorphism pruning?
93  * @todo Implement particular preprocessing rules
94  * @todo Separate permuted cuts (first experiments not successful)
95  * @todo Allow the computation of local symmetries
96  * @todo Order rows of orbitopes (in particular packing/partitioning) w.r.t. cliques in conflict graph.
97  * @todo A dynamic variant for packing-partitioning orbitopal structures
98  * @todo A dynamic variant for suborbitopes
99  */
100 /* #define SCIP_OUTPUT */
101 /* #define SCIP_OUTPUT_COMPONENT */
102 /* #define SCIP_DISPLAY_SYM_CHECK */
103 
104 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
105 
106 #include <scip/cons_linear.h>
107 #include <scip/cons_knapsack.h>
108 #include <scip/cons_varbound.h>
109 #include <scip/cons_setppc.h>
110 #include <scip/cons_and.h>
111 #include <scip/cons_logicor.h>
112 #include <scip/cons_or.h>
113 #include <scip/cons_orbitope.h>
114 #include <scip/cons_symresack.h>
115 #include <scip/cons_xor.h>
116 #include <scip/cons_linking.h>
118 #include <scip/cons_indicator.h>
119 #include <scip/cons_nonlinear.h>
120 #include <scip/cons_sos1.h>
121 #include <scip/cons_sos2.h>
122 #include <scip/expr_pow.h>
123 #include <scip/expr_product.h>
124 #include <scip/pub_expr.h>
125 #include <scip/misc.h>
127 
128 #include <scip/prop_symmetry.h>
130 #include <scip/event_shadowtree.h>
131 #include <scip/symmetry.h>
132 #include <scip/symmetry_graph.h>
133 #include <scip/symmetry_orbitopal.h>
134 #include <scip/symmetry_orbital.h>
135 #include <scip/symmetry_lexred.h>
136 
137 #include <math.h>
138 #include <string.h>
139 
140 /* propagator properties */
141 #define PROP_NAME "symmetry"
142 #define PROP_DESC "propagator for handling symmetry"
143 #define PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask */
144 #define PROP_PRIORITY -1000000 /**< propagator priority */
145 #define PROP_FREQ 1 /**< propagator frequency */
146 #define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */
147 
148 #define PROP_PRESOL_PRIORITY -10000000 /**< priority of the presolving method (>= 0: before, < 0: after constraint handlers) */
149 #define PROP_PRESOLTIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolving method (fast, medium, or exhaustive) */
150 #define PROP_PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
151 
152 
153 /* default parameter values for symmetry computation */
154 #define DEFAULT_MAXGENERATORS 1500 /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
155 #define DEFAULT_CHECKSYMMETRIES FALSE /**< Should all symmetries be checked after computation? */
156 #define DEFAULT_DISPLAYNORBITVARS FALSE /**< Should the number of variables affected by some symmetry be displayed? */
157 #define DEFAULT_USECOLUMNSPARSITY FALSE /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
158 #define DEFAULT_DOUBLEEQUATIONS FALSE /**< Double equations to positive/negative version? */
159 #define DEFAULT_COMPRESSSYMMETRIES TRUE /**< Should non-affected variables be removed from permutation to save memory? */
160 #define DEFAULT_COMPRESSTHRESHOLD 0.5 /**< Compression is used if percentage of moved vars is at most the threshold. */
161 #define DEFAULT_SYMFIXNONBINARYVARS FALSE /**< Disabled parameter */
162 #define DEFAULT_ENFORCECOMPUTESYMMETRY FALSE /**< always compute symmetries, even if they cannot be handled */
163 #define DEFAULT_SYMTYPE (int) SYM_SYMTYPE_PERM /**< type of symmetries to be computed */
164 
165 /* default parameters for linear symmetry constraints */
166 #define DEFAULT_CONSSADDLP TRUE /**< Should the symmetry breaking constraints be added to the LP? */
167 #define DEFAULT_ADDSYMRESACKS TRUE /**< Add inequalities for symresacks for each generator? */
168 #define DEFAULT_DETECTDOUBLELEX TRUE /**< Should we check whether the components of the symmetry group can be handled by double lex matrices? */
169 #define DEFAULT_DETECTORBITOPES TRUE /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
170 #define DEFAULT_DETECTSUBGROUPS TRUE /**< Should we try to detect orbitopes in subgroups of the symmetry group? */
171 #define DEFAULT_ADDWEAKSBCS TRUE /**< Should we add weak SBCs for enclosing orbit of symmetric subgroups? */
172 #define DEFAULT_ADDSTRONGSBCS FALSE /**< Should we add strong SBCs for enclosing orbit of symmetric subgroups if orbitopes are not used? */
173 #define DEFAULT_ADDCONSSTIMING 2 /**< timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) */
174 #define DEFAULT_MAXNCONSSSUBGROUP 500000 /**< Maximum number of constraints up to which subgroup structures are detected */
175 #define DEFAULT_USEDYNAMICPROP TRUE /**< whether dynamic propagation should be used for full orbitopes */
176 #define DEFAULT_PREFERLESSROWS TRUE /**< Shall orbitopes with less rows be preferred in detection? */
177 
178 /* default parameters for symmetry computation */
179 #define DEFAULT_SYMCOMPTIMING 2 /**< timing of symmetry computation (0 = before presolving, 1 = during presolving, 2 = at first call) */
180 #define DEFAULT_PERFORMPRESOLVING 0 /**< Run orbital fixing during presolving? (disabled parameter) */
181 #define DEFAULT_RECOMPUTERESTART 0 /**< Recompute symmetries after a restart has occurred? (0 = never) */
182 
183 /* default parameters for Schreier Sims constraints */
184 #define DEFAULT_SSTTIEBREAKRULE 1 /**< index of tie break rule for selecting orbit for Schreier Sims constraints? */
185 #define DEFAULT_SSTLEADERRULE 0 /**< index of rule for selecting leader variables for Schreier Sims constraints? */
186 #define DEFAULT_SSTLEADERVARTYPE 14 /**< bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);
187  * if multiple types are allowed, take the one with most affected vars */
188 #define DEFAULT_ADDCONFLICTCUTS TRUE /**< Should Schreier Sims constraints be added if we use a conflict based rule? */
189 #define DEFAULT_SSTADDCUTS TRUE /**< Should Schreier Sims constraints be added? */
190 #define DEFAULT_SSTMIXEDCOMPONENTS TRUE /**< Should Schreier Sims constraints be added if a symmetry component contains variables of different types? */
192 /* output table properties */
193 #define TABLE_NAME_SYMMETRY "symmetry"
194 #define TABLE_DESC_SYMMETRY "symmetry handling statistics"
195 #define TABLE_POSITION_SYMMETRY 7001 /**< the position of the statistics table */
196 #define TABLE_EARLIEST_SYMMETRY SCIP_STAGE_SOLVING /**< output of the statistics table is only printed from this stage onwards */
198 
199 /* other defines */
200 #define MAXGENNUMERATOR 64000000 /**< determine maximal number of generators by dividing this number by the number of variables */
201 #define COMPRESSNVARSLB 25000 /**< lower bound on the number of variables above which compression could be performed */
203 /* macros for getting activeness of symmetry handling methods */
204 #define ISSYMRETOPESACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_SYMBREAK) != 0)
205 #define ISORBITALREDUCTIONACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_ORBITALREDUCTION) != 0)
206 #define ISSSTACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_SST) != 0)
208 #define ISSSTBINACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_BINARY) != 0)
209 #define ISSSTINTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_INTEGER) != 0)
210 #define ISSSTIMPLINTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_IMPLINT) != 0)
211 #define ISSSTCONTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_CONTINUOUS) != 0)
213 /* enable symmetry statistics */
214 #define SYMMETRY_STATISTICS 1
216 /** propagator data */
217 struct SCIP_PropData
218 {
219  /* symmetry group information */
220  int npermvars; /**< number of variables for permutations */
221  int nbinpermvars; /**< number of binary variables for permutations */
222  SCIP_VAR** permvars; /**< variables on which permutations act */
223  int nperms; /**< number of permutations */
224  int nmaxperms; /**< maximal number of permutations (needed for freeing storage) */
225  int** perms; /**< pointer to store permutation generators as (nperms x npermvars) matrix */
226  int** permstrans; /**< pointer to store transposed permutation generators as (npermvars x nperms) matrix */
227  SCIP_HASHMAP* permvarmap; /**< map of variables to indices in permvars array */
228  int nmovedpermvars; /**< number of variables moved by any permutation */
229  int nmovedbinpermvars; /**< number of binary variables moved by any permutation */
230  int nmovedintpermvars; /**< number of integer variables moved by any permutation */
231  int nmovedimplintpermvars; /**< number of implicitly integer variables moved by any permutation */
232  int nmovedcontpermvars; /**< number of continuous variables moved by any permutation */
233  SCIP_HASHMAP* customsymopnodetypes; /**< types of operator nodes introduced
234  * by a user for symmetry detection */
235  int nopnodetypes; /**< current number of operator node types used for symmetry detection */
236  SCIP_Real* permvardomaincenter; /**< center of variable domains (needed for signed permutations) */
237  int symtype; /**< type of symmetries to be computed */
238 
239  /* components of symmetry group */
240  int ncomponents; /**< number of components of symmetry group */
241  int ncompblocked; /**< number of components that have been blocked */
242  int* components; /**< array containing the indices of permutations sorted by components */
243  int* componentbegins; /**< array containing in i-th position the first position of
244  * component i in components array */
245  int* vartocomponent; /**< array containing for each permvar the index of the component it is
246  * contained in (-1 if not affected) */
247  unsigned* componentblocked; /**< array to store which symmetry methods have been applied to a component using
248  * the same bitset as for misc/usesymmetry */
249  SCIP_Bool* componenthassignedperm; /**< array to indicate whether a component has a signed permutation */
250 
251  /* further symmetry information */
252  int nmovedvars; /**< number of variables moved by some permutation */
253  SCIP_Real log10groupsize; /**< log10 of size of symmetry group */
254  SCIP_Bool binvaraffected; /**< whether binary variables are affected by some symmetry */
255 
256  /* for symmetry computation */
257  int maxgenerators; /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
258  SCIP_Bool checksymmetries; /**< Should all symmetries be checked after computation? */
259  SCIP_Bool displaynorbitvars; /**< Whether the number of variables in non-trivial orbits shall be computed */
260  SCIP_Bool compresssymmetries; /**< Should non-affected variables be removed from permutation to save memory? */
261  SCIP_Real compressthreshold; /**< Compression is used if percentage of moved vars is at most the threshold. */
262  SCIP_Bool compressed; /**< Whether symmetry data has been compressed */
263  SCIP_Bool computedsymmetry; /**< Have we already tried to compute symmetries? */
264  int usesymmetry; /**< encoding of active symmetry handling methods (for debugging) */
265  SCIP_Bool usecolumnsparsity; /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
266  SCIP_Bool doubleequations; /**< Double equations to positive/negative version? */
267  SCIP_Bool enforcecomputesymmetry; /**< always compute symmetries, even if they cannot be handled */
268  int symtiming; /**< timing of computing and handling symmetries (0 = before presolving, 1 = during presolving, 2 = after presolving) */
269 
270  /* for symmetry constraints */
271  SCIP_Bool triedaddsymmethods; /**< whether we already tried to add symmetry handling methods */
272  SCIP_Bool conssaddlp; /**< Should the symmetry breaking constraints be added to the LP? */
273  SCIP_Bool addsymresacks; /**< Add symresack constraints for each generator? */
274  SCIP_CONS** genorbconss; /**< list of generated orbitope/orbisack/symresack constraints */
275  SCIP_CONS** genlinconss; /**< list of generated linear constraints */
276  int ngenorbconss; /**< number of generated orbitope/orbisack/symresack constraints */
277  int genorbconsssize; /**< size of generated orbitope/orbisack/symresack constraints array */
278  int ngenlinconss; /**< number of generated linear constraints */
279  int genlinconsssize; /**< size of linear constraints array */
280  int nsymresacks; /**< number of symresack constraints */
281  SCIP_Bool detectdoublelex; /**< Should we check whether the components of the symmetry group can be handled by double lex matrices? */
282  SCIP_Bool detectorbitopes; /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
283  SCIP_Bool detectsubgroups; /**< Should we try to detect orbitopes in subgroups of the symmetry group? */
284  SCIP_Bool addweaksbcs; /**< Should we add weak SBCs for enclosing orbit of symmetric subgroups? */
285  SCIP_Bool addstrongsbcs; /**< Should we add strong SBCs for enclosing orbit of symmetric subgroups if orbitopes are not used? */
286  int norbitopes; /**< number of orbitope constraints */
287  SCIP_Bool* isnonlinvar; /**< array indicating whether variables appear non-linearly */
288  SCIP_CONSHDLR* conshdlr_nonlinear; /**< nonlinear constraint handler */
289  int maxnconsssubgroup; /**< maximum number of constraints up to which subgroup structures are detected */
290  SCIP_Bool usedynamicprop; /**< whether dynamic propagation should be used for full orbitopes */
291  SCIP_Bool preferlessrows; /**< Shall orbitopes with less rows be preferred in detection? */
292 
293  /* data necessary for symmetry computation order */
294  int recomputerestart; /**< Recompute symmetries after a restart has occurred? (0 = never, 1 = always, 2 = if symmetry reduction found) */
295  int lastrestart; /**< last restart for which symmetries have been computed */
296  SCIP_Bool symfoundreduction; /**< whether symmetry handling propagation has found a reduction since the last time computing symmetries */
297 
298  /* data necessary for Schreier Sims constraints */
299  SCIP_CONS** sstconss; /**< list of generated schreier sims conss */
300  int nsstconss; /**< number of generated schreier sims conss */
301  int maxnsstconss; /**< maximum number of conss in sstconss */
302  int sstleaderrule; /**< rule to select leader */
303  int ssttiebreakrule; /**< tie break rule for leader selection */
304  int sstleadervartype; /**< bitset encoding which variable types can be leaders;
305  * if multiple types are allowed, take the one with most affected vars */
306  int* leaders; /**< index of orbit leaders in permvars */
307  int nleaders; /**< number of orbit leaders in leaders array */
308  int maxnleaders; /**< maximum number of leaders in leaders array */
309  SCIP_Bool addconflictcuts; /**< Should Schreier Sims constraints be added if we use a conflict based rule? */
310  SCIP_Bool sstaddcuts; /**< Should Schreier Sims constraints be added? */
311  SCIP_Bool sstmixedcomponents; /**< Should Schreier Sims constraints be added if a symmetry component contains variables of different types? */
312 
313  SCIP_EVENTHDLR* shadowtreeeventhdlr;/**< pointer to event handler for shadow tree */
314  SCIP_ORBITOPALREDDATA* orbitopalreddata; /**< container for the orbitopal reduction data */
315  SCIP_ORBITALREDDATA* orbitalreddata; /**< container for orbital reduction data */
316  SCIP_LEXREDDATA* lexreddata; /**< container for lexicographic reduction propagation */
317 };
318 
319 /** conflict data structure for SST cuts */
320 struct SCIP_ConflictData
321 {
322  SCIP_VAR* var; /**< variable belonging to node */
323  int orbitidx; /**< orbit of variable w.r.t. current stabilizer subgroup
324  * or -1 if not affected by symmetry */
325  int nconflictinorbit; /**< number of variables the node's var is in conflict with */
326  int orbitsize; /**< size of the variable's orbit */
327  int posinorbit; /**< position of variable in its orbit */
328  SCIP_Bool active; /**< whether variable has not been fixed by Schreier Sims code */
329  SCIP_CLIQUE** cliques; /**< List of setppc constraints. */
330  int ncliques; /**< Number of setppc constraints. */
331 };
332 typedef struct SCIP_ConflictData SCIP_CONFLICTDATA;
334 
335 /** compare function for sorting an array by the addresses of its members */
336 static
337 SCIP_DECL_SORTPTRCOMP(sortByPointerValue)
338 {
339  /* @todo move to misc.c? */
340  if ( elem1 < elem2 )
341  return -1;
342  else if ( elem1 > elem2 )
343  return +1;
344  return 0;
345 }
346 
347 
348 /** checks whether two arrays that are sorted with the same comparator have a common element */
349 static
351  void** arr1, /**< first array */
352  int narr1, /**< number of elements in first array */
353  void** arr2, /**< second array */
354  int narr2, /**< number of elements in second array */
355  SCIP_DECL_SORTPTRCOMP((*compfunc)) /**< comparator function that was used to sort arr1 and arr2; must define a total ordering */
356  )
357 {
358  /* @todo move to misc.c? */
359  int it1;
360  int it2;
361  int cmp;
362 
363  assert( arr1 != NULL || narr1 == 0 );
364  assert( narr1 >= 0 );
365  assert( arr2 != NULL || narr2 == 0 );
366  assert( narr2 >= 0 );
367  assert( compfunc != NULL );
368 
369  /* there is no overlap if one of the two arrays is empty */
370  if ( narr1 <= 0 )
371  return FALSE;
372  if ( narr2 <= 0 )
373  return FALSE;
374 
375  it1 = 0;
376  it2 = 0;
377 
378  while ( TRUE ) /*lint !e716*/
379  {
380  cmp = compfunc(arr1[it1], arr2[it2]);
381  if ( cmp < 0 )
382  {
383  /* comparison function determines arr1[it1] < arr2[it2]
384  * increase iterator for arr1
385  */
386  if ( ++it1 >= narr1 )
387  break;
388  continue;
389  }
390  else if ( cmp > 0 )
391  {
392  /* comparison function determines arr1[it1] > arr2[it2]
393  * increase iterator for arr2
394  */
395  if ( ++it2 >= narr2 )
396  break;
397  continue;
398  }
399  else
400  {
401  /* the entries arr1[it1] and arr2[it2] are the same with respect to the comparison function */
402  assert( cmp == 0 );
403  return TRUE;
404  }
405  }
406 
407  /* no overlap detected */
408  assert( it1 >= narr1 || it2 >= narr2 );
409  return FALSE;
410 }
411 
412 
413 /*
414  * Display dialog callback methods
415  */
416 
417 /** displays the cycle of a symmetry */
418 static
420  SCIP* scip, /**< SCIP pointer */
421  int* perm, /**< symmetry */
422  SYM_SYMTYPE symtype, /**< type of symmetry */
423  int baseidx, /**< variable index for which cycle is computed */
424  SCIP_Bool* covered, /**< allocated array to store covered variables */
425  int nvars, /**< number of (non-negated) variables in symmetry */
426  SCIP_VAR** vars /**< variables on which symmetry acts */
427  )
428 {
429  char* string;
430  int varidx;
431  int j;
432 
433  assert( scip != NULL );
434  assert( perm != NULL );
435  assert( 0 <= baseidx );
436  assert( (symtype == SYM_SYMTYPE_PERM && baseidx < nvars) ||
437  (symtype == SYM_SYMTYPE_SIGNPERM && baseidx < 2 * nvars) );
438  assert( covered != NULL );
439 
440  /* skip fixed points or elements already covered in previous cycle */
441  if ( perm[baseidx] == baseidx || covered[baseidx] )
442  return SCIP_OKAY;
443 
444  varidx = baseidx >= nvars ? baseidx - nvars : baseidx;
445  string = (char*) SCIPvarGetName(vars[varidx]);
446  SCIPinfoMessage(scip, NULL, " (%s<%s>", baseidx >= nvars ? "negated " : "", string);
447  j = perm[baseidx];
448  covered[baseidx] = TRUE;
449  while ( j != baseidx )
450  {
451  covered[j] = TRUE;
452  varidx = j >= nvars ? j - nvars : j;
453  string = (char*) SCIPvarGetName(vars[varidx]);
454  SCIPinfoMessage(scip, NULL, ",%s<%s>", j >= nvars ? "negated " : "", string);
455  j = perm[j];
456  }
457  SCIPinfoMessage(scip, NULL, ")\n");
458 
459  return SCIP_OKAY;
460 }
461 
462 /** displays symmetry information without taking components into account */
463 static
465  SCIP* scip, /**< SCIP pointer */
466  SCIP_PROPDATA* propdata /**< propagator data */
467  )
468 {
469  SCIP_Bool* covered;
470  SYM_SYMTYPE symtype;
471  int* perm;
472  int permlen;
473  int npermvars;
474  int i;
475  int p;
476 
477  assert( scip != NULL );
478  assert( propdata != NULL );
479  assert( propdata->nperms > 0 );
480  assert( propdata->permvars != NULL );
481  assert( propdata->npermvars > 0 );
482 
483  symtype = (SYM_SYMTYPE) propdata->symtype;
484  npermvars = propdata->npermvars;
485  permlen = symtype == SYM_SYMTYPE_PERM ? npermvars : 2 * npermvars;
486 
487  if ( symtype == SYM_SYMTYPE_SIGNPERM )
488  SCIPinfoMessage(scip, NULL, "Display permutations as signed permutations (allowing translations)\n");
489 
490  SCIP_CALL( SCIPallocClearBufferArray(scip, &covered, permlen) );
491 
492  for (p = 0; p < propdata->nperms; ++p)
493  {
494  SCIPinfoMessage(scip, NULL, "Permutation %d:\n", p);
495  perm = propdata->perms[p];
496 
497  for (i = 0; i < permlen; ++i)
498  {
499  SCIP_CALL( displayCycleOfSymmetry(scip, perm, symtype, i, covered, npermvars, propdata->permvars) );
500  }
501 
502  for (i = 0; i < permlen; ++i)
503  covered[i] = FALSE;
504  }
505 
506  SCIPfreeBufferArray(scip, &covered);
507 
508  return SCIP_OKAY;
509 }
510 
511 /** displays symmetry information taking components into account */
512 static
514  SCIP* scip, /**< SCIP pointer */
515  SCIP_PROPDATA* propdata /**< propagator data */
516  )
517 {
518  SCIP_Bool* covered;
519  SYM_SYMTYPE symtype;
520  int* perm;
521  int comppermlen;
522  int permlen;
523  int npermvars;
524  int i;
525  int p;
526  int c;
527 
528  assert( scip != NULL );
529  assert( propdata != NULL );
530  assert( propdata->nperms > 0 );
531  assert( propdata->permvars != NULL );
532  assert( propdata->npermvars > 0 );
533  assert( propdata->ncomponents > 0 );
534 
535  symtype = (SYM_SYMTYPE) propdata->symtype;
536  npermvars = propdata->npermvars;
537  permlen = symtype == SYM_SYMTYPE_PERM ? npermvars : 2 * npermvars;
538 
539  SCIP_CALL( SCIPallocClearBufferArray(scip, &covered, permlen) );
540 
541  for (c = 0; c < propdata->ncomponents; ++c)
542  {
543  int cnt;
544 
545  SCIPinfoMessage(scip, NULL, "Display symmetries of component %d.\n", c);
546  if ( propdata->componenthassignedperm[c] )
547  SCIPinfoMessage(scip, NULL, " Symmetries are displayed as signed permutations (allowing translations).\n");
548  else
549  SCIPinfoMessage(scip, NULL, " Symmetries are displayed as permutations.\n");
550 
551  comppermlen = propdata->componenthassignedperm[c] ? 2 * npermvars : npermvars;
552 
553  for (p = propdata->componentbegins[c], cnt = 0; p < propdata->componentbegins[c + 1]; ++p, ++cnt)
554  {
555  SCIPinfoMessage(scip, NULL, "Permutation %d:\n", cnt);
556  perm = propdata->perms[propdata->components[p]];
557 
558  for (i = 0; i < comppermlen; ++i)
559  {
560  SCIP_CALL( displayCycleOfSymmetry(scip, perm, symtype, i, covered, npermvars, propdata->permvars) );
561  }
562 
563  for (i = 0; i < comppermlen; ++i)
564  covered[i] = FALSE;
565  }
566  }
567 
568  SCIPfreeBufferArray(scip, &covered);
569 
570  return SCIP_OKAY;
571 }
572 
573 /** dialog execution method for the display symmetry information command */
574 static
575 SCIP_DECL_DIALOGEXEC(dialogExecDisplaySymmetry)
576 { /*lint --e{715}*/
577  SCIP_PROPDATA* propdata;
578 
579  /* add your dialog to history of dialogs that have been executed */
580  SCIP_CALL( SCIPdialoghdlrAddHistory(dialoghdlr, dialog, NULL, FALSE) );
581 
582  propdata = (SCIP_PROPDATA*)SCIPdialogGetData(dialog);
583  assert( propdata != NULL );
584 
585  if ( propdata->nperms == -1 )
586  {
587  SCIPinfoMessage(scip, NULL, "Cannot display symmetries. Symmetries have not been computed yet.\n");
588  }
589  else if ( propdata->nperms == 0 )
590  {
591  SCIPinfoMessage(scip, NULL, "Cannot display symmetries. No symmetries detected.\n");
592  }
593  else if ( propdata->ncomponents < 0 )
594  {
596  }
597  else
598  {
599  SCIP_CALL( displaySymmetriesWithComponents(scip, propdata) );
600  }
601 
602  /* next dialog will be root dialog again */
603  *nextdialog = SCIPdialoghdlrGetRoot(dialoghdlr);
604 
605  return SCIP_OKAY;
606 }
607 
608 
609 /*
610  * Table callback methods
611  */
612 
613 /** table data */
614 struct SCIP_TableData
615 {
616  SCIP_PROPDATA* propdata; /** pass data of propagator for table output function */
617 };
618 
619 
620 /** output method of symmetry propagator statistics table to output file stream 'file' */
621 static
622 SCIP_DECL_TABLEOUTPUT(tableOutputSymmetry)
623 {
624  SCIP_TABLEDATA* tabledata;
625  int nred;
626  int ncutoff;
627  SCIP_Real time;
628 
629  assert( scip != NULL );
630  assert( table != NULL );
631 
632  tabledata = SCIPtableGetData(table);
633  assert( tabledata != NULL );
634  assert( tabledata->propdata != NULL );
635 
636  if ( tabledata->propdata->orbitopalreddata || tabledata->propdata->orbitalreddata
637  || tabledata->propdata->lexreddata )
638  {
639  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, "Symmetry :\n");
640  if ( tabledata->propdata->orbitopalreddata )
641  {
642  SCIP_CALL( SCIPorbitopalReductionGetStatistics(scip, tabledata->propdata->orbitopalreddata, &nred, &ncutoff) );
643  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " orbitopal red. : %10d reductions applied,"
644  " %10d cutoffs\n", nred, ncutoff);
645  }
646  if ( tabledata->propdata->orbitalreddata )
647  {
648  SCIP_CALL( SCIPorbitalReductionGetStatistics(scip, tabledata->propdata->orbitalreddata, &nred, &ncutoff) );
649  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " orbital reduction: %10d reductions applied,"
650  " %10d cutoffs\n", nred, ncutoff);
651  }
652  if ( tabledata->propdata->lexreddata )
653  {
654  SCIP_CALL( SCIPlexicographicReductionGetStatistics(scip, tabledata->propdata->lexreddata, &nred, &ncutoff) );
655  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " lexicographic red: %10d reductions applied,"
656  " %10d cutoffs\n", nred, ncutoff);
657  }
658  if ( tabledata->propdata->shadowtreeeventhdlr )
659  {
660  time = SCIPgetShadowTreeEventHandlerExecutionTime(scip, tabledata->propdata->shadowtreeeventhdlr);
661  SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " shadow tree time : %10.2f s\n", time);
662  }
663  }
664 
665  return SCIP_OKAY;
666 }
667 
668 
669 /** destructor of statistics table to free user data (called when SCIP is exiting) */
670 static
671 SCIP_DECL_TABLEFREE(tableFreeSymmetry)
672 {
673  SCIP_TABLEDATA* tabledata;
674  tabledata = SCIPtableGetData(table);
675  assert( tabledata != NULL );
676 
677  SCIPfreeBlockMemory(scip, &tabledata);
678 
679  return SCIP_OKAY;
680 }
681 
682 
683 
684 /*
685  * local data structures
686  */
687 
688 /** data structure to store arrays used for sorting colored component types */
690 {
691  int* components; /**< array of components */
692  int* colors; /**< array of colors */
693 };
696 /** sorts variable indices according to their corresponding component in the graph
697  *
698  * Variables are sorted first by the color of their component and then by the component index.
699  *
700  * result:
701  * < 0: ind1 comes before (is better than) ind2
702  * = 0: both indices have the same value
703  * > 0: ind2 comes after (is worse than) ind2
704  */
705 static
706 SCIP_DECL_SORTINDCOMP(SYMsortGraphCompVars)
707 {
708  SYM_SORTGRAPHCOMPVARS* data;
709 
710  data = (SYM_SORTGRAPHCOMPVARS*) dataptr;
711 
712  if ( data->colors[ind1] < data->colors[ind2] )
713  return -1;
714  else if ( data->colors[ind1] > data->colors[ind2] )
715  return 1;
716 
717  if ( data->components[ind1] < data->components[ind2] )
718  return -1;
719  if ( data->components[ind1] > data->components[ind2] )
720  return 1;
721 
722  return 0;
723 }
724 
725 /** compares two symmetry detection graphs
726  *
727  * Graphs are compared by their number of consnodes, then their constypes, then by their lhs,
728  * then by their rhs, then by their total number of nodes, then by the number of operator nodes,
729  * then by their number of value nodes, and then by their number of edges.
730  *
731  * result:
732  * < 0: ind1 comes before (is better than) ind2
733  * = 0: both indices have the same value
734  * > 0: ind2 comes after (is worse than) ind2
735  */
736 static
737 int compareSymgraphs(
738  SCIP* scip, /**< SCIP pointer (or NULL) */
739  SYM_GRAPH* G1, /**< first graph in comparison */
740  SYM_GRAPH* G2 /**< second graph in comparison */
741  )
742 {
743  int c;
744  int perm1;
745  int perm2;
746 
747  /* compare the number of constraint nodes */
748  if ( G1->nconsnodes < G2->nconsnodes )
749  return -1;
750  if ( G1->nconsnodes > G2->nconsnodes )
751  return 1;
752 
753  /* compare the constraint nodes of the two graphs */
754  for (c = 0; c < G1->nconsnodes; ++c)
755  {
756  perm1 = G1->consnodeperm[c];
757  perm2 = G2->consnodeperm[c];
758 
759  if ( SCIPconsGetHdlr(G1->conss[perm1]) < SCIPconsGetHdlr(G2->conss[perm2]) )
760  return -1;
761  if ( SCIPconsGetHdlr(G1->conss[perm1]) > SCIPconsGetHdlr(G2->conss[perm2]) )
762  return 1;
763 
764  /* compare using SCIP functions when SCIP is available */
765  if ( scip != NULL )
766  {
767  if ( SCIPisLT(scip, G1->lhs[perm1], G2->lhs[perm2]) )
768  return -1;
769  if ( SCIPisGT(scip, G1->lhs[perm1], G2->lhs[perm2]) )
770  return 1;
771 
772  if ( SCIPisLT(scip, G1->rhs[perm1], G2->rhs[perm2]) )
773  return -1;
774  if ( SCIPisGT(scip, G1->rhs[perm1], G2->rhs[perm2]) )
775  return 1;
776  }
777  else
778  {
779  if ( G1->lhs[perm1] < G2->lhs[perm2] )
780  return -1;
781  if ( G1->lhs[perm1] > G2->lhs[perm2] )
782  return 1;
783 
784  if ( G1->rhs[perm1] < G2->rhs[perm2] )
785  return -1;
786  if ( G1->rhs[perm1] > G2->rhs[perm2] )
787  return 1;
788  }
789  }
790 
791  /* compare number of remaining node types */
792  if ( G1->nnodes < G2->nnodes )
793  return -1;
794  if ( G1->nnodes > G2->nnodes )
795  return 1;
796 
797  if ( G1->nopnodes < G2->nopnodes )
798  return -1;
799  if ( G1->nopnodes > G2->nopnodes )
800  return 1;
801 
802  if ( G1->nvalnodes < G2->nvalnodes )
803  return -1;
804  if ( G1->nvalnodes > G2->nvalnodes )
805  return 1;
806 
807  if ( G1->nedges < G2->nedges )
808  return -1;
809  if ( G1->nedges > G2->nedges )
810  return 1;
811 
812  return 0;
813 }
814 
815 /** sorts symmetry detection graphs
816  *
817  * Graphs are sorted by their number of consnodes, then their constypes, then by their lhs,
818  * then by their rhs, then by their total number of nodes, then by the number of operator nodes,
819  * then by their number of value nodes, and then by their number of edges.
820  *
821  * result:
822  * < 0: ind1 comes before (is better than) ind2
823  * = 0: both indices have the same value
824  * > 0: ind2 comes after (is worse than) ind2
825  */
826 static
827 SCIP_DECL_SORTINDCOMP(SYMsortSymgraphs)
828 {
829  SYM_GRAPH** data;
830  SYM_GRAPH* G1;
831  SYM_GRAPH* G2;
832 
833  data = (SYM_GRAPH**) dataptr;
834  G1 = data[ind1];
835  G2 = data[ind2];
836 
837  return compareSymgraphs(NULL, G1, G2);
838 }
839 
840 /*
841  * Local methods
842  */
843 
844 #ifndef NDEBUG
845 /** checks that symmetry data is all freed */
846 static
848  SCIP_PROPDATA* propdata /**< propagator data */
849  )
850 {
851  assert( propdata->permvarmap == NULL );
852  assert( propdata->genorbconss == NULL );
853  assert( propdata->genlinconss == NULL );
854  assert( propdata->ngenlinconss == 0 );
855  assert( propdata->ngenorbconss == 0 );
856  assert( propdata->genorbconsssize == 0 );
857  assert( propdata->genlinconsssize == 0 );
858  assert( propdata->sstconss == NULL );
859  assert( propdata->leaders == NULL );
860 
861  assert( propdata->permvardomaincenter == NULL );
862  assert( propdata->permvars == NULL );
863  assert( propdata->perms == NULL );
864  assert( propdata->permstrans == NULL );
865  assert( propdata->npermvars == 0 );
866  assert( propdata->nbinpermvars == 0 );
867  assert( propdata->nperms == -1 || propdata->nperms == 0 );
868  assert( propdata->nmaxperms == 0 );
869  assert( propdata->nmovedpermvars == -1 );
870  assert( propdata->nmovedbinpermvars == 0 );
871  assert( propdata->nmovedintpermvars == 0 );
872  assert( propdata->nmovedimplintpermvars == 0 );
873  assert( propdata->nmovedcontpermvars == 0 );
874  assert( propdata->nmovedvars == -1 );
875  assert( propdata->binvaraffected == FALSE );
876  assert( propdata->isnonlinvar == NULL );
877 
878  assert( propdata->componenthassignedperm == NULL );
879  assert( propdata->componentblocked == NULL );
880  assert( propdata->componentbegins == NULL );
881  assert( propdata->components == NULL );
882  assert( propdata->ncomponents == -1 );
883  assert( propdata->ncompblocked == 0 );
884 
885  return TRUE;
886 }
887 #endif
888 
889 
890 /** resets symmetry handling propagators that depend on the branch-and-bound tree structure */
891 static
893  SCIP* scip, /**< SCIP pointer */
894  SCIP_PROPDATA* propdata /**< propagator data */
895  )
896 {
897  assert( scip != NULL );
898  assert( propdata != NULL );
899 
900  /* propagators managed by a different file */
901  if ( propdata->orbitalreddata != NULL )
902  {
903  SCIP_CALL( SCIPorbitalReductionReset(scip, propdata->orbitalreddata) );
904  }
905  if ( propdata->orbitopalreddata != NULL )
906  {
907  SCIP_CALL( SCIPorbitopalReductionReset(scip, propdata->orbitopalreddata) );
908  }
909  if ( propdata->lexreddata != NULL )
910  {
911  SCIP_CALL( SCIPlexicographicReductionReset(scip, propdata->lexreddata) );
912  }
913 
914  return SCIP_OKAY;
915 }
916 
917 
918 /** frees symmetry data */
919 static
921  SCIP* scip, /**< SCIP pointer */
922  SCIP_PROPDATA* propdata /**< propagator data */
923  )
924 {
925  int i;
926 
927  assert( scip != NULL );
928  assert( propdata != NULL );
929 
930  SCIP_CALL( resetDynamicSymmetryHandling(scip, propdata) );
931 
932  if ( propdata->permvarmap != NULL )
933  {
934  SCIPhashmapFree(&propdata->permvarmap);
935  }
936 
937  /* release all variables contained in permvars array */
938  for (i = 0; i < propdata->npermvars; ++i)
939  {
940  assert( propdata->permvars[i] != NULL );
941  SCIP_CALL( SCIPreleaseVar(scip, &propdata->permvars[i]) );
942  }
943 
944  /* free permstrans matrix*/
945  if ( propdata->permstrans != NULL )
946  {
947  assert( propdata->nperms > 0 );
948  assert( propdata->permvars != NULL );
949  assert( propdata->npermvars > 0 );
950  assert( propdata->nmaxperms > 0 );
951 
952  for (i = 0; i < propdata->npermvars; ++i)
953  {
954  SCIPfreeBlockMemoryArray(scip, &propdata->permstrans[i], propdata->nmaxperms);
955  }
956  SCIPfreeBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars);
957  }
958 
959  /* free data of added orbitope/orbisack/symresack constraints */
960  if ( propdata->genorbconss != NULL )
961  {
962  assert( propdata->ngenorbconss > 0 );
963 
964  /* release constraints */
965  while ( propdata->ngenorbconss > 0 )
966  {
967  assert( propdata->genorbconss[propdata->ngenorbconss - 1] != NULL );
968  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genorbconss[--propdata->ngenorbconss]) );
969  }
970  assert( propdata->ngenorbconss == 0 );
971 
972  /* free pointers to symmetry group and binary variables */
973  SCIPfreeBlockMemoryArray(scip, &propdata->genorbconss, propdata->genorbconsssize);
974  propdata->genorbconsssize = 0;
975  }
976 
977  /* free data of added constraints */
978  if ( propdata->genlinconss != NULL )
979  {
980  /* release constraints */
981  for (i = 0; i < propdata->ngenlinconss; ++i)
982  {
983  assert( propdata->genlinconss[i] != NULL );
984  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genlinconss[i]) );
985  }
986 
987  /* free pointers to symmetry group and binary variables */
988  SCIPfreeBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize);
989  propdata->ngenlinconss = 0;
990  propdata->genlinconsssize = 0;
991  }
992 
993  if ( propdata->sstconss != NULL )
994  {
995  assert( propdata->nsstconss > 0 );
996 
997  /* release constraints */
998  for (i = 0; i < propdata->nsstconss; ++i)
999  {
1000  assert( propdata->sstconss[i] != NULL );
1001  SCIP_CALL( SCIPreleaseCons(scip, &propdata->sstconss[i]) );
1002  }
1003 
1004  /* free pointers to symmetry group and binary variables */
1005  SCIPfreeBlockMemoryArray(scip, &propdata->sstconss, propdata->maxnsstconss);
1006  propdata->sstconss = NULL;
1007  propdata->nsstconss = 0;
1008  propdata->maxnsstconss = 0;
1009  }
1010 
1011  if ( propdata->leaders != NULL )
1012  {
1013  assert( propdata->maxnleaders > 0 );
1014 
1015  SCIPfreeBlockMemoryArray(scip, &propdata->leaders, propdata->maxnleaders);
1016  propdata->maxnleaders = 0;
1017  propdata->leaders = NULL;
1018  propdata->nleaders = 0;
1019  }
1020 
1021  /* free components */
1022  if ( propdata->ncomponents > 0 )
1023  {
1024  assert( propdata->componentblocked != NULL );
1025  assert( propdata->vartocomponent != NULL );
1026  assert( propdata->componentbegins != NULL );
1027  assert( propdata->components != NULL );
1028 
1029  SCIPfreeBlockMemoryArray(scip, &propdata->componenthassignedperm, propdata->ncomponents);
1030  SCIPfreeBlockMemoryArray(scip, &propdata->componentblocked, propdata->ncomponents);
1031  SCIPfreeBlockMemoryArray(scip, &propdata->vartocomponent, propdata->npermvars);
1032  SCIPfreeBlockMemoryArray(scip, &propdata->componentbegins, propdata->ncomponents + 1);
1033  SCIPfreeBlockMemoryArray(scip, &propdata->components, propdata->nperms);
1034 
1035  propdata->ncomponents = -1;
1036  propdata->ncompblocked = 0;
1037  }
1038 
1039  /* free main symmetry data */
1040  if ( propdata->nperms > 0 )
1041  {
1042  int permlen;
1043 
1044  assert( propdata->permvars != NULL );
1045 
1046  if ( (SYM_SYMTYPE) propdata->symtype == SYM_SYMTYPE_SIGNPERM )
1047  permlen = 2 * propdata->npermvars;
1048  else
1049  permlen = propdata->npermvars;
1050 
1051  SCIPfreeBlockMemoryArray(scip, &propdata->permvars, propdata->npermvars);
1052  SCIPfreeBlockMemoryArray(scip, &propdata->permvardomaincenter, propdata->npermvars);
1053 
1054  if ( propdata->perms != NULL )
1055  {
1056  for (i = 0; i < propdata->nperms; ++i)
1057  {
1058  SCIPfreeBlockMemoryArray(scip, &propdata->perms[i], permlen);
1059  }
1060  SCIPfreeBlockMemoryArray(scip, &propdata->perms, propdata->nmaxperms);
1061  }
1062 
1063  SCIPfreeBlockMemoryArrayNull(scip, &propdata->isnonlinvar, propdata->npermvars);
1064 
1065  propdata->npermvars = 0;
1066  propdata->nbinpermvars = 0;
1067  propdata->nperms = -1;
1068  propdata->nmaxperms = 0;
1069  propdata->nmovedpermvars = -1;
1070  propdata->nmovedbinpermvars = 0;
1071  propdata->nmovedintpermvars = 0;
1072  propdata->nmovedimplintpermvars = 0;
1073  propdata->nmovedcontpermvars = 0;
1074  propdata->nmovedvars = -1;
1075  propdata->log10groupsize = -1.0;
1076  propdata->binvaraffected = FALSE;
1077  propdata->isnonlinvar = NULL;
1078  }
1079  propdata->nperms = -1;
1080 
1081  assert( checkSymmetryDataFree(propdata) );
1082 
1083  propdata->computedsymmetry = FALSE;
1084  propdata->compressed = FALSE;
1085 
1086  return SCIP_OKAY;
1087 }
1088 
1089 
1090 /** makes sure that the constraint array (potentially NULL) of given array size is sufficiently large */
1091 static
1093  SCIP* scip, /**< SCIP pointer */
1094  SCIP_CONS*** consarrptr, /**< constraint array pointer */
1095  int* consarrsizeptr, /**< constraint array size pointer */
1096  int consarrsizereq /**< constraint array size required */
1097  )
1098 {
1099  int newsize;
1100 
1101  assert( scip != NULL );
1102  assert( consarrptr != NULL );
1103  assert( consarrsizeptr != NULL );
1104  assert( consarrsizereq > 0 );
1105  assert( *consarrsizeptr >= 0 );
1106  assert( (*consarrsizeptr == 0) == (*consarrptr == NULL) );
1107 
1108  /* array is already sufficiently large */
1109  if ( consarrsizereq <= *consarrsizeptr )
1110  return SCIP_OKAY;
1111 
1112  /* compute new size */
1113  newsize = SCIPcalcMemGrowSize(scip, consarrsizereq);
1114  assert( newsize > *consarrsizeptr );
1115 
1116  /* allocate or reallocate */
1117  if ( *consarrptr == NULL )
1118  {
1119  SCIP_CALL( SCIPallocBlockMemoryArray(scip, consarrptr, newsize) );
1120  }
1121  else
1122  {
1123  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, consarrptr, *consarrsizeptr, newsize) );
1124  }
1125 
1126  *consarrsizeptr = newsize;
1127 
1128  return SCIP_OKAY;
1129 }
1130 
1131 /** set symmetry data */
1132 static
1134  SCIP* scip, /**< SCIP pointer */
1135  SYM_SYMTYPE symtype, /**< type of symmetries in perms */
1136  SCIP_VAR** vars, /**< vars present at time of symmetry computation */
1137  int nvars, /**< number of vars present at time of symmetry computation */
1138  int nbinvars, /**< number of binary vars present at time of symmetry computation */
1139  SCIP_VAR*** permvars, /**< pointer to permvars array */
1140  int* npermvars, /**< pointer to store number of permvars */
1141  int* nbinpermvars, /**< pointer to store number of binary permvars */
1142  SCIP_Real** permvardomaincenter, /**< pointer to store center points of variable domains */
1143  int** perms, /**< permutations matrix (nperms x nvars) */
1144  int nperms, /**< number of permutations */
1145  int* nmovedvars, /**< pointer to store number of vars affected by symmetry (if usecompression) or NULL */
1146  SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1147  SCIP_Bool usecompression, /**< whether symmetry data shall be compressed */
1148  SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1149  SCIP_Bool* compressed /**< pointer to store whether compression has been performed */
1150  )
1151 {
1152  SCIP_Real ub;
1153  SCIP_Real lb;
1154  int i;
1155  int p;
1156 
1157  assert( scip != NULL );
1158  assert( vars != NULL );
1159  assert( nvars > 0 );
1160  assert( permvars != NULL );
1161  assert( npermvars != NULL );
1162  assert( nbinpermvars != NULL );
1163  assert( perms != NULL );
1164  assert( nperms > 0 );
1165  assert( binvaraffected != NULL );
1166  assert( SCIPisGE(scip, compressthreshold, 0.0) );
1167  assert( SCIPisLE(scip, compressthreshold, 1.0) );
1168  assert( compressed != NULL );
1169 
1170  /* set default return values */
1171  *permvars = vars;
1172  *npermvars = nvars;
1173  *nbinpermvars = nbinvars;
1174  *binvaraffected = FALSE;
1175  *compressed = FALSE;
1176 
1177  /* if we possibly perform compression */
1178  if ( usecompression && SCIPgetNVars(scip) >= COMPRESSNVARSLB )
1179  {
1180  SCIP_Real percentagemovedvars;
1181  int* labelmovedvars;
1182  int* labeltopermvaridx;
1183  int nbinvarsaffected = 0;
1184 
1185  assert( nmovedvars != NULL );
1186 
1187  *nmovedvars = 0;
1188 
1189  /* detect number of moved vars and label moved vars */
1190  SCIP_CALL( SCIPallocBufferArray(scip, &labelmovedvars, nvars) );
1191  SCIP_CALL( SCIPallocBufferArray(scip, &labeltopermvaridx, nvars) );
1192  for (i = 0; i < nvars; ++i)
1193  {
1194  labelmovedvars[i] = -1;
1195 
1196  for (p = 0; p < nperms; ++p)
1197  {
1198  if ( perms[p][i] != i )
1199  {
1200  labeltopermvaridx[*nmovedvars] = i;
1201  labelmovedvars[i] = (*nmovedvars)++;
1202 
1203  if ( SCIPvarIsBinary(vars[i]) )
1204  ++nbinvarsaffected;
1205  break;
1206  }
1207  }
1208  }
1209 
1210  if ( nbinvarsaffected > 0 )
1211  *binvaraffected = TRUE;
1212 
1213  /* check whether compression should be performed */
1214  percentagemovedvars = (SCIP_Real) *nmovedvars / (SCIP_Real) nvars;
1215  if ( *nmovedvars > 0 && SCIPisLE(scip, percentagemovedvars, compressthreshold) )
1216  {
1217  /* remove variables from permutations that are not affected by any permutation */
1218  for (p = 0; p < nperms; ++p)
1219  {
1220  /* iterate over labels and adapt permutation (possibly taking signed permutations into account) */
1221  for (i = 0; i < *nmovedvars; ++i)
1222  {
1223  assert( i <= labeltopermvaridx[i] );
1224  if ( perms[p][labeltopermvaridx[i]] >= nvars )
1225  {
1226  int imgvaridx;
1227 
1228  assert( symtype == SYM_SYMTYPE_SIGNPERM );
1229 
1230  imgvaridx = perms[p][labeltopermvaridx[i]] - nvars;
1231  perms[p][i] = labelmovedvars[imgvaridx] + *nmovedvars;
1232 
1233  assert( 0 <= perms[p][i] && perms[p][i] < 2 * (*nmovedvars) );
1234  }
1235  else
1236  perms[p][i] = labelmovedvars[perms[p][labeltopermvaridx[i]]];
1237  }
1238 
1239  if ( symtype == SYM_SYMTYPE_SIGNPERM )
1240  {
1241  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], 2 * nvars, 2 * (*nmovedvars)) );
1242  }
1243  else
1244  {
1245  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], nvars, *nmovedvars) );
1246  }
1247  }
1248 
1249  /* remove variables from permvars array that are not affected by any symmetry */
1250  SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvars, *nmovedvars) );
1251  for (i = 0; i < *nmovedvars; ++i)
1252  {
1253  (*permvars)[i] = vars[labeltopermvaridx[i]];
1254  }
1255  *npermvars = *nmovedvars;
1256  *nbinpermvars = nbinvarsaffected;
1257  *compressed = TRUE;
1258 
1259  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
1260  }
1261  SCIPfreeBufferArray(scip, &labeltopermvaridx);
1262  SCIPfreeBufferArray(scip, &labelmovedvars);
1263  }
1264  else
1265  {
1266  /* detect whether binary variable is affected by symmetry and count number of binary permvars */
1267  for (i = 0; i < nbinvars; ++i)
1268  {
1269  for (p = 0; p < nperms && ! *binvaraffected; ++p)
1270  {
1271  if ( perms[p][i] != i )
1272  {
1273  if ( SCIPvarIsBinary(vars[i]) )
1274  *binvaraffected = TRUE;
1275  break;
1276  }
1277  }
1278  }
1279  }
1280 
1281  /* store center points of variable domains */
1282  SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvardomaincenter, *npermvars) );
1283  for (i = 0; i < *npermvars; ++i)
1284  {
1285  ub = SCIPvarGetUbGlobal((*permvars)[i]);
1286  lb = SCIPvarGetLbGlobal((*permvars)[i]);
1287 
1288  (*permvardomaincenter)[i] = 0.5 * (ub + lb);
1289  }
1290 
1291  return SCIP_OKAY;
1292 }
1293 
1294 /** returns whether a constraint handler can provide required symmetry information */
1295 static
1297  SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1298  SYM_SYMTYPE symtype /**< type of symmetries for which information are needed */
1299  )
1300 {
1301  assert( conshdlr != NULL );
1302 
1303  switch ( symtype )
1304  {
1305  case SYM_SYMTYPE_PERM:
1306  return SCIPconshdlrSupportsPermsymDetection(conshdlr);
1307  default:
1308  assert( symtype == SYM_SYMTYPE_SIGNPERM );
1310  } /*lint !e788*/
1311 }
1312 
1313 /** returns whether all constraint handlers with constraints can provide symmetry information */
1314 static
1316  SCIP* scip, /**< SCIP pointer */
1317  SYM_SYMTYPE symtype /**< type of symmetries for which information are needed */
1318  )
1319 {
1320  SCIP_CONSHDLR** conshdlrs;
1321  SCIP_CONSHDLR* conshdlr;
1322  int nconshdlrs;
1323  int c;
1324 
1325  conshdlrs = SCIPgetConshdlrs(scip);
1326  assert( conshdlrs != NULL );
1327 
1328  nconshdlrs = SCIPgetNConshdlrs(scip);
1329  for (c = 0; c < nconshdlrs; ++c)
1330  {
1331  conshdlr = conshdlrs[c];
1332  assert( conshdlr != NULL );
1333 
1334  if ( ! conshdlrCanProvideSymInformation(conshdlr, symtype) && SCIPconshdlrGetNConss(conshdlr) > 0 )
1335  {
1336  char name[SCIP_MAXSTRLEN];
1337 
1338  if ( symtype == SYM_SYMTYPE_PERM )
1339  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "CONSGETPERMSYMGRAPH");
1340  else
1341  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "CONSGETSIGNEDPERMSYMGRAPH");
1342 
1344  " Symmetry detection interrupted: constraints of type %s do not provide symmetry information.\n"
1345  " If symmetries shall be detected, implement the %s callback.\n",
1346  SCIPconshdlrGetName(conshdlr), name);
1347 
1348  return FALSE;
1349  }
1350  }
1351 
1352  /* check whether all expressions provide sufficient symmetry information */
1353  conshdlr = SCIPfindConshdlr(scip, "nonlinear");
1354  if ( conshdlr != NULL && SCIPconshdlrGetNConss(conshdlr) > 0 )
1355  {
1356  SCIP_EXPRHDLR* exprhdlr;
1357 
1358  for (c = 0; c < SCIPgetNExprhdlrs(scip); ++c)
1359  {
1360  SCIP_Bool found = FALSE;
1361  exprhdlr = SCIPgetExprhdlrs(scip)[c];
1362 
1363  if ( SCIPexprhdlrHasGetSymData(exprhdlr) )
1364  continue;
1365 
1366  /* check whether exprhdlr is known by SCIP (and handles symmetries correctly) */
1367  if ( strcmp(SCIPexprhdlrGetName(exprhdlr), "var") == 0
1368  || strcmp(SCIPexprhdlrGetName(exprhdlr), "sum") == 0
1369  || strcmp(SCIPexprhdlrGetName(exprhdlr), "product") == 0
1370  || strcmp(SCIPexprhdlrGetName(exprhdlr), "val") == 0
1371  || strcmp(SCIPexprhdlrGetName(exprhdlr), "pow") == 0
1372  || strcmp(SCIPexprhdlrGetName(exprhdlr), "signpow") == 0
1373  || strcmp(SCIPexprhdlrGetName(exprhdlr), "exp") == 0
1374  || strcmp(SCIPexprhdlrGetName(exprhdlr), "log") == 0
1375  || strcmp(SCIPexprhdlrGetName(exprhdlr), "abs") == 0
1376  || strcmp(SCIPexprhdlrGetName(exprhdlr), "sin") == 0
1377  || strcmp(SCIPexprhdlrGetName(exprhdlr), "cos") == 0
1378  || strcmp(SCIPexprhdlrGetName(exprhdlr), "entropy") == 0
1379  || strcmp(SCIPexprhdlrGetName(exprhdlr), "erf") == 0
1380  || strcmp(SCIPexprhdlrGetName(exprhdlr), "varidx") == 0 )
1381  found = TRUE;
1382 
1383  /* there exists an unknown expression handler that does not provide symmetry information */
1384  if ( ! found )
1385  {
1386  SCIPwarningMessage(scip, "Expression handler %s does not implement the EXPRGETSYMDATA callback.\n"
1387  "Computed symmetries might be incorrect if the expression uses different constants or assigns\n"
1388  "different coefficients to its children.\n", SCIPexprhdlrGetName(SCIPgetExprhdlrs(scip)[c]));
1389  }
1390  }
1391  }
1392 
1393  return TRUE;
1394 }
1395 
1396 /** provides estimates for the number of nodes and edges in a symmetry detection graph */
1397 static
1399  SCIP* scip, /**< SCIP pointer */
1400  int* nopnodes, /**< pointer to store estimate for number of operator nodes */
1401  int* nvalnodes, /**< pointer to store estimate for number of value nodes */
1402  int* nconsnodes, /**< pointer to store estimate for number of constraint nodes */
1403  int* nedges /**< pointer to store estimate for number of edges */
1404  )
1405 {
1406  SCIP_CONS** conss;
1407  SCIP_Bool success;
1408  int nvars;
1409  int nconss;
1410  int num;
1411  int c;
1412 
1413  assert( scip != NULL );
1414  assert( nopnodes != NULL );
1415  assert( nvalnodes != NULL );
1416  assert( nconsnodes != NULL );
1417  assert( nedges != NULL );
1418 
1419  nvars = SCIPgetNVars(scip);
1420  nconss = SCIPgetNConss(scip);
1421  conss = SCIPgetConss(scip);
1422  assert( conss != NULL || nconss == 0 );
1423 
1424  *nconsnodes = nconss;
1425 
1426  /* get estimate from different types of constraints */
1427  *nopnodes = 0;
1428  *nvalnodes = 0;
1429  for (c = 0; c < nconss; ++c)
1430  {
1431  if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "bounddisjunction") == 0 )
1432  {
1433  SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1434 
1435  if ( success )
1436  {
1437  *nopnodes += num;
1438  *nvalnodes += num;
1439  }
1440  }
1441  else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "indicator") == 0 )
1442  {
1443  *nopnodes += 3;
1444  *nvalnodes += 1;
1445  }
1446  else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "nonlinear") == 0 )
1447  {
1448  SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1449 
1450  /* use binary trees as a proxy for an expression tree */
1451  if ( success )
1452  {
1453  int depth;
1454  int numnodes;
1455  int expval;
1456 
1457  depth = (int) log2((double) num);
1458  expval = (int) exp2((double) (depth + 1));
1459  numnodes = MIN(expval, 100);
1460 
1461  *nopnodes += numnodes;
1462  *nvalnodes += MAX((int) 0.1 * numnodes, 1);
1463  }
1464  }
1465  else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "SOS1") == 0 )
1466  {
1467  SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1468 
1469  if ( success )
1470  *nopnodes += num;
1471  }
1472  else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "SOS2") == 0 )
1473  {
1474  SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1475 
1476  if ( success )
1477  *nopnodes += num - 1;
1478  }
1479  }
1480 
1481  /* use a staggered scheme for the number of edges since this can become large
1482  *
1483  * In most cases, edges represent variable coefficients from linear constraints.
1484  * For this reason, use number of variables as proxy.
1485  */
1486  if ( nvars <= 100000 )
1487  *nedges = 100 * nvars;
1488  else if ( nvars <= 1000000 )
1489  *nedges = 32 * nvars;
1490  else if ( nvars <= 16700000 )
1491  *nedges = 16 * nvars;
1492  else
1493  *nedges = INT_MAX / 10;
1494 
1495  return SCIP_OKAY;
1496 }
1497 
1498 /** checks whether computed symmetries are indeed symmetries */
1499 static
1501  SCIP* scip, /**< SCIP pointer */
1502  SYM_SYMTYPE symtype, /**< type of symmetries to be checked */
1503  int** perms, /**< array of permutations */
1504  int nperms, /**< number of permutations */
1505  int npermvars, /**< number of variables permutations act on */
1506  SYM_SPEC fixedtype /**< variable types that must be fixed by symmetries */
1507  )
1508 {
1509  SYM_GRAPH** graphs;
1510  SCIP_CONS** conss;
1511  SCIP_VAR** symvars;
1512  SCIP_Bool success;
1513  int* graphperm;
1514  int* groupbegins;
1515  int ngroups = 1;
1516  int nsymvars;
1517  int nconss;
1518  int p;
1519  int c;
1520  int g;
1521 #ifdef SCIP_DISPLAY_SYM_CHECK
1522  int permlen;
1523  SCIP_Bool* covered;
1524 #endif
1525 
1526  assert( scip != NULL );
1527  assert( perms != NULL );
1528  assert( nperms > 0 );
1529  assert( npermvars > 0 );
1530 
1531  /* get symmetry detection graphs for all constraints */
1532  nconss = SCIPgetNConss(scip);
1533  conss = SCIPgetConss(scip);
1534  assert( conss != NULL );
1535 
1536  symvars = SCIPgetVars(scip);
1537  nsymvars = SCIPgetNVars(scip);
1538  assert( nsymvars == npermvars );
1539 
1540  SCIP_CALL( SCIPallocBufferArray(scip, &graphs, nconss) );
1541 
1542  for (c = 0; c < nconss; ++c)
1543  {
1544  SCIP_CALL( SCIPcreateSymgraph(scip, symtype, &graphs[c], symvars, nsymvars, 10, 10, 1, 100) );
1545 
1546  switch ( symtype )
1547  {
1548  case SYM_SYMTYPE_PERM:
1549  SCIP_CALL( SCIPgetConsPermsymGraph(scip, conss[c], graphs[c], &success) );
1550  break;
1551  default:
1552  assert( symtype == SYM_SYMTYPE_SIGNPERM );
1553  SCIP_CALL( SCIPgetConsSignedPermsymGraph(scip, conss[c], graphs[c], &success) );
1554  } /*lint !e788*/
1555 
1556  SCIP_CALL( SCIPcomputeSymgraphColors(scip, graphs[c], fixedtype) );
1557 
1558  assert( success );
1559  }
1560 
1561  /* sort graphs for quicker comparisons */
1562  SCIP_CALL( SCIPallocBufferArray(scip, &graphperm, nconss) );
1563  SCIP_CALL( SCIPallocBufferArray(scip, &groupbegins, nconss + 1) );
1564  for (c = 0; c < nconss; ++c)
1565  {
1566  SCIP_CALL( SCIPcreateSymgraphConsnodeperm(scip, graphs[c]) );
1567  }
1568 
1569  SCIPsort(graphperm, SYMsortSymgraphs, graphs, nconss);
1570 
1571  groupbegins[0] = 0;
1572  for (c = 1; c < nconss; ++c)
1573  {
1574  if ( compareSymgraphs(scip, graphs[graphperm[c]], graphs[graphperm[c-1]]) != 0 )
1575  groupbegins[ngroups++] = c;
1576  }
1577  groupbegins[ngroups] = nconss;
1578 
1579  /* remove information from symmetry detection graph that is not needed anymore */
1580  for (c = 0; c < nconss; ++c)
1581  {
1582  SCIP_CALL( SCIPfreeSymgraphConsnodeperm(scip, graphs[c]) );
1583  }
1584 
1585 #ifdef SCIP_DISPLAY_SYM_CHECK
1586  permlen = symtype == SYM_SYMTYPE_SIGNPERM ? 2 * npermvars : npermvars;
1587  SCIP_CALL( SCIPallocClearBufferArray(scip, &covered, permlen) );
1588 #endif
1589 
1590  /* iterate over all permutations and check whether they define symmetries */
1591  for (p = 0; p < nperms; ++p)
1592  {
1593  SYM_GRAPH* graph;
1594  SCIP_Bool found = TRUE;
1595  int d;
1596 #ifdef SCIP_DISPLAY_SYM_CHECK
1597  int i;
1598 
1599  SCIPinfoMessage(scip, NULL, "Check whether permutation %d is a symmetry:\n", p);
1600  for (i = 0; i < permlen; ++i)
1601  {
1602  SCIP_CALL( displayCycleOfSymmetry(scip, perms[p], symtype, i, covered, npermvars, SCIPgetVars(scip)) );
1603  }
1604 
1605  for (i = 0; i < permlen; ++i)
1606  covered[i] = FALSE;
1607  SCIPinfoMessage(scip, NULL, "Check whether every constraint has a symmetric counterpart.\n");
1608 #endif
1609 
1610  /* for every constraint, create permuted graph by copying nodes and edges */
1611  for (g = 0; g < ngroups; ++g)
1612  {
1613  for (c = groupbegins[g]; c < groupbegins[g+1]; ++c)
1614  {
1615 #ifdef SCIP_DISPLAY_SYM_CHECK
1616  SCIPinfoMessage(scip, NULL, "Check whether constraint %d has a symmetric counterpart:\n",
1617  graphperm[c]);
1618  SCIP_CALL( SCIPprintCons(scip, conss[graphperm[c]], NULL) );
1619  SCIPinfoMessage(scip, NULL, "\n");
1620 #endif
1621  SCIP_CALL( SCIPcopySymgraph(scip, &graph, graphs[graphperm[c]], perms[p], fixedtype) );
1622 
1623  /* if adapted graph is equivalent to original graph, we don't need to check further graphs */
1624  if ( SYMcheckGraphsAreIdentical(scip, symtype, graph, graphs[graphperm[c]]) )
1625  {
1626 #ifdef SCIP_DISPLAY_SYM_CHECK
1627  SCIPinfoMessage(scip, NULL, "\tconstraint is symmetric to itself\n");
1628 #endif
1629  SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1630  continue;
1631  }
1632 
1633  /* check whether graph has an isomorphic counterpart */
1634  found = FALSE;
1635  for (d = groupbegins[g]; d < groupbegins[g+1] && ! found; ++d)
1636  {
1637  found = SYMcheckGraphsAreIdentical(scip, symtype, graph, graphs[graphperm[d]]);
1638 
1639 #ifdef SCIP_DISPLAY_SYM_CHECK
1640  SCIPinfoMessage(scip, NULL, "\tconstraint is %ssymmetric to constraint %d\n\t", !found ? "not " : "", d);
1641  SCIP_CALL( SCIPprintCons(scip, conss[graphperm[d]], NULL) );
1642  SCIPinfoMessage(scip, NULL, "\n");
1643 #endif
1644  }
1645 
1646  SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1647 
1648  if ( ! found )
1649  {
1650 #ifdef SCIP_DISPLAY_SYM_CHECK
1651  SCIPfreeBufferArray(scip, &covered);
1652 #endif
1653  SCIPerrorMessage("permutation %d is not a symmetry\n", p);
1654  return SCIP_ERROR;
1655  }
1656  }
1657  }
1658  }
1659 
1660 #ifdef SCIP_DISPLAY_SYM_CHECK
1661  SCIPfreeBufferArray(scip, &covered);
1662 #endif
1663 
1664  SCIPfreeBufferArray(scip, &groupbegins);
1665  SCIPfreeBufferArray(scip, &graphperm);
1666 
1667  for (c = nconss - 1; c >= 0; --c)
1668  {
1669  SCIP_CALL( SCIPfreeSymgraph(scip, &graphs[c]) );
1670  }
1671  SCIPfreeBufferArray(scip, &graphs);
1672 
1673  return SCIP_OKAY;
1674 }
1675 
1676 /** computes symmetry group of a CIP */
1677 static
1679  SCIP* scip, /**< SCIP pointer */
1680  SYM_SYMTYPE symtype, /**< type of symmetries to be computed */
1681  SCIP_Bool compresssymmetries, /**< Should non-affected variables be removed from permutation to save memory? */
1682  SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1683  int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
1684  SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1685  SCIP_Bool checksymmetries, /**< Should all symmetries be checked after computation? */
1686  SCIP_VAR*** permvars, /**< pointer to permvars array */
1687  int* npermvars, /**< pointer to store number of permvars */
1688  int* nbinpermvars, /**< pointer to store number of binary permvars */
1689  SCIP_Real** permvardomaincenter, /**< pointer to store center points of variable domains */
1690  int*** perms, /**< pointer to store permutation matrix (nperms x nvars) */
1691  int* nperms, /**< pointer to store number of permutations */
1692  int* nmaxperms, /**< pointer to store maximal number of permutations
1693  * (needed for freeing storage) */
1694  int* nmovedvars, /**< pointer to store number of vars affected
1695  * by symmetry (if usecompression) or NULL */
1696  SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1697  SCIP_Bool* compressed, /**< pointer to store whether compression has been performed */
1698  SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
1699  SCIP_Real* symcodetime, /**< pointer to store the time for symmetry code */
1700  SCIP_Bool* success /**< pointer to store whether symmetry computation was successful */
1701  )
1702 {
1703  SCIP_CONS** conss;
1704  SYM_GRAPH* graph;
1705  int nconsnodes = 0;
1706  int nvalnodes = 0;
1707  int nopnodes = 0;
1708  int nedges = 0;
1709  int nconss;
1710  int c;
1711 
1712  assert( scip != NULL );
1713  assert( permvars != NULL );
1714  assert( npermvars != NULL );
1715  assert( nbinpermvars != NULL );
1716  assert( perms != NULL );
1717  assert( nperms != NULL );
1718  assert( nmaxperms != NULL );
1719  assert( nmovedvars != NULL );
1720  assert( binvaraffected != NULL );
1721  assert( compressed != NULL );
1722  assert( log10groupsize != NULL );
1723  assert( symcodetime != NULL );
1724  assert( success != NULL );
1725 
1726  /* init pointers */
1727  *permvars = NULL;
1728  *npermvars = 0;
1729  *nbinpermvars = 0;
1730  *perms = NULL;
1731  *nperms = 0;
1732  *nmaxperms = 0;
1733  *nmovedvars = -1;
1734  *binvaraffected = FALSE;
1735  *compressed = FALSE;
1736  *log10groupsize = 0;
1737  *success = FALSE;
1738  *symcodetime = 0.0;
1739 
1740  /* check whether all constraints can provide symmetry information */
1741  if ( ! conshdlrsCanProvideSymInformation(scip, symtype) )
1742  return SCIP_OKAY;
1743 
1744  /* get symmetry detection graphs from constraints */
1745  conss = SCIPgetConss(scip);
1746  nconss = SCIPgetNConss(scip);
1747 
1748  assert( conss != NULL || nconss == 0 );
1749 
1750  /* exit if no constraints or no variables are available */
1751  if ( nconss == 0 || SCIPgetNVars(scip) == 0 )
1752  {
1753  *success = TRUE;
1754  return SCIP_OKAY;
1755  }
1756 
1757  /* get an estimate for the number of nodes and edges */
1758  SCIP_CALL( estimateSymgraphSize(scip, &nopnodes, &nvalnodes, &nconsnodes, &nedges) );
1759 
1760  /* create graph */
1761  SCIP_CALL( SCIPcreateSymgraph(scip, symtype, &graph, SCIPgetVars(scip), SCIPgetNVars(scip),
1762  nopnodes, nvalnodes, nconsnodes, nedges) );
1763 
1764  *success = TRUE;
1765  for (c = 0; c < nconss && *success; ++c)
1766  {
1767  if ( symtype == SYM_SYMTYPE_PERM )
1768  {
1769  SCIP_CALL( SCIPgetConsPermsymGraph(scip, conss[c], graph, success) );
1770  }
1771  else
1772  {
1773  assert( symtype == SYM_SYMTYPE_SIGNPERM );
1774  SCIP_CALL( SCIPgetConsSignedPermsymGraph(scip, conss[c], graph, success) );
1775  }
1776 
1777  /* terminate early if graph could not be returned */
1778  if ( ! *success )
1779  {
1780  SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1781 
1782  return SCIP_OKAY;
1783  }
1784  }
1785 
1786  SCIP_CALL( SCIPcomputeSymgraphColors(scip, graph, fixedtype) );
1787 
1788  /* terminate early in case all variables are different */
1789  if ( (symtype == SYM_SYMTYPE_PERM && SCIPgetSymgraphNVarcolors(graph) == SCIPgetNVars(scip))
1790  || (symtype == SYM_SYMTYPE_SIGNPERM && SCIPgetSymgraphNVarcolors(graph) == 2 * SCIPgetNVars(scip)) )
1791  {
1792  SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1793  return SCIP_OKAY;
1794  }
1795 
1796  /*
1797  * actually compute symmetries
1798  */
1799  SCIP_CALL( SYMcomputeSymmetryGenerators(scip, maxgenerators, graph, nperms, nmaxperms,
1800  perms, log10groupsize, symcodetime) );
1801 
1802  if ( checksymmetries && *nperms > 0 )
1803  {
1804  SCIP_CALL( checkSymmetriesAreSymmetries(scip, symtype, *perms, *nperms, SCIPgetNVars(scip), fixedtype) );
1805  }
1806 
1807  /* potentially store symmetries */
1808  if ( *nperms > 0 )
1809  {
1810  SCIP_VAR** vars;
1811  int nvars;
1812 
1813  nvars = SCIPgetNVars(scip);
1814  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &vars, SCIPgetVars(scip), nvars) ); /*lint !e666*/
1815 
1816  SCIP_CALL( setSymmetryData(scip, symtype, vars, nvars, SCIPgetNBinVars(scip), permvars, npermvars, nbinpermvars,
1817  permvardomaincenter, *perms, *nperms, nmovedvars, binvaraffected,
1818  compresssymmetries, compressthreshold, compressed) );
1819  }
1820 
1821  /* free symmetry graph */
1822  SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1823 
1824  return SCIP_OKAY;
1825 }
1826 
1827 /** returns whether a symmetry is a non-standard permutation */
1828 static
1830  SCIP* scip, /**< SCIP instance */
1831  int* symmetry, /**< a symmetry encoded as a signed permutation */
1832  SCIP_VAR** vars, /**< array of variables the symmetry acts on */
1833  int nvars /**< number of variables in vars */
1834  )
1835 {
1836  int v;
1837 
1838  assert( symmetry != NULL );
1839  assert( vars != NULL );
1840  assert( nvars > 0 );
1841 
1842  for (v = 0; v < nvars; ++v)
1843  {
1844  /* the symmetry is signed */
1845  if ( symmetry[v] >= nvars )
1846  return TRUE;
1847 
1848  /* the domain of symmetric variables is different */
1849  if ( !SCIPisEQ(scip, SCIPvarGetLbLocal(vars[v]), SCIPvarGetLbLocal(vars[symmetry[v]]))
1850  || !SCIPisEQ(scip, SCIPvarGetUbLocal(vars[v]), SCIPvarGetUbLocal(vars[symmetry[v]])) )
1851  {
1852  assert( SCIPisEQ(scip, SCIPvarGetUbLocal(vars[v]) - SCIPvarGetLbLocal(vars[v]),
1853  SCIPvarGetUbLocal(vars[symmetry[v]]) - SCIPvarGetLbLocal(vars[symmetry[v]])) );
1854  return TRUE;
1855  }
1856  }
1857 
1858  return FALSE;
1859 }
1860 
1861 /** checks whether component contains non-standard permutations
1862  *
1863  * If all symmetries are standard permutations, stores them as such.
1864  */
1865 static
1867  SCIP* scip, /**< SCIP instance */
1868  SCIP_PROPDATA* propdata /**< propagator data */
1869  )
1870 {
1871  int* components;
1872  int* componentbegins;
1873  int ncomponents;
1874  int i;
1875  int c;
1876 
1877  assert( scip != NULL );
1878  assert( propdata != NULL );
1879  assert( propdata->ncomponents > 0 );
1880  assert( propdata->components != NULL );
1881  assert( propdata->componentbegins != NULL );
1882 
1883  components = propdata->components;
1884  componentbegins = propdata->componentbegins;
1885  ncomponents = propdata->ncomponents;
1886 
1887  SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &(propdata->componenthassignedperm), ncomponents) );
1888 
1889  /* stop if no non-standard permutations can exist */
1890  if ( (SYM_SYMTYPE) propdata->symtype == SYM_SYMTYPE_PERM )
1891  return SCIP_OKAY;
1892 
1893  /* for each component, check whether it has a non-standard permutation */
1894  for (c = 0; c < ncomponents; ++c)
1895  {
1896  for (i = componentbegins[c]; i < componentbegins[c + 1]; ++i)
1897  {
1898  if ( isNonstandardPerm(scip, propdata->perms[components[i]], propdata->permvars, propdata->npermvars) )
1899  {
1900  propdata->componenthassignedperm[c] = TRUE;
1901  break;
1902  }
1903  }
1904  }
1905 
1906  return SCIP_OKAY;
1907 }
1908 
1909 /** ensures that the symmetry components are already computed */
1910 static
1912  SCIP* scip, /**< SCIP instance */
1913  SCIP_PROPDATA* propdata /**< propagator data */
1914  )
1915 {
1916  assert( scip != NULL );
1917  assert( propdata != NULL );
1918 
1919  /* symmetries must have been determined */
1920  assert( propdata->nperms >= 0 );
1921 
1922  /* stop if already computed */
1923  if ( propdata->ncomponents >= 0 )
1924  return SCIP_OKAY;
1925 
1926  /* compute components */
1927  assert( propdata->ncomponents == -1 );
1928  assert( propdata->components == NULL );
1929  assert( propdata->componentbegins == NULL );
1930  assert( propdata->vartocomponent == NULL );
1931 
1932 #ifdef SCIP_OUTPUT_COMPONENT
1933  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation started\n", SCIPgetSolvingTime(scip));
1934 #endif
1935 
1936  SCIP_CALL( SCIPcomputeComponentsSym(scip, (SYM_SYMTYPE) propdata->symtype, propdata->perms, propdata->nperms,
1937  propdata->permvars, propdata->npermvars, FALSE, &propdata->components, &propdata->componentbegins,
1938  &propdata->vartocomponent, &propdata->componentblocked, &propdata->ncomponents) );
1939 
1940 #ifdef SCIP_OUTPUT_COMPONENT
1941  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation finished\n", SCIPgetSolvingTime(scip));
1942 #endif
1943 
1944  assert( propdata->components != NULL );
1945  assert( propdata->componentbegins != NULL );
1946  assert( propdata->ncomponents > 0 );
1947 
1948  /* structure of symmetries can be simplified if they are standard permutations */
1949  SCIP_CALL( checkComponentsForNonstandardPerms(scip, propdata) );
1950  assert( propdata->componenthassignedperm != NULL );
1951 
1952  return SCIP_OKAY;
1953 }
1954 
1955 
1956 /** ensures that permvarmap is initialized */
1957 static
1959  SCIP* scip, /**< SCIP instance */
1960  SCIP_PROPDATA* propdata /**< propagator data */
1961  )
1962 {
1963  int v;
1964 
1965  assert( scip != NULL );
1966  assert( propdata != NULL );
1967 
1968  /* symmetries must have been determined */
1969  assert( propdata->nperms >= 0 );
1970 
1971  /* stop if already computed */
1972  if ( propdata->permvarmap != NULL )
1973  return SCIP_OKAY;
1974 
1975  /* create hashmap for storing the indices of variables */
1976  SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
1977 
1978  /* insert variables into hashmap */
1979  for (v = 0; v < propdata->npermvars; ++v)
1980  {
1981  SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
1982  }
1983 
1984  return SCIP_OKAY;
1985 }
1986 
1987 
1988 /** ensures that permstrans is initialized */
1989 static
1991  SCIP* scip, /**< SCIP instance */
1992  SCIP_PROPDATA* propdata /**< propagator data */
1993  )
1994 {
1995  int v;
1996  int p;
1997 
1998  assert( scip != NULL );
1999  assert( propdata != NULL );
2000 
2001  /* symmetries must have been determined */
2002  assert( propdata->nperms >= 0 );
2003 
2004  /* stop if already computed */
2005  if ( propdata->permstrans != NULL )
2006  return SCIP_OKAY;
2007 
2008  /* transpose symmetries matrix here */
2009  assert( propdata->permstrans == NULL );
2010  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
2011  for (v = 0; v < propdata->npermvars; ++v)
2012  {
2013  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
2014  for (p = 0; p < propdata->nperms; ++p)
2015  propdata->permstrans[v][p] = propdata->perms[p][v];
2016  }
2017 
2018  return SCIP_OKAY;
2019 }
2020 
2021 
2022 /** ensures that movedpermvarscounts is initialized */
2023 static
2025  SCIP* scip, /**< SCIP instance */
2026  SCIP_PROPDATA* propdata /**< propagator data */
2027  )
2028 {
2029  int v;
2030  int p;
2031 
2032  assert( scip != NULL );
2033  assert( propdata != NULL );
2034 
2035  /* symmetries must have been determined */
2036  assert( propdata->nperms >= 0 );
2037 
2038  /* stop if already computed */
2039  if ( propdata->nmovedpermvars >= 0 )
2040  return SCIP_OKAY;
2041  assert( propdata->nmovedpermvars == -1 );
2042 
2043  propdata->nmovedpermvars = 0;
2044  propdata->nmovedbinpermvars = 0;
2045  propdata->nmovedintpermvars = 0;
2046  propdata->nmovedimplintpermvars = 0;
2047  propdata->nmovedcontpermvars = 0;
2048 
2049  for (p = 0; p < propdata->nperms; ++p)
2050  {
2051  for (v = 0; v < propdata->npermvars; ++v)
2052  {
2053  if ( propdata->perms[p][v] != v )
2054  {
2055  ++propdata->nmovedpermvars;
2056 
2057  switch ( SCIPvarGetType(propdata->permvars[v]) )
2058  {
2059  case SCIP_VARTYPE_BINARY:
2060  ++propdata->nmovedbinpermvars;
2061  break;
2062  case SCIP_VARTYPE_INTEGER:
2063  ++propdata->nmovedintpermvars;
2064  break;
2065  case SCIP_VARTYPE_IMPLINT:
2066  ++propdata->nmovedimplintpermvars;
2067  break;
2069  ++propdata->nmovedcontpermvars;
2070  break;
2071  default:
2072  SCIPerrorMessage("Variable provided with unknown vartype\n");
2073  return SCIP_ERROR;
2074  }
2075  }
2076  }
2077  }
2078 
2079  return SCIP_OKAY;
2080 }
2081 
2082 
2083 /** returns whether any allowed symmetry handling method is effective for the problem instance */
2084 static
2086  SCIP* scip, /**< SCIP instance */
2087  SCIP_PROPDATA* propdata /**< propagator data */
2088  )
2089 {
2090  /* must always compute symmetry if it is enforced */
2091  if ( propdata->enforcecomputesymmetry )
2092  return TRUE;
2093 
2094  /* for dynamic symmetry handling or orbital reduction, branching must be possible */
2095  if ( propdata->usedynamicprop || ISORBITALREDUCTIONACTIVE(propdata->usesymmetry) )
2096  {
2097  /* @todo a proper test whether variables can be branched on or not */
2098  if ( SCIPgetNBinVars(scip) > 0 )
2099  return TRUE;
2100  if ( SCIPgetNIntVars(scip) > 0 )
2101  return TRUE;
2102  /* continuous variables can be branched on if nonlinear constraints exist */
2103  if ( ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2104  && SCIPconshdlrGetNActiveConss(propdata->conshdlr_nonlinear) > 0 )
2105  return TRUE;
2106  }
2107 
2108  /* for SST, matching leadervartypes */
2109  if ( ISSSTACTIVE(propdata->usesymmetry) )
2110  {
2111  if ( ISSSTBINACTIVE(propdata->sstleadervartype) && SCIPgetNBinVars(scip) > 0 ) /*lint !e641*/
2112  return TRUE;
2113  if ( ISSSTINTACTIVE(propdata->sstleadervartype) && SCIPgetNIntVars(scip) > 0 ) /*lint !e641*/
2114  return TRUE;
2115  if ( ISSSTIMPLINTACTIVE(propdata->sstleadervartype) && SCIPgetNImplVars(scip) > 0 ) /*lint !e641*/
2116  return TRUE;
2117  if ( ISSSTCONTACTIVE(propdata->sstleadervartype) && SCIPgetNContVars(scip) > 0 ) /*lint !e641*/
2118  return TRUE;
2119  }
2120 
2121  /* for static symmetry handling constraints, binary variables must be present */
2122  if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
2123  {
2124  if ( SCIPgetNBinVars(scip) > 0 )
2125  return TRUE;
2126  }
2127 
2128  /* if all tests above fail, then the symmetry handling methods cannot achieve anything */
2129  return FALSE;
2130 }
2131 
2132 /** determines symmetry */
2133 static
2135  SCIP* scip, /**< SCIP instance */
2136  SCIP_PROPDATA* propdata, /**< propagator data */
2137  SYM_SPEC symspecrequire, /**< symmetry specification for which we need to compute symmetries */
2138  SYM_SPEC symspecrequirefixed /**< symmetry specification of variables which must be fixed by symmetries */
2139  )
2140 { /*lint --e{641}*/
2141  SCIP_Bool successful;
2142  SCIP_Real symcodetime = 0.0;
2143  int maxgenerators;
2144  unsigned int type = 0;
2145  int nvars;
2146  int i;
2147 
2148  assert( scip != NULL );
2149  assert( propdata != NULL );
2150  assert( propdata->usesymmetry >= 0 );
2151 
2152  /* do not compute symmetry if reoptimization is enabled */
2153  if ( SCIPisReoptEnabled(scip) )
2154  return SCIP_OKAY;
2155 
2156  /* do not compute symmetry if Benders decomposition enabled */
2157  if ( SCIPgetNActiveBenders(scip) > 0 )
2158  return SCIP_OKAY;
2159 
2160  /* skip symmetry computation if no graph automorphism code was linked */
2161  if ( ! SYMcanComputeSymmetry() )
2162  {
2164  " Deactivated symmetry handling methods, since SCIP was built without symmetry detector (SYM=none).\n");
2165 
2166  return SCIP_OKAY;
2167  }
2168 
2169  /* do not compute symmetry if there are active pricers */
2170  if ( SCIPgetNActivePricers(scip) > 0 )
2171  return SCIP_OKAY;
2172 
2173  /* avoid trivial cases */
2174  nvars = SCIPgetNVars(scip);
2175  if ( nvars <= 0 )
2176  return SCIP_OKAY;
2177 
2178  /* do not compute symmetry if we cannot handle it */
2179  if ( !testSymmetryComputationRequired(scip, propdata) )
2180  return SCIP_OKAY;
2181 
2182  /* determine symmetry specification */
2183  if ( SCIPgetNBinVars(scip) > 0 )
2184  type |= (int) SYM_SPEC_BINARY;
2185  if ( SCIPgetNIntVars(scip) > 0 )
2186  type |= (int) SYM_SPEC_INTEGER;
2187  /* count implicit integer variables as real variables, since we cannot currently handle integral variables well */
2188  if ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2189  type |= (int) SYM_SPEC_REAL;
2190 
2191  /* skip symmetry computation if required variables are not present */
2192  if ( ! (type & symspecrequire) )
2193  {
2195  " (%.1fs) symmetry computation skipped: type (bin %c, int %c, cont %c) does not match requirements (bin %c, int %c, cont %c).\n",
2196  SCIPgetSolvingTime(scip),
2197  SCIPgetNBinVars(scip) > 0 ? '+' : '-',
2198  SCIPgetNIntVars(scip) > 0 ? '+' : '-',
2199  SCIPgetNContVars(scip) + SCIPgetNImplVars(scip) > 0 ? '+' : '-',
2200  (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2201  (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2202  (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2203 
2204  return SCIP_OKAY;
2205  }
2206 
2207  /* skip computation if symmetry has already been computed */
2208  if ( propdata->computedsymmetry )
2209  return SCIP_OKAY;
2210 
2211  assert( propdata->npermvars == 0 );
2212  assert( propdata->permvars == NULL );
2213  assert( propdata->nperms < 0 );
2214  assert( propdata->nmaxperms == 0 );
2215  assert( propdata->perms == NULL );
2216 
2217  /* output message */
2219  " (%.1fs) symmetry computation started: requiring (bin %c, int %c, cont %c), (fixed: bin %c, int %c, cont %c)\n",
2220  SCIPgetSolvingTime(scip),
2221  (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2222  (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2223  (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-',
2224  (symspecrequirefixed & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2225  (symspecrequirefixed & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2226  (symspecrequirefixed & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2227 
2228  /* output warning if we want to fix certain symmetry parts that we also want to compute */
2229  if ( symspecrequire & symspecrequirefixed )
2230  SCIPwarningMessage(scip, "Warning: some required symmetries must be fixed.\n");
2231 
2232  /* determine maximal number of generators depending on the number of variables */
2233  maxgenerators = propdata->maxgenerators;
2234  maxgenerators = MIN(maxgenerators, MAXGENNUMERATOR / nvars);
2235 
2236  /* actually compute (global) symmetry */
2237  SCIP_CALL( computeSymmetryGroup(scip, (SYM_SYMTYPE) propdata->symtype,
2238  propdata->compresssymmetries, propdata->compressthreshold,
2239  maxgenerators, symspecrequirefixed, propdata->checksymmetries, &propdata->permvars,
2240  &propdata->npermvars, &propdata->nbinpermvars, &propdata->permvardomaincenter,
2241  &propdata->perms, &propdata->nperms, &propdata->nmaxperms,
2242  &propdata->nmovedvars, &propdata->binvaraffected, &propdata->compressed,
2243  &propdata->log10groupsize, &symcodetime, &successful) );
2244 
2245  /* mark that we have computed the symmetry group */
2246  propdata->computedsymmetry = TRUE;
2247 
2248  /* store restart level */
2249  propdata->lastrestart = SCIPgetNRuns(scip);
2250 
2251  /* return if not successful */
2252  if ( ! successful )
2253  {
2254  assert( checkSymmetryDataFree(propdata) );
2255  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) could not compute symmetry\n", SCIPgetSolvingTime(scip));
2256 
2257  return SCIP_OKAY;
2258  }
2259 
2260  /* return if no symmetries found */
2261  if ( propdata->nperms == 0 )
2262  {
2263  assert( checkSymmetryDataFree(propdata) );
2264  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry present (symcode time: %.2f)\n", SCIPgetSolvingTime(scip), symcodetime);
2265 
2266  return SCIP_OKAY;
2267  }
2268  assert( propdata->nperms > 0 );
2269  assert( propdata->npermvars > 0 );
2270 
2271  /* display statistics */
2272  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) symmetry computation finished: %d generators found (max: ",
2273  SCIPgetSolvingTime(scip), propdata->nperms);
2274 
2275  /* display statistics: maximum number of generators */
2276  if ( maxgenerators == 0 )
2278  else
2279  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%d", maxgenerators);
2280 
2281  /* display statistics: log10 group size, number of affected vars*/
2282  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", log10 of symmetry group size: %.1f", propdata->log10groupsize);
2283 
2284  if ( propdata->displaynorbitvars )
2285  {
2286  if ( propdata->nmovedvars == -1 )
2287  {
2288  SCIP_CALL( SCIPdetermineNVarsAffectedSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2289  propdata->npermvars, &(propdata->nmovedvars)) );
2290  }
2291  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", number of affected variables: %d)\n", propdata->nmovedvars);
2292  }
2293  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ") (symcode time: %.2f)\n", symcodetime);
2294 
2295  /* capture all variables while they are in the permvars array */
2296  for (i = 0; i < propdata->npermvars; ++i)
2297  {
2298  SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[i]) );
2299  }
2300 
2301  return SCIP_OKAY;
2302 }
2303 
2304 
2305 /*
2306  * Functions for symmetry constraints
2307  */
2308 
2309 
2310 /** Checks whether given set of 2-cycle permutations forms an orbitope and if so, builds the variable index matrix.
2311  *
2312  * If @p activevars == NULL, then the function assumes all permutations of the component are active and therefore all
2313  * moved vars are considered.
2314  *
2315  * We need to keep track of the number of generated columns, because we might not be able to detect all orbitopes.
2316  * For example (1,2), (2,3), (3,4), (3,5) defines the symmetric group on {1,2,3,4,5}, but the generators we expect
2317  * in our construction need shape (1,2), (2,3), (3,4), (4,5).
2318  *
2319  * @pre @p orbitopevaridx has to be an initialized 2D array of size @p ntwocycles x @p nperms
2320  * @pre @p columnorder has to be an initialized array of size nperms
2321  * @pre @p nusedelems has to be an initialized array of size npermvars
2322  */
2323 static
2325  SCIP* scip, /**< SCIP instance */
2326  SCIP_VAR** permvars, /**< array of all permutation variables */
2327  int npermvars, /**< number of permutation variables */
2328  int** perms, /**< array of all permutations of the symmetry group */
2329  int* activeperms, /**< indices of the relevant permutations in perms */
2330  int ntwocycles, /**< number of 2-cycles in the permutations */
2331  int nactiveperms, /**< number of active permutations */
2332  int** orbitopevaridx, /**< pointer to store variable index matrix */
2333  int* columnorder, /**< pointer to store column order */
2334  int* nusedelems, /**< pointer to store how often each element was used */
2335  int* nusedcols, /**< pointer to store number of columns used in orbitope (or NULL) */
2336  SCIP_Shortbool* rowisbinary, /**< pointer to store which rows are binary (or NULL) */
2337  SCIP_Bool* isorbitope, /**< buffer to store result */
2338  SCIP_Shortbool* activevars /**< bitset to store whether a variable is active (or NULL) */
2339  )
2340 { /*lint --e{571}*/
2341  SCIP_Bool* usedperm;
2342  SCIP_Bool foundperm = FALSE;
2343  int nusedperms = 0;
2344  int nfilledcols;
2345  int coltoextend;
2346  int ntestedperms = 0;
2347  int row = 0;
2348  int j;
2349 
2350  assert( scip != NULL );
2351  assert( permvars != NULL );
2352  assert( perms != NULL );
2353  assert( activeperms != NULL );
2354  assert( orbitopevaridx != NULL );
2355  assert( columnorder != NULL );
2356  assert( nusedelems != NULL );
2357  assert( isorbitope != NULL );
2358  assert( nactiveperms > 0 );
2359  assert( ntwocycles > 0 );
2360  assert( npermvars > 0 );
2361  assert( activevars == NULL || (0 <= nactiveperms && nactiveperms < npermvars) );
2362 
2363  *isorbitope = TRUE;
2364  if ( nusedcols != NULL )
2365  *nusedcols = 0;
2366 
2367  /* whether a permutation was considered to contribute to orbitope */
2368  SCIP_CALL( SCIPallocClearBufferArray(scip, &usedperm, nactiveperms) );
2369 
2370  /* fill first two columns of orbitopevaridx matrix */
2371 
2372  /* look for the first active permutation which moves an active variable */
2373  while ( ! foundperm )
2374  {
2375  int permidx;
2376 
2377  assert( ntestedperms < nactiveperms );
2378 
2379  permidx = activeperms[ntestedperms];
2380 
2381  for (j = 0; j < npermvars; ++j)
2382  {
2383  if ( activevars != NULL && ! activevars[j] )
2384  continue;
2385 
2386  assert( activevars == NULL || activevars[perms[permidx][j]] );
2387 
2388  /* avoid adding the same 2-cycle twice */
2389  if ( perms[permidx][j] > j )
2390  {
2391  assert( SCIPvarIsBinary(permvars[j]) == SCIPvarIsBinary(permvars[perms[permidx][j]]) );
2392 
2393  if ( rowisbinary != NULL && SCIPvarIsBinary(permvars[j]) )
2394  rowisbinary[row] = TRUE;
2395 
2396  orbitopevaridx[row][0] = j;
2397  orbitopevaridx[row++][1] = perms[permidx][j];
2398  ++(nusedelems[j]);
2399  ++(nusedelems[perms[permidx][j]]);
2400 
2401  foundperm = TRUE;
2402  }
2403 
2404  if ( row == ntwocycles )
2405  break;
2406  }
2407 
2408  ++ntestedperms;
2409  }
2410 
2411  /* in the subgroup case it might happen that a generator has less than ntwocycles many 2-cyles */
2412  if ( row != ntwocycles )
2413  {
2414  *isorbitope = FALSE;
2415  SCIPfreeBufferArray(scip, &usedperm);
2416  return SCIP_OKAY;
2417  }
2418 
2419  usedperm[ntestedperms - 1] = TRUE;
2420  ++nusedperms;
2421  columnorder[0] = 0;
2422  columnorder[1] = 1;
2423  nfilledcols = 2;
2424 
2425  /* extend orbitopevaridx matrix to the left, i.e., iteratively find new permutations that
2426  * intersect the last added left column in each row in exactly one entry, starting with
2427  * column 0 */
2428  coltoextend = 0;
2429  for (j = ntestedperms; j < nactiveperms; ++j)
2430  { /* lint --e{850} */
2431  SCIP_Bool success = FALSE;
2432  SCIP_Bool infeasible = FALSE;
2433 
2434  if ( nusedperms == nactiveperms )
2435  break;
2436 
2437  if ( usedperm[j] )
2438  continue;
2439 
2440  SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
2441  perms[activeperms[j]], TRUE, &nusedelems, permvars, NULL, &success, &infeasible) );
2442 
2443  if ( infeasible )
2444  {
2445  *isorbitope = FALSE;
2446  break;
2447  }
2448  else if ( success )
2449  {
2450  usedperm[j] = TRUE;
2451  ++nusedperms;
2452  coltoextend = nfilledcols;
2453  columnorder[nfilledcols++] = -1; /* mark column to be filled from the left */
2454  j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
2455  }
2456  }
2457 
2458  if ( ! *isorbitope ) /*lint !e850*/
2459  {
2460  SCIPfreeBufferArray(scip, &usedperm);
2461  return SCIP_OKAY;
2462  }
2463 
2464  coltoextend = 1;
2465  for (j = ntestedperms; j < nactiveperms; ++j)
2466  { /*lint --e(850)*/
2467  SCIP_Bool success = FALSE;
2468  SCIP_Bool infeasible = FALSE;
2469 
2470  if ( nusedperms == nactiveperms )
2471  break;
2472 
2473  if ( usedperm[j] )
2474  continue;
2475 
2476  SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
2477  perms[activeperms[j]], FALSE, &nusedelems, permvars, NULL, &success, &infeasible) );
2478 
2479  if ( infeasible )
2480  {
2481  *isorbitope = FALSE;
2482  break;
2483  }
2484  else if ( success )
2485  {
2486  usedperm[j] = TRUE;
2487  ++nusedperms;
2488  coltoextend = nfilledcols;
2489  columnorder[nfilledcols] = 1; /* mark column to be filled from the right */
2490  ++nfilledcols;
2491  j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
2492  }
2493  }
2494 
2495  if ( activevars == NULL && nusedperms < nactiveperms ) /*lint !e850*/
2496  *isorbitope = FALSE;
2497 
2498  if ( nusedcols != NULL )
2499  *nusedcols = nfilledcols;
2500  assert( ! *isorbitope || activevars == NULL || nusedperms < nfilledcols );
2501 
2502  SCIPfreeBufferArray(scip, &usedperm);
2503 
2504  return SCIP_OKAY;
2505 }
2506 
2507 /** choose an order in which the generators should be added for subgroup detection */
2508 static
2510  SCIP* scip, /**< SCIP instance */
2511  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2512  int compidx, /**< index of component */
2513  int** genorder, /**< (initialized) buffer to store the resulting order of generator */
2514  int* ntwocycleperms /**< pointer to store the number of 2-cycle permutations in component compidx */
2515  )
2516 {
2517  int** perms;
2518  int* components;
2519  int* componentbegins;
2520  int* ntwocycles;
2521  int npermvars;
2522  int npermsincomp;
2523  int i;
2524 
2525  assert( scip != NULL );
2526  assert( propdata != NULL );
2527  assert( compidx >= 0 );
2528  assert( compidx < propdata->ncomponents );
2529  assert( genorder != NULL );
2530  assert( *genorder != NULL );
2531  assert( ntwocycleperms != NULL );
2532  assert( propdata->computedsymmetry );
2533  assert( propdata->nperms > 0 );
2534  assert( propdata->perms != NULL );
2535  assert( propdata->npermvars > 0 );
2536  assert( propdata->ncomponents > 0 );
2537  assert( propdata->components != NULL );
2538  assert( propdata->componentbegins != NULL );
2539 
2540  perms = propdata->perms;
2541  npermvars = propdata->npermvars;
2542  components = propdata->components;
2543  componentbegins = propdata->componentbegins;
2544  npermsincomp = componentbegins[compidx + 1] - componentbegins[compidx];
2545  *ntwocycleperms = npermsincomp;
2546 
2547  SCIP_CALL( SCIPallocBufferArray(scip, &ntwocycles, npermsincomp) );
2548 
2549  for (i = 0; i < npermsincomp; ++i)
2550  {
2551  int* perm;
2552  int nbincycles;
2553 
2554  perm = perms[components[componentbegins[compidx] + i]];
2555 
2556  SCIP_CALL( SCIPisInvolutionPerm(perm, propdata->permvars, npermvars, &(ntwocycles[i]), &nbincycles, FALSE) );
2557 
2558  /* we skip permutations which do not purely consist of 2-cycles */
2559  if ( ntwocycles[i] == 0 )
2560  {
2561  /* we change the number of two cycles for this perm so that it will be sorted to the end */
2562  if ( propdata->preferlessrows )
2563  ntwocycles[i] = npermvars;
2564  else
2565  ntwocycles[i] = 0;
2566  --(*ntwocycleperms);
2567  }
2568  else if ( ! propdata->preferlessrows )
2569  ntwocycles[i] = - ntwocycles[i];
2570  }
2571 
2572  SCIPsortIntInt(ntwocycles, *genorder, npermsincomp);
2573 
2574  SCIPfreeBufferArray(scip, &ntwocycles);
2575 
2576  return SCIP_OKAY;
2577 }
2578 
2579 
2580 /** builds the graph for symmetric subgroup detection from the given permutation of generators
2581  *
2582  * After execution, @p graphcomponents contains all permvars sorted by their color and component,
2583  * @p graphcompbegins points to the indices where new components in @p graphcomponents start and
2584  * @p compcolorbegins points to the indices where new colors in @p graphcompbegins start.
2585 */
2586 static
2588  SCIP* scip, /**< SCIP instance */
2589  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2590  int* genorder, /**< order in which the generators should be considered */
2591  int ntwocycleperms, /**< number of 2-cycle permutations in this component */
2592  int compidx, /**< index of the component */
2593  int** graphcomponents, /**< buffer to store the components of the graph (ordered var indices) */
2594  int** graphcompbegins, /**< buffer to store the indices of each new graph component */
2595  int** compcolorbegins, /**< buffer to store at which indices a new color begins */
2596  int* ngraphcomponents, /**< pointer to store the number of graph components */
2597  int* ncompcolors, /**< pointer to store the number of different colors */
2598  int** usedperms, /**< buffer to store the indices of permutations that were used */
2599  int* nusedperms, /**< pointer to store the number of used permutations in the graph */
2600  int usedpermssize, /**< initial size of usedperms */
2601  SCIP_Shortbool* permused /**< initialized buffer to store which permutations have been used
2602  * (identified by index in component) */
2603  )
2604 {
2605  SCIP_DISJOINTSET* vartocomponent;
2606  SCIP_DISJOINTSET* comptocolor;
2607  int** perms;
2608  int* components;
2609  int* componentbegins;
2610  int* componentslastperm;
2611  SYM_SORTGRAPHCOMPVARS graphcompvartype;
2612  int npermvars;
2613  int nextcolor;
2614  int nextcomp;
2615  int j;
2616  int k;
2617 
2618  assert( scip != NULL );
2619  assert( propdata != NULL );
2620  assert( graphcomponents != NULL );
2621  assert( graphcompbegins != NULL );
2622  assert( compcolorbegins != NULL );
2623  assert( ngraphcomponents != NULL );
2624  assert( ncompcolors != NULL );
2625  assert( genorder != NULL );
2626  assert( usedperms != NULL );
2627  assert( nusedperms != NULL );
2628  assert( usedpermssize > 0 );
2629  assert( permused != NULL );
2630  assert( ntwocycleperms >= 0 );
2631  assert( compidx >= 0 );
2632  assert( compidx < propdata->ncomponents );
2633  assert( propdata->computedsymmetry );
2634  assert( propdata->nperms > 0 );
2635  assert( propdata->perms != NULL );
2636  assert( propdata->npermvars > 0 );
2637  assert( propdata->ncomponents > 0 );
2638  assert( propdata->components != NULL );
2639  assert( propdata->componentbegins != NULL );
2640  assert( ! propdata->componentblocked[compidx] );
2641 
2642  perms = propdata->perms;
2643  npermvars = propdata->npermvars;
2644  components = propdata->components;
2645  componentbegins = propdata->componentbegins;
2646  *nusedperms = 0;
2647 
2648  assert( ntwocycleperms <= componentbegins[compidx + 1] - componentbegins[compidx] );
2649 
2650  SCIP_CALL( SCIPcreateDisjointset(scip, &vartocomponent, npermvars) );
2651  SCIP_CALL( SCIPcreateDisjointset(scip, &comptocolor, npermvars) );
2652  SCIP_CALL( SCIPallocBufferArray( scip, &componentslastperm, npermvars) );
2653 
2654  for (k = 0; k < npermvars; ++k)
2655  componentslastperm[k] = -1;
2656 
2657  for (j = 0; j < ntwocycleperms; ++j)
2658  {
2659  int* perm;
2660  int firstcolor = -1;
2661 
2662  /* use given order of generators */
2663  perm = perms[components[componentbegins[compidx] + genorder[j]]];
2664  assert( perm != NULL );
2665 
2666  /* iteratively handle each swap of perm until an invalid one is found or all edges have been added */
2667  for (k = 0; k < npermvars; ++k)
2668  {
2669  int comp1;
2670  int comp2;
2671  int color1;
2672  int color2;
2673  int img;
2674 
2675  img = perm[k];
2676  assert( perm[img] == k );
2677 
2678  if ( img <= k )
2679  continue;
2680 
2681  comp1 = SCIPdisjointsetFind(vartocomponent, k);
2682  comp2 = SCIPdisjointsetFind(vartocomponent, img);
2683 
2684  if ( comp1 == comp2 )
2685  {
2686  /* another permutation has already merged these variables into one component; store its color */
2687  if ( firstcolor < 0 )
2688  {
2689  assert( SCIPdisjointsetFind(comptocolor, comp1) == SCIPdisjointsetFind(comptocolor, comp2) );
2690  firstcolor = SCIPdisjointsetFind(comptocolor, comp1);
2691  }
2692  componentslastperm[comp1] = j;
2693  continue;
2694  }
2695 
2696  /* if it is the second time that the component is used for this generator,
2697  * it is not guaranteed that the group acts like the symmetric group, so skip it
2698  */
2699  if ( componentslastperm[comp1] == j || componentslastperm[comp2] == j )
2700  break;
2701 
2702  color1 = SCIPdisjointsetFind(comptocolor, comp1);
2703  color2 = SCIPdisjointsetFind(comptocolor, comp2);
2704 
2705  /* a generator is not allowed to connect two components of the same color, since they depend on each other */
2706  if ( color1 == color2 )
2707  break;
2708 
2709  componentslastperm[comp1] = j;
2710  componentslastperm[comp2] = j;
2711 
2712  if ( firstcolor < 0 )
2713  firstcolor = color1;
2714  }
2715 
2716  /* if the generator is invalid, delete the newly added edges, go to next generator */
2717  if ( k < npermvars )
2718  continue;
2719 
2720  /* if the generator only acts on already existing components, we don't have to store it */
2721  if ( firstcolor == -1 )
2722  continue;
2723 
2724  /* check whether we need to resize */
2725  if ( *nusedperms >= usedpermssize )
2726  {
2727  int newsize = SCIPcalcMemGrowSize(scip, (*nusedperms) + 1);
2728  assert( newsize > usedpermssize );
2729 
2730  SCIP_CALL( SCIPreallocBufferArray(scip, usedperms, newsize) );
2731 
2732  usedpermssize = newsize;
2733  }
2734 
2735  (*usedperms)[*nusedperms] = components[componentbegins[compidx] + genorder[j]];
2736  ++(*nusedperms);
2737  permused[genorder[j]] = TRUE;
2738 
2739  /* if the generator can be added, update the datastructures for graph components and colors */
2740  for (k = 0; k < npermvars; ++k)
2741  {
2742  int comp1;
2743  int comp2;
2744  int color1;
2745  int color2;
2746  int img;
2747 
2748  img = perm[k];
2749  assert( perm[img] == k );
2750 
2751  if ( img <= k )
2752  continue;
2753 
2754  comp1 = SCIPdisjointsetFind(vartocomponent, k);
2755  comp2 = SCIPdisjointsetFind(vartocomponent, img);
2756 
2757  /* components and colors don't have to be updated if the components are the same */
2758  if ( comp1 == comp2 )
2759  continue;
2760 
2761  color1 = SCIPdisjointsetFind(comptocolor, comp1);
2762  color2 = SCIPdisjointsetFind(comptocolor, comp2);
2763 
2764  if ( color1 != color2 )
2765  {
2766  SCIPdisjointsetUnion(comptocolor, firstcolor, color1, TRUE);
2767  SCIPdisjointsetUnion(comptocolor, firstcolor, color2, TRUE);
2768  }
2769 
2770  SCIPdisjointsetUnion(vartocomponent, comp1, comp2, FALSE);
2771 
2772  assert( SCIPdisjointsetFind(vartocomponent, k) == SCIPdisjointsetFind(vartocomponent, img) );
2773  assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, k)) == firstcolor );
2774  assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, img)) == firstcolor );
2775  }
2776  }
2777 
2778  SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcomponents, npermvars) );
2779  SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.components), npermvars) );
2780  SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.colors), npermvars) );
2781 
2782  /*
2783  * At this point, we have built the colored graph. Now we transform the information in the
2784  * disjoint sets to the arrays graphcomponents, graphcompbegins, and compcolorbegins (see above).
2785  */
2786 
2787  /* build the struct graphcompvartype which is used to sort the graphcomponents array */
2788  for (j = 0; j < npermvars; ++j)
2789  {
2790  int comp;
2791 
2792  comp = SCIPdisjointsetFind(vartocomponent, j);
2793 
2794  graphcompvartype.components[j] = comp;
2795  graphcompvartype.colors[j] = SCIPdisjointsetFind(comptocolor, comp);
2796 
2797  (*graphcomponents)[j] = j;
2798  }
2799 
2800  /* sort graphcomponents first by color, then by component */
2801  SCIPsort(*graphcomponents, SYMsortGraphCompVars, (void*) &graphcompvartype, npermvars);
2802 
2803  *ngraphcomponents = SCIPdisjointsetGetComponentCount(vartocomponent);
2804  *ncompcolors = SCIPdisjointsetGetComponentCount(comptocolor);
2805  SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcompbegins, (*ngraphcomponents) + 1) );
2806  SCIP_CALL( SCIPallocBlockMemoryArray(scip, compcolorbegins, (*ncompcolors) + 1) );
2807 
2808  nextcolor = 1;
2809  nextcomp = 1;
2810  (*graphcompbegins)[0] = 0;
2811  (*compcolorbegins)[0] = 0;
2812 
2813  /* find the starting indices of new components and new colors */
2814  for (j = 1; j < npermvars; ++j)
2815  {
2816  int idx1;
2817  int idx2;
2818 
2819  idx1 = (*graphcomponents)[j];
2820  idx2 = (*graphcomponents)[j-1];
2821 
2822  assert( graphcompvartype.colors[idx1] >= graphcompvartype.colors[idx2] );
2823 
2824  if ( graphcompvartype.components[idx1] != graphcompvartype.components[idx2] )
2825  {
2826  (*graphcompbegins)[nextcomp] = j;
2827 
2828  if ( graphcompvartype.colors[idx1] > graphcompvartype.colors[idx2] )
2829  {
2830  (*compcolorbegins)[nextcolor] = nextcomp;
2831  ++nextcolor;
2832  }
2833 
2834  ++nextcomp;
2835  }
2836  }
2837  assert( nextcomp == *ngraphcomponents );
2838  assert( nextcolor == *ncompcolors );
2839 
2840  (*compcolorbegins)[nextcolor] = *ngraphcomponents;
2841  (*graphcompbegins)[nextcomp] = npermvars;
2842 
2843  SCIPfreeBufferArray(scip, &(graphcompvartype.colors));
2844  SCIPfreeBufferArray(scip, &(graphcompvartype.components));
2845  SCIPfreeBufferArray(scip, &componentslastperm);
2846  SCIPfreeDisjointset(scip, &comptocolor);
2847  SCIPfreeDisjointset(scip, &vartocomponent);
2848 
2849  return SCIP_OKAY;
2850 }
2851 
2852 /** adds an orbitope constraint for a suitable color of the subgroup graph */
2853 static
2855  SCIP* scip, /**< SCIP instance */
2856  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2857  int* usedperms, /**< array of the permutations that build the orbitope */
2858  int nusedperms, /**< number of permutations in usedperms */
2859  int* compcolorbegins, /**< array indicating where a new graphcolor begins */
2860  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
2861  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
2862  int graphcoloridx, /**< index of the graph color */
2863  int nrows, /**< number of rows in the orbitope */
2864  int ncols, /**< number of columns in the orbitope */
2865  int* firstvaridx, /**< buffer to store the index of the largest variable (or NULL) */
2866  int* compidxfirstrow, /**< buffer to store the comp index for the first row (or NULL) */
2867  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
2868  int* nvarslexorder, /**< number of variables in lexicographic order */
2869  int* maxnvarslexorder, /**< maximum number of variables in lexicographic order */
2870  SCIP_Bool mayinteract, /**< whether orbitope's symmetries might interact with other symmetries */
2871  SCIP_Bool* success /**< whether the orbitope could be added */
2872  )
2873 { /*lint --e{571}*/
2874  char name[SCIP_MAXSTRLEN];
2875  SCIP_VAR*** orbitopevarmatrix;
2876  SCIP_Shortbool* activevars;
2877  int** orbitopevaridx;
2878  int* columnorder;
2879  int* nusedelems;
2880  SCIP_CONS* cons;
2881  SCIP_Bool isorbitope;
2882  SCIP_Bool infeasible = FALSE;
2883 #ifndef NDEBUG
2884  int nactivevars = 0;
2885 #endif
2886  int ngencols = 0;
2887  int k;
2888 
2889  assert( scip != NULL );
2890  assert( propdata != NULL );
2891  assert( usedperms != NULL );
2892  assert( compcolorbegins != NULL );
2893  assert( graphcompbegins != NULL );
2894  assert( graphcomponents != NULL );
2895  assert( nusedperms > 0 );
2896  assert( nrows > 0 );
2897  assert( ncols > 0 );
2898  assert( lexorder != NULL );
2899  assert( nvarslexorder != NULL );
2900  assert( maxnvarslexorder != NULL );
2901 
2902  *success = FALSE;
2903 
2904  /* create hashset to mark variables */
2905  SCIP_CALL( SCIPallocClearBufferArray(scip, &activevars, propdata->npermvars) );
2906 
2907  /* orbitope matrix for indices of variables in permvars array */
2908  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, nrows) );
2909  for (k = 0; k < nrows; ++k)
2910  {
2911  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[k], ncols) ); /*lint !e866*/
2912  }
2913 
2914  /* order of columns of orbitopevaridx */
2915  SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, ncols) );
2916  for (k = 0; k < ncols; ++k)
2917  columnorder[k] = ncols + 1;
2918 
2919  /* count how often an element was used in the potential orbitope */
2920  SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, propdata->npermvars) );
2921 
2922  /* mark variables in this subgroup orbitope */
2923  for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1]; ++k)
2924  {
2925  SCIP_VAR* firstvar;
2926  int compstart;
2927  int l;
2928 
2929  compstart = graphcompbegins[k];
2930  firstvar = propdata->permvars[graphcomponents[compstart]];
2931 
2932  if ( ! SCIPvarIsBinary(firstvar) )
2933  continue;
2934 
2935  for (l = 0; l < ncols; ++l)
2936  {
2937  int varidx;
2938 
2939  varidx = graphcomponents[compstart + l];
2940  assert( ! activevars[varidx] );
2941 
2942  activevars[varidx] = TRUE;
2943 #ifndef NDEBUG
2944  ++nactivevars;
2945 #endif
2946  }
2947  }
2948  assert( nactivevars == nrows * ncols );
2949 
2950  /* build the variable index matrix for the orbitope
2951  *
2952  * It is possible that we find an orbitope, but not using all possible columns. For example
2953  * (1,2), (2,3), (3,4), (3,5) defines the symmetric group on {1,2,3,4,5}, but the generators
2954  * we expect in our construction need shape (1,2), (2,3), (3,4), (4,5). For this reason,
2955  * we need to store how many columns have been generated.
2956  *
2957  * @todo ensure compatibility with more general generators
2958  */
2959  SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, propdata->permvars, propdata->npermvars,
2960  propdata->perms, usedperms, nrows, nusedperms, orbitopevaridx, columnorder,
2961  nusedelems, &ngencols, NULL, &isorbitope, activevars) );
2962 
2963  /* it might happen that we cannot detect the orbitope if it is generated by permutations with different
2964  * number of 2-cycles.
2965  */
2966  if ( ! isorbitope )
2967  {
2968  SCIPfreeBufferArray(scip, &nusedelems);
2969  SCIPfreeBufferArray(scip, &columnorder);
2970  for (k = nrows - 1; k >= 0; --k)
2971  {
2972  SCIPfreeBufferArray(scip, &orbitopevaridx[k]);
2973  }
2974  SCIPfreeBufferArray(scip, &orbitopevaridx);
2975  SCIPfreeBufferArray(scip, &activevars);
2976 
2977  return SCIP_OKAY;
2978  }
2979 
2980  /* There are three possibilities for the structure of columnorder:
2981  * 1) [0, 1, -1, -1, ..., -1]
2982  * 2) [0, 1, 1, 1, ..., 1]
2983  * 3) [0, 1, -1, -1, ...., -1, 1, 1, ..., 1]
2984  *
2985  * The '1'-columns will be added to the matrix first and in the last 2
2986  * cases the method starts from the right. So to store the variable index
2987  * that will be in the upper-left corner, we need either the entryin the
2988  * second column (case 1) or the entry in the last column (cases 2 and 3).
2989  */
2990  if ( firstvaridx != NULL )
2991  {
2992  if ( columnorder[ngencols-1] > -1 )
2993  *firstvaridx = orbitopevaridx[0][ngencols-1];
2994  else
2995  *firstvaridx = orbitopevaridx[0][1];
2996  }
2997 
2998  /* find corresponding graphcomponent of first variable (needed for weak sbcs) */
2999  if ( compidxfirstrow != NULL && firstvaridx != NULL )
3000  {
3001  *compidxfirstrow = -1;
3002 
3003  for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1] && (*compidxfirstrow) < 0; ++k)
3004  {
3005  SCIP_VAR* firstvar;
3006  int compstart;
3007  int l;
3008 
3009  compstart = graphcompbegins[k];
3010  firstvar = propdata->permvars[graphcomponents[compstart]];
3011 
3012  if ( ! SCIPvarIsBinary(firstvar) )
3013  continue;
3014 
3015  /* iterate over all columns (elements in orbit), because we cannot see from ngencols which columns
3016  * have been left out
3017  */
3018  for (l = 0; l < ncols; ++l)
3019  {
3020  if ( graphcomponents[compstart + l] == *firstvaridx )
3021  {
3022  *compidxfirstrow = k;
3023  break;
3024  }
3025  }
3026  }
3027  assert( *compidxfirstrow > -1 );
3028  }
3029 
3030  /* prepare orbitope variable matrix */
3031  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix, nrows) );
3032  for (k = 0; k < nrows; ++k)
3033  {
3034  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix[k], ngencols) );
3035  }
3036 
3037  /* build the matrix containing the actual variables of the orbitope */
3038  SCIP_CALL( SCIPgenerateOrbitopeVarsMatrix(scip, &orbitopevarmatrix, nrows, ngencols,
3039  propdata->permvars, propdata->npermvars, orbitopevaridx, columnorder,
3040  nusedelems, NULL, &infeasible, TRUE, lexorder, nvarslexorder, maxnvarslexorder) );
3041 
3042  assert( ! infeasible );
3043  assert( firstvaridx == NULL || propdata->permvars[*firstvaridx] == orbitopevarmatrix[0][0] );
3044 
3045  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "suborbitope_%d_%d", graphcoloridx, propdata->norbitopes);
3046 
3047  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopevarmatrix,
3048  SCIP_ORBITOPETYPE_FULL, nrows, ngencols, FALSE, mayinteract, FALSE, FALSE, propdata->conssaddlp,
3049  TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3050 
3051  SCIP_CALL( SCIPaddCons(scip, cons) );
3052  *success = TRUE;
3053 
3054  /* do not release constraint here - will be done later */
3055  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
3056  &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
3057  propdata->genorbconss[propdata->ngenorbconss++] = cons;
3058  ++propdata->norbitopes;
3059 
3060  for (k = nrows - 1; k >= 0; --k)
3061  SCIPfreeBufferArray(scip, &orbitopevarmatrix[k]);
3062  SCIPfreeBufferArray(scip, &orbitopevarmatrix);
3063  SCIPfreeBufferArray(scip, &nusedelems);
3064  SCIPfreeBufferArray(scip, &columnorder);
3065  for (k = nrows - 1; k >= 0; --k)
3066  SCIPfreeBufferArray(scip, &orbitopevaridx[k]);
3067  SCIPfreeBufferArray(scip, &orbitopevaridx);
3068  SCIPfreeBufferArray(scip, &activevars);
3069 
3070  return SCIP_OKAY;
3071 }
3072 
3073 /** adds strong SBCs for a suitable color of the subgroup graph */
3074 static
3076  SCIP* scip, /**< SCIP instance */
3077  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3078  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3079  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3080  int graphcompidx, /**< index of the graph component */
3081  SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3082  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3083  int* nvarsorder, /**< number of variables in lexicographic order */
3084  int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3085  )
3086 {
3087  int k;
3088 
3089  assert( scip != NULL );
3090  assert( propdata != NULL );
3091  assert( graphcompbegins != NULL );
3092  assert( graphcomponents != NULL );
3093  assert( graphcompidx >= 0 );
3094  assert( ! storelexorder || lexorder != NULL );
3095  assert( ! storelexorder || nvarsorder != NULL );
3096  assert( ! storelexorder || maxnvarsorder != NULL );
3097 
3098  /* possibly store lexicographic order defined by strong SBCs */
3099  if ( storelexorder )
3100  {
3101  if ( *maxnvarsorder == 0 )
3102  {
3103  *maxnvarsorder = graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx + 1];
3104  *nvarsorder = 0;
3105 
3106  SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
3107  }
3108  else
3109  {
3110  assert( *nvarsorder == *maxnvarsorder );
3111 
3112  *maxnvarsorder += graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx + 1];
3113 
3114  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
3115  }
3116 
3117  (*lexorder)[*nvarsorder++] = graphcomponents[graphcompbegins[graphcompidx]];
3118  }
3119 
3120  /* add strong SBCs (lex-max order) for chosen graph component */
3121  for (k = graphcompbegins[graphcompidx]+1; k < graphcompbegins[graphcompidx+1]; ++k)
3122  {
3123  char name[SCIP_MAXSTRLEN];
3124  SCIP_CONS* cons;
3125  SCIP_VAR* vars[2];
3126  SCIP_Real vals[2] = {1, -1};
3127 
3128  vars[0] = propdata->permvars[graphcomponents[k-1]];
3129  vars[1] = propdata->permvars[graphcomponents[k]];
3130 
3131  if ( storelexorder )
3132  (*lexorder)[*nvarsorder++] = graphcomponents[k];
3133 
3134  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "strong_sbcs_%s_%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3135 
3136  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3137  SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3138  TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3139 
3140  SCIP_CALL( SCIPaddCons(scip, cons) );
3141 
3142 #ifdef SCIP_MORE_DEBUG
3143  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3144  SCIPinfoMessage(scip, NULL, "\n");
3145 #endif
3146 
3147  /* check whether we need to resize */
3148  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genlinconss,
3149  &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
3150  propdata->genlinconss[propdata->ngenlinconss] = cons;
3151  ++propdata->ngenlinconss;
3152  }
3153 
3154  return SCIP_OKAY;
3155 }
3156 
3157 /** adds weak SBCs for a suitable color of the subgroup graph */
3158 static
3160  SCIP* scip, /**< SCIP instance */
3161  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3162  int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3163  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3164  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3165  int ncompcolors, /**< number of colors in the graph */
3166  int* chosencomppercolor, /**< array indicating which comp was handled per color */
3167  int* firstvaridxpercolor,/**< array indicating the largest variable per color */
3168  int symgrpcompidx, /**< index of the component of the symmetry group */
3169  int* naddedconss, /**< buffer to store the number of added constraints */
3170  SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3171  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3172  int* nvarsorder, /**< number of variables in lexicographic order */
3173  int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3174  )
3175 { /*lint --e{571}*/
3176  SCIP_HASHMAP* varsinlexorder;
3177  SCIP_Shortbool* usedvars;
3178  SCIP_VAR* vars[2];
3179  SCIP_Real vals[2] = {1, -1};
3180  SCIP_Shortbool* varfound;
3181  int* orbit[2];
3182  int orbitsize[2] = {1, 1};
3183  int activeorb = 0;
3184  int chosencolor = -1;
3185  int j;
3186  int k;
3187 
3188  assert( scip != NULL );
3189  assert( propdata != NULL );
3190  assert( compcolorbegins != NULL );
3191  assert( graphcompbegins != NULL );
3192  assert( graphcomponents != NULL );
3193  assert( firstvaridxpercolor != NULL );
3194  assert( chosencomppercolor != NULL );
3195  assert( naddedconss != NULL );
3196  assert( symgrpcompidx >= 0 );
3197  assert( symgrpcompidx < propdata->ncomponents );
3198  assert( ! storelexorder || lexorder != NULL );
3199  assert( ! storelexorder || nvarsorder != NULL );
3200  assert( ! storelexorder || maxnvarsorder != NULL );
3201 
3202  *naddedconss = 0;
3203 
3204  SCIP_CALL( SCIPallocCleanBufferArray(scip, &usedvars, propdata->npermvars) );
3205  SCIP_CALL( SCIPallocClearBufferArray(scip, &varfound, propdata->npermvars) );
3206  SCIP_CALL( SCIPallocBufferArray(scip, &orbit[0], propdata->npermvars) );
3207  SCIP_CALL( SCIPallocBufferArray(scip, &orbit[1], propdata->npermvars) );
3208 
3209  /* Store the entries in lexorder in a hashmap, for fast lookups. */
3210  if ( lexorder == NULL || *lexorder == NULL )
3211  {
3212  /* Lexorder does not exist, so do not create hashmap. */
3213  varsinlexorder = NULL;
3214  }
3215  else
3216  {
3217  assert( *maxnvarsorder >= 0 );
3218  assert( *nvarsorder >= 0 );
3219 
3220  SCIP_CALL( SCIPhashmapCreate(&varsinlexorder, SCIPblkmem(scip), *maxnvarsorder) );
3221 
3222  for (k = 0; k < *nvarsorder; ++k)
3223  {
3224  /* add element from lexorder to hashmap.
3225  * Use insert, as duplicate entries in lexorder is not permitted. */
3226  assert((*lexorder)[k] >= 0);
3227  assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) (*lexorder)[k]) ); /* Use int as pointer */
3228  SCIP_CALL( SCIPhashmapInsertInt(varsinlexorder, (void*) (size_t) (*lexorder)[k], k) );
3229  }
3230  }
3231 
3232  /* We will store the newest and the largest orbit and activeorb will be used to mark at which entry of the array
3233  * orbit the newly computed one will be stored. */
3234  if ( ncompcolors > 0 )
3235  {
3236  SCIP_CALL( ensureSymmetryPermstransComputed(scip, propdata) );
3237  }
3238  for (j = 0; j < ncompcolors; ++j)
3239  {
3240  int graphcomp;
3241  int graphcompsize;
3242  int varidx;
3243 
3244  /* skip color for which we did not add anything */
3245  if ( chosencomppercolor[j] < 0 )
3246  continue;
3247 
3248  assert( firstvaridxpercolor[j] >= 0 );
3249 
3250  graphcomp = chosencomppercolor[j];
3251  graphcompsize = graphcompbegins[graphcomp+1] - graphcompbegins[graphcomp];
3252  varidx = firstvaridxpercolor[j];
3253  assert(varidx >= 0);
3254 
3255  /* if the first variable was already contained in another orbit or if there are no variables left anyway, skip the
3256  * component */
3257  if ( varfound[varidx] || graphcompsize == propdata->npermvars )
3258  continue;
3259 
3260  /* If varidx is in lexorder, then it must be the first entry of lexorder. */
3261  if ( varsinlexorder != NULL
3262  && SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx)
3263  && lexorder != NULL && *lexorder != NULL && *maxnvarsorder > 0 && *nvarsorder > 0
3264  && (*lexorder)[0] != varidx )
3265  continue;
3266 
3267  /* mark all variables that have been used in strong SBCs */
3268  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
3269  {
3270  assert( 0 <= graphcomponents[k] && graphcomponents[k] < propdata->npermvars );
3271 
3272  usedvars[graphcomponents[k]] = TRUE;
3273  }
3274 
3275  SCIP_CALL( SCIPcomputeOrbitVar(scip, propdata->npermvars, propdata->perms,
3276  propdata->permstrans, propdata->components, propdata->componentbegins,
3277  usedvars, varfound, varidx, symgrpcompidx,
3278  orbit[activeorb], &orbitsize[activeorb]) );
3279 
3280  assert( orbit[activeorb][0] == varidx );
3281 
3282  if ( orbitsize[activeorb] > orbitsize[1 - activeorb] ) /*lint !e514*/
3283  {
3284  /* if the new orbit is larger then the old largest one, flip activeorb */
3285  activeorb = 1 - activeorb;
3286  chosencolor = j;
3287  }
3288 
3289  /* reset array */
3290  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
3291  usedvars[graphcomponents[k]] = FALSE;
3292  }
3293 
3294  /* check if we have found at least one non-empty orbit */
3295  if ( chosencolor > -1 )
3296  {
3297  /* flip activeorb again to avoid confusion, it is then at the largest orbit */
3298  activeorb = 1 - activeorb;
3299 
3300  assert( orbit[activeorb][0] == firstvaridxpercolor[chosencolor] );
3301  vars[0] = propdata->permvars[orbit[activeorb][0]];
3302 
3303  assert( chosencolor > -1 );
3304  SCIPdebugMsg(scip, " adding %d weak sbcs for enclosing orbit of color %d.\n", orbitsize[activeorb]-1, chosencolor);
3305 
3306  *naddedconss = orbitsize[activeorb] - 1;
3307 
3308  /* add weak SBCs for rest of enclosing orbit */
3309  for (j = 1; j < orbitsize[activeorb]; ++j)
3310  {
3311  SCIP_CONS* cons;
3312  char name[SCIP_MAXSTRLEN];
3313 
3314  vars[1] = propdata->permvars[orbit[activeorb][j]];
3315 
3316  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "weak_sbcs_%d_%s_%s", symgrpcompidx, SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3317 
3318  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3319  SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3320  TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3321 
3322  SCIP_CALL( SCIPaddCons(scip, cons) );
3323 
3324 #ifdef SCIP_MORE_DEBUG
3325  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3326  SCIPinfoMessage(scip, NULL, "\n");
3327 #endif
3328 
3329  /* check whether we need to resize */
3330  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genlinconss,
3331  &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
3332  propdata->genlinconss[propdata->ngenlinconss] = cons;
3333  ++propdata->ngenlinconss;
3334  }
3335 
3336  /* possibly store lexicographic order defined by weak SBCs */
3337  if ( storelexorder )
3338  {
3339  int varidx;
3340 
3341  varidx = orbit[activeorb][0];
3342  assert(varidx >= 0);
3343 
3344  if ( *maxnvarsorder == 0 )
3345  {
3346  *maxnvarsorder = 1;
3347  *nvarsorder = 0;
3348 
3349  SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
3350  (*lexorder)[(*nvarsorder)++] = varidx;
3351  }
3352  else
3353  {
3354  assert( *nvarsorder == *maxnvarsorder );
3355  assert( varsinlexorder != NULL );
3356  assert( lexorder != NULL );
3357  assert( *lexorder != NULL );
3358 
3359  /* the leader of the weak inequalities has to be the first element in the lexicographic order */
3360  if ( varidx == (*lexorder)[0] )
3361  {
3362  /* lexorder is already ok!! */
3363  assert( SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
3364  }
3365  else
3366  {
3367  /* Then varidx must not be in the lexorder,
3368  * We must add it at the front of the array, and maintain the current order. */
3369  assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
3370 
3371  ++(*maxnvarsorder);
3372  ++(*nvarsorder);
3373 
3374  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
3375 
3376  /* Shift array by one position to the right */
3377  for (k = *maxnvarsorder - 1; k >= 1; --k)
3378  (*lexorder)[k] = (*lexorder)[k - 1];
3379 
3380  (*lexorder)[0] = varidx;
3381  }
3382  }
3383  }
3384  }
3385  else
3386  SCIPdebugMsg(scip, " no further weak sbcs are valid\n");
3387 
3388  SCIPfreeBufferArray(scip, &orbit[1]);
3389  SCIPfreeBufferArray(scip, &orbit[0]);
3390  if ( varsinlexorder != NULL )
3391  SCIPhashmapFree(&varsinlexorder);
3392  SCIPfreeBufferArray(scip, &varfound);
3393  SCIPfreeCleanBufferArray(scip, &usedvars);
3394 
3395  return SCIP_OKAY;
3396 }
3397 
3398 
3399 /** temporarily adapt symmetry data to new variable order given by Schreier Sims */
3400 static
3402  SCIP* scip, /**< SCIP instance */
3403  int** origperms, /**< permutation matrix w.r.t. original variable ordering */
3404  int** modifiedperms, /**< memory for permutation matrix w.r.t. new variable ordering */
3405  int nperms, /**< number of permutations */
3406  SCIP_VAR** origpermvars, /**< array of permutation vars w.r.t. original variable ordering */
3407  SCIP_VAR** modifiedpermvars, /**< memory for array of permutation vars w.r.t. new variable ordering */
3408  int npermvars, /**< length or modifiedpermvars array */
3409  int* leaders, /**< leaders of Schreier Sims constraints */
3410  int nleaders /**< number of leaders */
3411  )
3412 {
3413  int* permvaridx;
3414  int* posinpermvar;
3415  int leader;
3416  int curposleader;
3417  int varidx;
3418  int lidx;
3419  int i;
3420  int l;
3421  int p;
3422 
3423  assert( scip != NULL );
3424  assert( origperms != NULL );
3425  assert( modifiedperms != NULL );
3426  assert( nperms > 0 );
3427  assert( origpermvars != NULL );
3428  assert( modifiedpermvars != NULL );
3429  assert( npermvars > 0 );
3430  assert( leaders != NULL );
3431  assert( nleaders > 0 );
3432 
3433  /* initialize map from position in lexicographic order to index of original permvar */
3434  SCIP_CALL( SCIPallocBufferArray(scip, &permvaridx, npermvars) );
3435  for (i = 0; i < npermvars; ++i)
3436  permvaridx[i] = i;
3437 
3438  /* initialize map from permvaridx to its current position in the reordered permvars array */
3439  SCIP_CALL( SCIPallocBufferArray(scip, &posinpermvar, npermvars) );
3440  for (i = 0; i < npermvars; ++i)
3441  posinpermvar[i] = i;
3442 
3443  /* Iterate over leaders and put the l-th leader to the l-th position of the lexicographic order.
3444  * We do this by swapping the l-th leader with the element at position l of the current permvars array. */
3445  for (l = 0; l < nleaders; ++l)
3446  {
3447  leader = leaders[l];
3448  curposleader = posinpermvar[leader];
3449  varidx = permvaridx[curposleader];
3450  lidx = permvaridx[l];
3451 
3452  /* swap the permvar at position l with the l-th leader */
3453  permvaridx[curposleader] = lidx;
3454  permvaridx[l] = varidx;
3455 
3456  /* update the position map */
3457  posinpermvar[lidx] = curposleader;
3458  posinpermvar[leader] = l;
3459  }
3460 
3461  /* update the permvars array to new variable order */
3462  for (i = 0; i < npermvars; ++i)
3463  modifiedpermvars[i] = origpermvars[permvaridx[i]];
3464 
3465  /* update the permutation to the new variable order */
3466  for (p = 0; p < nperms; ++p)
3467  {
3468  for (i = 0; i < npermvars; ++i)
3469  modifiedperms[p][i] = posinpermvar[origperms[p][permvaridx[i]]];
3470  }
3471 
3472  SCIPfreeBufferArray(scip, &permvaridx);
3473  SCIPfreeBufferArray(scip, &posinpermvar);
3474 
3475  return SCIP_OKAY;
3476 }
3477 
3478 
3479 /* returns the number of found orbitopes with at least three columns per graph component or 0
3480  * if the found orbitopes do not satisfy certain criteria for being used
3481  */
3482 static
3484  SCIP_VAR** permvars, /**< array of variables affected by symmetry */
3485  int* graphcomponents, /**< array of graph components */
3486  int* graphcompbegins, /**< array indicating starting position of graph components */
3487  int* compcolorbegins, /**< array indicating starting positions of potential orbitopes */
3488  int ncompcolors, /**< number of components encoded in compcolorbegins */
3489  int symcompsize /**< size of symmetry component for that we detect suborbitopes */
3490  )
3491 {
3492  SCIP_Bool oneorbitopecriterion = FALSE;
3493  SCIP_Bool multorbitopecriterion = FALSE;
3494  int norbitopes = 0;
3495  int j;
3496 
3497  assert( graphcompbegins != NULL );
3498  assert( compcolorbegins != NULL );
3499  assert( ncompcolors >= 0 );
3500  assert( symcompsize > 0 );
3501 
3502  for (j = 0; j < ncompcolors; ++j)
3503  {
3504  SCIP_VAR* firstvar;
3505  int largestcompsize = 0;
3506  int nbinrows= 0;
3507  int k;
3508 
3509  /* skip trivial components */
3510  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
3511  continue;
3512 
3513  /* check whether components of this color build an orbitope (with > 2 columns) */
3514  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
3515  {
3516  int compsize;
3517 
3518  compsize = graphcompbegins[k+1] - graphcompbegins[k];
3519 
3520  /* the first component that we are looking at for this color */
3521  if ( largestcompsize < 1 )
3522  {
3523  if ( compsize < 3 )
3524  break;
3525 
3526  largestcompsize = compsize;
3527  }
3528  else if ( compsize != largestcompsize )
3529  break;
3530 
3531  firstvar = permvars[graphcomponents[graphcompbegins[k]]];
3532 
3533  /* count number of binary orbits (comps) */
3534  if ( SCIPvarIsBinary(firstvar) )
3535  ++nbinrows;
3536  }
3537 
3538  /* we have found an orbitope */
3539  if ( k == compcolorbegins[j+1] )
3540  {
3541  SCIP_Real threshold;
3542  int ncols;
3543 
3544  ++norbitopes;
3545  ncols = graphcompbegins[compcolorbegins[j] + 1] - graphcompbegins[compcolorbegins[j]];
3546 
3547  threshold = 0.7 * (SCIP_Real) symcompsize;
3548 
3549  /* check whether criteria for adding orbitopes are satisfied */
3550  if ( nbinrows <= 2 * ncols || (nbinrows <= 8 * ncols && nbinrows < 100) )
3551  multorbitopecriterion = TRUE;
3552  else if ( nbinrows <= 3 * ncols || (SCIP_Real) nbinrows * ncols >= threshold )
3553  oneorbitopecriterion = TRUE;
3554  }
3555  }
3556 
3557  if ( (norbitopes == 1 && oneorbitopecriterion) || (norbitopes >= 2 && multorbitopecriterion) )
3558  return norbitopes;
3559 
3560  return 0;
3561 }
3562 
3563 
3564 /** checks whether subgroups of the components are symmetric groups and adds SBCs for them */
3565 static
3567  SCIP* scip, /**< SCIP instance */
3568  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3569  int cidx /**< index of component which shall be handled */
3570  )
3571 {
3572  int* genorder;
3573  int p;
3574 #ifdef SCIP_DEBUG
3575  int norbitopes = 0;
3576  int nstrongsbcs = 0;
3577  int nweaksbcs = 0;
3578 #endif
3579  int** modifiedperms;
3580  SCIP_VAR** modifiedpermvars;
3581  int* nvarsincomponent;
3582 
3583  int* graphcomponents;
3584  int* graphcompbegins;
3585  int* compcolorbegins;
3586  int* chosencomppercolor = NULL;
3587  int* firstvaridxpercolor = NULL;
3588  int* usedperms;
3589  int usedpermssize;
3590  int ngraphcomponents;
3591  int ncompcolors;
3592  int ntwocycleperms;
3593  int npermsincomp;
3594  int nusedperms;
3595  int ntrivialcolors = 0;
3596  int j;
3597  int* lexorder = NULL;
3598  int nvarslexorder = 0;
3599  int maxnvarslexorder = 0;
3600  SCIP_Shortbool* permused;
3601  SCIP_Bool allpermsused = FALSE;
3602  SCIP_Bool handlednonbinarysymmetry = FALSE;
3603  int norbitopesincomp;
3604 
3605  assert( scip != NULL );
3606  assert( propdata != NULL );
3607  assert( propdata->computedsymmetry );
3608  assert( propdata->nperms >= 0 );
3609  assert( 0 <= cidx && cidx < propdata->ncomponents );
3610  assert( propdata->components != NULL );
3611  assert( propdata->componentbegins != NULL );
3612 
3613  /* exit if no symmetry is present or component is blocked */
3614  if ( propdata->nperms == 0 || propdata->componentblocked[cidx] )
3615  return SCIP_OKAY;
3616 
3617  /* exit if instance is too large */
3618  if ( SCIPgetNConss(scip) > propdata->maxnconsssubgroup )
3619  return SCIP_OKAY;
3620 
3621  assert( propdata->nperms > 0 );
3622  assert( propdata->perms != NULL );
3623  assert( propdata->npermvars > 0 );
3624  assert( propdata->permvars != NULL );
3625 
3626  /* create array for permutation order */
3627  SCIP_CALL( SCIPallocBufferArray(scip, &genorder, propdata->nperms) );
3628 
3629  /* create arrays for modified permutations in case we adapt the lexicographic order because of suborbitopes */
3630  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, propdata->nperms) );
3631  for (p = 0; p < propdata->nperms; ++p)
3632  {
3633  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[p], propdata->npermvars) );
3634  }
3635  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, propdata->npermvars) );
3636 
3637  SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarsincomponent, propdata->npermvars) );
3638  for (p = 0; p < propdata->npermvars; ++p)
3639  {
3640  if ( propdata->vartocomponent[p] >= 0 )
3641  ++nvarsincomponent[propdata->vartocomponent[p]];
3642  }
3643 
3644  SCIPdebugMsg(scip, "starting subgroup detection routine for component %d\n", cidx);
3645 
3646  npermsincomp = propdata->componentbegins[cidx + 1] - propdata->componentbegins[cidx];
3647 
3648  /* set the first npermsincomp entries of genorder; the others are not used for this component */
3649  for (j = 0; j < npermsincomp; ++j)
3650  genorder[j] = j;
3651 
3652  SCIP_CALL( chooseOrderOfGenerators(scip, propdata, cidx, &genorder, &ntwocycleperms) );
3653 
3654  assert( ntwocycleperms >= 0 );
3655  assert( ntwocycleperms <= npermsincomp );
3656 
3657  SCIPdebugMsg(scip, "component %d has %d permutations consisting of 2-cycles\n", cidx, ntwocycleperms);
3658 
3659 #ifdef SCIP_MORE_DEBUG
3660  SCIP_Bool* used;
3661  int perm;
3662  int p;
3663  int k;
3664 
3665  SCIP_CALL( SCIPallocBufferArray(scip, &used, propdata->npermvars) );
3666  for (p = propdata->componentbegins[cidx]; p < propdata->componentbegins[cidx+1]; ++p)
3667  {
3668  perm = propdata->components[p];
3669 
3670  for (k = 0; k < propdata->npermvars; ++k)
3671  used[k] = FALSE;
3672 
3673  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "permutation %d\n", perm);
3674 
3675  for (k = 0; k < propdata->npermvars; ++k)
3676  {
3677  if ( used[k] )
3678  continue;
3679 
3680  j = propdata->perms[perm][k];
3681 
3682  if ( k == j )
3683  continue;
3684 
3685  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "(%s,", SCIPvarGetName(propdata->permvars[k]));
3686  used[k] = TRUE;
3687  while (j != k)
3688  {
3689  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%s,", SCIPvarGetName(propdata->permvars[j]));
3690  used[j] = TRUE;
3691 
3692  j = propdata->perms[perm][j];
3693  }
3695  }
3697  }
3698 
3699  SCIPfreeBufferArray(scip, &used);
3700 #endif
3701 
3702  if ( ntwocycleperms < 2 )
3703  {
3704  SCIPdebugMsg(scip, " --> skip\n");
3705  goto FREEBASICMEM;
3706  }
3707 
3708  usedpermssize = ntwocycleperms / 2;
3709  SCIP_CALL( SCIPallocBufferArray(scip, &usedperms, usedpermssize) );
3710  SCIP_CALL( SCIPallocClearBufferArray(scip, &permused, npermsincomp) );
3711 
3712  SCIP_CALL( buildSubgroupGraph(scip, propdata, genorder, ntwocycleperms, cidx,
3713  &graphcomponents, &graphcompbegins, &compcolorbegins, &ngraphcomponents,
3714  &ncompcolors, &usedperms, &nusedperms, usedpermssize, permused) );
3715 
3716  SCIPdebugMsg(scip, " created subgroup detection graph using %d of the permutations\n", nusedperms);
3717 
3718  if ( nusedperms == npermsincomp )
3719  allpermsused = TRUE;
3720 
3721  assert( graphcomponents != NULL );
3722  assert( graphcompbegins != NULL );
3723  assert( compcolorbegins != NULL );
3724  assert( ngraphcomponents > 0 );
3725  assert( ncompcolors > 0 );
3726  assert( nusedperms <= ntwocycleperms );
3727  assert( ncompcolors < propdata->npermvars );
3728 
3729  if ( nusedperms == 0 )
3730  {
3731  SCIPdebugMsg(scip, " -> skipping component, since less no permutation was used\n");
3732 
3733  SCIPfreeBufferArray(scip, &permused);
3734  SCIPfreeBufferArray(scip, &usedperms);
3735 
3736  goto FREEBASICMEM;
3737  }
3738 
3739  SCIPdebugMsg(scip, " number of different colors in the graph: %d\n", ncompcolors);
3740 
3741  if ( propdata->addstrongsbcs || propdata->addweaksbcs )
3742  {
3743  SCIP_CALL( SCIPallocBufferArray(scip, &chosencomppercolor, ncompcolors) );
3744  SCIP_CALL( SCIPallocBufferArray(scip, &firstvaridxpercolor, ncompcolors) );
3745 
3746  /* Initialize the arrays with -1 to encode that we have not added orbitopes/strong SBCs
3747  * yet. In case we do not modify this entry, no weak inequalities are added based on
3748  * this component.
3749  */
3750  for (j = 0; j < ncompcolors; ++j)
3751  {
3752  chosencomppercolor[j] = -1;
3753  firstvaridxpercolor[j] = -1;
3754  }
3755  }
3756 
3757  norbitopesincomp = getNOrbitopesInComp(propdata->permvars, graphcomponents, graphcompbegins, compcolorbegins,
3758  ncompcolors, nvarsincomponent[cidx]);
3759 
3760  /* if there is just one orbitope satisfying the requirements, handle the full component by symresacks */
3761  if ( norbitopesincomp == 1 )
3762  {
3763  int k;
3764 
3765  for (k = 0; k < npermsincomp; ++k)
3766  {
3767  SCIP_CONS* cons;
3768  char name[SCIP_MAXSTRLEN];
3769 
3770  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", cidx, k);
3771 
3772  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name,
3773  propdata->perms[propdata->components[propdata->componentbegins[cidx] + k]],
3774  propdata->permvars, propdata->npermvars, FALSE,
3775  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3776  SCIP_CALL( SCIPaddCons(scip, cons));
3777 
3778  /* do not release constraint here - will be done later */
3779  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
3780  &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
3781  propdata->genorbconss[propdata->ngenorbconss++] = cons;
3782  ++propdata->nsymresacks;
3783 
3784  if ( ! propdata->componentblocked[cidx] )
3785  {
3786  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
3787  ++propdata->ncompblocked;
3788  }
3789 
3790  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d\n", k, cidx);
3791  }
3792 
3793  goto FREEMEMORY;
3794  }
3795 
3796  for (j = 0; j < ncompcolors; ++j)
3797  {
3798  int nbinarycomps = 0;
3799  int largestcolorcomp = -1;
3800  int largestcompsize = 0;
3801  int k;
3802  SCIP_Bool isorbitope = TRUE;
3803  SCIP_Bool orbitopeadded = FALSE;
3804  SCIP_Bool useorbitope;
3805 #ifdef SCIP_DEBUG
3806  SCIP_Bool binaffected = FALSE;
3807  SCIP_Bool intaffected = FALSE;
3808  SCIP_Bool contaffected = FALSE;
3809 #endif
3810 
3811  /* skip trivial components */
3812  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
3813  {
3814  if( chosencomppercolor != NULL )
3815  chosencomppercolor[j] = -1;
3816 
3817  ++ntrivialcolors;
3818  continue;
3819  }
3820 
3821  SCIPdebugMsg(scip, " color %d has %d components with overall %d variables\n", j, compcolorbegins[j+1] - compcolorbegins[j],
3822  graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]]);
3823 
3824  /* check whether components of this color might build an orbitope (with > 2 columns) */
3825  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
3826  {
3827  SCIP_VAR* firstvar;
3828  int compsize;
3829 
3830  compsize = graphcompbegins[k+1] - graphcompbegins[k];
3831 
3832  /* the first component that we are looking at for this color */
3833  if ( largestcompsize < 1 )
3834  {
3835  if ( compsize < 3 )
3836  {
3837  isorbitope = FALSE;
3838  break;
3839  }
3840 
3841  largestcompsize = compsize;
3842  largestcolorcomp = k;
3843  }
3844  else if ( compsize != largestcompsize )
3845  {
3846  /* variable orbits (compsize) have not the same size, cannot define orbitope */
3847  isorbitope = FALSE;
3848  break;
3849  }
3850 
3851  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
3852 
3853  /* count number of binary orbits (comps) */
3854  if ( SCIPvarIsBinary(firstvar) )
3855  ++nbinarycomps;
3856  }
3857 
3858 #ifdef SCIP_DEBUG
3859  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
3860  {
3861  SCIP_VAR* firstvar;
3862 
3863  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
3864 
3865  if ( SCIPvarIsBinary(firstvar) )
3866  binaffected = TRUE;
3867  else if (SCIPvarIsIntegral(firstvar) )
3868  intaffected = TRUE;
3869  else
3870  contaffected = TRUE;
3871  }
3872 
3873  SCIPdebugMsg(scip, " affected types (bin,int,cont): (%d,%d,%d)\n", binaffected, intaffected, contaffected);
3874 #endif
3875 
3876  /* only use the orbitope if there are binary rows */
3877  useorbitope = FALSE;
3878  if ( norbitopesincomp > 0 && nbinarycomps > 0 )
3879  useorbitope = TRUE;
3880 
3881  if ( isorbitope && useorbitope )
3882  {
3883  int firstvaridx;
3884  int chosencomp;
3885 
3886  SCIPdebugMsg(scip, " detected an orbitope with %d rows and %d columns\n", nbinarycomps, largestcompsize);
3887 
3888  assert( nbinarycomps > 0 );
3889  assert( largestcompsize > 2 );
3890 
3891  /* add the orbitope constraint for this color
3892  *
3893  * It might happen that we cannot generate the orbitope matrix if the orbitope is not generated by permutations
3894  * all having the same number of 2-cycles, e.g., the orbitope generated by (1,2)(4,5), (2,3), (5,6).
3895  */
3896  SCIP_CALL( addOrbitopeSubgroup(scip, propdata, usedperms, nusedperms, compcolorbegins,
3897  graphcompbegins, graphcomponents, j, nbinarycomps, largestcompsize, &firstvaridx, &chosencomp,
3898  &lexorder, &nvarslexorder, &maxnvarslexorder, allpermsused, &orbitopeadded) );
3899 
3900  if ( orbitopeadded )
3901  {
3902  if ( propdata->addstrongsbcs || propdata->addweaksbcs )
3903  {
3904  assert( chosencomppercolor != NULL );
3905  assert( firstvaridxpercolor != NULL );
3906 
3907  /* adapt the first variable per color to be compatible with the created orbiope (upper left variable) */
3908  assert( compcolorbegins[j] <= chosencomp && chosencomp < compcolorbegins[j+1] );
3909  assert( 0 <= firstvaridx && firstvaridx < propdata->npermvars );
3910 
3911  chosencomppercolor[j] = chosencomp;
3912  firstvaridxpercolor[j] = firstvaridx;
3913  }
3914 
3915  if ( ! propdata->componentblocked[cidx] )
3916  {
3917  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
3918  ++propdata->ncompblocked;
3919  }
3920 
3921 #ifdef SCIP_DEBUG
3922  ++norbitopes;
3923 #endif
3924  }
3925  }
3926 
3927  /* if no (useable) orbitope was found, possibly add strong SBCs */
3928  if ( propdata->addstrongsbcs && ! orbitopeadded )
3929  {
3930  assert( largestcolorcomp >= 0 );
3931  assert( largestcolorcomp < ngraphcomponents );
3932  assert( largestcompsize > 0 );
3933 
3934  if( propdata->addweaksbcs )
3935  {
3936  assert( chosencomppercolor != NULL );
3937  assert( firstvaridxpercolor != NULL );
3938 
3939  chosencomppercolor[j] = largestcolorcomp;
3940  firstvaridxpercolor[j] = graphcomponents[graphcompbegins[largestcolorcomp]];
3941  }
3942 
3943  SCIPdebugMsg(scip, " choosing component %d with %d variables and adding strong SBCs\n",
3944  largestcolorcomp, graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp]);
3945 
3946  /* add the strong SBCs for the corresponding component */
3947  SCIP_CALL( addStrongSBCsSubgroup(scip, propdata, graphcompbegins, graphcomponents, largestcolorcomp,
3948  propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
3949 
3950  /* store whether symmetries on non-binary symmetries have been handled */
3951  if ( ! SCIPvarIsBinary(propdata->permvars[graphcomponents[graphcompbegins[largestcolorcomp]]]) )
3952  handlednonbinarysymmetry = TRUE;
3953 
3954  if ( ! propdata->componentblocked[cidx] )
3955  {
3956  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
3957  ++propdata->ncompblocked;
3958  }
3959 
3960 #ifdef SCIP_DEBUG
3961  nstrongsbcs += graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp] - 1;
3962 #endif
3963  }
3964  else if ( ! orbitopeadded )
3965  {
3966  SCIPdebugMsg(scip, " no useable orbitope found and no SBCs added\n");
3967 
3968  /* mark the color as not handled */
3969  if ( propdata->addweaksbcs )
3970  {
3971  assert( chosencomppercolor != NULL );
3972  chosencomppercolor[j] = -1; /*lint !e613*/
3973  }
3974  }
3975  }
3976 
3977  SCIPdebugMsg(scip, " skipped %d trivial colors\n", ntrivialcolors);
3978 
3979  /* possibly add weak SBCs for enclosing orbit of first component */
3980  if ( propdata->addweaksbcs && propdata->componentblocked[cidx] && nusedperms < npermsincomp )
3981  {
3982  int naddedconss;
3983 
3984  assert( firstvaridxpercolor != NULL );
3985  assert( chosencomppercolor != NULL );
3986 
3987  SCIP_CALL( addWeakSBCsSubgroup(scip, propdata, compcolorbegins, graphcompbegins,
3988  graphcomponents, ncompcolors, chosencomppercolor, firstvaridxpercolor,
3989  cidx, &naddedconss, propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
3990 
3991  assert( naddedconss < propdata->npermvars );
3992 
3993 #ifdef SCIP_DEBUG
3994  nweaksbcs += naddedconss;
3995 #endif
3996  }
3997  else
3998  SCIPdebugMsg(scip, " don't add weak sbcs because all generators were used or the settings forbid it\n");
3999 
4000  /* if suborbitopes or strong group actions have been found, potentially add symresacks adapted to
4001  * variable order given by lexorder if no symmetries on non-binary variables have been handled
4002  */
4003  if ( nvarslexorder > 0 && propdata->addsymresacks && ! handlednonbinarysymmetry )
4004  {
4005  int k;
4006 
4007  SCIP_CALL( adaptSymmetryDataSST(scip, propdata->perms, modifiedperms, propdata->nperms,
4008  propdata->permvars, modifiedpermvars, propdata->npermvars, lexorder, nvarslexorder) );
4009 
4010  for (k = 0; k < npermsincomp; ++k)
4011  {
4012  SCIP_CONS* cons;
4013  char name[SCIP_MAXSTRLEN];
4014  int* symresackperm;
4015  SCIP_Bool actsonbinary = FALSE;
4016 
4017  /* skip permutations that have been used to build an orbitope */
4018  if ( permused[k] )
4019  continue;
4020 
4021  /* skip permutations that do not act on binary variables */
4022  symresackperm = modifiedperms[propdata->components[propdata->componentbegins[cidx] + k]];
4023  for (j = 0; j < propdata->nperms && !actsonbinary; ++j)
4024  {
4025  if ( symresackperm[j] != j && SCIPvarIsBinary(modifiedpermvars[j]) )
4026  actsonbinary = TRUE;
4027  }
4028 
4029  if ( ! actsonbinary )
4030  continue;
4031 
4032  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", cidx, k);
4033 
4034  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name,
4035  symresackperm, modifiedpermvars, propdata->npermvars, FALSE,
4036  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4037  SCIP_CALL( SCIPaddCons(scip, cons));
4038 
4039  /* do not release constraint here - will be done later */
4040  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
4041  &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
4042  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4043  ++propdata->nsymresacks;
4044 
4045  if ( ! propdata->componentblocked[cidx] )
4046  {
4047  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
4048  ++propdata->ncompblocked;
4049  }
4050 
4051  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d adapted to suborbitope lexorder\n", k, cidx);
4052  }
4053  }
4054 
4055  FREEMEMORY:
4056  SCIPfreeBlockMemoryArrayNull(scip, &lexorder, maxnvarslexorder);
4057 
4058  SCIPfreeBufferArrayNull(scip, &firstvaridxpercolor);
4059  SCIPfreeBufferArrayNull(scip, &chosencomppercolor);
4060  SCIPfreeBlockMemoryArrayNull(scip, &compcolorbegins, ncompcolors + 1);
4061  SCIPfreeBlockMemoryArrayNull(scip, &graphcompbegins, ngraphcomponents + 1);
4062  SCIPfreeBlockMemoryArrayNull(scip, &graphcomponents, propdata->npermvars);
4063  SCIPfreeBufferArrayNull(scip, &permused);
4064  SCIPfreeBufferArrayNull(scip, &usedperms);
4065 
4066 #ifdef SCIP_DEBUG
4067  SCIPdebugMsg(scip, "total number of added (sub-)orbitopes: %d\n", norbitopes);
4068  SCIPdebugMsg(scip, "total number of added strong sbcs: %d\n", nstrongsbcs);
4069  SCIPdebugMsg(scip, "total number of added weak sbcs: %d\n", nweaksbcs);
4070 #endif
4071 
4072  FREEBASICMEM:
4073  SCIPfreeBufferArray(scip, &nvarsincomponent);
4074 
4075  SCIPfreeBufferArray(scip, &modifiedpermvars);
4076  for (p = propdata->nperms - 1; p >= 0; --p)
4077  {
4078  SCIPfreeBufferArray(scip, &modifiedperms[p]);
4079  }
4080  SCIPfreeBufferArray(scip, &modifiedperms);
4081  SCIPfreeBufferArray(scip, &genorder);
4082 
4083  return SCIP_OKAY;
4084 }
4085 
4086 
4087 /*
4088  * Functions for symmetry constraints
4089  */
4090 
4091 
4092 /** update symmetry information of conflict graph */
4093 static
4095  SCIP* scip, /**< SCIP instance */
4096  SCIP_CONFLICTDATA* varconflicts, /**< conflict structure */
4097  SCIP_VAR** conflictvars, /**< variables encoded in conflict structure */
4098  int nconflictvars, /**< number of nodes/vars in conflict structure */
4099  int* orbits, /**< array of non-trivial orbits */
4100  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
4101  int norbits /**< number of non-trivial orbits */
4102  )
4103 {
4104  int i;
4105  int j;
4106  int ii;
4107  int jj;
4108  int r; /* r from orbit, the orbit index. */
4109 
4110  assert( scip != NULL );
4111  assert( varconflicts != NULL );
4112  assert( conflictvars != NULL );
4113  assert( nconflictvars > 0 );
4114  assert( orbits != NULL );
4115  assert( orbitbegins != NULL );
4116  assert( norbits >= 0 );
4117 
4118  /* initialize/reset variable information of nodes in conflict graph */
4119  for (i = 0; i < nconflictvars; ++i)
4120  {
4121  /* (re-)set node data */
4122  varconflicts[i].orbitidx = -1;
4123  varconflicts[i].nconflictinorbit = 0;
4124  varconflicts[i].orbitsize = -1;
4125  varconflicts[i].posinorbit = -1;
4126  }
4127 
4128  /* add orbit information to nodes of conflict graph */
4129  for (r = 0; r < norbits; ++r)
4130  {
4131  int posinorbit = 0;
4132  int orbitsize;
4133 
4134  orbitsize = orbitbegins[r + 1] - orbitbegins[r];
4135  assert( orbitsize >= 0 );
4136 
4137  for (i = orbitbegins[r]; i < orbitbegins[r + 1]; ++i)
4138  {
4139  int pos;
4140 
4141  /* get variable and position in conflict graph */
4142  pos = orbits[i];
4143  assert( pos < nconflictvars );
4144  assert( varconflicts[pos].var == conflictvars[pos] );
4145 
4146  varconflicts[pos].orbitidx = r;
4147  varconflicts[pos].nconflictinorbit = 0;
4148  varconflicts[pos].orbitsize = orbitsize;
4149  varconflicts[pos].posinorbit = posinorbit++;
4150  }
4151 
4152  /* determine nconflictsinorbit
4153  *
4154  * For each pair of active variables in this orbit, check if it is part of a conflict clique.
4155  * Use that we store the cliques of this type in varconflicts[pos].cliques.
4156  * These lists are sorted (by the address of the constraint), so we only need to check for each i, j in the orbit
4157  * whether they are contained in the same clique.
4158  */
4159  for (i = orbitbegins[r]; i < orbitbegins[r + 1]; ++i)
4160  {
4161  ii = orbits[i];
4162  assert( varconflicts[ii].orbitidx == r );
4163 
4164  /* skip inactive variables */
4165  if ( ! varconflicts[ii].active )
4166  continue;
4167 
4168  for (j = i + 1; j < orbitbegins[r + 1]; ++j)
4169  {
4170  jj = orbits[j];
4171  assert( varconflicts[jj].orbitidx == r );
4172 
4173  /* skip inactive variables */
4174  if ( ! varconflicts[jj].active )
4175  continue;
4176 
4177  /* Check if i and j are overlapping in some clique, where only one of the two could have value 1.
4178  * Use that cliques are sorted by the constraint address.
4179  *
4180  * @todo A better sorted order would be: First constraints with large variables (higher hitting probability)
4181  * and then by a unique constraint identifier (address, or conspos).
4182  */
4183  if ( checkSortedArraysHaveOverlappingEntry((void**)varconflicts[ii].cliques,
4184  varconflicts[ii].ncliques, (void**)varconflicts[jj].cliques, varconflicts[jj].ncliques,
4185  sortByPointerValue) )
4186  {
4187  /* there is overlap! */
4188  ++varconflicts[ii].nconflictinorbit;
4189  ++varconflicts[jj].nconflictinorbit;
4190  }
4191  }
4192  }
4193  }
4194 
4195  return SCIP_OKAY;
4196 }
4197 
4198 
4199 /** create conflict graph either for symmetric or for all variables
4200  *
4201  * This routine just creates the graph, but does not add (symmetry) information to its nodes.
4202  * This has to be done separately by the routine updateSymInfoConflictGraphSST().
4203  *
4204  * The function returns with varconflicts as NULL when we do not create it.
4205  */
4206 static
4208  SCIP* scip, /**< SCIP instance */
4209  SCIP_CONFLICTDATA** varconflicts, /**< pointer to store the variable conflict data */
4210  SCIP_VAR** conflictvars, /**< array of variables to encode in conflict graph */
4211  int nconflictvars, /**< number of vars to encode in conflict graph */
4212  SCIP_HASHMAP* conflictvarmap /**< map of variables to indices in conflictvars array */
4213  )
4214 {
4215  SCIP_CLIQUE** cliques;
4216  SCIP_VAR** cliquevars;
4217  SCIP_CLIQUE* clique;
4218  int* tmpncliques;
4219  int ncliques;
4220  int ncliquevars;
4221  int node;
4222  int c;
4223  int i;
4224 
4225 #ifdef SCIP_DEBUG
4226  int varncliques = 0;
4227 #endif
4228 
4229  assert( scip != NULL );
4230  assert( varconflicts != NULL );
4231  assert( conflictvars != NULL );
4232  assert( nconflictvars > 0 );
4233 
4234  /* we set the pointer of varconflicts to NULL to illustrate that we didn't generate it */
4235  *varconflicts = NULL;
4236 
4237  /* get cliques for creating conflict structure */
4238 
4239  cliques = SCIPgetCliques(scip);
4240  ncliques = SCIPgetNCliques(scip);
4241  if ( ncliques == 0 )
4242  {
4243  SCIPdebugMsg(scip, "No cliques present --> construction of conflict structure aborted.\n");
4244  return SCIP_OKAY;
4245  }
4246 
4247  /* construct variable conflicts */
4248  SCIPdebugMsg(scip, "Construction of conflict structure:\n");
4249  SCIP_CALL( SCIPallocBlockMemoryArray(scip, varconflicts, nconflictvars) );
4250  for (i = 0; i < nconflictvars; ++i)
4251  {
4252  (*varconflicts)[i].ncliques = 0;
4253  (*varconflicts)[i].active = TRUE;
4254  (*varconflicts)[i].var = conflictvars[i];
4255  /* set remaining variable conflictdata at neutral entries */
4256  (*varconflicts)[i].cliques = NULL;
4257  (*varconflicts)[i].orbitidx = -1;
4258  (*varconflicts)[i].nconflictinorbit = 0;
4259  (*varconflicts)[i].orbitsize = -1;
4260  (*varconflicts)[i].posinorbit = -1;
4261  }
4262 
4263  /* Store, for each variable, the conflict cliques it is contained in.
4264  * In three steps:
4265  * (1.) Count the number of cliques it's contained in, per var, then
4266  * (2.) Create the array of this size, and
4267  * (3.) Fill the array with the cliques.
4268  * Starting with (1.):
4269  */
4270  for (c = 0; c < ncliques; ++c)
4271  {
4272  clique = cliques[c];
4273  assert( clique != NULL );
4274 
4275  cliquevars = SCIPcliqueGetVars(clique);
4276  ncliquevars = SCIPcliqueGetNVars(clique);
4277  assert( cliquevars != NULL );
4278  assert( ncliquevars > 0 );
4279 
4280  SCIPdebugMsg(scip, "\tIdentify edges for clique ID: %u; Index: %d).\n", SCIPcliqueGetId(clique),
4281  SCIPcliqueGetIndex(clique));
4282 
4283  /* for all variables, list which cliques it is part of */
4284  for (i = 0; i < ncliquevars; ++i)
4285  {
4286  node = SCIPhashmapGetImageInt(conflictvarmap, cliquevars[i]);
4287 
4288  /* skip variables not in the conflictvars array (so not in hashmap, too) */
4289  if ( node == INT_MAX )
4290  continue;
4291  assert( node >= 0 );
4292  assert( node < nconflictvars );
4293 
4294  assert( (*varconflicts)[node].var == cliquevars[i] );
4295  (*varconflicts)[node].active = TRUE;
4296  (*varconflicts)[node].ncliques++;
4297  }
4298  }
4299 
4300  /* (2.) allocate the arrays */
4301  for (i = 0; i < nconflictvars; ++i)
4302  {
4303  assert( (*varconflicts)[i].ncliques >= 0 );
4304  assert( (*varconflicts)[i].cliques == NULL );
4305  if ( (*varconflicts)[i].ncliques > 0 )
4306  {
4307  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*varconflicts)[i].cliques, (*varconflicts)[i].ncliques) );
4308  }
4309  }
4310 
4311  /* (3.) fill the clique constraints */
4312  SCIP_CALL( SCIPallocClearBufferArray(scip, &tmpncliques, nconflictvars) );
4313  for (c = 0; c < ncliques; ++c)
4314  {
4315  clique = cliques[c];
4316  assert( clique != NULL );
4317 
4318  cliquevars = SCIPcliqueGetVars(clique);
4319  ncliquevars = SCIPcliqueGetNVars(clique);
4320  assert( cliquevars != NULL );
4321  assert( ncliquevars > 0 );
4322 
4323  SCIPdebugMsg(scip, "\tAdd edges for clique ID: %u; Index: %d).\n", SCIPcliqueGetId(clique),
4324  SCIPcliqueGetIndex(clique));
4325 
4326  /* for all variables, list which cliques it is part of */
4327  for (i = 0; i < ncliquevars; ++i)
4328  {
4329  node = SCIPhashmapGetImageInt(conflictvarmap, cliquevars[i]);
4330 
4331  /* skip variables not in the conflictvars array (so not in hashmap, too) */
4332  if ( node == INT_MAX )
4333  continue;
4334 
4335  assert( node >= 0 );
4336  assert( node < nconflictvars );
4337  assert( (*varconflicts)[node].var == cliquevars[i] );
4338 
4339  /* add clique to the cliques */
4340  assert( tmpncliques[node] < (*varconflicts)[node].ncliques );
4341  assert( (*varconflicts)[node].cliques != NULL );
4342  (*varconflicts)[node].cliques[tmpncliques[node]++] = clique;
4343 
4344 #ifdef SCIP_DEBUG
4345  varncliques++;
4346 #endif
4347  }
4348  }
4349 
4350  /* sort the variable cliques by the address, so checkSortedArraysHaveOverlappingEntry can detect intersections */
4351  for (i = 0; i < nconflictvars; ++i)
4352  {
4353  SCIPsortPtr((void**)(*varconflicts)[i].cliques, sortByPointerValue, (*varconflicts)[i].ncliques);
4354  }
4355 
4356 #ifndef NDEBUG
4357  for (i = 0; i < nconflictvars; ++i)
4358  {
4359  assert( tmpncliques[i] == (*varconflicts)[i].ncliques );
4360  }
4361 #endif
4362 
4363  SCIPfreeBufferArray(scip, &tmpncliques);
4364 
4365 #ifdef SCIP_DEBUG
4366  SCIPdebugMsg(scip, "Construction of conflict graph terminated; %d variable-clique combinations detected.\n",
4367  varncliques);
4368 #endif
4369 
4370  return SCIP_OKAY;
4371 }
4372 
4373 /** frees conflict graph */
4374 static
4376  SCIP* scip, /**< SCIP instance */
4377  SCIP_CONFLICTDATA** varconflicts, /**< conflict graph */
4378  int nvars /**< number of nodes in conflict graph */
4379  )
4380 {
4381  int i;
4382  int n;
4383 
4384  assert( scip != NULL );
4385  assert( varconflicts != NULL );
4386  assert( *varconflicts != NULL );
4387  assert( nvars >= 0 );
4388 
4389  for (i = nvars - 1; i >= 0; --i)
4390  {
4391  n = (*varconflicts)[i].ncliques;
4392  SCIPfreeBlockMemoryArray(scip, &(*varconflicts)[i].cliques, n);
4393  }
4394  SCIPfreeBlockMemoryArray(scip, varconflicts, nvars);
4395 
4396  return SCIP_OKAY;
4397 }
4398 
4399 
4400 /** adds symresack constraints */
4401 static
4403  SCIP* scip, /**< SCIP instance */
4404  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
4405  int cidx /**< index of component to be handled */
4406  )
4407 { /*lint --e{641}*/
4408  int* components;
4409  int* componentbegins;
4410  SCIP_VAR** permvars;
4411  SCIP_Bool conssaddlp;
4412  int** modifiedperms = NULL;
4413  SCIP_VAR** modifiedpermvars = NULL;
4414  int** perms;
4415  int nsymresackcons = 0;
4416  int npermvars;
4417  int nperms;
4418  int i;
4419  int p;
4420 
4421  assert( scip != NULL );
4422  assert( propdata != NULL );
4423  assert( propdata->npermvars >= 0 );
4424  assert( propdata->nbinpermvars >= 0 );
4425 
4426  /* if no symmetries on binary variables are present */
4427  if ( propdata->nbinpermvars == 0 )
4428  {
4429  assert( propdata->binvaraffected == 0 );
4430  return SCIP_OKAY;
4431  }
4432 
4433  perms = propdata->perms;
4434  nperms = propdata->nperms;
4435  permvars = propdata->permvars;
4436  npermvars = propdata->npermvars;
4437  conssaddlp = propdata->conssaddlp;
4438  components = propdata->components;
4439  componentbegins = propdata->componentbegins;
4440 
4441  assert( nperms <= 0 || perms != NULL );
4442  assert( permvars != NULL );
4443  assert( npermvars > 0 );
4444  assert( components != NULL );
4445  assert( componentbegins != NULL );
4446  assert( 0 <= cidx && cidx < propdata->ncomponents );
4447 
4448  /* exit if component is already blocked by incompatible methods */
4449  if ( propdata->componentblocked[cidx] & (~SYM_HANDLETYPE_SST) )
4450  return SCIP_OKAY;
4451  if ( (propdata->componentblocked[cidx] & SYM_HANDLETYPE_SST) )
4452  {
4453  /* the leader must be binary for compatability */
4454  if ( (ISSSTINTACTIVE(propdata->sstleadervartype)
4455  || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
4456  || ISSSTCONTACTIVE(propdata->sstleadervartype)) )
4457  return SCIP_OKAY;
4458  }
4459 
4460  /* skip component if it has signed permutations */
4461  if ( propdata->componenthassignedperm[cidx] )
4462  return SCIP_OKAY;
4463 
4464  /* adapt natural variable order to a variable order that is compatible with Schreier Sims constraints */
4465  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
4466  {
4467  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, nperms) );
4468  for (p = 0; p < nperms; ++p)
4469  {
4470  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[p], npermvars) );
4471  }
4472  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, npermvars) );
4473 
4474  for (i = 0; i < npermvars; ++i)
4475  modifiedpermvars[i] = permvars[i];
4476 
4477  SCIP_CALL( adaptSymmetryDataSST(scip, perms, modifiedperms, nperms, permvars, modifiedpermvars, npermvars,
4478  propdata->leaders, propdata->nleaders) );
4479  }
4480 
4481  /* loop through perms in component cidx and add symresack constraints */
4482  for (p = componentbegins[cidx]; p < componentbegins[cidx + 1]; ++p)
4483  {
4484  SCIP_CONS* cons;
4485  int permidx;
4486  char name[SCIP_MAXSTRLEN];
4487 
4488  permidx = components[p];
4489 
4490  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_component%d_perm%d", cidx, permidx);
4491 
4492  /* adapt permutation to leader */
4493  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
4494  {
4495  assert( (propdata->componentblocked[cidx] & SYM_HANDLETYPE_SST) != 0 );
4496  assert( modifiedperms != NULL );
4497  assert( modifiedpermvars != NULL );
4498 
4499  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[permidx], modifiedpermvars, npermvars, FALSE,
4500  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4501  }
4502  else
4503  {
4504  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[permidx], permvars, npermvars, FALSE,
4505  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4506  }
4507  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
4508  SCIP_CALL( SCIPaddCons(scip, cons) );
4509 
4510  /* do not release constraint here - will be done later */
4511  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
4512  &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
4513  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4514  ++propdata->nsymresacks;
4515  ++nsymresackcons;
4516  }
4517 
4518  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
4519  {
4520  assert( modifiedperms != NULL );
4521  assert( modifiedpermvars != NULL );
4522 
4523  SCIPfreeBufferArray(scip, &modifiedpermvars);
4524  for (p = nperms - 1; p >= 0; --p)
4525  {
4526  SCIPfreeBufferArray(scip, &modifiedperms[p]);
4527  }
4528  SCIPfreeBufferArray(scip, &modifiedperms);
4529  }
4530 
4531  SCIPdebugMsg(scip, "Added %d symresack constraints.\n", nsymresackcons);
4532 
4533  return SCIP_OKAY;
4534 }
4535 
4536 
4537 /** add Schreier Sims constraints for a specific orbit and update Schreier Sims table */
4538 static
4540  SCIP* scip, /**< SCIP instance */
4541  SCIP_CONFLICTDATA* varconflicts, /**< conflict graph or NULL if useconflictgraph == FALSE */
4542  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
4543  SCIP_VAR** permvars, /**< permvars array */
4544  int* orbits, /**< symmetry orbits */
4545  int* orbitbegins, /**< array storing begin position for each orbit */
4546  int orbitidx, /**< index of orbit for Schreier Sims constraints */
4547  int orbitleaderidx, /**< index of leader variable for Schreier Sims constraints */
4548  SCIP_Shortbool* orbitvarinconflict, /**< indicator whether orbitvar is in conflict with orbit leader */
4549  int norbitvarinconflict,/**< number of variables in conflict with orbit leader */
4550  int* nchgbds /**< pointer to store number of bound changes (or NULL) */
4551  )
4552 { /*lint --e{613,641}*/
4553  SCIP_CONS* cons;
4554  char name[SCIP_MAXSTRLEN];
4555  SCIP_VAR* vars[2];
4556  SCIP_Real vals[2];
4557  int orbitsize;
4558  int posleader;
4559  int poscur;
4560  int ncuts = 0;
4561  SCIP_Bool addcuts = FALSE;
4562  int i;
4563 #ifndef NDEBUG
4564  int j;
4565 #endif
4566 
4567  assert( scip != NULL );
4568  assert( propdata != NULL );
4569  assert( permvars != NULL );
4570  assert( orbits != NULL );
4571  assert( orbitbegins != NULL );
4572  assert( orbitidx >= 0 );
4573  assert( orbitleaderidx >= 0 );
4574  assert( orbitvarinconflict != NULL || varconflicts == NULL );
4575  assert( norbitvarinconflict >= 0 );
4576  assert( nchgbds != NULL );
4577 
4578  orbitsize = orbitbegins[orbitidx + 1] - orbitbegins[orbitidx];
4579 
4580  /* variables in conflict with leader are fixed and not treated by a cut; trailing -1 to not count the leader */
4581  if ( propdata->sstaddcuts )
4582  addcuts = TRUE;
4583  else if ( propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
4584  || propdata->ssttiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT )
4585  addcuts = propdata->addconflictcuts;
4586 
4587  if ( addcuts )
4588  ncuts = orbitsize - norbitvarinconflict - 1;
4589 
4590  /* (re-)allocate memory for Schreier Sims constraints and leaders */
4591  if ( ncuts > 0 )
4592  {
4593  if ( propdata->nsstconss == 0 )
4594  {
4595  assert( propdata->sstconss == NULL );
4596  assert( propdata->maxnsstconss == 0 );
4597  propdata->maxnsstconss = 2 * ncuts;
4598  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->sstconss), propdata->maxnsstconss) );
4599  }
4600  else if ( propdata->nsstconss + ncuts > propdata->maxnsstconss )
4601  {
4602  int newsize;
4603 
4604  newsize = SCIPcalcMemGrowSize(scip, propdata->maxnsstconss + 2 * ncuts);
4605  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(propdata->sstconss),
4606  propdata->maxnsstconss, newsize) );
4607  propdata->maxnsstconss = newsize;
4608  }
4609  }
4610 
4611  if ( propdata->nleaders == 0 )
4612  {
4613  propdata->maxnleaders = MIN(propdata->nperms, propdata->npermvars);
4614  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->leaders), propdata->maxnleaders) );
4615  }
4616  assert( propdata->nleaders < propdata->maxnleaders );
4617 
4618  /* add Schreier Sims constraints vars[0] >= vars[1], where vars[0] is always the leader */
4619  posleader = orbitbegins[orbitidx] + orbitleaderidx;
4620  vars[0] = permvars[orbits[posleader]];
4621  vals[0] = -1.0;
4622  vals[1] = 1.0;
4623  propdata->leaders[propdata->nleaders++] = orbits[posleader];
4624  *nchgbds = 0;
4625  for (i = 0, poscur = orbitbegins[orbitidx]; i < orbitsize; ++i, ++poscur)
4626  {
4627  if ( i == orbitleaderidx )
4628  {
4629  assert( orbitvarinconflict == NULL || ! orbitvarinconflict[i] );
4630  continue;
4631  }
4632 
4633  vars[1] = permvars[orbits[poscur]];
4634 #ifndef NDEBUG
4635  for (j = 0; j < propdata->nleaders - 1; ++j)
4636  {
4637  assert( propdata->leaders[j] != orbits[poscur] );
4638  }
4639 #endif
4640 
4641  /* if the i-th variable in the orbit is in a conflict with the leader, fix it to 0 */
4642  if ( varconflicts != NULL )
4643  {
4644  if ( orbitvarinconflict[i] )
4645  {
4646  assert( SCIPvarIsBinary(vars[1]) );
4647  assert( SCIPvarGetLbLocal(vars[1]) < 0.5 );
4648  assert( varconflicts != NULL );
4649 
4650  /* if variable is fixed */
4651  if ( SCIPvarGetUbLocal(vars[1]) > 0.5 )
4652  {
4653  SCIP_CALL( SCIPchgVarUb(scip, vars[1], 0.0) );
4654  ++(*nchgbds);
4655 
4656  /* deactivate the fixed variable (cannot contribute to a conflict anymore) */
4657  assert( varconflicts[orbits[poscur]].active );
4658  varconflicts[orbits[poscur]].active = FALSE;
4659  }
4660 
4661  /* reset value */
4662  orbitvarinconflict[i] = FALSE;
4663  }
4664  else if ( addcuts )
4665  {
4666  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
4667  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
4669 
4670  SCIP_CALL( SCIPaddCons(scip, cons) );
4671  propdata->sstconss[propdata->nsstconss++] = cons;
4672  }
4673  }
4674  else if ( addcuts )
4675  {
4676  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
4677  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
4679 
4680  SCIP_CALL( SCIPaddCons(scip, cons) );
4681  propdata->sstconss[propdata->nsstconss++] = cons;
4682  }
4683  }
4684 
4685  return SCIP_OKAY;
4686 }
4687 
4688 
4689 /** selection rule of next orbit/leader in orbit for Schreier Sims constraints */
4690 static
4692  SCIP* scip, /**< SCIP instance */
4693  SCIP_CONFLICTDATA* varconflicts, /**< variable conflicts structure, or NULL if we do not use it */
4694  SCIP_VAR** conflictvars, /**< variables encoded in conflict graph */
4695  int nconflictvars, /**< number of variables encoded in conflict graph */
4696  int* orbits, /**< orbits of stabilizer subgroup, expressed in terms of conflictvars */
4697  int* orbitbegins, /**< array storing the begin position of each orbit in orbits */
4698  int norbits, /**< number of orbits */
4699  int leaderrule, /**< rule to select leader */
4700  int tiebreakrule, /**< tie break rule to select leader */
4701  SCIP_VARTYPE leadervartype, /**< variable type of leader */
4702  int* orbitidx, /**< pointer to index of selected orbit */
4703  int* leaderidx, /**< pointer to leader in orbit */
4704  SCIP_Shortbool* orbitvarinconflict, /**< array to store whether a var in the orbit is conflicting with leader */
4705  int* norbitvarinconflict,/**< pointer to store number of vars in the orbit in conflict with leader */
4706  SCIP_Bool* success /**< pointer to store whether orbit cut be selected successfully */
4707  )
4708 {
4709  int varidx;
4710  int orbitcriterion;
4711  int curcriterion = INT_MIN;
4712  int orbitsize;
4713  int i;
4714  int leader = -1;
4715 
4716  assert( scip != NULL );
4717  assert( conflictvars != NULL );
4718  assert( nconflictvars > 0 );
4719  assert( orbits != NULL );
4720  assert( orbitbegins != NULL );
4721  assert( norbits > 0 );
4722  assert( orbitidx != NULL );
4723  assert( leaderidx != NULL );
4724  assert( orbitvarinconflict != NULL || varconflicts == NULL );
4725  assert( norbitvarinconflict != NULL );
4726  assert( success != NULL );
4727 
4728  *orbitidx = 0;
4729  *leaderidx = 0;
4730  *norbitvarinconflict = 0;
4731  *success = FALSE;
4732 
4733  /* terminate if leader or tiebreak rule cannot be checked */
4734  if ( varconflicts == NULL && (leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT
4735  || tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
4736  return SCIP_OKAY;
4737 
4738  /* select the leader and its orbit */
4739  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT || leaderrule == (int) SCIP_LEADERRULE_LASTINORBIT )
4740  {
4741  orbitcriterion = INT_MIN;
4742 
4743  /* iterate over orbits and select the first one that meets the tiebreak rule */
4744  for (i = 0; i < norbits; ++i)
4745  {
4746  /* skip orbits containing vars different to the leader's vartype */
4747  /* Conflictvars is permvars! */
4748  if ( SCIPvarGetType(conflictvars[orbits[orbitbegins[i]]]) != leadervartype )
4749  continue;
4750 
4751  if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MINORBIT )
4752  curcriterion = orbitbegins[i] - orbitbegins[i + 1];
4753  else if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXORBIT )
4754  curcriterion = orbitbegins[i + 1] - orbitbegins[i];
4755  else
4756  {
4757  assert( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT );
4758 
4759  /* get first or last active variable in orbit */
4760  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
4761  {
4762  int cnt;
4763 
4764  cnt = orbitbegins[i];
4765 
4766  do
4767  {
4768  varidx = orbits[cnt++];
4769  }
4770  while ( SCIPvarGetProbindex(conflictvars[varidx]) == -1 && cnt < orbitbegins[i + 1]);
4771  }
4772  else
4773  {
4774  int cnt;
4775 
4776  cnt = orbitbegins[i + 1] - 1;
4777 
4778  do
4779  {
4780  varidx = orbits[cnt--];
4781  }
4782  while ( SCIPvarGetProbindex(conflictvars[varidx]) == -1 && cnt >= orbitbegins[i]);
4783  }
4784 
4785  /* skip inactive variables */
4786  if ( SCIPvarGetProbindex(conflictvars[varidx]) == -1 )
4787  continue;
4788 
4789  assert( varconflicts[varidx].orbitidx == i );
4790  /* coverity[var_deref_op] */
4791  curcriterion = varconflicts[varidx].nconflictinorbit;
4792  }
4793 
4794  /* update selected orbit */
4795  if ( curcriterion > orbitcriterion )
4796  {
4797  orbitcriterion = curcriterion;
4798  *orbitidx = i;
4799  *success = TRUE;
4800 
4801  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
4802  *leaderidx = 0;
4803  else
4804  *leaderidx = orbitbegins[i + 1] - orbitbegins[i] - 1;
4805  }
4806  }
4807 
4808  /* store variables in conflict with leader */
4809  if ( *success && varconflicts != NULL )
4810  {
4811  leader = orbits[orbitbegins[*orbitidx] + *leaderidx];
4812  assert( leader < nconflictvars );
4813 
4814  if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT
4815  && varconflicts[leader].ncliques > 0 )
4816  {
4817  /* count how many active variables in the orbit conflict with "leader"
4818  * This is only needed if there are possible conflicts.
4819  */
4820  int varmapid;
4821 
4822  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
4823  assert( varconflicts != NULL );
4824  assert( leader >= 0 && leader < nconflictvars );
4825 
4826  assert( orbitvarinconflict != NULL );
4827 
4828  for (i = 0; i < orbitsize; ++i)
4829  {
4830  /* skip the leader */
4831  if ( i == *leaderidx )
4832  continue;
4833 
4834  /* get variable index in conflict graph */
4835  varmapid = orbits[orbitbegins[*orbitidx] + i];
4836 
4837  /* only active variables */
4838  if ( ! varconflicts[varmapid].active )
4839  continue;
4840 
4841  /* check if leader and var have overlap */
4842  if ( checkSortedArraysHaveOverlappingEntry((void**)varconflicts[leader].cliques,
4843  varconflicts[leader].ncliques, (void**)varconflicts[varmapid].cliques,
4844  varconflicts[varmapid].ncliques, sortByPointerValue) )
4845  {
4846  /* there is overlap! */
4847  orbitvarinconflict[i] = TRUE;
4848  ++(*norbitvarinconflict);
4849  }
4850  }
4851  }
4852  }
4853  }
4854  else
4855  {
4856  /* only three possible values for leaderrules, so it must be MAXCONFLICTSINORBIT
4857  * In this case, the code must have computed the conflict graph.
4858  */
4859  assert( leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT );
4860  assert( varconflicts != NULL );
4861 
4862  orbitcriterion = 0;
4863 
4864  /* iterate over variables and select the first one that meets the tiebreak rule */
4865  for (i = 0; i < nconflictvars; ++i)
4866  {
4867  /* skip vars different to the leader's vartype */
4868  if ( SCIPvarGetType(conflictvars[i]) != leadervartype )
4869  continue;
4870 
4871  /* skip variables not affected by symmetry */
4872  /* coverity[var_deref_op] */
4873  if ( varconflicts[i].orbitidx == -1 )
4874  continue;
4875 
4876  curcriterion = varconflicts[i].nconflictinorbit;
4877 
4878  if ( curcriterion > orbitcriterion )
4879  {
4880  orbitcriterion = curcriterion;
4881  *orbitidx = varconflicts[i].orbitidx;
4882  *leaderidx = varconflicts[i].posinorbit;
4883  *success = TRUE;
4884  }
4885  }
4886 
4887  /* store variables in conflict with leader */
4888  leader = orbits[orbitbegins[*orbitidx] + *leaderidx];
4889  assert( leader < nconflictvars );
4890  assert( norbitvarinconflict != NULL );
4891 
4892  if ( *success && varconflicts[leader].ncliques > 0 )
4893  {
4894  /* count how many active variables in the orbit conflict with leader */
4895  int varmapid;
4896 
4897  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
4898  assert( varconflicts != NULL );
4899  assert( leader >= 0 && leader < nconflictvars );
4900 
4901  assert( orbitvarinconflict != NULL );
4902 
4903  for (i = 0; i < orbitsize; ++i)
4904  {
4905  /* skip the leader */
4906  if ( i == *leaderidx )
4907  continue;
4908 
4909  /* get variable index in conflict graph */
4910  varmapid = orbits[orbitbegins[*orbitidx] + i];
4911  /* only active variables */
4912  if ( ! varconflicts[varmapid].active )
4913  continue;
4914 
4915  /* check if leader and var have overlap */
4916  if ( checkSortedArraysHaveOverlappingEntry((void**)varconflicts[leader].cliques,
4917  varconflicts[leader].ncliques, (void**)varconflicts[varmapid].cliques,
4918  varconflicts[varmapid].ncliques, sortByPointerValue) )
4919  {
4920  /* there is overlap! */
4921  orbitvarinconflict[i] = TRUE;
4922  ++(*norbitvarinconflict);
4923  }
4924  }
4925  }
4926  }
4927 
4928  return SCIP_OKAY;
4929 }
4930 
4931 
4932 /** add Schreier Sims constraints to the problem */
4933 static
4935  SCIP* scip, /**< SCIP instance */
4936  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
4937  SCIP_Bool onlywithcontvars, /**< only handle components that contain continuous variables with SST */
4938  int* nchgbds, /**< pointer to store number of bound changes (or NULL) */
4939  int cidx /**< index of component which shall be handled */
4940  )
4941 { /*lint --e{641}*/
4942  SCIP_CONFLICTDATA* varconflicts = NULL;
4943  SCIP_HASHMAP* permvarmap;
4944  SCIP_VAR** permvars;
4945  int** permstrans;
4946  int npermvars;
4947  int nmovedpermvars;
4948  int nmovedbinpermvars;
4949  int nmovedintpermvars;
4950  int nmovedimplintpermvars;
4951  int nmovedcontpermvars;
4952  int nperms;
4953 
4954  int* orbits;
4955  int* orbitbegins;
4956  int norbits;
4957  int* components;
4958  int* componentbegins;
4959  int* vartocomponent;
4960  int ncomponents;
4961  unsigned* componentblocked;
4962 
4963  int orbitidx;
4964  int orbitleaderidx;
4965  SCIP_Shortbool* orbitvarinconflict = NULL;
4966  int norbitvarinconflict;
4967  SCIP_Shortbool* inactiveperms;
4968  int ninactiveperms;
4969  int posleader;
4970  int leaderrule;
4971  int tiebreakrule;
4972  int leadervartype;
4973  SCIP_VARTYPE selectedtype = SCIP_VARTYPE_CONTINUOUS;
4974  int nvarsselectedtype;
4975  SCIP_Bool conflictgraphcreated = FALSE;
4976  SCIP_Bool mixedcomponents;
4977  int norbitleadercomponent;
4978  int* perm;
4979  SCIP_VARTYPE vartype;
4980 
4981  int i;
4982  int c;
4983  int p;
4984  SCIP_Bool success = TRUE;
4985 
4986  assert( scip != NULL );
4987  assert( propdata != NULL );
4988  assert( propdata->computedsymmetry );
4989 
4990  permvars = propdata->permvars;
4991  npermvars = propdata->npermvars;
4992  nperms = propdata->nperms;
4993  assert( permvars != NULL );
4994  assert( npermvars > 0 );
4995  assert( nperms > 0 );
4996 
4997  SCIP_CALL( ensureSymmetryPermvarmapComputed(scip, propdata) );
4998  permvarmap = propdata->permvarmap;
4999  assert( permvarmap != NULL );
5000 
5001  SCIP_CALL( ensureSymmetryPermstransComputed(scip, propdata) );
5002  permstrans = propdata->permstrans;
5003  assert( permstrans != NULL );
5004 
5005  components = propdata->components;
5006  componentbegins = propdata->componentbegins;
5007  componentblocked = propdata->componentblocked;
5008  vartocomponent = propdata->vartocomponent;
5009  ncomponents = propdata->ncomponents;
5010 
5011  assert( components != NULL );
5012  assert( componentbegins != NULL );
5013  assert( vartocomponent != NULL );
5014  assert( componentblocked != NULL );
5015  assert( ncomponents > 0 );
5016  assert( 0 <= cidx && cidx < ncomponents );
5017 
5018  /* exit if component is blocked */
5019  if ( componentblocked[cidx] )
5020  return SCIP_OKAY;
5021 
5022  /* skip component if it has signed permutations */
5023  if ( propdata->componenthassignedperm[cidx] )
5024  return SCIP_OKAY;
5025 
5026  leaderrule = propdata->sstleaderrule;
5027  tiebreakrule = propdata->ssttiebreakrule;
5028  leadervartype = propdata->sstleadervartype;
5029  mixedcomponents = propdata->sstmixedcomponents;
5030 
5031  /* if not already computed, get number of affected vars */
5033  nmovedpermvars = propdata->nmovedpermvars;
5034  nmovedbinpermvars = propdata->nmovedbinpermvars;
5035  nmovedintpermvars = propdata->nmovedintpermvars;
5036  nmovedimplintpermvars = propdata->nmovedimplintpermvars;
5037  nmovedcontpermvars = propdata->nmovedcontpermvars;
5038  assert( nmovedpermvars > 0 ); /* nperms > 0 implies this */
5039 
5040  /* determine the leader's vartype */
5041  nvarsselectedtype = 0;
5042  if ( ISSSTBINACTIVE(leadervartype) && nmovedbinpermvars > nvarsselectedtype )
5043  {
5044  selectedtype = SCIP_VARTYPE_BINARY;
5045  nvarsselectedtype = nmovedbinpermvars;
5046  }
5047 
5048  if ( ISSSTINTACTIVE(leadervartype) && nmovedintpermvars > nvarsselectedtype )
5049  {
5050  selectedtype = SCIP_VARTYPE_INTEGER;
5051  nvarsselectedtype = nmovedintpermvars;
5052  }
5053 
5054  if ( ISSSTIMPLINTACTIVE(leadervartype) && nmovedimplintpermvars > nvarsselectedtype )
5055  {
5056  selectedtype = SCIP_VARTYPE_IMPLINT;
5057  nvarsselectedtype = nmovedimplintpermvars;
5058  }
5059 
5060  if ( ISSSTCONTACTIVE(leadervartype) && nmovedcontpermvars > nvarsselectedtype )
5061  {
5062  selectedtype = SCIP_VARTYPE_CONTINUOUS;
5063  nvarsselectedtype = nmovedcontpermvars;
5064  }
5065 
5066  /* terminate if no variables of a possible leader type is affected */
5067  if ( nvarsselectedtype == 0 )
5068  return SCIP_OKAY;
5069 
5070  /* ignore this component if no continuous variables are contained */
5071  if ( onlywithcontvars )
5072  {
5073  for (p = componentbegins[cidx]; p < componentbegins[cidx + 1]; ++p)
5074  {
5075  perm = propdata->perms[p];
5076  for (i = 0; i < propdata->npermvars; ++i)
5077  {
5078  if ( perm[i] == i )
5079  continue;
5080  vartype = SCIPvarGetType(propdata->permvars[i]);
5081  if ( vartype == SCIP_VARTYPE_CONTINUOUS || vartype == SCIP_VARTYPE_IMPLINT )
5082  goto COMPONENTOK;
5083  }
5084  }
5085  /* loop terminated naturally, so component does not have continuous or implicitly integer variables. */
5086  return SCIP_OKAY;
5087 
5088  COMPONENTOK:
5089  ;
5090  }
5091 
5092  /* @todo online create the conflict graph for the variable in the current component */
5093  /* possibly create conflict graph; graph is not created if no cliques are present */
5094  if ( selectedtype == SCIP_VARTYPE_BINARY && (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5095  || tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
5096  {
5097  SCIP_CALL( createConflictGraphSST(scip, &varconflicts, permvars, npermvars, permvarmap) );
5098  conflictgraphcreated = varconflicts != NULL;
5099  }
5100 
5101  /* allocate data structures necessary for orbit computations and conflict graph */
5102  SCIP_CALL( SCIPallocBufferArray(scip, &inactiveperms, nperms) );
5103  SCIP_CALL( SCIPallocBufferArray(scip, &orbits, npermvars) );
5104  SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, npermvars) );
5105 
5106  if ( conflictgraphcreated )
5107  {
5108  SCIP_CALL( SCIPallocClearBufferArray(scip, &orbitvarinconflict, npermvars) );
5109  }
5110 
5111  SCIPdebugMsg(scip, "Start selection of orbits and leaders for Schreier Sims constraints.\n");
5112  SCIPdebugMsg(scip, "orbitidx\tleaderidx\torbitsize\n");
5113 
5114  if ( nchgbds != NULL )
5115  *nchgbds = 0;
5116 
5117  /* initialize array indicating whether permutations shall not be considered for orbit permutations */
5118  for (c = 0; c < ncomponents; ++c)
5119  {
5120  for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
5121  {
5122  if ( c == cidx )
5123  inactiveperms[components[p]] = FALSE;
5124  else
5125  inactiveperms[components[p]] = TRUE;
5126  }
5127  }
5128  ninactiveperms = nperms - componentbegins[cidx + 1] + componentbegins[cidx];
5129 
5130  /* as long as the stabilizer is non-trivial, add Schreier Sims constraints */
5131  norbitleadercomponent = 0;
5132  while ( ninactiveperms < nperms )
5133  {
5134  int nchanges = 0;
5135 
5136  /* compute orbits w.r.t. active perms */
5137  SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, npermvars, permstrans, nperms, inactiveperms,
5138  orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent,
5139  componentblocked, ncomponents, nmovedpermvars) );
5140 
5141  /* stop if we require pure components and a component contains variables of different types */
5142  if ( ! mixedcomponents )
5143  {
5144  for (p = 0; p < norbits; ++p)
5145  {
5146  /* stop if the first element of an orbits has the wrong vartype */
5147  if ( SCIPvarGetType(permvars[orbits[orbitbegins[p]]]) != selectedtype )
5148  {
5149  success = FALSE;
5150  break;
5151  }
5152  }
5153  }
5154 
5155  if ( ! success )
5156  break;
5157 
5158  /* update symmetry information of conflict graph */
5159  if ( conflictgraphcreated )
5160  {
5161  SCIP_CALL( updateSymInfoConflictGraphSST(scip, varconflicts, permvars, npermvars, orbits, orbitbegins,
5162  norbits) );
5163  }
5164 
5165  /* possibly adapt the leader and tie-break rule */
5166  if ( leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT && ! conflictgraphcreated )
5167  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
5168  if ( leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT && selectedtype != SCIP_VARTYPE_BINARY )
5169  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
5170  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && ! conflictgraphcreated )
5171  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
5172  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && selectedtype != SCIP_VARTYPE_BINARY )
5173  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
5174 
5175  /* select orbit and leader */
5176  SCIP_CALL( selectOrbitLeaderSSTConss(scip, varconflicts, permvars, npermvars, orbits, orbitbegins,
5177  norbits, propdata->sstleaderrule, propdata->ssttiebreakrule, selectedtype, &orbitidx, &orbitleaderidx,
5178  orbitvarinconflict, &norbitvarinconflict, &success) );
5179 
5180  if ( ! success )
5181  break;
5182 
5183  assert( 0 <= orbitidx && orbitidx < norbits );
5184  assert( 0 <= orbitleaderidx && orbitleaderidx < orbitbegins[orbitidx + 1] - orbitbegins[orbitidx] );
5185  SCIPdebugMsg(scip, "%d\t\t%d\t\t%d\n", orbitidx, orbitleaderidx, orbitbegins[orbitidx + 1] - orbitbegins[orbitidx]);
5186 
5187  /* add Schreier Sims constraints for the selected orbit and update Schreier Sims table */
5188  SCIP_CALL( addSSTConssOrbitAndUpdateSST(scip, varconflicts, propdata, permvars,
5189  orbits, orbitbegins, orbitidx, orbitleaderidx, orbitvarinconflict, norbitvarinconflict, &nchanges) );
5190 
5191  ++norbitleadercomponent;
5192 
5193  if ( nchgbds != NULL )
5194  *nchgbds += nchanges;
5195 
5196  /* deactivate permutations that move the orbit leader */
5197  posleader = orbits[orbitbegins[orbitidx] + orbitleaderidx];
5198  for (p = 0; p < nperms; ++p)
5199  {
5200  if ( inactiveperms[p] )
5201  continue;
5202 
5203  if ( permstrans[posleader][p] != posleader )
5204  {
5205  inactiveperms[p] = TRUE;
5206  ++ninactiveperms;
5207  }
5208  }
5209  }
5210 
5211  /* if Schreier Sims constraints have been added, store that Schreier Sims has been used for this component */
5212  if ( norbitleadercomponent > 0 )
5213  componentblocked[cidx] |= SYM_HANDLETYPE_SST;
5214 
5215  if ( conflictgraphcreated )
5216  {
5217  SCIPfreeBufferArray(scip, &orbitvarinconflict);
5218  }
5219  SCIPfreeBufferArray(scip, &orbitbegins);
5220  SCIPfreeBufferArray(scip, &orbits);
5221  if ( varconflicts != NULL )
5222  {
5223  /* nconflictvars at construction is npermvars */
5224  SCIP_CALL( freeConflictGraphSST(scip, &varconflicts, npermvars) );
5225  }
5226  SCIPfreeBufferArray(scip, &inactiveperms);
5227 
5228  return SCIP_OKAY;
5229 }
5230 
5231 
5232 /** orbitopal reduction */
5233 static
5235  SCIP* scip, /**< SCIP instance */
5236  SCIP_PROPDATA* propdata, /**< propdata */
5237  int id, /**< ID for orbitope constraint (needed for name) */
5238  int** varidxmatrix, /**< matrix containing variable indices in orbitope matrix */
5239  int nrows, /**< number of rows of orbitope */
5240  int ncols, /**< number of columns of orbitope */
5241  SCIP_Bool* success /**< pointer to store whether orbitope could be added successfully */
5242  )
5243 {
5244  char name[SCIP_MAXSTRLEN];
5245  int i;
5246  int j;
5247 
5248  SCIP_Bool ispporbitope;
5249  SCIP_VAR*** varmatrix;
5250  SCIP_Bool* pprows;
5251  int npprows;
5252  SCIP_ORBITOPETYPE type;
5253 
5254  assert( scip != NULL );
5255  assert( propdata != NULL );
5256  assert( propdata->usedynamicprop );
5257  assert( varidxmatrix != NULL );
5258  assert( nrows > 0 );
5259  assert( ncols > 0 );
5260  assert( success != NULL );
5261 
5262  *success = FALSE;
5263 
5264  /* add linear constraints x_1 >= x_2 >= ... >= x_ncols for single-row orbitopes */
5265  if ( nrows == 1 )
5266  {
5267  /* restrict to the packing and partitioning rows */
5268  SCIP_CONS* cons;
5269  SCIP_VAR* consvars[2];
5270  SCIP_Real conscoefs[2] = { -1.0, 1.0 };
5271 
5272  /* for all adjacent column pairs, add linear constraint */
5273  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genlinconss,
5274  &propdata->genlinconsssize, propdata->ngenlinconss + ncols - 1) );
5275  for (i = 0; i < ncols - 1; ++i)
5276  {
5277  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_1row_comp_%d_col%d", id, i);
5278 
5279  consvars[0] = propdata->permvars[varidxmatrix[0][i]];
5280  consvars[1] = propdata->permvars[varidxmatrix[0][i + 1]];
5281 
5282  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, consvars, conscoefs, -SCIPinfinity(scip), 0.0,
5283  propdata->conssaddlp, propdata->conssaddlp, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE ) );
5284 
5285  SCIP_CALL( SCIPaddCons(scip, cons) );
5286  propdata->genlinconss[propdata->ngenlinconss++] = cons;
5287  }
5288 
5289  *success = TRUE;
5290  return SCIP_OKAY;
5291  }
5292 
5293  /* for only 2 columns, the the component can be completely handled by lexicographic reduction */
5294  if ( ncols == 2 && propdata->lexreddata != NULL )
5295  {
5296  int* orbisackperm;
5297 
5298  /* If the component is an orbitope with 2 columns, then there is 1 generator of order 2. */
5299  orbisackperm = propdata->perms[propdata->components[propdata->componentbegins[id]]];
5300 
5301  SCIP_CALL( SCIPlexicographicReductionAddPermutation(scip, propdata->lexreddata,
5302  propdata->permvars, propdata->npermvars, orbisackperm, (SYM_SYMTYPE) propdata->symtype,
5303  propdata->permvardomaincenter, TRUE, success) );
5304  if ( *success )
5305  return SCIP_OKAY;
5306  }
5307 
5308  /* create orbitope variable matrix */
5309  SCIP_CALL( SCIPallocBufferArray(scip, &varmatrix, nrows) );
5310  for (i = 0; i < nrows; ++i)
5311  {
5312  SCIP_CALL( SCIPallocBufferArray(scip, &varmatrix[i], ncols) );
5313  for (j = 0; j < ncols; ++j)
5314  varmatrix[i][j] = propdata->permvars[varidxmatrix[i][j]];
5315  }
5316 
5317  pprows = NULL;
5318  SCIP_CALL( SCIPisPackingPartitioningOrbitope(scip, varmatrix, nrows, ncols, &pprows, &npprows, &type) );
5319 
5320  /* does it have at least 3 packing-partitioning rows? */
5321  ispporbitope = npprows >= 3; /* (use same magic number as cons_orbitope.c) */
5322 
5323  if ( ispporbitope ) /* @todo if it's a pporbitope, we do it statically right now. */
5324  {
5325  /* restrict to the packing and partitioning rows */
5326  SCIP_CONS* cons;
5327  SCIP_VAR*** ppvarsarrayonlypprows;
5328  int r;
5329 
5330  assert( pprows != NULL );
5331 
5332  SCIP_CALL( SCIPallocBufferArray(scip, &ppvarsarrayonlypprows, npprows) );
5333 
5334  r = 0;
5335  for (i = 0; i < nrows; ++i)
5336  {
5337  if ( pprows[i] )
5338  {
5339  assert( r < npprows );
5340  ppvarsarrayonlypprows[r++] = varmatrix[i];
5341  }
5342  }
5343  assert( r == npprows );
5344 
5345  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_pp_comp_%d", id);
5346  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, ppvarsarrayonlypprows, SCIP_ORBITOPETYPE_PACKING,
5347  npprows, ncols, FALSE, FALSE, FALSE, FALSE, propdata->conssaddlp,
5348  TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5349 
5350  SCIP_CALL( SCIPaddCons(scip, cons) );
5351 
5352  /* check whether we need to resize */
5353  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genlinconss,
5354  &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
5355  /* @todo we add orbitopes to the dynamically sized array `genlinconss` instead of `genorbconss` to ensure
5356  * compatability with the static orbitope function, which allocates this array statically
5357  */
5358  propdata->genlinconss[propdata->ngenlinconss++] = cons;
5359  *success = TRUE;
5360 
5361  SCIPfreeBufferArray(scip, &ppvarsarrayonlypprows);
5362  }
5363  else
5364  {
5365  /* use orbitopal reduction for component */
5366  SCIP_COLUMNORDERING columnordering;
5367  SCIP_VAR** orbitopevarmatrix;
5368  int nelem;
5369  int pos = 0;
5370 
5371  /* variable array */
5372  nelem = nrows * ncols;
5373  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix, nelem) );
5374  for (i = 0; i < nrows; ++i)
5375  {
5376  for (j = 0; j < ncols; ++j)
5377  orbitopevarmatrix[pos++] = varmatrix[i][j];
5378  }
5379 
5380  /* get column ordering */
5381  columnordering = SCIPorbitopalReductionGetDefaultColumnOrdering(propdata->orbitopalreddata);
5382 
5383  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_full_comp_%d", id);
5384  SCIP_CALL( SCIPorbitopalReductionAddOrbitope(scip, propdata->orbitopalreddata,
5385  SCIP_ROWORDERING_BRANCHING, columnordering,
5386  orbitopevarmatrix, nrows, ncols, success) );
5387  *success = TRUE;
5388 
5389  SCIPfreeBufferArray(scip, &orbitopevarmatrix);
5390  }
5391 
5392  SCIPfreeBlockMemoryArrayNull(scip, &pprows, nrows);
5393 
5394  for (i = nrows - 1; i >= 0; --i)
5395  {
5396  SCIPfreeBufferArray(scip, &varmatrix[i]);
5397  }
5398  SCIPfreeBufferArray(scip, &varmatrix);
5399 
5400  return SCIP_OKAY;
5401 }
5402 
5403 
5404 /** applies pp-orbitope upgrade if at least 50% of the permutations in a component correspond to pp-orbisacks */
5405 static
5407  SCIP* scip, /**< SCIP instance */
5408  SCIP_PROPDATA* propdata, /**< propdata */
5409  int** componentperms, /**< permutations in the component */
5410  int componentsize, /**< number of permutations in the component */
5411  SCIP_Bool hassignedperm, /**< whether the component has a signed permutation */
5412  SCIP_Bool* success /**< whether the packing partitioning upgrade succeeded */
5413  )
5414 {
5415  int c;
5416  int i;
5417  int j;
5418  int p;
5419  int* perm;
5420  SCIP_CONSHDLR* setppcconshdlr;
5421  SCIP_CONS** setppcconss;
5422  SCIP_CONS* cons;
5423  SCIP_CONS** setppconsssort;
5424  int nsetppconss;
5425  int nsetppcvars;
5426  SCIP_VAR** setppcvars;
5427  int nsetppcconss;
5428  int** pporbisackperms;
5429  int npporbisackperms;
5430  SCIP_VAR* var;
5431  int varid;
5432  SCIP_CONS*** permvarssetppcconss;
5433  int* npermvarssetppcconss;
5434  int* maxnpermvarssetppcconss;
5435  int maxntwocycles;
5436  int ntwocycles;
5437 
5438  assert( scip != NULL );
5439  assert( propdata != NULL );
5440  assert( componentperms != NULL );
5441  assert( componentsize > 0 );
5442  assert( success != NULL );
5443 
5444  /* we did not upgrade yet */
5445  *success = FALSE;
5446 
5447  /* currently, we cannot handle signed permutations */
5448  if ( hassignedperm )
5449  return SCIP_OKAY;
5450 
5451  setppcconshdlr = SCIPfindConshdlr(scip, "setppc");
5452  if ( setppcconshdlr == NULL )
5453  return SCIP_OKAY;
5454 
5455  nsetppcconss = SCIPconshdlrGetNConss(setppcconshdlr);
5456  if ( nsetppcconss == 0 )
5457  return SCIP_OKAY;
5458 
5459  setppcconss = SCIPconshdlrGetConss(setppcconshdlr);
5460  assert( setppcconss != NULL );
5461 
5462  SCIP_CALL( ensureSymmetryPermvarmapComputed(scip, propdata) );
5463 
5464  /* collect non-covering constraints and sort by pointer for easy intersection finding */
5465  SCIP_CALL( SCIPallocBufferArray(scip, &setppconsssort, nsetppcconss) );
5466  nsetppconss = 0;
5467  for (c = 0; c < nsetppcconss; ++c)
5468  {
5469  cons = setppcconss[c];
5470 
5471  /* only packing or partitioning constraints, no covering types */
5472  if ( SCIPgetTypeSetppc(scip, cons) == SCIP_SETPPCTYPE_COVERING )
5473  continue;
5474 
5475  setppconsssort[nsetppconss++] = cons;
5476  }
5477  SCIPsortPtr((void**) setppconsssort, sortByPointerValue, nsetppcconss);
5478 
5479  /* For each permvar, introduce an array of setppc constraints (initially NULL) for each variable,
5480  * and populate it with the setppc constraints that it contains. This array follows the ordering by cons ptr address.
5481  */
5482  SCIP_CALL( SCIPallocCleanBufferArray(scip, &permvarssetppcconss, propdata->npermvars) );
5483  SCIP_CALL( SCIPallocCleanBufferArray(scip, &npermvarssetppcconss, propdata->npermvars) );
5484  SCIP_CALL( SCIPallocCleanBufferArray(scip, &maxnpermvarssetppcconss, propdata->npermvars) );
5485  for (c = 0; c < nsetppconss; ++c)
5486  {
5487  assert( c >= 0 );
5488  assert( c < nsetppconss );
5489  cons = setppconsssort[c];
5490  assert( cons != NULL );
5491 
5492  setppcvars = SCIPgetVarsSetppc(scip, cons);
5493  nsetppcvars = SCIPgetNVarsSetppc(scip, cons);
5494 
5495  for (i = 0; i < nsetppcvars; ++i)
5496  {
5497  var = setppcvars[i];
5498  assert( var != NULL );
5499  varid = SCIPhashmapGetImageInt(propdata->permvarmap, (void*) var);
5500  assert( varid == INT_MAX || varid < propdata->npermvars );
5501  assert( varid >= 0 );
5502  if ( varid < propdata->npermvars )
5503  {
5505  &(permvarssetppcconss[varid]), &maxnpermvarssetppcconss[varid], npermvarssetppcconss[varid] + 1) );
5506  assert( npermvarssetppcconss[varid] < maxnpermvarssetppcconss[varid] );
5507  permvarssetppcconss[varid][npermvarssetppcconss[varid]++] = cons;
5508  }
5509  }
5510  }
5511 
5512  /* for all permutations, test involutions on binary variables and test if they are captured by setppc conss */
5513  SCIP_CALL( SCIPallocBufferArray(scip, &pporbisackperms, componentsize) );
5514  maxntwocycles = 0;
5515  npporbisackperms = 0;
5516  for (p = 0; p < componentsize; ++p)
5517  {
5518  perm = componentperms[p];
5519  ntwocycles = 0;
5520 
5521  /* check if the binary orbits are involutions */
5522  for (i = 0; i < propdata->npermvars; ++i)
5523  {
5524  j = perm[i];
5525 
5526  /* ignore fixed points in permutation */
5527  if ( i == j )
5528  continue;
5529  /* only check for situations where i and j are binary variables */
5530  assert( SCIPvarGetType(propdata->permvars[i]) == SCIPvarGetType(propdata->permvars[j]) );
5531  if ( SCIPvarGetType(propdata->permvars[i]) != SCIP_VARTYPE_BINARY )
5532  continue;
5533  /* the permutation must be an involution on binary variables */
5534  if ( perm[j] != i )
5535  goto NEXTPERMITER;
5536  /* i and j are a two-cycle, so we find this once for i and once for j. Only handle this once for i < j. */
5537  if ( i > j )
5538  continue;
5539  /* disqualify permutation if i and j are not in a common set packing constraint */
5540  if ( !checkSortedArraysHaveOverlappingEntry((void**) permvarssetppcconss[i], npermvarssetppcconss[i],
5541  (void**) permvarssetppcconss[j], npermvarssetppcconss[j], sortByPointerValue) )
5542  goto NEXTPERMITER;
5543  ++ntwocycles;
5544  }
5545 
5546  /* The permutation qualifies if all binary variables are either a reflection or in a 2-cycle. There must be at
5547  * least one binary 2-cycle, because otherwise the permutation is the identity, or it permutes
5548  * nonbinary variables.
5549  */
5550  if ( ntwocycles > 0 )
5551  {
5552  pporbisackperms[npporbisackperms++] = perm;
5553  if ( ntwocycles > maxntwocycles )
5554  maxntwocycles = ntwocycles;
5555  }
5556 
5557  NEXTPERMITER:
5558  ;
5559  }
5560 
5561  /* if at least 50% of such permutations are packing-partitioning type, apply packing upgrade */
5562  if ( npporbisackperms * 2 >= componentsize )
5563  {
5564  char name[SCIP_MAXSTRLEN];
5565  SCIP_VAR** ppvarsblock;
5566  SCIP_VAR*** ppvarsmatrix;
5567  SCIP_VAR** row;
5568  int nrows;
5569 
5570  assert( npporbisackperms > 0 );
5571  assert( maxntwocycles > 0 );
5572 
5573  /* instead of allocating and re-allocating multiple times, recycle the ppvars array */
5574  SCIP_CALL( SCIPallocBufferArray(scip, &ppvarsblock, 2 * maxntwocycles) );
5575  SCIP_CALL( SCIPallocBufferArray(scip, &ppvarsmatrix, maxntwocycles) );
5576  for (i = 0; i < maxntwocycles; ++i)
5577  ppvarsmatrix[i] = &(ppvarsblock[2 * i]);
5578 
5579  /* for each of these perms, create the packing orbitope matrix and add constraint*/
5580  for (p = 0; p < npporbisackperms; ++p)
5581  {
5582  perm = pporbisackperms[p];
5583 
5584  /* populate ppvarsmatrix */
5585  nrows = 0;
5586  for (i = 0; i < propdata->npermvars; ++i)
5587  {
5588  j = perm[i];
5589 
5590  /* ignore fixed points in permutation, and only consider rows with i < j */
5591  if ( i >= j )
5592  continue;
5593  /* only for situations where i and j are binary variables */
5594  assert( SCIPvarGetType(propdata->permvars[i]) == SCIPvarGetType(propdata->permvars[j]) );
5595  if ( SCIPvarGetType(propdata->permvars[i]) != SCIP_VARTYPE_BINARY )
5596  continue;
5597  assert( perm[j] == i );
5598  assert( checkSortedArraysHaveOverlappingEntry((void**) permvarssetppcconss[i], npermvarssetppcconss[i],
5599  (void**) permvarssetppcconss[j], npermvarssetppcconss[j], sortByPointerValue) );
5600 
5601  assert( nrows < maxntwocycles );
5602  row = ppvarsmatrix[nrows++];
5603  row[0] = propdata->permvars[i];
5604  row[1] = propdata->permvars[j];
5605  assert( row[0] != row[1] );
5606  }
5607  assert( nrows > 0 );
5608 
5609  /* create constraint, use same parameterization as in orbitope packing partitioning checker */
5610  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_pp_upgrade_lexred%d", p);
5611  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, ppvarsmatrix, SCIP_ORBITOPETYPE_PACKING, nrows, 2,
5612  FALSE, FALSE, FALSE, FALSE,
5613  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5614 
5615  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genlinconss,
5616  &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
5617  /* @todo we add orbitopes to the dynamically sized array `genlinconss` instead of `genorbconss` to ensure
5618  * compatability with the static orbitope function, which allocates this array statically
5619  */
5620  propdata->genlinconss[propdata->ngenlinconss++] = cons;
5621  SCIP_CALL( SCIPaddCons(scip, cons) );
5622  }
5623 
5624  SCIPfreeBufferArray(scip, &ppvarsmatrix);
5625  SCIPfreeBufferArray(scip, &ppvarsblock);
5626 
5627  *success = TRUE;
5628  }
5629 
5630  /* free pp orbisack array */
5631  SCIPfreeBufferArray(scip, &pporbisackperms);
5632 
5633  /* clean the non-clean arrays */
5634  for (varid = 0; varid < propdata->npermvars; ++varid)
5635  {
5636  assert( (permvarssetppcconss[varid] == NULL) == (maxnpermvarssetppcconss[varid] == 0) );
5637  assert( npermvarssetppcconss[varid] >= 0 );
5638  assert( maxnpermvarssetppcconss[varid] >= 0 );
5639  assert( npermvarssetppcconss[varid] <= maxnpermvarssetppcconss[varid] );
5640  if ( npermvarssetppcconss[varid] == 0 )
5641  continue;
5642  SCIPfreeBlockMemoryArray(scip, &permvarssetppcconss[varid], maxnpermvarssetppcconss[varid]);
5643  permvarssetppcconss[varid] = NULL;
5644  npermvarssetppcconss[varid] = 0;
5645  maxnpermvarssetppcconss[varid] = 0;
5646  }
5647  SCIPfreeCleanBufferArray(scip, &maxnpermvarssetppcconss);
5648  SCIPfreeCleanBufferArray(scip, &npermvarssetppcconss);
5649  SCIPfreeCleanBufferArray(scip, &permvarssetppcconss);
5650  SCIPfreeBufferArray(scip, &setppconsssort);
5651 
5652  return SCIP_OKAY;
5653 }
5654 
5655 
5656 /** dynamic permutation lexicographic reduction */
5657 static
5659  SCIP* scip, /**< SCIP instance */
5660  SCIP_PROPDATA* propdata, /**< propdata */
5661  int cidx /**< index of component */
5662  )
5663 {
5664  int componentsize;
5665  int** componentperms;
5666  int p;
5667 
5668  SCIP_Bool checkorbired;
5669  SCIP_Bool checklexred;
5670  SCIP_Bool success;
5671  SCIP_PARAM* checkpporbisack;
5672 
5673  assert( scip != NULL );
5674  assert( propdata != NULL );
5675  assert( ISORBITALREDUCTIONACTIVE(propdata->usesymmetry)
5676  || (
5677  ISSYMRETOPESACTIVE(propdata->usesymmetry)
5678  && propdata->usedynamicprop
5679  && propdata->addsymresacks
5680  ) );
5681  assert( propdata->nperms > 0 );
5682  assert( 0 <= cidx && cidx < propdata->ncomponents );
5683  assert( propdata->componentblocked != NULL );
5684 
5685  /* exit if component is already blocked */
5686  if ( propdata->componentblocked[cidx] )
5687  return SCIP_OKAY;
5688 
5689  /* in this function orbital reduction or dynamic lexicographic reduction propagation must be enabled */
5690  checkorbired = ISORBITALREDUCTIONACTIVE(propdata->usesymmetry);
5691  checklexred = ISSYMRETOPESACTIVE(propdata->usesymmetry) && propdata->usedynamicprop && propdata->addsymresacks;
5692  assert( checkorbired || checklexred );
5693 
5695  assert( propdata->nmovedpermvars );
5696 
5697  /* collect the permutations of this component */
5698  componentsize = propdata->componentbegins[cidx + 1] - propdata->componentbegins[cidx];
5699  SCIP_CALL( SCIPallocBufferArray(scip, &componentperms, componentsize) );
5700  for (p = 0; p < componentsize; ++p)
5701  componentperms[p] = propdata->perms[propdata->components[propdata->componentbegins[cidx] + p]];
5702 
5703  /* check if many component permutations contain many packing partitioning orbisacks
5704  *
5705  * 1. Get the checkpporbisack param from the parameter hashset. This returns NULL if it is not initialized,
5706  * likely because the orbisack constraint handler is not loaded.
5707  * 2. If the param is not NULL, then we only do the packing-partitioning upgrade step if its value is TRUE.
5708  * Packing-partitioning orbitopes are only implemented for binary orbitopes, so binary variables must be moved.
5709  */
5710  checkpporbisack = SCIPgetParam(scip, "constraints/orbisack/checkpporbisack");
5711  if ( ( checkpporbisack == NULL || SCIPparamGetBool(checkpporbisack) == TRUE ) && propdata->nmovedbinpermvars > 0 )
5712  {
5714  componentperms, componentsize, propdata->componenthassignedperm[cidx], &success) );
5715 
5716  if ( success )
5717  {
5718  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
5719  goto FINISHCOMPONENT;
5720  }
5721  }
5722 
5723  /* handle component permutations with orbital reduction */
5724  if ( checkorbired && !propdata->componenthassignedperm[cidx] )
5725  {
5726  SCIP_CALL( SCIPorbitalReductionAddComponent(scip, propdata->orbitalreddata,
5727  propdata->permvars, propdata->npermvars, componentperms, componentsize, &success) );
5728  if ( success )
5729  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_ORBITALREDUCTION;
5730  }
5731 
5732  /* handle component permutations with the dynamic lexicographic reduction propagator */
5733  if ( checklexred )
5734  {
5735  /* handle every permutation in the component with the dynamic lexicographic reduction propagator */
5736  for (p = 0; p < componentsize; ++p)
5737  {
5738  assert( componentperms[p] != NULL );
5739  SCIP_CALL( SCIPlexicographicReductionAddPermutation(scip, propdata->lexreddata,
5740  propdata->permvars, propdata->npermvars, componentperms[p],
5741  (SYM_SYMTYPE) propdata->symtype, propdata->permvardomaincenter, TRUE, &success) );
5742  if ( success )
5743  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
5744  }
5745  }
5746 
5747  FINISHCOMPONENT:
5748  /* if it got blocked here */
5749  if ( propdata->componentblocked[cidx] )
5750  ++propdata->ncompblocked;
5751 
5752  SCIPfreeBufferArray(scip, &componentperms);
5753 
5754  return SCIP_OKAY;
5755 }
5756 
5757 
5758 /** displays statistics on the used symmetry handling methods */
5759 static
5761  SCIP* scip, /**< SCIP instance */
5762  SCIP_PROPDATA* propdata /**< data of symmetry propagator */
5763  )
5764 {
5765  int ncomponentshandled;
5766  int i;
5767 
5768  assert( scip != NULL );
5769  assert( propdata != NULL );
5770 
5771  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "dynamic symmetry handling statistics:\n");
5772  if ( propdata->orbitopalreddata )
5773  {
5774  SCIP_CALL( SCIPorbitopalReductionPrintStatistics(scip, propdata->orbitopalreddata) );
5775  }
5776  if ( propdata->orbitalreddata )
5777  {
5778  SCIP_CALL( SCIPorbitalReductionPrintStatistics(scip, propdata->orbitalreddata) );
5779  }
5780  if ( propdata->lexreddata )
5781  {
5782  SCIP_CALL( SCIPlexicographicReductionPrintStatistics(scip, propdata->lexreddata) );
5783  }
5784  if ( propdata->ncomponents >= 0 )
5785  {
5786  /* report the number of handled components
5787  *
5788  * Since SST is compatible with static symresacks, the propdata->ncompblocked counter is not the number of
5789  * handled components. Compute this statistic based on the componentblocked array.
5790  */
5791  ncomponentshandled = 0;
5792  for (i = 0; i < propdata->ncomponents; ++i)
5793  {
5794  if ( propdata->componentblocked[i] )
5795  ++ncomponentshandled;
5796  }
5797  assert( propdata->ncompblocked <= ncomponentshandled );
5798  assert( ncomponentshandled <= propdata->ncomponents );
5799  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "handled %d out of %d symmetry components\n",
5800  ncomponentshandled, propdata->ncomponents);
5801  }
5802 
5803  return SCIP_OKAY;
5804 }
5805 
5806 /** handles orbitope action by static or dynamic symmetry handling methods */
5807 static
5809  SCIP* scip, /**< SCIP instance */
5810  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5811  int id, /**< ID of orbitope (used for constraint name) */
5812  int** varidxmatrix, /**< matrix containing variable indices of orbitope */
5813  int nrows, /**< number of rows of matrix */
5814  int ncols, /**< number of columns of matrix */
5815  SCIP_Bool* success /**< pointer to store whether orbitope could be added successfully */
5816  )
5817 {
5818  assert( scip != NULL );
5819  assert( propdata != NULL );
5820  assert( varidxmatrix != NULL );
5821  assert( nrows > 0 );
5822  assert( ncols > 0 );
5823  assert( success != NULL );
5824 
5825  *success = FALSE;
5826 
5827  /* dynamic propagation */
5828  if ( propdata->usedynamicprop )
5829  {
5830  SCIP_CALL( addOrbitopesDynamic(scip, propdata, id, varidxmatrix, nrows, ncols, success) );
5831  }
5832  /* static variant only for binary variables */
5833  else if ( propdata->binvaraffected )
5834  {
5835  char name[SCIP_MAXSTRLEN];
5836  SCIP_VAR*** orbitopematrix;
5837  SCIP_CONS* cons;
5838  int i;
5839  int j;
5840  int nbinrows = 0;
5841 
5842  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_component_%d", id);
5843 
5844  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix, nrows) );
5845  for (i = 0; i < nrows; ++i)
5846  {
5847  /* skip rows without binary variables */
5848  if ( ! SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][0]]) )
5849  continue;
5850 
5851  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix[nbinrows], ncols) );
5852  for (j = 0; j < ncols; ++j)
5853  {
5854  assert( SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][j]]) );
5855  orbitopematrix[nbinrows][j] = propdata->permvars[varidxmatrix[i][j]];
5856  }
5857  ++nbinrows;
5858  }
5859 
5860  if ( nbinrows > 0 )
5861  {
5862  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopematrix, SCIP_ORBITOPETYPE_FULL,
5863  nbinrows, ncols, propdata->usedynamicprop /* @todo disable */, FALSE, FALSE, FALSE,
5864  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5865 
5866  SCIP_CALL( SCIPaddCons(scip, cons) );
5867 
5868  /* do not release constraint here - will be done later */
5869  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
5870  &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
5871  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5872  ++propdata->norbitopes;
5873 
5874  *success = TRUE;
5875  }
5876 
5877  for (i = nbinrows - 1; i >= 0; --i)
5878  {
5879  SCIPfreeBufferArray(scip, &orbitopematrix[i]);
5880  }
5881  SCIPfreeBufferArray(scip, &orbitopematrix);
5882  }
5883 
5884  return SCIP_OKAY;
5885 }
5886 
5887 /** handles binary double lex matrix by adding static orbitope constraints
5888  *
5889  * @todo Extend method to general variable types and dynamic variable orders.
5890  */
5891 static
5893  SCIP* scip, /**< SCIP instance */
5894  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5895  int id, /**< ID of double lex matrix (used for constraint names) */
5896  int** varidxmatrix, /**< matrix containing variable indices of double lex matrix */
5897  int nrows, /**< number of rows of matrix */
5898  int ncols, /**< number of columns of matrix */
5899  int* rowsbegin, /**< array indicating where a new row block begins */
5900  int* colsbegin, /**< array indicating where a new column block begins */
5901  int nrowblocks, /**< number of row blocks */
5902  int ncolblocks, /**< number of column blocks */
5903  SCIP_Bool* success /**< pointer to store whether orbitope could be added successfully */
5904  )
5905 {
5906  char name[SCIP_MAXSTRLEN];
5907  SCIP_VAR*** orbitopematrix;
5908  SCIP_CONS* cons;
5909  int maxdim;
5910  int i;
5911  int p;
5912  int j;
5913  int col;
5914  int nbinrows;
5915 
5916  assert( scip != NULL );
5917  assert( propdata != NULL );
5918  assert( varidxmatrix != NULL );
5919  assert( nrows > 0 );
5920  assert( ncols > 0 );
5921  assert( rowsbegin != NULL );
5922  assert( colsbegin != NULL );
5923  assert( nrowblocks > 0 );
5924  assert( ncolblocks > 0 );
5925  assert( success != NULL );
5926 
5927  /* ensure that we can store orbitope constraints in probdata */
5928  SCIP_CALL( ensureDynamicConsArrayAllocatedAndSufficientlyLarge(scip, &propdata->genorbconss,
5929  &propdata->genorbconsssize, propdata->ngenorbconss + nrowblocks + ncolblocks) );
5930 
5931  maxdim = MAX(nrows, ncols);
5932  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix, maxdim) );
5933  for (i = 0; i < maxdim; ++i)
5934  {
5935  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix[i], maxdim) );
5936  }
5937 
5938  /* add orbitopes corresponding to column blocks of doublelexmatrix */
5939  for (p = 0; p < ncolblocks; ++p)
5940  {
5941  nbinrows = 0;
5942  for (i = 0; i < nrows; ++i)
5943  {
5944  /* skip rows that do not contain binary variables */
5945  if ( ! SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][colsbegin[p]]]) )
5946  continue;
5947 
5948  for (col = 0, j = colsbegin[p]; j < colsbegin[p + 1]; ++j, ++col)
5949  {
5950  assert( SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][j]]) );
5951  orbitopematrix[nbinrows][col] = propdata->permvars[varidxmatrix[i][j]];
5952  }
5953  ++nbinrows;
5954  }
5955 
5956  if ( nbinrows > 0 )
5957  {
5958  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "doublelex_cols_%d_%d", id, p);
5959  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopematrix, SCIP_ORBITOPETYPE_FULL,
5960  nrows, colsbegin[p + 1] - colsbegin[p], FALSE, FALSE, TRUE, FALSE,
5961  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5962  SCIP_CALL( SCIPaddCons(scip, cons) );
5963  propdata->genorbconss[(propdata->ngenorbconss)++] = cons;
5964  /* do not release constraint here - will be done later */
5965  }
5966  }
5967 
5968  /* add orbitopes corresponding to row blocks of doublelexmatrix */
5969  for (p = 0; p < nrowblocks; ++p)
5970  {
5971  nbinrows = 0;
5972  for (i = 0; i < ncols; ++i)
5973  {
5974  /* skip rows that do not contain binary variables */
5975  if ( ! SCIPvarIsBinary(propdata->permvars[varidxmatrix[rowsbegin[p]][i]]) )
5976  continue;
5977 
5978  for (col = 0, j = rowsbegin[p]; j < rowsbegin[p + 1]; ++j, ++col)
5979  {
5980  assert( SCIPvarIsBinary(propdata->permvars[varidxmatrix[j][i]]) );
5981  orbitopematrix[nbinrows][col] = propdata->permvars[varidxmatrix[j][i]];
5982  }
5983  ++nbinrows;
5984  }
5985 
5986  if ( nbinrows > 0 )
5987  {
5988  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "doublelex_rows_%d_%d", id, p);
5989  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopematrix, SCIP_ORBITOPETYPE_FULL,
5990  ncols, rowsbegin[p + 1] - rowsbegin[p], FALSE, FALSE, TRUE, FALSE,
5991  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5992  SCIP_CALL( SCIPaddCons(scip, cons) );
5993  propdata->genorbconss[(propdata->ngenorbconss)++] = cons;
5994  /* do not release constraint here - will be done later */
5995  }
5996  }
5997 
5998  for (i = maxdim - 1; i >= 0; --i)
5999  {
6000  SCIPfreeBufferArray(scip, &orbitopematrix[i]);
6001  }
6002  SCIPfreeBufferArray(scip, &orbitopematrix);
6003 
6004  return SCIP_OKAY;
6005 }
6006 
6007 /** tries to handle symmetries of single lex matrices (orbitopes) or double lex matrices */
6008 static
6010  SCIP* scip, /**< SCIP instance */
6011  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6012  SCIP_Bool detectsinglelex, /**< whether single lex matrices shall be detected */
6013  int cidx /**< index of component */
6014  )
6015 {
6016  int** lexmatrix = NULL;
6017  int* lexrowsbegin = NULL;
6018  int* lexcolsbegin = NULL;
6019  int nrows;
6020  int ncols;
6021  int nrowmatrices;
6022  int ncolmatrices;
6023  int** perms;
6024  int compsize;
6025  int i;
6026  int p;
6027  SCIP_Bool isorbitope;
6028  SCIP_Bool success = FALSE;
6029 
6030  assert( scip != NULL );
6031  assert( propdata != NULL );
6032  assert( 0 <= cidx && cidx < propdata->ncomponents );
6033 
6034  /* exit if component is already blocked */
6035  if ( propdata->componentblocked[cidx] )
6036  return SCIP_OKAY;
6037 
6038  /* exit if component has non-standard permutations */
6039  if ( propdata->componenthassignedperm[cidx] )
6040  return SCIP_OKAY;
6041 
6042  /* exit if polyhedral methods are disabled when looking for double lex matrices */
6043  if ( !ISSYMRETOPESACTIVE(propdata->usesymmetry) && !detectsinglelex )
6044  return SCIP_OKAY;
6045 
6046  /* get permutations of component */
6047  compsize = propdata->componentbegins[cidx + 1] - propdata->componentbegins[cidx];
6048  SCIP_CALL( SCIPallocBufferArray(scip, &perms, compsize) );
6049  for (p = 0, i = propdata->componentbegins[cidx]; i < propdata->componentbegins[cidx + 1]; ++i)
6050  perms[p++] = propdata->perms[propdata->components[i]];
6051 
6052  SCIP_CALL( SCIPdetectSingleOrDoubleLexMatrices(scip, detectsinglelex, perms, compsize, propdata->npermvars,
6053  &success, &isorbitope, &lexmatrix, &nrows, &ncols,
6054  &lexrowsbegin, &lexcolsbegin, &nrowmatrices, &ncolmatrices) );
6055 
6056  SCIPfreeBufferArray(scip, &perms);
6057 
6058  /* possibly handle double lex matrix or orbitope */
6059  if ( success )
6060  {
6061  assert( lexmatrix != NULL );
6062  assert( nrows > 0 );
6063  assert( ncols > 0 );
6064 
6065  if ( isorbitope )
6066  {
6067  SCIP_CALL( handleOrbitope(scip, propdata, cidx, lexmatrix, nrows, ncols, &success) );
6068  }
6069  else
6070  {
6071  SCIP_Bool hasbinaryvar = FALSE;
6072 
6073  /* check whether a binary variable is contained in the matrix */
6074  for (i = 0; i < nrows && !hasbinaryvar; ++i)
6075  {
6076  for (p = 0; p < ncols; ++p)
6077  {
6078  if ( SCIPvarIsBinary(propdata->permvars[lexmatrix[i][p]]) )
6079  {
6080  hasbinaryvar = TRUE;
6081  break;
6082  }
6083  }
6084  }
6085 
6086  if ( hasbinaryvar )
6087  {
6088  SCIP_CALL( handleDoublelLexMatrix(scip, propdata, cidx, lexmatrix, nrows, ncols,
6089  lexrowsbegin, lexcolsbegin, nrowmatrices, ncolmatrices, &success) );
6090  }
6091  else
6092  success = FALSE;
6093  }
6094 
6095  /* free memory not needed anymore */
6096  for (i = nrows - 1; i >= 0; --i)
6097  {
6098  SCIPfreeBlockMemoryArray(scip, &lexmatrix[i], ncols);