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