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);
6099  }
6100  SCIPfreeBlockMemoryArray(scip, &lexmatrix, nrows);
6101  if ( ncolmatrices > 0 )
6102  {
6103  SCIPfreeBlockMemoryArray(scip, &lexcolsbegin, ncolmatrices);
6104  }
6105  if ( nrowmatrices > 0 )
6106  {
6107  SCIPfreeBlockMemoryArray(scip, &lexrowsbegin, nrowmatrices);
6108  }
6109  }
6110 
6111  if ( success )
6112  {
6113  propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
6114  ++(propdata->ncompblocked);
6115  }
6116 
6117  return SCIP_OKAY;
6118 }
6119 
6120 /** tries to handle subgroups of component */
6121 static
6123  SCIP* scip, /**< SCIP instance */
6124  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6125  int cidx /**< index of component */
6126  )
6127 {
6128  assert( scip != NULL );
6129  assert( propdata != NULL );
6130  assert( 0 <= cidx && cidx < propdata->ncomponents );
6131 
6132  /* exit if component is already blocked */
6133  if ( propdata->componentblocked[cidx] )
6134  return SCIP_OKAY;
6135 
6136  /* skip component if it has signed permutations */
6137  if ( propdata->componenthassignedperm[cidx] )
6138  return SCIP_OKAY;
6139 
6140  /* only run if subgroups shall be detected and we can handle them */
6141  if ( !propdata->usedynamicprop && ISSYMRETOPESACTIVE(propdata->usesymmetry) && propdata->detectsubgroups
6142  && propdata->binvaraffected && propdata->ncompblocked < propdata->ncomponents )
6143  {
6144  /* @todo also implement a dynamic variant */
6145  SCIP_CALL( detectAndHandleSubgroups(scip, propdata, cidx) );
6146  }
6147 
6148  return SCIP_OKAY;
6149 }
6150 
6151 
6152 /** tries to add symmetry handling methods to component of symmetry group
6153  *
6154  * For a component, we handle the symmetries as follows:
6155  * 1. If orbitope detection is enabled and the component is an orbitope: Apply one of the following:
6156  * 1.1. If dynamic symmetry handling methods are used:
6157  * 1.1.1. If the orbitope has a single row, add linear constraints x_1 >= x_2 ... >= x_n.
6158  * 1.1.2. If it has only two columns only, use lexicographic reduction; cf. symmetry_lexred.c
6159  * 1.1.3. If there are at least 3 binary rows with packing-partitioning constraints,
6160  * use a static packing-partitioning orbitopal fixing; cf. cons_orbitope.c
6161  * @todo make a dynamic adaptation for packing-partitioning orbitopes.
6162  * 1.1.4. If none of these standard cases apply, use dynamic orbitopal reduction; cf. symmetry_orbitopal.c
6163  * 1.2. If static symmetry handling methods are used: Use static orbitopal fixing (binary variables only);
6164  * cf. cons_orbitope.c
6165  * 2. If no dynamic symmetry handling methods are used, and if (orbitopal) subgroup detection is enabled,
6166  * detect those and add static orbitopes if necessary.
6167  * 3. Otherwise, if orbital reduction is enabled, or if dynamic methods are enabled and lexicographic reduction
6168  * propagations can be applied:
6169  * 3.1. If orbital reduction is enabled: Use orbital reduction.
6170  * 3.2. And, if dynamic methods and lexicographic for single permutations reduction are enabled, use that.
6171  * 4. Otherwise, if possible, use SST cuts.
6172  * 5. Otherwise, if possible, add symresacks (lexicographic reduction on binary variables using a static ordering).
6173  */
6174 static
6176  SCIP* scip, /**< SCIP instance */
6177  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6178  int cidx, /**< index of component */
6179  int* nchgbds /**< pointer to store number of bound changes (or NULL)*/
6180  )
6181 {
6182  SCIP_Bool useorbitalredorlexred;
6183 
6184  assert( scip != NULL );
6185  assert( propdata != NULL );
6186  assert( propdata->ncomponents >= 0 );
6187  assert( 0 <= cidx && cidx < propdata->ncomponents );
6188 
6189  /* ignore blocked components */
6190  if ( propdata->componentblocked[cidx] )
6191  return SCIP_OKAY;
6192 
6193  /* detect if orbital reduction or lexicographic reduction shall be applied */
6194  useorbitalredorlexred = ISORBITALREDUCTIONACTIVE(propdata->usesymmetry)
6195  || ( ISSYMRETOPESACTIVE(propdata->usesymmetry) && propdata->usedynamicprop && propdata->addsymresacks );
6196 
6197  /* try to apply symmetry handling methods */
6198  if ( propdata->detectdoublelex || propdata->detectorbitopes )
6199  {
6200  SCIP_Bool detectsinglelex;
6201 
6202  detectsinglelex = propdata->detectdoublelex ? FALSE : TRUE;
6203 
6204  SCIP_CALL( tryHandleSingleOrDoubleLexMatricesComponent(scip, propdata, detectsinglelex, cidx) );
6205  }
6206  SCIP_CALL( tryHandleSubgroups(scip, propdata, cidx) );
6207  if ( ISSSTACTIVE(propdata->usesymmetry) )
6208  {
6209  SCIP_CALL( addSSTConss(scip, propdata, useorbitalredorlexred, nchgbds, cidx) );
6210  }
6211  if ( useorbitalredorlexred )
6212  {
6213  SCIP_CALL( tryAddOrbitalRedLexRed(scip, propdata, cidx) );
6214  }
6215  SCIP_CALL( addSymresackConss(scip, propdata, cidx) );
6216 
6217  return SCIP_OKAY;
6218 }
6219 
6220 
6221 /** determines problem symmetries and activates symmetry handling methods */
6222 static
6224  SCIP* scip, /**< SCIP instance */
6225  SCIP_PROP* prop, /**< symmetry breaking propagator */
6226  int* nchgbds, /**< pointer to store number of bound changes (or NULL)*/
6227  SCIP_Bool* earlyterm /**< pointer to store whether we terminated early (or NULL) */
6228  )
6229 {
6230  SCIP_PROPDATA* propdata;
6231  int c;
6232 
6233  assert( prop != NULL );
6234  assert( scip != NULL );
6235 
6236  if ( nchgbds != NULL )
6237  *nchgbds = 0;
6238  if ( earlyterm != NULL )
6239  *earlyterm = FALSE;
6240 
6241  /* only allow symmetry handling methods if strong and weak dual reductions are permitted */
6242  if ( !SCIPallowStrongDualReds(scip) || !SCIPallowWeakDualReds(scip) )
6243  {
6244  if ( earlyterm != NULL )
6245  *earlyterm = TRUE;
6246  return SCIP_OKAY;
6247  }
6248 
6249  propdata = SCIPpropGetData(prop);
6250  assert( propdata != NULL );
6251  assert( propdata->usesymmetry >= 0 );
6252 
6253  /* if no symmetries may be handled, stop here */
6254  if ( propdata->usesymmetry == 0 )
6255  {
6256  if ( earlyterm != NULL )
6257  *earlyterm = TRUE;
6258  return SCIP_OKAY;
6259  }
6260 
6261  /* if symmetry handling methods have already been added */
6262  if ( propdata->triedaddsymmethods )
6263  {
6264  assert( propdata->nperms >= 0 );
6265 
6266  if ( earlyterm != NULL )
6267  *earlyterm = TRUE;
6268 
6269  return SCIP_OKAY;
6270  }
6271  assert( !propdata->triedaddsymmethods );
6272 
6273  /* compute symmetries, if it is not computed before */
6274  if ( !propdata->computedsymmetry )
6275  {
6276  /* verify that no symmetry information is present */
6277  assert( checkSymmetryDataFree(propdata) );
6279  }
6280 
6281  /* stop if symmetry computation failed, the reason should be given inside determineSymmetry */
6282  if ( !propdata->computedsymmetry )
6283  return SCIP_OKAY;
6284 
6285  /* mark that symmetry handling methods are now tried to be added */
6286  propdata->triedaddsymmethods = TRUE;
6287  assert( propdata->nperms >= 0 );
6288 
6289  /* no symmetries present, so nothing to be handled */
6290  if ( propdata->nperms == 0 )
6291  return SCIP_OKAY;
6292 
6293  /* compute components of symmetry group */
6294  SCIP_CALL( ensureSymmetryComponentsComputed(scip, propdata) );
6295  assert( propdata->ncomponents > 0 );
6296 
6297  /* iterate over components and handle each by suitable symmetry handling methods */
6298  for (c = 0; c < propdata->ncomponents; ++c)
6299  {
6300  SCIP_CALL( tryAddSymmetryHandlingMethodsComponent(scip, propdata, c, nchgbds) );
6301 
6302  if ( SCIPisStopped(scip) || propdata->ncompblocked >= propdata->ncomponents )
6303  break;
6304  }
6305 
6306 #ifdef SYMMETRY_STATISTICS
6307  SCIP_CALL( SCIPdisplaySymmetryStatistics(scip, propdata) );
6308 #endif
6309 
6310  return SCIP_OKAY;
6311 }
6312 
6313 
6314 /** apply propagation methods for various symmetry handling constraints */
6315 static
6317  SCIP* scip, /**< SCIP pointer */
6318  SCIP_PROPDATA* propdata, /**< propagator data */
6319  SCIP_Bool* infeasible, /**< pointer for storing feasibility state */
6320  int* nred, /**< pointer for number of reductions */
6321  SCIP_Bool* didrun /**< pointer for storing whether a propagator actually ran */
6322  )
6323 {
6324  int nredlocal;
6325 
6326  assert( scip != NULL );
6327  assert( propdata != NULL );
6328  assert( infeasible != NULL );
6329  assert( nred != NULL );
6330  assert( didrun != NULL );
6331 
6332  *nred = 0;
6333  *infeasible = FALSE;
6334  *didrun = FALSE;
6335 
6336  /* apply orbitopal reduction */
6337  SCIP_CALL( SCIPorbitopalReductionPropagate(scip, propdata->orbitopalreddata, infeasible, &nredlocal, didrun) );
6338  *nred += nredlocal;
6339  if ( *infeasible )
6340  return SCIP_OKAY;
6341 
6342  /* apply orbital reduction */
6343  SCIP_CALL( SCIPorbitalReductionPropagate(scip, propdata->orbitalreddata, infeasible, &nredlocal, didrun) );
6344  *nred += nredlocal;
6345  if ( *infeasible )
6346  return SCIP_OKAY;
6347 
6348  /* apply dynamic lexicographic reduction */
6349  SCIP_CALL( SCIPlexicographicReductionPropagate(scip, propdata->lexreddata, infeasible, &nredlocal, didrun) );
6350  *nred += nredlocal;
6351  if ( *infeasible )
6352  return SCIP_OKAY;
6353 
6354  return SCIP_OKAY;
6355 }
6356 
6357 
6358 /*
6359  * Callback methods of propagator
6360  */
6361 
6362 /** presolving initialization method of propagator (called when presolving is about to begin) */
6363 static
6364 SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
6365 { /*lint --e{715}*/
6366  SCIP_PROPDATA* propdata;
6367 
6368  assert( scip != NULL );
6369  assert( prop != NULL );
6370 
6371  propdata = SCIPpropGetData(prop);
6372  assert( propdata != NULL );
6373 
6374  /* get nonlinear conshdlr for future checks on whether there are nonlinear constraints */
6375  propdata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
6376 
6377  /* check whether we should run */
6378  if ( propdata->usesymmetry < 0 )
6379  {
6380  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
6381  }
6382  assert( propdata->usesymmetry >= 0 );
6383 
6384  /* terminate early if no symmetries will be handled */
6385  if ( propdata->usesymmetry == 0 )
6386  return SCIP_OKAY;
6387 
6388  /* compute and handle symmetries if required */
6389  if ( propdata->symtiming == SYM_TIMING_BEFOREPRESOL )
6390  {
6391  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Symmetry computation before presolving:\n");
6392 
6394  }
6395 
6396  return SCIP_OKAY;
6397 }
6398 
6399 
6400 /** presolving deinitialization method of propagator (called after presolving has been finished) */
6401 static
6402 SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
6403 { /*lint --e{715}*/
6404  SCIP_PROPDATA* propdata;
6405 
6406  assert( scip != NULL );
6407  assert( prop != NULL );
6408  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6409 
6410  SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
6411 
6412  propdata = SCIPpropGetData(prop);
6413  assert( propdata != NULL );
6414  assert( propdata->usesymmetry >= 0 );
6415 
6416  /* terminate early if no symmetries will be handled */
6417  if ( propdata->usesymmetry == 0 )
6418  return SCIP_OKAY;
6419 
6420  /* guarantee that symmetries are computed (and handled) if the solving process has not been interrupted
6421  * and even if presolving has been disabled */
6422  if ( SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
6423  {
6425  }
6426 
6427  return SCIP_OKAY;
6428 }
6429 
6430 
6431 /** solving process deinitialization method of propagator (called before branch and bound process data is freed) */
6432 static
6433 SCIP_DECL_PROPEXITSOL(propExitsolSymmetry)
6435  SCIP_PROPDATA* propdata;
6436 
6437  assert( scip != NULL );
6438  assert( prop != NULL );
6439  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6440 
6441  SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
6442 
6443  propdata = SCIPpropGetData(prop);
6444  assert( propdata != NULL );
6445 
6446  /* reset symmetry handling propagators that depend on the branch-and-bound tree structure */
6447  SCIP_CALL( resetDynamicSymmetryHandling(scip, propdata) );
6448 
6449  return SCIP_OKAY;
6450 } /*lint !e715*/
6451 
6452 
6453 /** presolving method of propagator */
6454 static
6455 SCIP_DECL_PROPPRESOL(propPresolSymmetry)
6456 { /*lint --e{715}*/
6457  SCIP_PROPDATA* propdata;
6458  int i;
6459  int noldngenconns;
6460  int nchanges;
6461  SCIP_Bool earlyterm;
6462 
6463  assert( scip != NULL );
6464  assert( prop != NULL );
6465  assert( result != NULL );
6466  assert( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING );
6467 
6468  *result = SCIP_DIDNOTRUN;
6469 
6470  propdata = SCIPpropGetData(prop);
6471  assert( propdata != NULL );
6472  assert( propdata->usesymmetry >= 0 );
6473 
6474  /* terminate early if no symmetries will be handled */
6475  if ( propdata->usesymmetry == 0 )
6476  return SCIP_OKAY;
6477 
6478  /* possibly create symmetry handling constraints */
6479 
6480  /* skip presolving if we are not at the end and if symtiming == SYM_TIMING_DURINGPRESOL */
6481  assert( 0 <= propdata->symtiming && propdata->symtiming <= SYM_TIMING_AFTERPRESOL );
6482  if ( propdata->symtiming > SYM_TIMING_DURINGPRESOL && ! SCIPisPresolveFinished(scip) )
6483  return SCIP_OKAY;
6484 
6485  /* possibly stop */
6486  if ( SCIPisStopped(scip) )
6487  return SCIP_OKAY;
6488 
6489  noldngenconns = propdata->ngenorbconss + propdata->nsstconss + propdata->ngenlinconss;
6490 
6491  SCIP_CALL( tryAddSymmetryHandlingMethods(scip, prop, &nchanges, &earlyterm) );
6492 
6493  /* if we actually tried to add symmetry handling constraints */
6494  if ( ! earlyterm ) /*lint !e774*/
6495  {
6496  *result = SCIP_DIDNOTFIND;
6497 
6498  if ( nchanges > 0 )
6499  {
6500  *result = SCIP_SUCCESS;
6501  *nchgbds += nchanges;
6502  }
6503 
6504  /* if symmetry handling constraints have been added, presolve each */
6505  if ( propdata->ngenorbconss > 0 || propdata->ngenlinconss > 0 || propdata->nsstconss > 0 )
6506  {
6507  /* at this point, the symmetry group should be computed and nontrivial */
6508  assert( propdata->nperms > 0 );
6509  assert( propdata->triedaddsymmethods );
6510 
6511  /* we have added at least one symmetry handling constraints, i.e., we were successful */
6512  *result = SCIP_SUCCESS;
6513 
6514  *naddconss += propdata->ngenorbconss + propdata->ngenlinconss + propdata->nsstconss - noldngenconns;
6515  SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", *naddconss);
6516 
6517  /* if constraints have been added, loop through generated constraints and presolve each */
6518  for (i = 0; i < propdata->ngenorbconss; ++i)
6519  {
6520  SCIP_CALL( SCIPpresolCons(scip, propdata->genorbconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6521  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6522  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6523 
6524  /* exit if cutoff or unboundedness has been detected */
6525  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6526  {
6527  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genorbconss[i]));
6528  return SCIP_OKAY;
6529  }
6530  }
6531 
6532  for (i = 0; i < propdata->ngenlinconss; ++i)
6533  {
6534  SCIP_CALL( SCIPpresolCons(scip, propdata->genlinconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6535  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6536  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6537 
6538  /* exit if cutoff or unboundedness has been detected */
6539  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6540  {
6541  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genlinconss[i]));
6542  return SCIP_OKAY;
6543  }
6544  }
6545  SCIPdebugMsg(scip, "Presolved %d generated constraints.\n",
6546  propdata->ngenorbconss + propdata->ngenlinconss);
6547 
6548  for (i = 0; i < propdata->nsstconss; ++i)
6549  {
6550  SCIP_CALL( SCIPpresolCons(scip, propdata->sstconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6551  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6552  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6553 
6554  /* exit if cutoff or unboundedness has been detected */
6555  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6556  {
6557  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->sstconss[i]));
6558  return SCIP_OKAY;
6559  }
6560  }
6561  SCIPdebugMsg(scip, "Presolved %d generated Schreier Sims constraints.\n", propdata->nsstconss);
6562  }
6563  }
6564 
6565  return SCIP_OKAY;
6566 }
6567 
6568 
6569 /** execution method of propagator */
6570 static
6571 SCIP_DECL_PROPEXEC(propExecSymmetry)
6572 { /*lint --e{715}*/
6573  SCIP_PROPDATA* propdata;
6574  SCIP_Bool infeasible;
6575  SCIP_Bool didrun;
6576  int nred;
6577 
6578  assert( scip != NULL );
6579  assert( result != NULL );
6580 
6581  *result = SCIP_DIDNOTRUN;
6582 
6583  /* do not run if we are in the root or not yet solving */
6584  if ( SCIPgetDepth(scip) <= 0 || SCIPgetStage(scip) < SCIP_STAGE_SOLVING )
6585  return SCIP_OKAY;
6586 
6587  /* get data */
6588  propdata = SCIPpropGetData(prop);
6589  assert( propdata != NULL );
6590 
6591  /* usesymmetry must be read in order for propdata to have initialized symmetry handling propagators */
6592  if ( propdata->usesymmetry < 0 )
6593  return SCIP_OKAY;
6594 
6595  SCIP_CALL( propagateSymmetry(scip, propdata, &infeasible, &nred, &didrun) );
6596 
6597  if ( infeasible )
6598  {
6599  *result = SCIP_CUTOFF;
6600  propdata->symfoundreduction = TRUE;
6601  return SCIP_OKAY;
6602  }
6603  if ( nred > 0 )
6604  {
6605  assert( didrun );
6606  *result = SCIP_REDUCEDDOM;
6607  propdata->symfoundreduction = TRUE;
6608  }
6609  else if ( didrun )
6610  *result = SCIP_DIDNOTFIND;
6611 
6612  return SCIP_OKAY;
6613 }
6614 
6615 
6616 /** deinitialization method of propagator (called before transformed problem is freed) */
6617 static
6618 SCIP_DECL_PROPEXIT(propExitSymmetry)
6620  SCIP_PROPDATA* propdata;
6621 
6622  assert( scip != NULL );
6623  assert( prop != NULL );
6624  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6625 
6626  SCIPdebugMsg(scip, "Exiting propagator <%s>.\n", PROP_NAME);
6627 
6628  propdata = SCIPpropGetData(prop);
6629  assert( propdata != NULL );
6630 
6631  SCIP_CALL( freeSymmetryData(scip, propdata) );
6632 
6633  /* reset basic data */
6634  propdata->usesymmetry = -1;
6635  propdata->triedaddsymmethods = FALSE;
6636  propdata->nsymresacks = 0;
6637  propdata->norbitopes = 0;
6638  propdata->lastrestart = 0;
6639  propdata->symfoundreduction = FALSE;
6640 
6641  return SCIP_OKAY;
6642 }
6643 
6644 
6645 /** propagation conflict resolving method of propagator
6646  *
6647  * @todo Implement reverse propagation.
6648  *
6649  * Note that this is relatively difficult to obtain: One needs to include all bounds of variables that are responsible
6650  * for creating the orbit in which the variables that was propagated lies. This includes all variables that are moved
6651  * by the permutations which are involved in creating the orbit.
6652  */
6653 static
6654 SCIP_DECL_PROPRESPROP(propRespropSymmetry)
6655 { /*lint --e{715,818}*/
6656  assert( result != NULL );
6657 
6658  *result = SCIP_DIDNOTFIND;
6659 
6660  return SCIP_OKAY;
6661 }
6662 
6663 
6664 /** destructor of propagator to free user data (called when SCIP is exiting) */
6665 static
6666 SCIP_DECL_PROPFREE(propFreeSymmetry)
6667 { /*lint --e{715}*/
6668  SCIP_PROPDATA* propdata;
6669 
6670  assert( scip != NULL );
6671  assert( prop != NULL );
6672  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6673 
6674  SCIPdebugMsg(scip, "Freeing symmetry propagator.\n");
6675 
6676  propdata = SCIPpropGetData(prop);
6677  assert( propdata != NULL );
6678  assert( propdata->customsymopnodetypes != NULL );
6679 
6680  SCIPhashmapFree(&propdata->customsymopnodetypes);
6681 
6682  assert( propdata->lexreddata != NULL );
6683  SCIP_CALL( SCIPlexicographicReductionFree(scip, &propdata->lexreddata) );
6684 
6685  assert( propdata->orbitalreddata != NULL );
6686  SCIP_CALL( SCIPorbitalReductionFree(scip, &propdata->orbitalreddata) );
6687 
6688  assert( propdata->orbitopalreddata != NULL );
6689  SCIP_CALL( SCIPorbitopalReductionFree(scip, &propdata->orbitopalreddata) );
6690 
6691  SCIPfreeBlockMemory(scip, &propdata);
6692 
6693  return SCIP_OKAY;
6694 }
6695 
6696 
6697 /*
6698  * External methods
6699  */
6700 
6701 /** include symmetry propagator */
6703  SCIP* scip /**< SCIP data structure */
6704  )
6705 {
6706  SCIP_TABLEDATA* tabledata;
6707  SCIP_PROPDATA* propdata = NULL;
6708  SCIP_PROP* prop = NULL;
6709  SCIP_DIALOG* rootdialog;
6710  SCIP_DIALOG* displaymenu;
6711  SCIP_DIALOG* dialog;
6712 
6713  SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
6714  assert( propdata != NULL );
6715 
6716  propdata->npermvars = 0;
6717  propdata->nbinpermvars = 0;
6718  propdata->permvars = NULL;
6719  propdata->nperms = -1;
6720  propdata->nmaxperms = 0;
6721  propdata->perms = NULL;
6722  propdata->permstrans = NULL;
6723  propdata->permvarmap = NULL;
6724  propdata->permvardomaincenter = NULL;
6725 
6726  propdata->ncomponents = -1;
6727  propdata->ncompblocked = 0;
6728  propdata->components = NULL;
6729  propdata->componentbegins = NULL;
6730  propdata->vartocomponent = NULL;
6731  propdata->componentblocked = NULL;
6732  propdata->componenthassignedperm = NULL;
6733 
6734  propdata->log10groupsize = -1.0;
6735  propdata->nmovedvars = -1;
6736  propdata->binvaraffected = FALSE;
6737  propdata->computedsymmetry = FALSE;
6738  propdata->conshdlr_nonlinear = NULL;
6739 
6740  propdata->usesymmetry = -1;
6741  propdata->triedaddsymmethods = FALSE;
6742  propdata->genorbconss = NULL;
6743  propdata->genlinconss = NULL;
6744  propdata->ngenorbconss = 0;
6745  propdata->ngenlinconss = 0;
6746  propdata->genorbconsssize = 0;
6747  propdata->genlinconsssize = 0;
6748  propdata->nsymresacks = 0;
6749  propdata->norbitopes = 0;
6750  propdata->isnonlinvar = NULL;
6751 
6752  propdata->nmovedpermvars = -1;
6753  propdata->nmovedbinpermvars = 0;
6754  propdata->nmovedintpermvars = 0;
6755  propdata->nmovedimplintpermvars = 0;
6756  propdata->nmovedcontpermvars = 0;
6757  propdata->lastrestart = 0;
6758  propdata->symfoundreduction = FALSE;
6759 
6760  propdata->sstconss = NULL;
6761  propdata->nsstconss = 0;
6762  propdata->maxnsstconss = 0;
6763  propdata->leaders = NULL;
6764  propdata->nleaders = 0;
6765  propdata->maxnleaders = 0;
6766 
6767  SCIP_CALL( SCIPhashmapCreate(&propdata->customsymopnodetypes, SCIPblkmem(scip), 10) );
6768  propdata->nopnodetypes = (int) SYM_CONSOPTYPE_LAST;
6769 
6770  /* include constraint handler */
6772  PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING, propExecSymmetry, propdata) );
6773  assert( prop != NULL );
6774 
6775  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSymmetry) );
6776  SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSymmetry) );
6777  SCIP_CALL( SCIPsetPropInitpre(scip, prop, propInitpreSymmetry) );
6778  SCIP_CALL( SCIPsetPropExitpre(scip, prop, propExitpreSymmetry) );
6779  SCIP_CALL( SCIPsetPropExitsol(scip, prop, propExitsolSymmetry) );
6780  SCIP_CALL( SCIPsetPropResprop(scip, prop, propRespropSymmetry) );
6782 
6783  /* include table */
6784  SCIP_CALL( SCIPallocBlockMemory(scip, &tabledata) );
6785  tabledata->propdata = propdata;
6787  NULL, tableFreeSymmetry, NULL, NULL, NULL, NULL, tableOutputSymmetry,
6789 
6790  /* include display dialog */
6791  rootdialog = SCIPgetRootDialog(scip);
6792  assert(rootdialog != NULL);
6793  if( SCIPdialogFindEntry(rootdialog, "display", &displaymenu) != 1 )
6794  {
6795  SCIPerrorMessage("display sub menu not found\n");
6796  return SCIP_PLUGINNOTFOUND;
6797  }
6798  assert( ! SCIPdialogHasEntry(displaymenu, "symmetries") );
6799  SCIP_CALL( SCIPincludeDialog(scip, &dialog,
6800  NULL, dialogExecDisplaySymmetry, NULL, NULL,
6801  "symmetry", "display generators of symmetry group in cycle notation, if available",
6802  FALSE, (SCIP_DIALOGDATA*)propdata) );
6803  SCIP_CALL( SCIPaddDialogEntry(scip, displaymenu, dialog) );
6804  SCIP_CALL( SCIPreleaseDialog(scip, &dialog) );
6805 
6806  /* add parameters for computing symmetry */
6807  SCIP_CALL( SCIPaddIntParam(scip,
6808  "propagating/" PROP_NAME "/maxgenerators",
6809  "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
6810  &propdata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
6811 
6813  "propagating/" PROP_NAME "/checksymmetries",
6814  "Should all symmetries be checked after computation?",
6815  &propdata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
6816 
6818  "propagating/" PROP_NAME "/displaynorbitvars",
6819  "Should the number of variables affected by some symmetry be displayed?",
6820  &propdata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
6821 
6823  "propagating/" PROP_NAME "/doubleequations",
6824  "Double equations to positive/negative version?",
6825  &propdata->doubleequations, TRUE, DEFAULT_DOUBLEEQUATIONS, NULL, NULL) );
6826 
6827  /* add parameters for adding symmetry handling constraints */
6829  "propagating/" PROP_NAME "/conssaddlp",
6830  "Should the symmetry breaking constraints be added to the LP?",
6831  &propdata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
6832 
6834  "propagating/" PROP_NAME "/addsymresacks",
6835  "Add inequalities for symresacks for each generator?",
6836  &propdata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
6837 
6839  "propagating/" PROP_NAME "/detectdoublelex",
6840  "Should we check whether the components of the symmetry group can be handled by double lex matrices?",
6841  &propdata->detectdoublelex, TRUE, DEFAULT_DETECTDOUBLELEX, NULL, NULL) );
6842 
6844  "propagating/" PROP_NAME "/detectorbitopes",
6845  "Should we check whether the components of the symmetry group can be handled by orbitopes?",
6846  &propdata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
6847 
6849  "propagating/" PROP_NAME "/detectsubgroups",
6850  "Should we try to detect symmetric subgroups of the symmetry group on binary variables?",
6851  &propdata->detectsubgroups, TRUE, DEFAULT_DETECTSUBGROUPS, NULL, NULL) );
6852 
6854  "propagating/" PROP_NAME "/addweaksbcs",
6855  "Should we add weak SBCs for enclosing orbit of symmetric subgroups?",
6856  &propdata->addweaksbcs, TRUE, DEFAULT_ADDWEAKSBCS, NULL, NULL) );
6857 
6858  SCIP_CALL( SCIPaddIntParam(scip,
6859  "propagating/" PROP_NAME "/addconsstiming",
6860  "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) [disabled parameter]",
6861  NULL, TRUE, DEFAULT_ADDCONSSTIMING, 0, 2, NULL, NULL) );
6862 
6863  /* add parameters for orbital reduction */
6864  SCIP_CALL( SCIPaddIntParam(scip,
6865  "propagating/" PROP_NAME "/ofsymcomptiming",
6866  "timing of symmetry computation (0 = before presolving, 1 = during presolving, 2 = at first call) [disabled parameter]",
6867  NULL, TRUE, DEFAULT_SYMCOMPTIMING, 0, 2, NULL, NULL) );
6868 
6870  "propagating/" PROP_NAME "/performpresolving",
6871  "run orbital fixing during presolving? (disabled)",
6873 
6874  SCIP_CALL( SCIPaddIntParam(scip,
6875  "propagating/" PROP_NAME "/recomputerestart",
6876  "recompute symmetries after a restart has occurred? (0 = never)",
6877  &propdata->recomputerestart, TRUE, DEFAULT_RECOMPUTERESTART, 0, 0, NULL, NULL) );
6878 
6880  "propagating/" PROP_NAME "/compresssymmetries",
6881  "Should non-affected variables be removed from permutation to save memory?",
6882  &propdata->compresssymmetries, TRUE, DEFAULT_COMPRESSSYMMETRIES, NULL, NULL) );
6883 
6885  "propagating/" PROP_NAME "/compressthreshold",
6886  "Compression is used if percentage of moved vars is at most the threshold.",
6887  &propdata->compressthreshold, TRUE, DEFAULT_COMPRESSTHRESHOLD, 0.0, 1.0, NULL, NULL) );
6888 
6890  "propagating/" PROP_NAME "/usecolumnsparsity",
6891  "Should the number of conss a variable is contained in be exploited in symmetry detection?",
6892  &propdata->usecolumnsparsity, TRUE, DEFAULT_USECOLUMNSPARSITY, NULL, NULL) );
6893 
6894  SCIP_CALL( SCIPaddIntParam(scip,
6895  "propagating/" PROP_NAME "/maxnconsssubgroup",
6896  "maximum number of constraints up to which subgroup structures are detected",
6897  &propdata->maxnconsssubgroup, TRUE, DEFAULT_MAXNCONSSSUBGROUP, 0, INT_MAX, NULL, NULL) );
6898 
6900  "propagating/" PROP_NAME "/usedynamicprop",
6901  "whether dynamified symmetry handling constraint methods should be used",
6902  &propdata->usedynamicprop, TRUE, DEFAULT_USEDYNAMICPROP, NULL, NULL) );
6903 
6905  "propagating/" PROP_NAME "/addstrongsbcs",
6906  "Should strong SBCs for enclosing orbit of symmetric subgroups be added if orbitopes are not used?",
6907  &propdata->addstrongsbcs, TRUE, DEFAULT_ADDSTRONGSBCS, NULL, NULL) );
6908 
6909  SCIP_CALL( SCIPaddIntParam(scip,
6910  "propagating/" PROP_NAME "/ssttiebreakrule",
6911  "rule to select the orbit in Schreier Sims inequalities (variable in 0: minimum size orbit; 1: maximum size orbit; 2: orbit with most variables in conflict with leader)",
6912  &propdata->ssttiebreakrule, TRUE, DEFAULT_SSTTIEBREAKRULE, 0, 2, NULL, NULL) );
6913 
6914  SCIP_CALL( SCIPaddIntParam(scip,
6915  "propagating/" PROP_NAME "/sstleaderrule",
6916  "rule to select the leader in an orbit (0: first var; 1: last var; 2: var having most conflicting vars in orbit)",
6917  &propdata->sstleaderrule, TRUE, DEFAULT_SSTLEADERRULE, 0, 2, NULL, NULL) );
6918 
6919  SCIP_CALL( SCIPaddIntParam(scip,
6920  "propagating/" PROP_NAME "/sstleadervartype",
6921  "bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);" \
6922  "if multiple types are allowed, take the one with most affected vars",
6923  &propdata->sstleadervartype, TRUE, DEFAULT_SSTLEADERVARTYPE, 1, 15, NULL, NULL) );
6924 
6926  "propagating/" PROP_NAME "/addconflictcuts",
6927  "Should Schreier Sims constraints be added if we use a conflict based rule?",
6928  &propdata->addconflictcuts, TRUE, DEFAULT_ADDCONFLICTCUTS, NULL, NULL) );
6929 
6931  "propagating/" PROP_NAME "/sstaddcuts",
6932  "Should Schreier Sims constraints be added?",
6933  &propdata->sstaddcuts, TRUE, DEFAULT_SSTADDCUTS, NULL, NULL) );
6934 
6936  "propagating/" PROP_NAME "/sstmixedcomponents",
6937  "Should Schreier Sims constraints be added if a symmetry component contains variables of different types?",
6938  &propdata->sstmixedcomponents, TRUE, DEFAULT_SSTMIXEDCOMPONENTS, NULL, NULL) );
6939 
6941  "propagating/" PROP_NAME "/symfixnonbinaryvars",
6942  "Whether all non-binary variables shall be not affected by symmetries if OF is active? (disabled)",
6944 
6946  "propagating/" PROP_NAME "/enforcecomputesymmetry",
6947  "Is only symmetry on binary variables used?",
6948  &propdata->enforcecomputesymmetry, TRUE, DEFAULT_ENFORCECOMPUTESYMMETRY, NULL, NULL) );
6949 
6951  "propagating/" PROP_NAME "/preferlessrows",
6952  "Shall orbitopes with less rows be preferred in detection?",
6953  &propdata->preferlessrows, TRUE, DEFAULT_PREFERLESSROWS, NULL, NULL) );
6954 
6955  SCIP_CALL( SCIPaddIntParam(scip,
6956  "propagating/" PROP_NAME "/symtype",
6957  "Type of symmetries that shall be computed?",
6958  &propdata->symtype, TRUE, DEFAULT_SYMTYPE, 0, 1, NULL, NULL) );
6959 
6960  SCIP_CALL( SCIPaddIntParam(scip,
6961  "propagating/" PROP_NAME "/symtiming",
6962  "timing of symmetry computation and handling (0 = before presolving, 1 = during presolving, 2 = after presolving)",
6963  &propdata->symtiming, TRUE, DEFAULT_SYMCOMPTIMING, 0, 2, NULL, NULL) );
6964 
6965  /* possibly add description */
6966  if ( SYMcanComputeSymmetry() )
6967  {
6969  if ( SYMsymmetryGetAddName() != NULL )
6970  {
6972  }
6973  }
6974 
6975  /* depending functionality */
6976  SCIP_CALL( SCIPincludeEventHdlrShadowTree(scip, &propdata->shadowtreeeventhdlr) );
6977  assert( propdata->shadowtreeeventhdlr != NULL );
6978 
6979  SCIP_CALL( SCIPincludeOrbitopalReduction(scip, &propdata->orbitopalreddata) );
6980  assert( propdata->orbitopalreddata != NULL );
6981 
6982  SCIP_CALL( SCIPincludeOrbitalReduction(scip, &propdata->orbitalreddata, propdata->shadowtreeeventhdlr) );
6983  assert( propdata->orbitalreddata != NULL );
6984 
6985  SCIP_CALL( SCIPincludeLexicographicReduction(scip, &propdata->lexreddata, propdata->shadowtreeeventhdlr) );
6986  assert( propdata->lexreddata != NULL );
6987 
6988  return SCIP_OKAY;
6989 }
6990 
6991 
6992 /** return currently available symmetry group information */
6994  SCIP* scip, /**< SCIP data structure */
6995  int* npermvars, /**< pointer to store number of variables for permutations */
6996  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
6997  SCIP_HASHMAP** permvarmap, /**< pointer to store hash map of permvars (or NULL) */
6998  int* nperms, /**< pointer to store number of permutations */
6999  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix (or NULL)*/
7000  int*** permstrans, /**< pointer to store permutation generators as (npermvars x nperms) matrix (or NULL)*/
7001  SCIP_Real* log10groupsize, /**< pointer to store log10 of group size (or NULL) */
7002  SCIP_Bool* binvaraffected, /**< pointer to store whether binary variables are affected (or NULL) */
7003  int** components, /**< pointer to store components of symmetry group (or NULL) */
7004  int** componentbegins, /**< pointer to store begin positions of components in components array (or NULL) */
7005  int** vartocomponent, /**< pointer to store assignment from variable to its component (or NULL) */
7006  int* ncomponents /**< pointer to store number of components (or NULL) */
7007  )
7008 {
7009  SCIP_PROPDATA* propdata;
7010  SCIP_PROP* prop;
7011 
7012  assert( scip != NULL );
7013  assert( npermvars != NULL );
7014  assert( permvars != NULL );
7015  assert( nperms != NULL );
7016  assert( perms != NULL || permstrans != NULL );
7017  assert( ncomponents != NULL || (components == NULL && componentbegins == NULL && vartocomponent == NULL) );
7018 
7019  /* find symmetry propagator */
7020  prop = SCIPfindProp(scip, "symmetry");
7021  if ( prop == NULL )
7022  {
7023  SCIPerrorMessage("Could not find symmetry propagator.\n");
7024  return SCIP_PLUGINNOTFOUND;
7025  }
7026  assert( prop != NULL );
7027  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7028 
7029  propdata = SCIPpropGetData(prop);
7030  assert( propdata != NULL );
7031 
7032  *npermvars = propdata->npermvars;
7033  *permvars = propdata->permvars;
7034 
7035  if ( permvarmap != NULL )
7036  {
7037  if ( propdata->nperms > 0 )
7038  {
7039  SCIP_CALL( ensureSymmetryPermvarmapComputed(scip, propdata) );
7040  }
7041  *permvarmap = propdata->permvarmap;
7042  }
7043 
7044  *nperms = propdata->nperms;
7045  if ( perms != NULL )
7046  {
7047  *perms = propdata->perms;
7048  assert( *perms != NULL || *nperms <= 0 );
7049  }
7050 
7051  if ( permstrans != NULL )
7052  {
7053  if ( propdata->nperms > 0 )
7054  {
7055  SCIP_CALL( ensureSymmetryPermstransComputed(scip, propdata) );
7056  }
7057  *permstrans = propdata->permstrans;
7058  assert( *permstrans != NULL || *nperms <= 0 );
7059  }
7060 
7061  if ( log10groupsize != NULL )
7062  *log10groupsize = propdata->log10groupsize;
7063 
7064  if ( binvaraffected != NULL )
7065  *binvaraffected = propdata->binvaraffected;
7066 
7067  if ( components != NULL || componentbegins != NULL || vartocomponent != NULL || ncomponents != NULL )
7068  {
7069  if ( propdata->nperms > 0 )
7070  {
7071  SCIP_CALL( ensureSymmetryComponentsComputed(scip, propdata) );
7072  }
7073  }
7074 
7075  if ( components != NULL )
7076  *components = propdata->components;
7077 
7078  if ( componentbegins != NULL )
7079  *componentbegins = propdata->componentbegins;
7080 
7081  if ( vartocomponent )
7082  *vartocomponent = propdata->vartocomponent;
7083 
7084  if ( ncomponents )
7085  *ncomponents = propdata->ncomponents;
7086 
7087  return SCIP_OKAY;
7088 }
7089 
7090 
7091 /** return number of the symmetry group's generators */
7093  SCIP* scip /**< SCIP data structure */
7094  )
7095 {
7096  SCIP_PROP* prop;
7097  SCIP_PROPDATA* propdata;
7098 
7099  assert( scip != NULL );
7100 
7101  prop = SCIPfindProp(scip, PROP_NAME);
7102  if ( prop == NULL )
7103  return 0;
7104 
7105  propdata = SCIPpropGetData(prop);
7106  assert( propdata != NULL );
7107 
7108  if ( propdata->nperms < 0 )
7109  return 0;
7110  else
7111  return propdata->nperms;
7112 }
7113 
7114 /** creates new operator node type (used for symmetry detection) and returns its representation
7115  *
7116  * If the operator node already exists, the function terminates with SCIP_INVALIDDATA.
7117  */
7119  SCIP* scip, /**< SCIP pointer */
7120  const char* opnodename, /**< name of new operator node type */
7121  int* nodetype /**< pointer to store the node type */
7122  )
7123 {
7124  SCIP_PROP* prop;
7125  SCIP_PROPDATA* propdata;
7126 
7127  assert( scip != NULL );
7128  assert( nodetype != NULL );
7129 
7130  prop = SCIPfindProp(scip, PROP_NAME);
7131  if ( prop == NULL )
7132  {
7133  SCIPerrorMessage("Cannot create operator node type, symmetry propagator has not been included.\n");
7134  return SCIP_PLUGINNOTFOUND;
7135  }
7136 
7137  propdata = SCIPpropGetData(prop);
7138  assert( propdata != NULL );
7139  assert( propdata->customsymopnodetypes != NULL );
7140 
7141  if ( SCIPhashmapExists(propdata->customsymopnodetypes, (void*) opnodename) )
7142  {
7143  SCIPerrorMessage("Cannot create operator node type %s, it already exists.\n", opnodename);
7144  return SCIP_INVALIDDATA;
7145  }
7146 
7147  SCIP_CALL( SCIPhashmapInsertInt(propdata->customsymopnodetypes, (void*) opnodename, propdata->nopnodetypes) );
7148  *nodetype = propdata->nopnodetypes++;
7149 
7150  return SCIP_OKAY;
7151 }
7152 
7153 /** returns representation of an operator node type.
7154  *
7155  * If the node type does not already exist, a new node type will be created.
7156  */
7158  SCIP* scip, /**< SCIP pointer */
7159  const char* opnodename, /**< name of new operator node type */
7160  int* nodetype /**< pointer to store the node type */
7161  )
7162 {
7163  SCIP_PROP* prop;
7164  SCIP_PROPDATA* propdata;
7165 
7166  assert( scip != NULL );
7167 
7168  prop = SCIPfindProp(scip, PROP_NAME);
7169  if ( prop == NULL )
7170  {
7171  SCIPerrorMessage("Cannot return operator node type, symmetry propagator has not been included.\n");
7172  return SCIP_PLUGINNOTFOUND;
7173  }
7174 
7175  propdata = SCIPpropGetData(prop);
7176  assert( propdata != NULL );
7177  assert( propdata->customsymopnodetypes != NULL );
7178 
7179  if ( ! SCIPhashmapExists(propdata->customsymopnodetypes, (void*) opnodename) )
7180  {
7181  SCIP_CALL( SCIPcreateSymOpNodeType(scip, opnodename, nodetype) );
7182  }
7183  else
7184  *nodetype = SCIPhashmapGetImageInt(propdata->customsymopnodetypes, (void*) opnodename);
7185 
7186  return SCIP_OKAY;
7187 }
static SCIP_RETCODE detectAndHandleSubgroups(SCIP *scip, SCIP_PROPDATA *propdata, int cidx)
int SCIPgetNExprhdlrs(SCIP *scip)
Definition: scip_expr.c:857
SCIP_RETCODE SCIPsetPropPresol(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPPRESOL((*proppresol)), int presolpriority, int presolmaxrounds, SCIP_PRESOLTIMING presoltiming)
Definition: scip_prop.c:279
SCIP_RETCODE SCIPorbitopalReductionFree(SCIP *scip, SCIP_ORBITOPALREDDATA **orbireddata)
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:110
int SCIPgetNIntVars(SCIP *scip)
Definition: scip_prob.c:2082
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:99
static SCIP_RETCODE SCIPdisplaySymmetryStatistics(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_Real SCIPgetSolvingTime(SCIP *scip)
Definition: scip_timing.c:378
SCIP_RETCODE SCIPlexicographicReductionAddPermutation(SCIP *scip, SCIP_LEXREDDATA *masterdata, SCIP_VAR **permvars, int npermvars, int *perm, SYM_SYMTYPE symtype, SCIP_Real *permvardomaincenter, SCIP_Bool usedynamicorder, SCIP_Bool *success)
#define DEFAULT_COMPRESSTHRESHOLD
#define NULL
Definition: def.h:267
#define DEFAULT_SSTMIXEDCOMPONENTS
SCIP_RETCODE SCIPorbitalReductionPropagate(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
static SCIP_RETCODE displaySymmetriesWithoutComponents(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_VAR ** SCIPcliqueGetVars(SCIP_CLIQUE *clique)
Definition: implics.c:3380
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:93
SCIP_Bool SCIPconshdlrSupportsSignedPermsymDetection(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:5305
enum SCIP_OrbitopeType SCIP_ORBITOPETYPE
SCIP_EXPRHDLR ** SCIPgetExprhdlrs(SCIP *scip)
Definition: scip_expr.c:846
SCIP_RETCODE SCIPincludeTable(SCIP *scip, const char *name, const char *desc, SCIP_Bool active, SCIP_DECL_TABLECOPY((*tablecopy)), SCIP_DECL_TABLEFREE((*tablefree)), SCIP_DECL_TABLEINIT((*tableinit)), SCIP_DECL_TABLEEXIT((*tableexit)), SCIP_DECL_TABLEINITSOL((*tableinitsol)), SCIP_DECL_TABLEEXITSOL((*tableexitsol)), SCIP_DECL_TABLEOUTPUT((*tableoutput)), SCIP_TABLEDATA *tabledata, int position, SCIP_STAGE earlieststage)
Definition: scip_table.c:56
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:380
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition: misc.c:11296
#define DEFAULT_CHECKSYMMETRIES
Constraint handler for variable bound constraints .
SCIP_RETCODE SCIPfreeSymgraph(SCIP *scip, SYM_GRAPH **graph)
#define DEFAULT_SSTADDCUTS
SCIP_RETCODE SCIPorbitopalReductionPrintStatistics(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata)
static SCIP_DECL_PROPPRESOL(propPresolSymmetry)
SCIP_RETCODE SCIPaddDialogEntry(SCIP *scip, SCIP_DIALOG *dialog, SCIP_DIALOG *subdialog)
Definition: scip_dialog.c:171
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:941
#define DEFAULT_CONSSADDLP
static SCIP_RETCODE displaySymmetriesWithComponents(SCIP *scip, SCIP_PROPDATA *propdata)
enum SCIP_ColumnOrdering SCIP_COLUMNORDERING
static SCIP_DECL_PROPEXEC(propExecSymmetry)
int SCIPgetNVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9553
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition: scip_mem.h:126
static SCIP_RETCODE createConflictGraphSST(SCIP *scip, SCIP_CONFLICTDATA **varconflicts, SCIP_VAR **conflictvars, int nconflictvars, SCIP_HASHMAP *conflictvarmap)
SCIP_PROP * SCIPfindProp(SCIP *scip, const char *name)
Definition: scip_prop.c:329
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:18079
#define TABLE_POSITION_SYMMETRY
static SCIP_Bool testSymmetryComputationRequired(SCIP *scip, SCIP_PROPDATA *propdata)
#define SCIP_MAXSTRLEN
Definition: def.h:288
#define SYM_HANDLETYPE_ORBITALREDUCTION
Definition: type_symmetry.h:96
SCIP_RETCODE SCIPgetSymmetry(SCIP *scip, int *npermvars, SCIP_VAR ***permvars, SCIP_HASHMAP **permvarmap, int *nperms, int ***perms, int ***permstrans, SCIP_Real *log10groupsize, SCIP_Bool *binvaraffected, int **components, int **componentbegins, int **vartocomponent, int *ncomponents)
const char * SCIPexprhdlrGetName(SCIP_EXPRHDLR *exprhdlr)
Definition: expr.c:545
SCIP_RETCODE SCIPcreateSymbreakCons(SCIP *scip, SCIP_CONS **cons, const char *name, int *perm, SCIP_VAR **vars, int nvars, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
#define ISSSTIMPLINTACTIVE(x)
#define SCIPallocClearBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:97
static SCIP_RETCODE addSSTConss(SCIP *scip, SCIP_PROPDATA *propdata, SCIP_Bool onlywithcontvars, int *nchgbds, int cidx)
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip_mem.c:139
#define SYM_TIMING_BEFOREPRESOL
Definition: type_symmetry.h:50
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:18135
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
#define TABLE_NAME_SYMMETRY
static SCIP_Bool conshdlrsCanProvideSymInformation(SCIP *scip, SYM_SYMTYPE symtype)
static SCIP_RETCODE freeSymmetryData(SCIP *scip, SCIP_PROPDATA *propdata)
#define PROP_TIMING
SCIP_RETCODE SCIPcomputeComponentsSym(SCIP *scip, SYM_SYMTYPE symtype, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, SCIP_Bool transposed, int **components, int **componentbegins, int **vartocomponent, unsigned **componentblocked, int *ncomponents)
Definition: symmetry.c:775
static SCIP_DECL_PROPRESPROP(propRespropSymmetry)
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1250
SCIP_Bool SCIPvarIsBinary(SCIP_VAR *var)
Definition: var.c:17600
SCIP_RETCODE SCIPgetConsSignedPermsymGraph(SCIP *scip, SCIP_CONS *cons, SYM_GRAPH *graph, SCIP_Bool *success)
Definition: scip_cons.c:2688
constraint handler for indicator constraints
static SCIP_RETCODE updateSymInfoConflictGraphSST(SCIP *scip, SCIP_CONFLICTDATA *varconflicts, SCIP_VAR **conflictvars, int nconflictvars, int *orbits, int *orbitbegins, int norbits)
SCIP_RETCODE SCIPcomputeSymgraphColors(SCIP *scip, SYM_GRAPH *graph, SYM_SPEC fixedtype)
SCIP_RETCODE SCIPcomputeOrbitsFilterSym(SCIP *scip, int npermvars, int **permstrans, int nperms, SCIP_Shortbool *inactiveperms, int *orbits, int *orbitbegins, int *norbits, int *components, int *componentbegins, int *vartocomponent, unsigned *componentblocked, int ncomponents, int nmovedpermvars)
Definition: symmetry.c:172
static SCIP_RETCODE ensureSymmetryMovedpermvarscountsComputed(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPincludeOrbitopalReduction(SCIP *scip, SCIP_ORBITOPALREDDATA **orbireddata)
#define DEFAULT_DISPLAYNORBITVARS
static SCIP_RETCODE tryAddSymmetryHandlingMethodsComponent(SCIP *scip, SCIP_PROPDATA *propdata, int cidx, int *nchgbds)
#define ISSSTINTACTIVE(x)
int SCIPcliqueGetIndex(SCIP_CLIQUE *clique)
Definition: implics.c:3416
SCIP_CONS ** SCIPconshdlrGetConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4595
#define FALSE
Definition: def.h:94
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:3074
#define SYM_TIMING_AFTERPRESOL
Definition: type_symmetry.h:52
int SCIPgetNActivePricers(SCIP *scip)
Definition: scip_pricer.c:348
SCIP_Real SCIPinfinity(SCIP *scip)
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:10877
#define TRUE
Definition: def.h:93
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition: misc.c:3192
#define SYM_HANDLETYPE_SYMBREAK
Definition: type_symmetry.h:95
static SCIP_RETCODE ensureSymmetryComponentsComputed(SCIP *scip, SCIP_PROPDATA *propdata)
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition: var.c:17769
int SCIPgetSymmetryNGenerators(SCIP *scip)
static SCIP_RETCODE addOrbitopesDynamic(SCIP *scip, SCIP_PROPDATA *propdata, int id, int **varidxmatrix, int nrows, int ncols, SCIP_Bool *success)
int SCIPdialogFindEntry(SCIP_DIALOG *dialog, const char *entryname, SCIP_DIALOG **subdialog)
Definition: dialog.c:1028
#define DEFAULT_RECOMPUTERESTART
static SCIP_RETCODE ensureDynamicConsArrayAllocatedAndSufficientlyLarge(SCIP *scip, SCIP_CONS ***consarrptr, int *consarrsizeptr, int consarrsizereq)
#define ISSSTBINACTIVE(x)
static GRAPHNODE ** active
SCIP_RETCODE SCIPorbitopalReductionGetStatistics(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, int *nred, int *ncutoff)
struct SCIP_DialogData SCIP_DIALOGDATA
Definition: type_dialog.h:51
static SCIP_RETCODE ensureSymmetryPermvarmapComputed(SCIP *scip, SCIP_PROPDATA *propdata)
#define SCIPfreeBlockMemory(scip, ptr)
Definition: scip_mem.h:108
Constraint handler for AND constraints, .
static SCIP_RETCODE computeSymmetryGroup(SCIP *scip, SYM_SYMTYPE symtype, SCIP_Bool compresssymmetries, SCIP_Real compressthreshold, int maxgenerators, SYM_SPEC fixedtype, SCIP_Bool checksymmetries, SCIP_VAR ***permvars, int *npermvars, int *nbinpermvars, SCIP_Real **permvardomaincenter, int ***perms, int *nperms, int *nmaxperms, int *nmovedvars, SCIP_Bool *binvaraffected, SCIP_Bool *compressed, SCIP_Real *log10groupsize, SCIP_Real *symcodetime, SCIP_Bool *success)
SCIP_CONS ** SCIPgetConss(SCIP *scip)
Definition: scip_prob.c:3088
static SCIP_DECL_TABLEOUTPUT(tableOutputSymmetry)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
static SCIP_RETCODE handleDoublelLexMatrix(SCIP *scip, SCIP_PROPDATA *propdata, int id, int **varidxmatrix, int nrows, int ncols, int *rowsbegin, int *colsbegin, int nrowblocks, int ncolblocks, SCIP_Bool *success)
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:136
SCIP_Real * rhs
Constraint handler for the set partitioning / packing / covering constraints .
#define DEFAULT_DETECTSUBGROUPS
#define SCIPallocBlockMemory(scip, ptr)
Definition: scip_mem.h:89
static SCIP_RETCODE determineSymmetry(SCIP *scip, SCIP_PROPDATA *propdata, SYM_SPEC symspecrequire, SYM_SPEC symspecrequirefixed)
SCIP_RETCODE SCIPincludeLexicographicReduction(SCIP *scip, SCIP_LEXREDDATA **masterdata, SCIP_EVENTHDLR *shadowtreeeventhdlr)
#define DEFAULT_SYMCOMPTIMING
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
Definition: scip_message.c:120
#define DEFAULT_SSTLEADERRULE
#define SCIPdebugMsg
Definition: scip_message.h:78
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:83
static SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
Definition: scip_message.c:208
int SCIPgetNContVars(SCIP *scip)
Definition: scip_prob.c:2172
#define DEFAULT_MAXNCONSSSUBGROUP
static SCIP_DECL_SORTINDCOMP(SYMsortGraphCompVars)
#define TABLE_EARLIEST_SYMMETRY
#define DEFAULT_COMPRESSSYMMETRIES
int SCIPgetNActiveBenders(SCIP *scip)
Definition: scip_benders.c:532
SCIP_RETCODE SCIPincludeEventHdlrShadowTree(SCIP *scip, SCIP_EVENTHDLR **eventhdlrptr)
static SCIP_RETCODE addWeakSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int ncompcolors, int *chosencomppercolor, int *firstvaridxpercolor, int symgrpcompidx, int *naddedconss, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
SCIP_RETCODE SCIPcreateSymgraph(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH **graph, SCIP_VAR **symvars, int nsymvars, int nopnodes, int nvalnodes, int nconsnodes, int nedges)
public functions to work with algebraic expressions
int SCIPgetNConshdlrs(SCIP *scip)
Definition: scip_cons.c:965
interface for symmetry computations
SCIP_Bool SCIPisReoptEnabled(SCIP *scip)
Definition: scip_solve.c:3488
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3423
#define DEFAULT_ADDWEAKSBCS
constraint handler for (partitioning/packing/full) orbitope constraints w.r.t. the full symmetric gro...
constraint handler for symresack constraints
static SCIP_RETCODE checkTwoCyclePermsAreOrbitope(SCIP *scip, SCIP_VAR **permvars, int npermvars, int **perms, int *activeperms, int ntwocycles, int nactiveperms, int **orbitopevaridx, int *columnorder, int *nusedelems, int *nusedcols, SCIP_Shortbool *rowisbinary, SCIP_Bool *isorbitope, SCIP_Shortbool *activevars)
Constraint handler for "or" constraints, .
#define SCIPallocCleanBufferArray(scip, ptr, num)
Definition: scip_mem.h:142
#define DEFAULT_USEDYNAMICPROP
SCIP_DIALOG * SCIPgetRootDialog(SCIP *scip)
Definition: scip_dialog.c:157
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:18089
#define SYM_SPEC_BINARY
Definition: type_symmetry.h:44
SCIP_RETCODE SCIPorbitalReductionFree(SCIP *scip, SCIP_ORBITALREDDATA **orbireddata)
SCIP_CONS ** conss
#define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
Definition: scip_mem.h:105
SCIP_RETCODE SCIPincludeDialog(SCIP *scip, SCIP_DIALOG **dialog, SCIP_DECL_DIALOGCOPY((*dialogcopy)), SCIP_DECL_DIALOGEXEC((*dialogexec)), SCIP_DECL_DIALOGDESC((*dialogdesc)), SCIP_DECL_DIALOGFREE((*dialogfree)), const char *name, const char *desc, SCIP_Bool issubmenu, SCIP_DIALOGDATA *dialogdata)
Definition: scip_dialog.c:59
Constraint handler for knapsack constraints of the form , x binary and .
SCIP_RETCODE SCIPsetPropExitpre(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPEXITPRE((*propexitpre)))
Definition: scip_prop.c:263
SCIP_RETCODE SCIPorbitopalReductionAddOrbitope(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, SCIP_ROWORDERING rowordering, SCIP_COLUMNORDERING colordering, SCIP_VAR **vars, int nrows, int ncols, SCIP_Bool *success)
#define DEFAULT_SYMTYPE
static SCIP_RETCODE chooseOrderOfGenerators(SCIP *scip, SCIP_PROPDATA *propdata, int compidx, int **genorder, int *ntwocycleperms)
methods for dealing with symmetry detection graphs
SCIP_Bool SCIPisPresolveFinished(SCIP *scip)
Definition: scip_general.c:627
static SCIP_RETCODE addSSTConssOrbitAndUpdateSST(SCIP *scip, SCIP_CONFLICTDATA *varconflicts, SCIP_PROPDATA *propdata, SCIP_VAR **permvars, int *orbits, int *orbitbegins, int orbitidx, int orbitleaderidx, SCIP_Shortbool *orbitvarinconflict, int norbitvarinconflict, int *nchgbds)
#define ISSSTCONTACTIVE(x)
#define TABLE_DESC_SYMMETRY
const char * SYMsymmetryGetAddName(void)
SCIP_RETCODE SCIPlexicographicReductionPropagate(SCIP *scip, SCIP_LEXREDDATA *masterdata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
#define DEFAULT_DETECTDOUBLELEX
#define SCIPerrorMessage
Definition: pub_message.h:64
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4199
SCIP_RETCODE SCIPgetConsNVars(SCIP *scip, SCIP_CONS *cons, int *nvars, SCIP_Bool *success)
Definition: scip_cons.c:2622
static SCIP_RETCODE estimateSymgraphSize(SCIP *scip, int *nopnodes, int *nvalnodes, int *nconsnodes, int *nedges)
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:2770
SCIP_CONSHDLR ** SCIPgetConshdlrs(SCIP *scip)
Definition: scip_cons.c:954
void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
static SCIP_RETCODE tryHandleSingleOrDoubleLexMatricesComponent(SCIP *scip, SCIP_PROPDATA *propdata, SCIP_Bool detectsinglelex, int cidx)
#define SYM_SPEC_INTEGER
Definition: type_symmetry.h:43
Constraint handler for logicor constraints (equivalent to set covering, but algorithms are suited fo...
static SCIP_RETCODE displayCycleOfSymmetry(SCIP *scip, int *perm, SYM_SYMTYPE symtype, int baseidx, SCIP_Bool *covered, int nvars, SCIP_VAR **vars)
#define ISSSTACTIVE(x)
SCIP_RETCODE SCIPpresolCons(SCIP *scip, SCIP_CONS *cons, int nrounds, SCIP_PRESOLTIMING presoltiming, int nnewfixedvars, int nnewaggrvars, int nnewchgvartypes, int nnewchgbds, int nnewholes, int nnewdelconss, int nnewaddconss, int nnewupgdconss, int nnewchgcoefs, int nnewchgsides, int *nfixedvars, int *naggrvars, int *nchgvartypes, int *nchgbds, int *naddholes, int *ndelconss, int *naddconss, int *nupgdconss, int *nchgcoefs, int *nchgsides, SCIP_RESULT *result)
Definition: scip_cons.c:2407
SCIP_RETCODE SCIPcreateSymgraphConsnodeperm(SCIP *scip, SYM_GRAPH *graph)
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition: scip_mem.h:137
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip_general.c:498
SCIP_RETCODE SCIPlexicographicReductionGetStatistics(SCIP *scip, SCIP_LEXREDDATA *masterdata, int *nred, int *ncutoff)
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip_mem.c:57
SCIP_RETCODE SCIPisPackingPartitioningOrbitope(SCIP *scip, SCIP_VAR ***vars, int nrows, int ncols, SCIP_Bool **pprows, int *npprows, SCIP_ORBITOPETYPE *type)
Definition: symmetry.c:1178
SCIP_RETCODE SCIPincludePropSymmetry(SCIP *scip)
#define DEFAULT_PERFORMPRESOLVING
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition: cons.c:8216
SCIP_RETCODE SCIPchgVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip_var.c:4768
static SCIP_DECL_PROPEXIT(propExitSymmetry)
SCIP_Bool SCIPexprhdlrHasGetSymData(SCIP_EXPRHDLR *exprhdlr)
Definition: expr.c:685
#define PROP_PRESOL_MAXROUNDS
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:17420
static SCIP_RETCODE addOrbitopeSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *usedperms, int nusedperms, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int graphcoloridx, int nrows, int ncols, int *firstvaridx, int *compidxfirstrow, int **lexorder, int *nvarslexorder, int *maxnvarslexorder, SCIP_Bool mayinteract, SCIP_Bool *success)
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition: misc.c:3108
#define DEFAULT_ADDCONFLICTCUTS
#define DEFAULT_ADDSYMRESACKS
internal miscellaneous methods
#define PROP_PRESOL_PRIORITY
#define SCIP_Shortbool
Definition: def.h:99
SCIP_RETCODE SCIPorbitopalReductionPropagate(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
SCIP_RETCODE SCIPdetectSingleOrDoubleLexMatrices(SCIP *scip, SCIP_Bool detectsinglelex, int **perms, int nperms, int permlen, SCIP_Bool *success, SCIP_Bool *isorbitope, int ***lexmatrix, int *nrows, int *ncols, int **lexrowsbegin, int **lexcolsbegin, int *nrowmatrices, int *ncolmatrices)
Definition: symmetry.c:2052
power and signed power expression handlers
SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
Definition: scip_param.c:269
SCIP_PARAM * SCIPgetParam(SCIP *scip, const char *name)
Definition: scip_param.c:234
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition: misc.c:11269
#define SCIP_PROPTIMING_ALWAYS
Definition: type_timing.h:73
SCIP_RETCODE SCIPincludeOrbitalReduction(SCIP *scip, SCIP_ORBITALREDDATA **orbireddata, SCIP_EVENTHDLR *shadowtreeeventhdlr)
#define PROP_FREQ
static SCIP_RETCODE addStrongSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *graphcompbegins, int *graphcomponents, int graphcompidx, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
struct SCIP_OrbitopalReductionData SCIP_ORBITOPALREDDATA
void SCIPsortPtr(void **ptrarray, SCIP_DECL_SORTPTRCOMP((*ptrcomp)), int len)
#define SCIP_CALL(x)
Definition: def.h:380
SCIP_RETCODE SCIPcopySymgraph(SCIP *scip, SYM_GRAPH **graph, SYM_GRAPH *origgraph, int *perm, SYM_SPEC fixedtype)
static SCIP_RETCODE addSymresackConss(SCIP *scip, SCIP_PROPDATA *propdata, int cidx)
propagator for symmetry handling
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:225
SCIP_RETCODE SCIPcreateDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset, int ncomponents)
int SCIPdisjointsetGetComponentCount(SCIP_DISJOINTSET *djset)
Definition: misc.c:11366
static int getNOrbitopesInComp(SCIP_VAR **permvars, int *graphcomponents, int *graphcompbegins, int *compcolorbegins, int ncompcolors, int symcompsize)
SCIP_Real * lhs
int SCIPconshdlrGetNConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4638
const char * SYMsymmetryGetAddDesc(void)
SCIP_COLUMNORDERING SCIPorbitopalReductionGetDefaultColumnOrdering(SCIP_ORBITOPALREDDATA *orbireddata)
constraint handler for linking binary variables to a linking (continuous or integer) variable ...
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:124
SCIP_RETCODE SCIPdetermineNVarsAffectedSym(SCIP *scip, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, int *nvarsaffected)
Definition: symmetry.c:593
#define SCIP_Bool
Definition: def.h:91
static SCIP_RETCODE resetDynamicSymmetryHandling(SCIP *scip, SCIP_PROPDATA *propdata)
#define DEFAULT_PREFERLESSROWS
int SCIPgetNImplVars(SCIP *scip)
Definition: scip_prob.c:2127
static SCIP_RETCODE checkSymmetriesAreSymmetries(SCIP *scip, SYM_SYMTYPE symtype, int **perms, int nperms, int npermvars, SYM_SPEC fixedtype)
uint32_t SYM_SPEC
Definition: type_symmetry.h:47
static SCIP_Bool checkSortedArraysHaveOverlappingEntry(void **arr1, int narr1, void **arr2, int narr2, SCIP_DECL_SORTPTRCOMP((*compfunc)))
#define DEFAULT_SSTTIEBREAKRULE
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_GRAPH *graph, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Real *symcodetime)
SCIP_RETCODE SCIPorbitopalReductionReset(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata)
SCIP_RETCODE SCIPorbitalReductionGetStatistics(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, int *nred, int *ncutoff)
SCIP_Bool SCIPdialogHasEntry(SCIP_DIALOG *dialog, const char *entryname)
Definition: dialog.c:995
int SCIPgetDepth(SCIP *scip)
Definition: scip_tree.c:670
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9599
constraint handler for nonlinear constraints specified by algebraic expressions
static SCIP_Bool checkSymmetryDataFree(SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPisInvolutionPerm(int *perm, SCIP_VAR **vars, int nvars, int *ntwocyclesperm, int *nbincyclesperm, SCIP_Bool earlytermination)
Definition: symmetry.c:542
SCIP_RETCODE SCIPprintCons(SCIP *scip, SCIP_CONS *cons, FILE *file)
Definition: scip_cons.c:2537
SCIP_Bool SYMcanComputeSymmetry(void)
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition: cons.c:8236
#define MIN(x, y)
Definition: def.h:243
static SCIP_RETCODE propagateSymmetry(SCIP *scip, SCIP_PROPDATA *propdata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
SCIP_RETCODE SCIPorbitalReductionAddComponent(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, SCIP_VAR **permvars, int npermvars, int **perms, int nperms, SCIP_Bool *success)
static SCIP_RETCODE adaptSymmetryDataSST(SCIP *scip, int **origperms, int **modifiedperms, int nperms, SCIP_VAR **origpermvars, SCIP_VAR **modifiedpermvars, int npermvars, int *leaders, int nleaders)
SCIP_RETCODE SCIPorbitalReductionPrintStatistics(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata)
static SCIP_DECL_SORTPTRCOMP(sortByPointerValue)
SCIP_RETCODE SCIPlexicographicReductionPrintStatistics(SCIP *scip, SCIP_LEXREDDATA *masterdata)
SCIP_Bool SCIPallowWeakDualReds(SCIP *scip)
Definition: scip_var.c:8658
int SCIPgetNRuns(SCIP *scip)
enum SYM_Symtype SYM_SYMTYPE
Definition: type_symmetry.h:64
static SCIP_RETCODE handleOrbitope(SCIP *scip, SCIP_PROPDATA *propdata, int id, int **varidxmatrix, int nrows, int ncols, SCIP_Bool *success)
static SCIP_RETCODE tryAddOrbitalRedLexRed(SCIP *scip, SCIP_PROPDATA *propdata, int cidx)
SCIP_RETCODE SCIPlexicographicReductionFree(SCIP *scip, SCIP_LEXREDDATA **masterdata)
Constraint handler for linear constraints in their most general form, .
SCIP_Bool SCIPparamGetBool(SCIP_PARAM *param)
Definition: paramset.c:709
SCIP_RETCODE SCIPgenerateOrbitopeVarsMatrix(SCIP *scip, SCIP_VAR ****vars, int nrows, int ncols, SCIP_VAR **permvars, int npermvars, int **orbitopevaridx, int *columnorder, int *nusedelems, SCIP_Shortbool *rowisbinary, SCIP_Bool *infeasible, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
Definition: symmetry.c:987
int * consnodeperm
#define DEFAULT_MAXGENERATORS
#define PROP_NAME
SCIP_RETCODE SCIPdialoghdlrAddHistory(SCIP_DIALOGHDLR *dialoghdlr, SCIP_DIALOG *dialog, const char *command, SCIP_Bool escapecommand)
Definition: dialog.c:726
SCIP_RETCODE SCIPsetPropExitsol(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPEXITSOL((*propexitsol)))
Definition: scip_prop.c:231
int SCIPgetNBinVars(SCIP *scip)
Definition: scip_prob.c:2037
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4672
const char * SCIPpropGetName(SCIP_PROP *prop)
Definition: prop.c:941
static SCIP_DECL_TABLEFREE(tableFreeSymmetry)
SCIP_RETCODE SCIPextendSubOrbitope(int **suborbitope, int nrows, int nfilledcols, int coltoextend, int *perm, SCIP_Bool leftextension, int **nusedelems, SCIP_VAR **permvars, SCIP_Shortbool *rowisbinary, SCIP_Bool *success, SCIP_Bool *infeasible)
Definition: symmetry.c:645
SCIP_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9576
int SCIPgetNVars(SCIP *scip)
Definition: scip_prob.c:1992
product expression handler
static SCIP_Bool conshdlrCanProvideSymInformation(SCIP_CONSHDLR *conshdlr, SYM_SYMTYPE symtype)
SCIP_Real * r
Definition: circlepacking.c:59
SCIP_RETCODE SCIPgetSymOpNodeType(SCIP *scip, const char *opnodename, int *nodetype)
#define SYM_TIMING_DURINGPRESOL
Definition: type_symmetry.h:51
SCIP_RETCODE SCIPcreateConsLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
#define COMPRESSNVARSLB
SCIP_CLIQUE ** SCIPgetCliques(SCIP *scip)
Definition: scip_var.c:7631
Constraint handler for XOR constraints, .
#define DEFAULT_DOUBLEEQUATIONS
#define DEFAULT_ADDSTRONGSBCS
static SCIP_DECL_DIALOGEXEC(dialogExecDisplaySymmetry)
#define PROP_PRIORITY
static SCIP_RETCODE tryAddSymmetryHandlingMethods(SCIP *scip, SCIP_PROP *prop, int *nchgbds, SCIP_Bool *earlyterm)
#define MAX(x, y)
Definition: def.h:239
SCIP_DIALOG * SCIPdialoghdlrGetRoot(SCIP_DIALOGHDLR *dialoghdlr)
Definition: dialog.c:436
SCIP_RETCODE SCIPsetPropResprop(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPRESPROP((*propresprop)))
Definition: scip_prop.c:312
#define PROP_DELAY
void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
Definition: misc.c:5538
SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
#define PROP_DESC
static int compareSymgraphs(SCIP *scip, SYM_GRAPH *G1, SYM_GRAPH *G2)
SCIP_Bool SYMcheckGraphsAreIdentical(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH *G1, SYM_GRAPH *G2)
methods for handling symmetries
const char * SYMsymmetryGetName(void)
static SCIP_RETCODE checkComponentsForNonstandardPerms(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPcomputeOrbitVar(SCIP *scip, int npermvars, int **perms, int **permstrans, int *components, int *componentbegins, SCIP_Shortbool *ignoredvars, SCIP_Shortbool *varfound, int varidx, int component, int *orbit, int *orbitsize)
Definition: symmetry.c:320
SCIP_RETCODE SCIPorbitalReductionReset(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata)
#define ISORBITALREDUCTIONACTIVE(x)
static SCIP_RETCODE ensureSymmetryPermstransComputed(SCIP *scip, SCIP_PROPDATA *propdata)
int SCIPgetNConss(SCIP *scip)
Definition: scip_prob.c:3042
SCIP_RETCODE SCIPgetConsPermsymGraph(SCIP *scip, SCIP_CONS *cons, SYM_GRAPH *graph, SCIP_Bool *success)
Definition: scip_cons.c:2655
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip_cons.c:1174
static SCIP_RETCODE setSymmetryData(SCIP *scip, SYM_SYMTYPE symtype, SCIP_VAR **vars, int nvars, int nbinvars, SCIP_VAR ***permvars, int *npermvars, int *nbinpermvars, SCIP_Real **permvardomaincenter, int **perms, int nperms, int *nmovedvars, SCIP_Bool *binvaraffected, SCIP_Bool usecompression, SCIP_Real compressthreshold, SCIP_Bool *compressed)
int SCIPgetNCliques(SCIP *scip)
Definition: scip_var.c:7577
#define MAXGENNUMERATOR
#define SYM_SPEC_REAL
Definition: type_symmetry.h:45
#define ISSYMRETOPESACTIVE(x)
SCIP_VAR ** SCIPgetVars(SCIP *scip)
Definition: scip_prob.c:1947
SCIP_RETCODE SCIPfreeSymgraphConsnodeperm(SCIP *scip, SYM_GRAPH *graph)
methods for handling symmetries by dynamic lexicographic ordering reduction
struct SCIP_LexRedData SCIP_LEXREDDATA
SCIP_RETCODE SCIPcaptureVar(SCIP *scip, SCIP_VAR *var)
Definition: scip_var.c:1216
#define SCIP_Real
Definition: def.h:173
#define DEFAULT_SSTLEADERVARTYPE
#define SCIPfreeCleanBufferArray(scip, ptr)
Definition: scip_mem.h:146
struct SCIP_PropData SCIP_PROPDATA
Definition: type_prop.h:52
SCIP_Bool SCIPisStopped(SCIP *scip)
Definition: scip_general.c:718
struct SCIP_OrbitalReductionData SCIP_ORBITALREDDATA
SCIP_TABLEDATA * SCIPtableGetData(SCIP_TABLE *table)
Definition: table.c:288
#define SYM_HANDLETYPE_SST
Definition: type_symmetry.h:97
constraint handler for SOS type 1 constraints
SCIP_RETCODE SCIPcreateSymOpNodeType(SCIP *scip, const char *opnodename, int *nodetype)
#define DEFAULT_SYMFIXNONBINARYVARS
public methods for data structures
#define DEFAULT_ADDCONSSTIMING
SCIP_RETCODE SCIPsetPropFree(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPFREE((*propfree)))
Definition: scip_prop.c:167
static SCIP_Bool isNonstandardPerm(SCIP *scip, int *symmetry, SCIP_VAR **vars, int nvars)
SCIP_PROPDATA * SCIPpropGetData(SCIP_PROP *prop)
Definition: prop.c:789
SCIP_Real SCIPgetShadowTreeEventHandlerExecutionTime(SCIP *scip, SCIP_EVENTHDLR *eventhdlr)
unsigned int SCIPcliqueGetId(SCIP_CLIQUE *clique)
Definition: implics.c:3402
static SCIP_RETCODE componentPackingPartitioningOrbisackUpgrade(SCIP *scip, SCIP_PROPDATA *propdata, int **componentperms, int componentsize, SCIP_Bool hassignedperm, SCIP_Bool *success)
static SCIP_DECL_PROPEXITSOL(propExitsolSymmetry)
const char * SYMsymmetryGetDesc(void)
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition: var.c:17585
SCIP_RETCODE SCIPreleaseDialog(SCIP *scip, SCIP_DIALOG **dialog)
Definition: scip_dialog.c:124
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
Definition: scip_general.c:728
SCIP_RETCODE SCIPcreateConsOrbitope(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR ***vars, SCIP_ORBITOPETYPE orbitopetype, int nspcons, int nblocks, SCIP_Bool usedynamicprop, SCIP_Bool mayinteract, SCIP_Bool resolveprop, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
enum SCIP_Vartype SCIP_VARTYPE
Definition: type_var.h:73
constraint handler for SOS type 2 constraints
#define PROP_PRESOLTIMING
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:18145
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip_mem.h:111
int SCIPcliqueGetNVars(SCIP_CLIQUE *clique)
Definition: implics.c:3370
static SCIP_RETCODE tryHandleSubgroups(SCIP *scip, SCIP_PROPDATA *propdata, int cidx)
int SCIPgetSymgraphNVarcolors(SYM_GRAPH *graph)
static SCIP_RETCODE freeConflictGraphSST(SCIP *scip, SCIP_CONFLICTDATA **varconflicts, int nvars)
static SCIP_RETCODE selectOrbitLeaderSSTConss(SCIP *scip, SCIP_CONFLICTDATA *varconflicts, SCIP_VAR **conflictvars, int nconflictvars, int *orbits, int *orbitbegins, int norbits, int leaderrule, int tiebreakrule, SCIP_VARTYPE leadervartype, int *orbitidx, int *leaderidx, SCIP_Shortbool *orbitvarinconflict, int *norbitvarinconflict, SCIP_Bool *success)
SCIP_DIALOGDATA * SCIPdialogGetData(SCIP_DIALOG *dialog)
Definition: dialog.c:1253
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3281
SCIP_RETCODE SCIPsetPropInitpre(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPINITPRE((*propinitpre)))
Definition: scip_prop.c:247
constraint handler for bound disjunction constraints
void SCIPfreeDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset)
SCIP_RETCODE SCIPsetPropExit(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPEXIT((*propexit)))
Definition: scip_prop.c:199
#define DEFAULT_ENFORCECOMPUTESYMMETRY
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition: var.c:17611
static SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
#define DEFAULT_USECOLUMNSPARSITY
#define DEFAULT_DETECTORBITOPES
SCIP_RETCODE SCIPlexicographicReductionReset(SCIP *scip, SCIP_LEXREDDATA *masterdata)
SCIP_Bool SCIPallowStrongDualReds(SCIP *scip)
Definition: scip_var.c:8631
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:139
SCIP_Bool SCIPconshdlrSupportsPermsymDetection(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:5295
static SCIP_RETCODE buildSubgroupGraph(SCIP *scip, SCIP_PROPDATA *propdata, int *genorder, int ntwocycleperms, int compidx, int **graphcomponents, int **graphcompbegins, int **compcolorbegins, int *ngraphcomponents, int *ncompcolors, int **usedperms, int *nusedperms, int usedpermssize, SCIP_Shortbool *permused)
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:57
struct SCIP_TableData SCIP_TABLEDATA
Definition: type_table.h:58
#define SCIPreallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:128
SCIP_RETCODE SCIPincludePropBasic(SCIP *scip, SCIP_PROP **propptr, const char *name, const char *desc, int priority, int freq, SCIP_Bool delay, SCIP_PROPTIMING timingmask, SCIP_DECL_PROPEXEC((*propexec)), SCIP_PROPDATA *propdata)
Definition: scip_prop.c:114
static SCIP_DECL_PROPFREE(propFreeSymmetry)