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 /** returns whether a recomputation of symmetries is required */
735 static
737  SCIP* scip, /**< SCIP pointer */
738  SCIP_PROPDATA* propdata /**< propagator data */
739  )
740 { /*lint --e{641}*/
741  assert( scip != NULL );
742  assert( propdata != NULL );
743 
744  if ( propdata->recomputerestart == SCIP_RECOMPUTESYM_NEVER )
745  return FALSE;
746 
747  /* we do not need to recompute symmetries if no restart has occured */
748  if ( SCIPgetNRuns(scip) == propdata->lastrestart || propdata->lastrestart == 0 || SCIPgetNRuns(scip) == 1 )
749  return FALSE;
750 
751  if ( propdata->recomputerestart == SCIP_RECOMPUTESYM_ALWAYS )
752  return TRUE;
753 
754  /* recompute symmetries if OF found a reduction */
755  assert( propdata->recomputerestart == SCIP_RECOMPUTESYM_OFFOUNDRED );
756  if ( propdata->offoundreduction )
757  return TRUE;
758 
759  return FALSE;
760 }
761 
762 
763 /** sets in propdata which symmetry handling methods are active */
764 static
766  SCIP_PROPDATA* propdata /**< propagator data */
767  )
768 {
769  assert( propdata != NULL );
770 
771  if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
772  propdata->symconsenabled = TRUE;
773  else
774  propdata->symconsenabled = FALSE;
775 
776  if ( ISORBITALFIXINGACTIVE(propdata->usesymmetry) )
777  propdata->ofenabled = TRUE;
778  else
779  propdata->ofenabled = FALSE;
780 
781  if ( ISSSTACTIVE(propdata->usesymmetry) )
782  propdata->sstenabled = TRUE;
783  else
784  propdata->sstenabled = FALSE;
785 
786  return SCIP_OKAY;
787 }
788 
789 
790 /** frees symmetry data */
791 static
793  SCIP* scip, /**< SCIP pointer */
794  SCIP_PROPDATA* propdata /**< propagator data */
795  )
796 {
797  int i;
798 
799  assert( scip != NULL );
800  assert( propdata != NULL );
801 
802  if ( propdata->permvarmap != NULL )
803  {
804  SCIPhashmapFree(&propdata->permvarmap);
805  }
806 
807  /* drop events */
808  if ( propdata->permvarsevents != NULL )
809  {
810  assert( propdata->permvars != NULL );
811  assert( propdata->npermvars > 0 );
812 
813  for (i = 0; i < propdata->npermvars; ++i)
814  {
815  if ( SCIPvarGetType(propdata->permvars[i]) == SCIP_VARTYPE_BINARY )
816  {
817  /* If symmetry is computed before presolving, it might happen that some variables are turned into binary
818  * variables, for which no event has been catched. Since there currently is no way of checking whether a var
819  * event has been caught for a particular variable, we use the stored eventfilter positions. */
820  if ( propdata->permvarsevents[i] >= 0 )
821  {
823  propdata->eventhdlr, (SCIP_EVENTDATA*) propdata, propdata->permvarsevents[i]) );
824  }
825  }
826  }
827  SCIPfreeBlockMemoryArray(scip, &propdata->permvarsevents, propdata->npermvars);
828  }
829 
830  /* release variables */
831  if ( propdata->nonbinpermvarcaptured != NULL )
832  {
833  int cnt;
834 
835  /* memory should have been allocated only if the leader type is not binary */
836  assert( propdata->sstenabled && propdata->sstleadervartype != (int) SCIP_SSTTYPE_BINARY );
837 
838  for (i = propdata->nbinpermvars, cnt = 0; i < propdata->npermvars; ++i, ++cnt)
839  {
840  /* release captured non-binary variables
841  * (cannot use isLeadervartypeCompatible(), because vartype may have changed in between)
842  */
843  if ( propdata->nonbinpermvarcaptured[cnt] )
844  {
845  SCIP_CALL( SCIPreleaseVar(scip, &propdata->permvars[i]) );
846  }
847  }
848  SCIPfreeBlockMemoryArray(scip, &propdata->nonbinpermvarcaptured, propdata->npermvars - propdata->nbinpermvars);
849  propdata->nonbinpermvarcaptured = NULL;
850  }
851 
852  if ( propdata->binvaraffected )
853  {
854  for (i = 0; i < propdata->nbinpermvars; ++i)
855  {
856  SCIP_CALL( SCIPreleaseVar(scip, &propdata->permvars[i]) );
857  }
858  }
859 
860  /* free lists for orbitopal fixing */
861  if ( propdata->bg0list != NULL )
862  {
863  assert( propdata->bg0 != NULL );
864  assert( propdata->bg1list != NULL );
865  assert( propdata->bg1 != NULL );
866 
867  SCIPfreeBlockMemoryArray(scip, &propdata->bg0list, propdata->npermvars);
868  SCIPfreeBlockMemoryArray(scip, &propdata->bg0, propdata->npermvars);
869  SCIPfreeBlockMemoryArray(scip, &propdata->bg1list, propdata->npermvars);
870  SCIPfreeBlockMemoryArray(scip, &propdata->bg1, propdata->npermvars);
871 
872  propdata->nbg0 = 0;
873  propdata->nbg1 = 0;
874  }
875 
876  /* other data */
877  SCIPfreeBlockMemoryArrayNull(scip, &propdata->inactiveperms, propdata->nperms);
878 
879  /* free permstrans matrix*/
880  if ( propdata->permstrans != NULL )
881  {
882  assert( propdata->nperms > 0 );
883  assert( propdata->permvars != NULL );
884  assert( propdata->npermvars > 0 );
885  assert( propdata->nmaxperms > 0 );
886 
887  for (i = 0; i < propdata->npermvars; ++i)
888  {
889  SCIPfreeBlockMemoryArray(scip, &propdata->permstrans[i], propdata->nmaxperms);
890  }
891  SCIPfreeBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars);
892  }
893 
894  /* free data of added orbitope/orbisack/symresack constraints */
895  if ( propdata->genorbconss != NULL )
896  {
897  assert( propdata->ngenorbconss + propdata->ngenlinconss > 0
898  || (ISORBITALFIXINGACTIVE(propdata->usesymmetry) && propdata->norbitopes == 0) );
899 
900  /* release constraints */
901  for (i = 0; i < propdata->ngenorbconss; ++i)
902  {
903  assert( propdata->genorbconss[i] != NULL );
904  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genorbconss[i]) );
905  }
906 
907  /* free pointers to symmetry group and binary variables */
908  SCIPfreeBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms);
909  propdata->ngenorbconss = 0;
910  }
911 
912  /* free data of added constraints */
913  if ( propdata->genlinconss != NULL )
914  {
915  /* release constraints */
916  for (i = 0; i < propdata->ngenlinconss; ++i)
917  {
918  assert( propdata->genlinconss[i] != NULL );
919  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genlinconss[i]) );
920  }
921 
922  /* free pointers to symmetry group and binary variables */
923  SCIPfreeBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize);
924  propdata->ngenlinconss = 0;
925  propdata->genlinconsssize = 0;
926  }
927 
928  if ( propdata->sstconss != NULL )
929  {
930  assert( propdata->nsstconss > 0 );
931 
932  /* release constraints */
933  for (i = 0; i < propdata->nsstconss; ++i)
934  {
935  assert( propdata->sstconss[i] != NULL );
936  SCIP_CALL( SCIPreleaseCons(scip, &propdata->sstconss[i]) );
937  }
938 
939  /* free pointers to symmetry group and binary variables */
940  SCIPfreeBlockMemoryArray(scip, &propdata->sstconss, propdata->maxnsstconss);
941  propdata->sstconss = NULL;
942  propdata->nsstconss = 0;
943  propdata->maxnsstconss = 0;
944  }
945 
946  if ( propdata->leaders != NULL )
947  {
948  assert( propdata->maxnleaders > 0 );
949 
950  SCIPfreeBlockMemoryArray(scip, &propdata->leaders, propdata->maxnleaders);
951  propdata->maxnleaders = 0;
952  propdata->leaders = NULL;
953  propdata->nleaders = 0;
954  }
955 
956  /* free components */
957  if ( propdata->ncomponents > 0 )
958  {
959  assert( propdata->componentblocked != NULL );
960  assert( propdata->vartocomponent != NULL );
961  assert( propdata->componentbegins != NULL );
962  assert( propdata->components != NULL );
963 
964  SCIPfreeBlockMemoryArray(scip, &propdata->componentblocked, propdata->ncomponents);
965  SCIPfreeBlockMemoryArray(scip, &propdata->vartocomponent, propdata->npermvars);
966  SCIPfreeBlockMemoryArray(scip, &propdata->componentbegins, propdata->ncomponents + 1);
967  SCIPfreeBlockMemoryArray(scip, &propdata->components, propdata->nperms);
968 
969  propdata->ncomponents = -1;
970  propdata->ncompblocked = 0;
971  }
972 
973  /* free main symmetry data */
974  if ( propdata->nperms > 0 )
975  {
976  assert( propdata->permvars != NULL );
977 
978  SCIPfreeBlockMemoryArray(scip, &propdata->permvars, propdata->npermvars);
979 
980  /* if orbital fixing runs exclusively, propdata->perms was already freed in determineSymmetry() */
981  if ( propdata->perms != NULL )
982  {
983  for (i = 0; i < propdata->nperms; ++i)
984  {
985  SCIPfreeBlockMemoryArray(scip, &propdata->perms[i], propdata->npermvars);
986  }
987  SCIPfreeBlockMemoryArray(scip, &propdata->perms, propdata->nmaxperms);
988  }
989 
990 #ifndef NDEBUG
991  SCIPfreeBlockMemoryArrayNull(scip, &propdata->permvarsobj, propdata->npermvars);
992 #endif
993 
994  SCIPfreeBlockMemoryArrayNull(scip, &propdata->isnonlinvar, propdata->npermvars);
995 
996  propdata->npermvars = 0;
997  propdata->nbinpermvars = 0;
998  propdata->nperms = -1;
999  propdata->nmaxperms = 0;
1000  propdata->nmovedpermvars = -1;
1001  propdata->nmovedbinpermvars = 0;
1002  propdata->nmovedintpermvars = 0;
1003  propdata->nmovedimplintpermvars = 0;
1004  propdata->nmovedcontpermvars = 0;
1005  propdata->nmovedvars = -1;
1006  propdata->log10groupsize = -1.0;
1007  propdata->binvaraffected = FALSE;
1008  propdata->isnonlinvar = NULL;
1009  }
1010  propdata->nperms = -1;
1011 
1012  assert( checkSymmetryDataFree(propdata) );
1013 
1014  propdata->computedsymmetry = FALSE;
1015  propdata->compressed = FALSE;
1016 
1017  return SCIP_OKAY;
1018 }
1019 
1020 
1021 /** deletes symmetry handling constraints */
1022 static
1024  SCIP* scip, /**< SCIP pointer */
1025  SCIP_PROPDATA* propdata /**< propagator data */
1026  )
1027 {
1028  int i;
1029 
1030  assert( scip != NULL );
1031  assert( propdata != NULL );
1032 
1033  if ( propdata->ngenorbconss == 0 )
1034  {
1035  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
1036  }
1037  else
1038  {
1039  assert( propdata->genorbconss != NULL );
1040  assert( propdata->nperms > 0 );
1041  assert( propdata->nperms >= propdata->ngenorbconss );
1042 
1043  for (i = 0; i < propdata->ngenorbconss; ++i)
1044  {
1045  assert( propdata->genorbconss[i] != NULL );
1046 
1047  SCIP_CALL( SCIPdelCons(scip, propdata->genorbconss[i]) );
1048  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genorbconss[i]) );
1049  }
1050 
1051  SCIPfreeBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms);
1052  propdata->ngenorbconss = 0;
1053  }
1054 
1055  /* free Schreier Sims data */
1056  if ( propdata->nsstconss > 0 )
1057  {
1058  for (i = 0; i < propdata->nsstconss; ++i)
1059  {
1060  assert( propdata->sstconss[i] != NULL );
1061 
1062  SCIP_CALL( SCIPdelCons(scip, propdata->sstconss[i]) );
1063  SCIP_CALL( SCIPreleaseCons(scip, &propdata->sstconss[i]) );
1064  }
1065 
1066  SCIPfreeBlockMemoryArray(scip, &propdata->sstconss, propdata->maxnsstconss);
1067  propdata->nsstconss = 0;
1068  propdata->maxnsstconss = 0;
1069  }
1070 
1071  if ( propdata->ngenlinconss == 0 )
1072  {
1073  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genlinconss, propdata->genlinconsssize);
1074  }
1075  else
1076  {
1077  assert( propdata->genlinconss != NULL );
1078  assert( propdata->nperms > 0 );
1079 
1080  for (i = 0; i < propdata->ngenlinconss; ++i)
1081  {
1082  assert( propdata->genlinconss[i] != NULL );
1083 
1084  SCIP_CALL( SCIPdelCons(scip, propdata->genlinconss[i]) );
1085  SCIP_CALL( SCIPreleaseCons(scip, &propdata->genlinconss[i]) );
1086  }
1087 
1088  SCIPfreeBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize);
1089  propdata->ngenlinconss = 0;
1090  }
1091 
1092  /* free pointers to symmetry group and binary variables */
1093  assert( propdata->nperms > 0 || propdata->genorbconss == NULL );
1094  assert( propdata->nperms >= propdata->ngenorbconss || propdata->genorbconss == NULL );
1095  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
1096  propdata->ngenorbconss = 0;
1097  propdata->triedaddconss = FALSE;
1098 
1099  return SCIP_OKAY;
1100 }
1101 
1102 
1103 /** determines whether variable should be fixed by permutations */
1104 static
1106  SYM_SPEC fixedtype, /**< bitset of variable types that should be fixed */
1107  SCIP_VAR* var /**< variable to be considered */
1108  )
1109 {
1110  if ( (fixedtype & SYM_SPEC_INTEGER) && SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER )
1111  return TRUE;
1112  if ( (fixedtype & SYM_SPEC_BINARY) && SCIPvarGetType(var) == SCIP_VARTYPE_BINARY )
1113  return TRUE;
1114  if ( (fixedtype & SYM_SPEC_REAL) &&
1116  return TRUE;
1117  return FALSE;
1118 }
1119 
1120 
1121 /** Transforms given variables, scalars, and constant to the corresponding active variables, scalars, and constant.
1122  *
1123  * @note @p constant needs to be initialized!
1124  */
1125 static
1127  SCIP* scip, /**< SCIP data structure */
1128  SCIP_VAR*** vars, /**< pointer to vars array to get active variables for */
1129  SCIP_Real** scalars, /**< pointer to scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
1130  int* nvars, /**< pointer to number of variables and values in vars and vals array */
1131  SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
1132  SCIP_Bool transformed /**< transformed constraint? */
1133  )
1134 {
1135  int requiredsize;
1136  int v;
1137 
1138  assert( scip != NULL );
1139  assert( vars != NULL );
1140  assert( scalars != NULL );
1141  assert( *vars != NULL );
1142  assert( *scalars != NULL );
1143  assert( nvars != NULL );
1144  assert( constant != NULL );
1145 
1146  if ( transformed )
1147  {
1148  SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, *nvars, constant, &requiredsize, TRUE) );
1149 
1150  if ( requiredsize > *nvars )
1151  {
1152  SCIP_CALL( SCIPreallocBufferArray(scip, vars, requiredsize) );
1153  SCIP_CALL( SCIPreallocBufferArray(scip, scalars, requiredsize) );
1154 
1155  SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, requiredsize, constant, &requiredsize, TRUE) );
1156  assert( requiredsize <= *nvars );
1157  }
1158  }
1159  else
1160  {
1161  for (v = 0; v < *nvars; ++v)
1162  {
1163  SCIP_CALL( SCIPvarGetOrigvarSum(&(*vars)[v], &(*scalars)[v], constant) );
1164  }
1165  }
1166  return SCIP_OKAY;
1167 }
1168 
1169 
1170 /** fills in matrix elements into coefficient arrays */
1171 static
1173  SCIP* scip, /**< SCIP data structure */
1174  SCIP_Bool doubleequations, /**< Double equations to positive/negative version? */
1175  SCIP_VAR** linvars, /**< array of linear variables */
1176  SCIP_Real* linvals, /**< array of linear coefficients values (or NULL if all linear coefficient values are 1) */
1177  int nlinvars, /**< number of linear variables */
1178  SCIP_Real lhs, /**< left hand side */
1179  SCIP_Real rhs, /**< right hand side */
1180  SCIP_Bool istransformed, /**< whether the constraint is transformed */
1181  SYM_RHSSENSE rhssense, /**< identifier of constraint type */
1182  SYM_MATRIXDATA* matrixdata, /**< matrix data to be filled in */
1183  int* nconssforvar /**< pointer to array to store for each var the number of conss */
1184  )
1185 {
1186  SCIP_VAR** vars;
1187  SCIP_Real* vals;
1188  SCIP_Real constant = 0.0;
1189  int nrhscoef;
1190  int nmatcoef;
1191  int nvars;
1192  int j;
1193 
1194  assert( scip != NULL );
1195  assert( nlinvars == 0 || linvars != NULL );
1196  assert( lhs <= rhs );
1197 
1198  /* do nothing if constraint is empty */
1199  if ( nlinvars == 0 )
1200  return SCIP_OKAY;
1201 
1202  /* ignore redundant constraints */
1203  if ( SCIPisInfinity(scip, -lhs) && SCIPisInfinity(scip, rhs) )
1204  return SCIP_OKAY;
1205 
1206  /* duplicate variable and value array */
1207  nvars = nlinvars;
1208  SCIP_CALL( SCIPduplicateBufferArray(scip, &vars, linvars, nvars) );
1209  if ( linvals != NULL )
1210  {
1211  SCIP_CALL( SCIPduplicateBufferArray(scip, &vals, linvals, nvars) );
1212  }
1213  else
1214  {
1215  SCIP_CALL( SCIPallocBufferArray(scip, &vals, nvars) );
1216  for (j = 0; j < nvars; ++j)
1217  vals[j] = 1.0;
1218  }
1219  assert( vars != NULL );
1220  assert( vals != NULL );
1221 
1222  /* get active variables */
1223  SCIP_CALL( getActiveVariables(scip, &vars, &vals, &nvars, &constant, istransformed) );
1224 
1225  /* check whether constraint is empty after transformation to active variables */
1226  if ( nvars <= 0 )
1227  {
1228  SCIPfreeBufferArray(scip, &vals);
1229  SCIPfreeBufferArray(scip, &vars);
1230  return SCIP_OKAY;
1231  }
1232 
1233  /* handle constant */
1234  if ( ! SCIPisInfinity(scip, -lhs) )
1235  lhs -= constant;
1236  if ( ! SCIPisInfinity(scip, rhs) )
1237  rhs -= constant;
1238 
1239  /* check whether we have to resize; note that we have to add 2 * nvars since two inequalities may be added */
1240  if ( matrixdata->nmatcoef + 2 * nvars > matrixdata->nmaxmatcoef )
1241  {
1242  int newsize;
1243 
1244  newsize = SCIPcalcMemGrowSize(scip, matrixdata->nmatcoef + 2 * nvars);
1245  assert( newsize >= 0 );
1246  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matidx), matrixdata->nmaxmatcoef, newsize) );
1247  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matrhsidx), matrixdata->nmaxmatcoef, newsize) );
1248  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matvaridx), matrixdata->nmaxmatcoef, newsize) );
1249  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matcoef), matrixdata->nmaxmatcoef, newsize) );
1250  SCIPdebugMsg(scip, "Resized matrix coefficients from %d to %d.\n", matrixdata->nmaxmatcoef, newsize);
1251  matrixdata->nmaxmatcoef = newsize;
1252  }
1253 
1254  nrhscoef = matrixdata->nrhscoef;
1255  nmatcoef = matrixdata->nmatcoef;
1256 
1257  /* check lhs/rhs */
1258  if ( SCIPisEQ(scip, lhs, rhs) )
1259  {
1260  SCIP_Bool poscoef = FALSE;
1261  SCIP_Bool negcoef = FALSE;
1262 
1263  assert( ! SCIPisInfinity(scip, rhs) );
1264 
1265  /* equality constraint */
1266  matrixdata->rhscoef[nrhscoef] = rhs;
1267 
1268  /* if we deal with special constraints */
1269  if ( rhssense >= SYM_SENSE_XOR )
1270  matrixdata->rhssense[nrhscoef] = rhssense;
1271  else
1272  matrixdata->rhssense[nrhscoef] = SYM_SENSE_EQUATION;
1273  matrixdata->rhsidx[nrhscoef] = nrhscoef;
1274 
1275  for (j = 0; j < nvars; ++j)
1276  {
1277  assert( nmatcoef < matrixdata->nmaxmatcoef );
1278 
1279  matrixdata->matidx[nmatcoef] = nmatcoef;
1280  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1281 
1282  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
1283 
1284  if ( nconssforvar != NULL )
1285  nconssforvar[SCIPvarGetProbindex(vars[j])] += 1;
1286  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1287  matrixdata->matcoef[nmatcoef++] = vals[j];
1288  if ( SCIPisPositive(scip, vals[j]) )
1289  poscoef = TRUE;
1290  else
1291  negcoef = TRUE;
1292  }
1293  nrhscoef++;
1294 
1295  /* add negative of equation; increases chance to detect symmetry, but might increase time to compute symmetry. */
1296  if ( doubleequations && poscoef && negcoef )
1297  {
1298  for (j = 0; j < nvars; ++j)
1299  {
1300  assert( nmatcoef < matrixdata->nmaxmatcoef );
1301  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
1302 
1303  matrixdata->matidx[nmatcoef] = nmatcoef;
1304  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1305  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1306  matrixdata->matcoef[nmatcoef++] = -vals[j];
1307  }
1308  matrixdata->rhssense[nrhscoef] = SYM_SENSE_EQUATION;
1309  matrixdata->rhsidx[nrhscoef] = nrhscoef;
1310  matrixdata->rhscoef[nrhscoef++] = -rhs;
1311  }
1312  }
1313  else
1314  {
1315 #ifndef NDEBUG
1316  if ( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 )
1317  {
1318  assert( ! SCIPisInfinity(scip, -lhs) );
1319  assert( ! SCIPisInfinity(scip, rhs) );
1320  }
1321 #endif
1322 
1323  if ( ! SCIPisInfinity(scip, -lhs) )
1324  {
1325  matrixdata->rhscoef[nrhscoef] = -lhs;
1326  if ( rhssense >= SYM_SENSE_XOR )
1327  {
1328  assert( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 );
1329  matrixdata->rhssense[nrhscoef] = rhssense;
1330  }
1331  else
1332  matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
1333 
1334  matrixdata->rhsidx[nrhscoef] = nrhscoef;
1335 
1336  for (j = 0; j < nvars; ++j)
1337  {
1338  assert( nmatcoef < matrixdata->nmaxmatcoef );
1339  matrixdata->matidx[nmatcoef] = nmatcoef;
1340  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1341  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1342 
1343  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
1344 
1345  if ( nconssforvar != NULL )
1346  nconssforvar[SCIPvarGetProbindex(vars[j])] += 1;
1347 
1348  matrixdata->matcoef[nmatcoef++] = -vals[j];
1349  }
1350  nrhscoef++;
1351  }
1352 
1353  if ( ! SCIPisInfinity(scip, rhs) )
1354  {
1355  matrixdata->rhscoef[nrhscoef] = rhs;
1356  if ( rhssense >= SYM_SENSE_XOR )
1357  {
1358  assert( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 );
1359  matrixdata->rhssense[nrhscoef] = rhssense;
1360  }
1361  else
1362  matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
1363 
1364  matrixdata->rhsidx[nrhscoef] = nrhscoef;
1365 
1366  for (j = 0; j < nvars; ++j)
1367  {
1368  assert( nmatcoef < matrixdata->nmaxmatcoef );
1369  matrixdata->matidx[nmatcoef] = nmatcoef;
1370  matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1371 
1372  assert( 0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < SCIPgetNVars(scip) );
1373 
1374  if ( nconssforvar != NULL )
1375  nconssforvar[SCIPvarGetProbindex(vars[j])] += 1;
1376 
1377  matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1378  matrixdata->matcoef[nmatcoef++] = vals[j];
1379  }
1380  nrhscoef++;
1381  }
1382  }
1383  matrixdata->nrhscoef = nrhscoef;
1384  matrixdata->nmatcoef = nmatcoef;
1385 
1386  SCIPfreeBufferArray(scip, &vals);
1387  SCIPfreeBufferArray(scip, &vars);
1388 
1389  return SCIP_OKAY;
1390 }
1391 
1392 
1393 /** checks whether given permutations form a symmetry of a MIP
1394  *
1395  * We need the matrix and rhs in the original order in order to speed up the comparison process. The matrix is needed
1396  * in the right order to easily check rows. The rhs is used because of cache effects.
1397  */
1398 static
1400  SCIP* scip, /**< SCIP data structure */
1401  SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1402  SYM_MATRIXDATA* matrixdata, /**< matrix data */
1403  int nperms, /**< number of permutations */
1404  int** perms /**< permutations */
1405  )
1406 {
1407  SCIP_CONSHDLR* conshdlr;
1408  SCIP_HASHMAP* varmap;
1409  SCIP_VAR** occuringvars;
1410  SCIP_Real* permrow = 0;
1411  SCIP_Bool success;
1412  int* rhsmatbeg = 0;
1413  int nconss;
1414  int noccuringvars;
1415  int oldrhs;
1416  int i;
1417  int j;
1418  int p;
1419 
1420  SCIPdebugMsg(scip, "Checking whether symmetries are symmetries (generators: %d).\n", nperms);
1421 
1422  /* set up dense row for permuted row */
1423  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &permrow, matrixdata->npermvars) );
1424 
1425  /* set up map between rows and first entry in matcoef array */
1426  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &rhsmatbeg, matrixdata->nrhscoef) );
1427  for (j = 0; j < matrixdata->nrhscoef; ++j)
1428  rhsmatbeg[j] = -1;
1429 
1430  /* get info for non-linear part */
1431  conshdlr = SCIPfindConshdlr(scip, "nonlinear");
1432  nconss = conshdlr != NULL ? SCIPconshdlrGetNConss(conshdlr) : 0;
1433 
1434  /* create hashmaps for variable permutation and constraints in non-linear part array for occuring variables */
1435  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), matrixdata->npermvars) );
1436  SCIP_CALL( SCIPallocBufferArray(scip, &occuringvars, matrixdata->npermvars) );
1437 
1438  /* build map from rhs into matrix */
1439  oldrhs = -1;
1440  for (j = 0; j < matrixdata->nmatcoef; ++j)
1441  {
1442  int rhs;
1443 
1444  rhs = matrixdata->matrhsidx[j];
1445  if ( rhs != oldrhs )
1446  {
1447  assert( 0 <= rhs && rhs < matrixdata->nrhscoef );
1448  rhsmatbeg[rhs] = j;
1449  oldrhs = rhs;
1450  }
1451  }
1452 
1453  /* create row */
1454  for (j = 0; j < matrixdata->npermvars; ++j)
1455  permrow[j] = 0.0;
1456 
1457  /* check all generators */
1458  for (p = 0; p < nperms; ++p)
1459  {
1460  int* P;
1461  int r1;
1462  int r2;
1463 
1464  SCIPdebugMsg(scip, "Verifying automorphism group generator #%d for linear part ...\n", p);
1465  P = perms[p];
1466  assert( P != NULL );
1467 
1468  for (j = 0; j < matrixdata->npermvars; ++j)
1469  {
1470  if ( SymmetryFixVar(fixedtype, matrixdata->permvars[j]) && P[j] != j )
1471  {
1472  SCIPdebugMsg(scip, "Permutation does not fix types %u, moving variable %d.\n", fixedtype, j);
1473  return SCIP_ERROR;
1474  }
1475  }
1476 
1477  /*
1478  * linear part
1479  */
1480 
1481  /* check all linear constraints == rhs */
1482  for (r1 = 0; r1 < matrixdata->nrhscoef; ++r1)
1483  {
1484  int npermuted = 0;
1485 
1486  /* fill row into permrow (dense) */
1487  j = rhsmatbeg[r1];
1488  assert( 0 <= j && j < matrixdata->nmatcoef );
1489  assert( matrixdata->matrhsidx[j] == r1 ); /* note: row cannot be empty by construction */
1490 
1491  /* loop through row */
1492  while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
1493  {
1494  int varidx;
1495 
1496  assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
1497  varidx = P[matrixdata->matvaridx[j]];
1498  assert( 0 <= varidx && varidx < matrixdata->npermvars );
1499  if ( varidx != matrixdata->matvaridx[j] )
1500  ++npermuted;
1501  assert( SCIPisZero(scip, permrow[varidx]) );
1502  permrow[varidx] = matrixdata->matcoef[j];
1503  ++j;
1504  }
1505 
1506  /* if row is not affected by permutation, we do not have to check it */
1507  if ( npermuted > 0 )
1508  {
1509  /* check other rows (sparse) */
1510  SCIP_Bool found = FALSE;
1511  for (r2 = 0; r2 < matrixdata->nrhscoef; ++r2)
1512  {
1513  /* a permutation must map constraints of the same type and respect rhs coefficients */
1514  if ( matrixdata->rhssense[r1] == matrixdata->rhssense[r2] && SCIPisEQ(scip, matrixdata->rhscoef[r1], matrixdata->rhscoef[r2]) )
1515  {
1516  j = rhsmatbeg[r2];
1517  assert( 0 <= j && j < matrixdata->nmatcoef );
1518  assert( matrixdata->matrhsidx[j] == r2 );
1519  assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
1520 
1521  /* loop through row r2 and check whether it is equal to permuted row r */
1522  while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r2 && SCIPisEQ(scip, permrow[matrixdata->matvaridx[j]], matrixdata->matcoef[j] ) )
1523  ++j;
1524 
1525  /* check whether rows are completely equal */
1526  if ( j >= matrixdata->nmatcoef || matrixdata->matrhsidx[j] != r2 )
1527  {
1528  /* perm[p] is indeed a symmetry */
1529  found = TRUE;
1530  break;
1531  }
1532  }
1533  }
1534 
1535  assert( found );
1536  if ( ! found ) /*lint !e774*/
1537  {
1538  SCIPerrorMessage("Found permutation that is not a symmetry.\n");
1539  return SCIP_ERROR;
1540  }
1541  }
1542 
1543  /* reset permrow */
1544  j = rhsmatbeg[r1];
1545  while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
1546  {
1547  int varidx;
1548  varidx = P[matrixdata->matvaridx[j]];
1549  permrow[varidx] = 0.0;
1550  ++j;
1551  }
1552  }
1553 
1554  /*
1555  * non-linear part
1556  */
1557 
1558  SCIPdebugMsg(scip, "Verifying automorphism group generator #%d for non-linear part ...\n", p);
1559 
1560  /* fill hashmap according to permutation */
1561  for (j = 0; j < matrixdata->npermvars; ++j)
1562  {
1563  SCIP_CALL( SCIPhashmapInsert(varmap, matrixdata->permvars[j], matrixdata->permvars[P[j]]) );
1564  }
1565 
1566  /* check all non-linear constraints */
1567  for (i = 0; i < nconss; ++i)
1568  {
1569  SCIP_CONS* cons1;
1570  SCIP_Bool permuted = FALSE;
1571 
1572  cons1 = SCIPconshdlrGetConss(conshdlr)[i];
1573 
1574  SCIP_CALL( SCIPgetConsVars(scip, cons1, occuringvars, matrixdata->npermvars, &success) );
1575  assert(success);
1576  SCIP_CALL( SCIPgetConsNVars(scip, cons1, &noccuringvars, &success) );
1577  assert(success);
1578 
1579  /* count number of affected variables in this constraint */
1580  for (j = 0; j < noccuringvars && ! permuted; ++j)
1581  {
1582  int varidx;
1583 
1584  varidx = SCIPvarGetProbindex(occuringvars[j]);
1585  assert( varidx >= 0 && varidx < matrixdata->npermvars );
1586 
1587  if ( P[varidx] != varidx )
1588  permuted = TRUE;
1589  }
1590 
1591  /* if constraint is not affected by permutation, we do not have to check it */
1592  if ( permuted )
1593  {
1594  SCIP_CONS* permutedcons = NULL;
1595  SCIP_EXPR* permutedexpr;
1596  SCIP_Bool found = FALSE;
1597  SCIP_Bool infeasible;
1598 
1599  /* copy contraints but exchange variables according to hashmap */
1600  SCIP_CALL( SCIPgetConsCopy(scip, scip, cons1, &permutedcons, conshdlr, varmap, NULL, NULL,
1604  SCIPconsIsStickingAtNode(cons1), FALSE, &success) );
1605  assert(success);
1606  assert(permutedcons != NULL);
1607 
1608  /* simplify permuted expr in order to guarantee sorted variables */
1609  permutedexpr = SCIPgetExprNonlinear(permutedcons);
1610  SCIP_CALL( SCIPsimplifyExpr(scip, permutedexpr, &permutedexpr, &success, &infeasible, NULL, NULL) );
1611  assert( !infeasible );
1612 
1613  /* look for a constraint with same lhs, rhs and expression */
1614  for (j = 0; j < nconss; ++j)
1615  {
1616  SCIP_CONS* cons2;
1617 
1618  cons2 = SCIPconshdlrGetConss(conshdlr)[j];
1619 
1620  if ( SCIPisEQ(scip, SCIPgetRhsNonlinear(cons2), SCIPgetRhsNonlinear(permutedcons))
1621  && SCIPisEQ(scip, SCIPgetLhsNonlinear(cons2), SCIPgetLhsNonlinear(permutedcons))
1622  && (SCIPcompareExpr(scip, SCIPgetExprNonlinear(cons2), permutedexpr) == 0) )
1623  {
1624  found = TRUE;
1625  break;
1626  }
1627  }
1628 
1629  /* release copied constraint and expression because simplify captures it */
1630  SCIP_CALL( SCIPreleaseExpr(scip, &permutedexpr) );
1631  SCIP_CALL( SCIPreleaseCons(scip, &permutedcons) );
1632 
1633  assert( found );
1634  if( !found ) /*lint !e774*/
1635  {
1636  SCIPerrorMessage("Found permutation that is not a symmetry.\n");
1637  return SCIP_ERROR;
1638  }
1639  }
1640  }
1641 
1642  /* reset varmap */
1643  SCIP_CALL( SCIPhashmapRemoveAll(varmap) );
1644  }
1645 
1646  SCIPhashmapFree(&varmap);
1647  SCIPfreeBufferArray(scip, &occuringvars);
1648  SCIPfreeBlockMemoryArray(scip, &rhsmatbeg, matrixdata->nrhscoef);
1649  SCIPfreeBlockMemoryArray(scip, &permrow, matrixdata->npermvars);
1650 
1651  return SCIP_OKAY;
1652 }
1653 
1654 
1655 /** returns the number of active constraints that can be handled by symmetry */
1656 static
1658  SCIP* scip, /**< SCIP instance */
1659  SCIP_CONSHDLR* conshdlr_nonlinear /**< nonlinear constraint handler, if included */
1660  )
1661 {
1662  SCIP_CONSHDLR* conshdlr;
1663  int nhandleconss = 0;
1664 
1665  assert( scip != NULL );
1666 
1667  conshdlr = SCIPfindConshdlr(scip, "linear");
1668  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1669  conshdlr = SCIPfindConshdlr(scip, "linking");
1670  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1671  conshdlr = SCIPfindConshdlr(scip, "setppc");
1672  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1673  conshdlr = SCIPfindConshdlr(scip, "xor");
1674  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1675  conshdlr = SCIPfindConshdlr(scip, "and");
1676  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1677  conshdlr = SCIPfindConshdlr(scip, "or");
1678  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1679  conshdlr = SCIPfindConshdlr(scip, "logicor");
1680  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1681  conshdlr = SCIPfindConshdlr(scip, "knapsack");
1682  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1683  conshdlr = SCIPfindConshdlr(scip, "varbound");
1684  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1685  conshdlr = SCIPfindConshdlr(scip, "bounddisjunction");
1686  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr);
1687  if( conshdlr_nonlinear != NULL )
1688  nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr_nonlinear);
1689 
1690  return nhandleconss;
1691 }
1692 
1693 /** returns whether there are any active nonlinear constraints */
1694 static
1696  SCIP_PROPDATA* propdata /**< propagator data */
1697  )
1698 {
1699  assert(propdata != NULL);
1700 
1701  return propdata->conshdlr_nonlinear != NULL && SCIPconshdlrGetNActiveConss(propdata->conshdlr_nonlinear) > 0;
1702 }
1703 
1704 /** set symmetry data */
1705 static
1707  SCIP* scip, /**< SCIP pointer */
1708  SCIP_VAR** vars, /**< vars present at time of symmetry computation */
1709  int nvars, /**< number of vars present at time of symmetry computation */
1710  int nbinvars, /**< number of binary vars present at time of symmetry computation */
1711  SCIP_VAR*** permvars, /**< pointer to permvars array */
1712  int* npermvars, /**< pointer to store number of permvars */
1713  int* nbinpermvars, /**< pointer to store number of binary permvars */
1714  int** perms, /**< permutations matrix (nperms x nvars) */
1715  int nperms, /**< number of permutations */
1716  int* nmovedvars, /**< pointer to store number of vars affected by symmetry (if usecompression) or NULL */
1717  SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1718  SCIP_Bool usecompression, /**< whether symmetry data shall be compressed */
1719  SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1720  SCIP_Bool* compressed /**< pointer to store whether compression has been performed */
1721  )
1722 {
1723  int i;
1724  int p;
1725 
1726  assert( scip != NULL );
1727  assert( vars != NULL );
1728  assert( nvars > 0 );
1729  assert( permvars != NULL );
1730  assert( npermvars != NULL );
1731  assert( nbinpermvars != NULL );
1732  assert( perms != NULL );
1733  assert( nperms > 0 );
1734  assert( binvaraffected != NULL );
1735  assert( SCIPisGE(scip, compressthreshold, 0.0) );
1736  assert( SCIPisLE(scip, compressthreshold, 1.0) );
1737  assert( compressed != NULL );
1738 
1739  /* set default return values */
1740  *permvars = vars;
1741  *npermvars = nvars;
1742  *nbinpermvars = nbinvars;
1743  *binvaraffected = FALSE;
1744  *compressed = FALSE;
1745 
1746  /* if we possibly perform compression */
1747  if ( usecompression && SCIPgetNVars(scip) >= COMPRESSNVARSLB )
1748  {
1749  SCIP_Real percentagemovedvars;
1750  int* labelmovedvars;
1751  int* labeltopermvaridx;
1752  int nbinvarsaffected = 0;
1753 
1754  assert( nmovedvars != NULL );
1755 
1756  *nmovedvars = 0;
1757 
1758  /* detect number of moved vars and label moved vars */
1759  SCIP_CALL( SCIPallocBufferArray(scip, &labelmovedvars, nvars) );
1760  SCIP_CALL( SCIPallocBufferArray(scip, &labeltopermvaridx, nvars) );
1761  for (i = 0; i < nvars; ++i)
1762  {
1763  labelmovedvars[i] = -1;
1764 
1765  for (p = 0; p < nperms; ++p)
1766  {
1767  if ( perms[p][i] != i )
1768  {
1769  labeltopermvaridx[*nmovedvars] = i;
1770  labelmovedvars[i] = (*nmovedvars)++;
1771 
1772  if ( SCIPvarIsBinary(vars[i]) )
1773  ++nbinvarsaffected;
1774  break;
1775  }
1776  }
1777  }
1778 
1779  if ( nbinvarsaffected > 0 )
1780  *binvaraffected = TRUE;
1781 
1782  /* check whether compression should be performed */
1783  percentagemovedvars = (SCIP_Real) *nmovedvars / (SCIP_Real) nvars;
1784  if ( *nmovedvars > 0 && SCIPisLE(scip, percentagemovedvars, compressthreshold) )
1785  {
1786  /* remove variables from permutations that are not affected by any permutation */
1787  for (p = 0; p < nperms; ++p)
1788  {
1789  /* iterate over labels and adapt permutation */
1790  for (i = 0; i < *nmovedvars; ++i)
1791  {
1792  assert( i <= labeltopermvaridx[i] );
1793  perms[p][i] = labelmovedvars[perms[p][labeltopermvaridx[i]]];
1794  }
1795 
1796  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], nvars, *nmovedvars) );
1797  }
1798 
1799  /* remove variables from permvars array that are not affected by any symmetry */
1800  SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvars, *nmovedvars) );
1801  for (i = 0; i < *nmovedvars; ++i)
1802  {
1803  (*permvars)[i] = vars[labeltopermvaridx[i]];
1804  }
1805  *npermvars = *nmovedvars;
1806  *nbinpermvars = nbinvarsaffected;
1807  *compressed = TRUE;
1808 
1809  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
1810  }
1811  SCIPfreeBufferArray(scip, &labeltopermvaridx);
1812  SCIPfreeBufferArray(scip, &labelmovedvars);
1813  }
1814  else
1815  {
1816  /* detect whether binary variable is affected by symmetry and count number of binary permvars */
1817  for (i = 0; i < nbinvars; ++i)
1818  {
1819  for (p = 0; p < nperms && ! *binvaraffected; ++p)
1820  {
1821  if ( perms[p][i] != i )
1822  {
1823  if ( SCIPvarIsBinary(vars[i]) )
1824  *binvaraffected = TRUE;
1825  break;
1826  }
1827  }
1828  }
1829  }
1830 
1831  return SCIP_OKAY;
1832 }
1833 
1834 
1835 /** computes symmetry group of a MIP */
1836 static
1838  SCIP* scip, /**< SCIP pointer */
1839  SCIP_Bool doubleequations, /**< Double equations to positive/negative version? */
1840  SCIP_Bool compresssymmetries, /**< Should non-affected variables be removed from permutation to save memory? */
1841  SCIP_Real compressthreshold, /**< Compression is used if percentage of moved vars is at most the threshold. */
1842  int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
1843  SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1844  SCIP_Bool local, /**< Use local variable bounds? */
1845  SCIP_Bool checksymmetries, /**< Should all symmetries be checked after computation? */
1846  SCIP_Bool usecolumnsparsity, /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
1847  SCIP_CONSHDLR* conshdlr_nonlinear, /**< Nonlinear constraint handler, if included */
1848  int* npermvars, /**< pointer to store number of variables for permutations */
1849  int* nbinpermvars, /**< pointer to store number of binary variables for permutations */
1850  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
1851  int* nperms, /**< pointer to store number of permutations */
1852  int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
1853  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
1854  SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
1855  int* nmovedvars, /**< pointer to store number of moved vars */
1856  SCIP_Bool** isnonlinvar, /**< pointer to store which variables appear nonlinearly */
1857  SCIP_Bool* binvaraffected, /**< pointer to store wether a binary variable is affected by symmetry */
1858  SCIP_Bool* compressed, /**< pointer to store whether compression has been performed */
1859  SCIP_Bool* success /**< pointer to store whether symmetry computation was successful */
1860  )
1861 {
1862  SCIP_CONSHDLR* conshdlr;
1863  SYM_MATRIXDATA matrixdata;
1864  SYM_EXPRDATA exprdata;
1865  SCIP_HASHTABLE* vartypemap;
1866  SCIP_VAR** consvars;
1867  SCIP_Real* consvals;
1868  SCIP_CONS** conss;
1869  SCIP_VAR** vars;
1870  SCIP_EXPRITER* it = NULL;
1871  SCIP_HASHSET* auxvars = NULL;
1872  SYM_VARTYPE* uniquevararray;
1873  SYM_RHSSENSE oldsense = SYM_SENSE_UNKOWN;
1874  SYM_SORTRHSTYPE sortrhstype;
1875  SCIP_Real oldcoef = SCIP_INVALID;
1876  SCIP_Real val;
1877  int* nconssforvar = NULL;
1878  int nuniquevararray = 0;
1879  int nhandleconss;
1880  int nactiveconss;
1881  int nnlconss;
1882  int nconss;
1883  int nvars;
1884  int nbinvars;
1885  int nvarsorig;
1886  int nallvars;
1887  int c;
1888  int j;
1889 
1890  assert( scip != NULL );
1891  assert( npermvars != NULL );
1892  assert( nbinpermvars != NULL );
1893  assert( permvars != NULL );
1894  assert( nperms != NULL );
1895  assert( nmaxperms != NULL );
1896  assert( perms != NULL );
1897  assert( log10groupsize != NULL );
1898  assert( binvaraffected != NULL );
1899  assert( compressed != NULL );
1900  assert( success != NULL );
1901  assert( isnonlinvar != NULL );
1902  assert( SYMcanComputeSymmetry() );
1903 
1904  /* init */
1905  *npermvars = 0;
1906  *nbinpermvars = 0;
1907  *permvars = NULL;
1908  *nperms = 0;
1909  *nmaxperms = 0;
1910  *perms = NULL;
1911  *log10groupsize = 0;
1912  *nmovedvars = -1;
1913  *binvaraffected = FALSE;
1914  *compressed = FALSE;
1915  *success = FALSE;
1916 
1917  nconss = SCIPgetNConss(scip);
1918  nvars = SCIPgetNVars(scip);
1919  nbinvars = SCIPgetNBinVars(scip);
1920  nvarsorig = nvars;
1921 
1922  /* exit if no constraints or no variables are available */
1923  if ( nconss == 0 || nvars == 0 )
1924  {
1925  *success = TRUE;
1926  return SCIP_OKAY;
1927  }
1928 
1929  conss = SCIPgetConss(scip);
1930  assert( conss != NULL );
1931 
1932  /* compute the number of active constraints */
1933  nactiveconss = SCIPgetNActiveConss(scip);
1934  nnlconss = conshdlr_nonlinear != NULL ? SCIPconshdlrGetNActiveConss(conshdlr_nonlinear) : 0;
1935 
1936  /* exit if no active constraints are available */
1937  if ( nactiveconss == 0 )
1938  {
1939  *success = TRUE;
1940  return SCIP_OKAY;
1941  }
1942 
1943  /* before we set up the matrix, check whether we can handle all constraints */
1944  nhandleconss = getNSymhandableConss(scip, conshdlr_nonlinear);
1945  assert( nhandleconss <= nactiveconss );
1946  if ( nhandleconss < nactiveconss )
1947  {
1948  /* In this case we found unkown constraints and we exit, since we cannot handle them. */
1949  *success = FALSE;
1950  *nperms = -1;
1951  return SCIP_OKAY;
1952  }
1953 
1954  SCIPdebugMsg(scip, "Detecting %ssymmetry on %d variables and %d constraints.\n", local ? "local " : "", nvars, nactiveconss);
1955 
1956  /* copy variables */
1957  SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &vars, SCIPgetVars(scip), nvars) ); /*lint !e666*/
1958  assert( vars != NULL );
1959 
1960  /* fill matrixdata */
1961 
1962  /* use a staggered scheme for allocating space for non-zeros of constraint matrix since it can become large */
1963  if ( nvars <= 100000 )
1964  matrixdata.nmaxmatcoef = 100 * nvars;
1965  else if ( nvars <= 1000000 )
1966  matrixdata.nmaxmatcoef = 32 * nvars;
1967  else if ( nvars <= 16700000 )
1968  matrixdata.nmaxmatcoef = 16 * nvars;
1969  else
1970  matrixdata.nmaxmatcoef = INT_MAX / 10;
1971 
1972  matrixdata.nmatcoef = 0;
1973  matrixdata.nrhscoef = 0;
1974  matrixdata.nuniquemat = 0;
1975  matrixdata.nuniquevars = 0;
1976  matrixdata.nuniquerhs = 0;
1977  matrixdata.npermvars = nvars;
1978  matrixdata.permvars = vars;
1979  matrixdata.permvarcolors = NULL;
1980  matrixdata.matcoefcolors = NULL;
1981  matrixdata.rhscoefcolors = NULL;
1982 
1983  /* fill exprdata */
1984  exprdata.nuniqueoperators = 0;
1985  exprdata.nuniquecoefs = 0;
1986  exprdata.nuniqueconstants = 0;
1987 
1988  /* prepare matrix data (use block memory, since this can become large) */
1989  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef) );
1990  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef) );
1991  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef) );
1992  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef) );
1993  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoef, 2 * nactiveconss) );
1994  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhssense, 2 * nactiveconss) );
1995  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhsidx, 2 * nactiveconss) );
1996 
1997  /* prepare temporary constraint data (use block memory, since this can become large);
1998  * also allocate memory for fixed vars since some vars might have been deactivated meanwhile */
1999  nallvars = nvars + SCIPgetNFixedVars(scip);
2000  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consvars, nallvars) );
2001  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consvals, nallvars) );
2002 
2003  /* create hashset for auxvars and iterator for nonlinear constraints */
2004  if( nnlconss > 0 )
2005  {
2006  SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, isnonlinvar, nvars) );
2007  SCIP_CALL( SCIPhashsetCreate(&auxvars, SCIPblkmem(scip), nnlconss) );
2008  SCIP_CALL( SCIPcreateExpriter(scip, &it) );
2009  }
2010  else
2011  *isnonlinvar = NULL;
2012 
2013  /* allocate memory for getting the number of constraints that contain a variable */
2014  if ( usecolumnsparsity )
2015  {
2016  SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &nconssforvar, nvars) );
2017  }
2018 
2019  /* loop through all constraints */
2020  for (c = 0; c < nconss; ++c)
2021  {
2022  const char* conshdlrname;
2023  SCIP_CONS* cons;
2024  SCIP_VAR** linvars;
2025  int nconsvars;
2026 
2027  /* get constraint */
2028  cons = conss[c];
2029  assert( cons != NULL );
2030 
2031  /* skip non-active constraints */
2032  if ( ! SCIPconsIsActive(cons) )
2033  continue;
2034 
2035  /* Skip conflict constraints if we are late in the solving process */
2036  if ( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsConflict(cons) )
2037  continue;
2038 
2039  /* get constraint handler */
2040  conshdlr = SCIPconsGetHdlr(cons);
2041  assert( conshdlr != NULL );
2042 
2043  conshdlrname = SCIPconshdlrGetName(conshdlr);
2044  assert( conshdlrname != NULL );
2045 
2046  /* check type of constraint */
2047  if ( strcmp(conshdlrname, "linear") == 0 )
2048  {
2049  SCIP_CALL( collectCoefficients(scip, doubleequations, SCIPgetVarsLinear(scip, cons), SCIPgetValsLinear(scip, cons),
2050  SCIPgetNVarsLinear(scip, cons), SCIPgetLhsLinear(scip, cons), SCIPgetRhsLinear(scip, cons),
2051  SCIPconsIsTransformed(cons), SYM_SENSE_UNKOWN, &matrixdata, nconssforvar) );
2052  }
2053  else if ( strcmp(conshdlrname, "linking") == 0 )
2054  {
2055  SCIP_VAR** curconsvars;
2056  SCIP_Real* curconsvals;
2057  int i;
2058 
2059  /* get constraint variables and their coefficients */
2060  curconsvals = SCIPgetValsLinking(scip, cons);
2061  SCIP_CALL( SCIPgetBinvarsLinking(scip, cons, &curconsvars, &nconsvars) );
2062  /* SCIPgetBinVarsLinking returns the number of binary variables, but we also need the integer variable */
2063  nconsvars++;
2064 
2065  /* copy vars and vals for binary variables */
2066  for (i = 0; i < nconsvars - 1; i++)
2067  {
2068  consvars[i] = curconsvars[i];
2069  consvals[i] = (SCIP_Real) curconsvals[i];
2070  }
2071 
2072  /* set final entry of vars and vals to the linking variable and its coefficient, respectively */
2073  consvars[nconsvars - 1] = SCIPgetLinkvarLinking(scip, cons);
2074  consvals[nconsvars - 1] = -1.0;
2075 
2076  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
2077  SCIPconsIsTransformed(cons), SYM_SENSE_UNKOWN, &matrixdata, nconssforvar) );
2078  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, NULL, nconsvars - 1, 1.0, 1.0,
2079  SCIPconsIsTransformed(cons), SYM_SENSE_UNKOWN, &matrixdata, nconssforvar) );
2080  }
2081  else if ( strcmp(conshdlrname, "setppc") == 0 )
2082  {
2083  linvars = SCIPgetVarsSetppc(scip, cons);
2084  nconsvars = SCIPgetNVarsSetppc(scip, cons);
2085 
2086  switch ( SCIPgetTypeSetppc(scip, cons) )
2087  {
2089  SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, 1.0, 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_EQUATION, &matrixdata, nconssforvar) );
2090  break;
2092  SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, -SCIPinfinity(scip), 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
2093  break;
2095  SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, 1.0, SCIPinfinity(scip), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
2096  break;
2097  default:
2098  SCIPerrorMessage("Unknown setppc type %d.\n", SCIPgetTypeSetppc(scip, cons));
2099  return SCIP_ERROR;
2100  }
2101  }
2102  else if ( strcmp(conshdlrname, "xor") == 0 )
2103  {
2104  SCIP_VAR** curconsvars;
2105  SCIP_VAR* var;
2106 
2107  /* get number of variables of XOR constraint (without integer variable) */
2108  nconsvars = SCIPgetNVarsXor(scip, cons);
2109 
2110  /* get variables of XOR constraint */
2111  curconsvars = SCIPgetVarsXor(scip, cons);
2112  for (j = 0; j < nconsvars; ++j)
2113  {
2114  assert( curconsvars[j] != NULL );
2115  consvars[j] = curconsvars[j];
2116  consvals[j] = 1.0;
2117  }
2118 
2119  /* intVar of xor constraint might have been removed */
2120  var = SCIPgetIntVarXor(scip, cons);
2121  if ( var != NULL )
2122  {
2123  consvars[nconsvars] = var;
2124  consvals[nconsvars++] = 2.0;
2125  }
2126  assert( nconsvars <= nallvars );
2127 
2128  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, (SCIP_Real) SCIPgetRhsXor(scip, cons),
2129  (SCIP_Real) SCIPgetRhsXor(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_XOR, &matrixdata, nconssforvar) );
2130  }
2131  else if ( strcmp(conshdlrname, "and") == 0 )
2132  {
2133  SCIP_VAR** curconsvars;
2134 
2135  /* get number of variables of AND constraint (without resultant) */
2136  nconsvars = SCIPgetNVarsAnd(scip, cons);
2137 
2138  /* get variables of AND constraint */
2139  curconsvars = SCIPgetVarsAnd(scip, cons);
2140 
2141  for (j = 0; j < nconsvars; ++j)
2142  {
2143  assert( curconsvars[j] != NULL );
2144  consvars[j] = curconsvars[j];
2145  consvals[j] = 1.0;
2146  }
2147 
2148  assert( SCIPgetResultantAnd(scip, cons) != NULL );
2149  consvars[nconsvars] = SCIPgetResultantAnd(scip, cons);
2150  consvals[nconsvars++] = 2.0;
2151  assert( nconsvars <= nallvars );
2152 
2153  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
2154  SCIPconsIsTransformed(cons), SYM_SENSE_AND, &matrixdata, nconssforvar) );
2155  }
2156  else if ( strcmp(conshdlrname, "or") == 0 )
2157  {
2158  SCIP_VAR** curconsvars;
2159 
2160  /* get number of variables of OR constraint (without resultant) */
2161  nconsvars = SCIPgetNVarsOr(scip, cons);
2162 
2163  /* get variables of OR constraint */
2164  curconsvars = SCIPgetVarsOr(scip, cons);
2165 
2166  for (j = 0; j < nconsvars; ++j)
2167  {
2168  assert( curconsvars[j] != NULL );
2169  consvars[j] = curconsvars[j];
2170  consvals[j] = 1.0;
2171  }
2172 
2173  assert( SCIPgetResultantOr(scip, cons) != NULL );
2174  consvars[nconsvars] = SCIPgetResultantOr(scip, cons);
2175  consvals[nconsvars++] = 2.0;
2176  assert( nconsvars <= nallvars );
2177 
2178  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
2179  SCIPconsIsTransformed(cons), SYM_SENSE_OR, &matrixdata, nconssforvar) );
2180  }
2181  else if ( strcmp(conshdlrname, "logicor") == 0 )
2182  {
2183  SCIP_CALL( collectCoefficients(scip, doubleequations, SCIPgetVarsLogicor(scip, cons), 0, SCIPgetNVarsLogicor(scip, cons),
2184  1.0, SCIPinfinity(scip), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
2185  }
2186  else if ( strcmp(conshdlrname, "knapsack") == 0 )
2187  {
2188  SCIP_Longint* weights;
2189 
2190  nconsvars = SCIPgetNVarsKnapsack(scip, cons);
2191 
2192  /* copy Longint array to SCIP_Real array and get active variables of constraint */
2193  weights = SCIPgetWeightsKnapsack(scip, cons);
2194  for (j = 0; j < nconsvars; ++j)
2195  consvals[j] = (SCIP_Real) weights[j];
2196  assert( nconsvars <= nallvars );
2197 
2198  SCIP_CALL( collectCoefficients(scip, doubleequations, SCIPgetVarsKnapsack(scip, cons), consvals, nconsvars, -SCIPinfinity(scip),
2199  (SCIP_Real) SCIPgetCapacityKnapsack(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
2200  }
2201  else if ( strcmp(conshdlrname, "varbound") == 0 )
2202  {
2203  consvars[0] = SCIPgetVarVarbound(scip, cons);
2204  consvals[0] = 1.0;
2205 
2206  consvars[1] = SCIPgetVbdvarVarbound(scip, cons);
2207  consvals[1] = SCIPgetVbdcoefVarbound(scip, cons);
2208 
2209  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, 2, SCIPgetLhsVarbound(scip, cons),
2210  SCIPgetRhsVarbound(scip, cons), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
2211  }
2212  else if ( strcmp(conshdlrname, "bounddisjunction") == 0 )
2213  {
2214  /* To model bound disjunctions, we normalize each constraint
2215  * \f[
2216  * (x_1 \{\leq,\geq\} b_1) \vee \ldots \vee (x_n \{\leq,\geq\} b_n)
2217  * \f]
2218  * to a constraint of type
2219  * \f[
2220  * (x_1 \leq b'_1 \vee \ldots \vee (x_n \leq b'_n).
2221  * \f]
2222  *
2223  * If no variable appears twice in such a normalized constraint, we say this bound disjunction
2224  * is of type 1. If the bound disjunction has length two and both disjunctions contain the same variable,
2225  * we say the bound disjunction is of type 2. Further bound disjunctions are possible, but can currently
2226  * not be handled.
2227  *
2228  * Bound disjunctions of type 1 are modeled as the linear constraint
2229  * \f[
2230  * b'_1 \cdot x_1 + \ldots + b'_n \cdot x_n = 0
2231  * \f]
2232  * and bound disjunctions of type 2 are modeled as the linear constraint
2233  * \f[
2234  * \min\{b'_1, b'_2\} \leq x_1 \leq \max\{b'_1, b'_2\}.
2235  * \f]
2236  * Note that problems arise if \fb'_i = 0\f for some variable \fx_i\f, because its coefficient in the
2237  * linear constraint is 0. To avoid this, we replace 0 by a special number.
2238  */
2239  SCIP_VAR** bounddisjvars;
2240  SCIP_BOUNDTYPE* boundtypes;
2241  SCIP_Real* bounds;
2242  SCIP_Bool repetition = FALSE;
2243  int nbounddisjvars;
2244  int k;
2245 
2246  /* collect coefficients for normalized constraint */
2247  nbounddisjvars = SCIPgetNVarsBounddisjunction(scip, cons);
2248  bounddisjvars = SCIPgetVarsBounddisjunction(scip, cons);
2249  boundtypes = SCIPgetBoundtypesBounddisjunction(scip, cons);
2250  bounds = SCIPgetBoundsBounddisjunction(scip, cons);
2251 
2252  /* copy data */
2253  for (j = 0; j < nbounddisjvars; ++j)
2254  {
2255  consvars[j] = bounddisjvars[j];
2256 
2257  /* normalize bounddisjunctions to SCIP_BOUNDTYPE_LOWER */
2258  if ( boundtypes[j] == SCIP_BOUNDTYPE_LOWER )
2259  consvals[j] = - bounds[j];
2260  else
2261  consvals[j] = bounds[j];
2262 
2263  /* special treatment of 0 values */
2264  if ( SCIPisZero(scip, consvals[j]) )
2265  consvals[j] = SCIP_SPECIALVAL;
2266 
2267  /* detect whether a variable appears in two literals */
2268  for (k = 0; k < j && ! repetition; ++k)
2269  {
2270  if ( consvars[j] == consvars[k] )
2271  repetition = TRUE;
2272  }
2273 
2274  /* stop, we cannot handle bounddisjunctions with more than two variables that contain a variable twice */
2275  if ( repetition && nbounddisjvars > 2 )
2276  {
2277  *success = FALSE;
2278 
2280  " Deactivated symmetry handling methods, there exist constraints that cannot be handled by symmetry methods.\n");
2281 
2282  if ( usecolumnsparsity )
2283  SCIPfreeBlockMemoryArrayNull(scip, &nconssforvar, nvars);
2284 
2285  SCIPfreeBlockMemoryArrayNull(scip, &consvals, nallvars);
2286  SCIPfreeBlockMemoryArrayNull(scip, &consvars, nallvars);
2287  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2288  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2289  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2290  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2291  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2292  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef);
2293  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef);
2294  SCIPfreeBlockMemoryArrayNull(scip, &vars, nvars);
2295 
2296  return SCIP_OKAY;
2297  }
2298  }
2299  assert( ! repetition || nbounddisjvars == 2 );
2300 
2301  /* if no variable appears twice */
2302  if ( ! repetition )
2303  {
2304  /* add information for bounddisjunction of type 1 */
2305  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nbounddisjvars, 0.0, 0.0,
2306  SCIPconsIsTransformed(cons), SYM_SENSE_BOUNDIS_TYPE_1, &matrixdata, nconssforvar) );
2307  }
2308  else
2309  {
2310  /* add information for bounddisjunction of type 2 */
2311  SCIP_Real lhs;
2312  SCIP_Real rhs;
2313 
2314  lhs = MIN(consvals[0], consvals[1]);
2315  rhs = MAX(consvals[0], consvals[1]);
2316 
2317  consvals[0] = 1.0;
2318 
2319  SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, 1, lhs, rhs,
2320  SCIPconsIsTransformed(cons), SYM_SENSE_BOUNDIS_TYPE_2, &matrixdata, nconssforvar) );
2321  }
2322  }
2323  else if ( strcmp(conshdlrname, "nonlinear") == 0 )
2324  {
2325  SCIP_EXPR* expr;
2326  SCIP_EXPR* rootexpr;
2327 
2328  rootexpr = SCIPgetExprNonlinear(cons);
2329  assert(rootexpr != NULL);
2330 
2331  /* for nonlinear constraints, only collect auxiliary variables for now */
2332  SCIP_CALL( SCIPexpriterInit(it, rootexpr, SCIP_EXPRITER_DFS, TRUE) );
2334 
2335  for (expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it)) /*lint !e441*/ /*lint !e440*/
2336  {
2338 
2339  /* for variables, we check whether they appear nonlinearly and store the result in the resp. array */
2340  if ( SCIPisExprVar(scip, expr) )
2341  {
2342  assert(*isnonlinvar != NULL);
2343  (*isnonlinvar)[SCIPvarGetProbindex(SCIPgetVarExprVar(expr))] = (SCIPexpriterGetParentDFS(it) != rootexpr || !SCIPisExprSum(scip, rootexpr));
2344  }
2345  else
2346  {
2347  SCIP_VAR* auxvar = SCIPgetExprAuxVarNonlinear(expr);
2348 
2349  if ( auxvar != NULL && !SCIPhashsetExists(auxvars, (void*) auxvar) )
2350  {
2351  SCIP_CALL( SCIPhashsetInsert(auxvars, SCIPblkmem(scip), (void*) auxvar) );
2352  }
2353 
2354  if ( SCIPisExprValue(scip, expr) )
2355  ++exprdata.nuniqueconstants;
2356  else if ( SCIPisExprSum(scip, expr) )
2357  {
2358  ++exprdata.nuniqueoperators;
2359  ++exprdata.nuniqueconstants;
2360  exprdata.nuniquecoefs += SCIPexprGetNChildren(expr);
2361  }
2362  else
2363  ++exprdata.nuniqueoperators;
2364  }
2365  }
2366  }
2367  else
2368  {
2369  /* if constraint is not one of the previous types, it cannot be handled */
2370  SCIPerrorMessage("Cannot determine symmetries for constraint <%s> of constraint handler <%s>.\n",
2371  SCIPconsGetName(cons), SCIPconshdlrGetName(conshdlr) );
2372  return SCIP_ERROR;
2373  }
2374  }
2375  assert( matrixdata.nrhscoef <= 2 * (nactiveconss - nnlconss) );
2376  assert( matrixdata.nrhscoef >= 0 );
2377 
2378  SCIPfreeBlockMemoryArray(scip, &consvals, nallvars);
2379  SCIPfreeBlockMemoryArray(scip, &consvars, nallvars);
2380 
2381  /* if no active constraint contains active variables */
2382  if ( nnlconss == 0 && matrixdata.nrhscoef == 0 )
2383  {
2384  *success = TRUE;
2385 
2386  if ( usecolumnsparsity )
2387  SCIPfreeBlockMemoryArrayNull(scip, &nconssforvar, nvars);
2388 
2389  /* free matrix data */
2390  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2391  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2392  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2393  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2394  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2395  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef);
2396  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef);
2397 
2398  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
2399 
2400  return SCIP_OKAY;
2401  }
2402 
2403  /* sort matrix coefficients (leave matrix array intact) */
2404  SCIPsort(matrixdata.matidx, SYMsortMatCoef, (void*) matrixdata.matcoef, matrixdata.nmatcoef);
2405 
2406  /* sort rhs types (first by sense, then by value, leave rhscoef intact) */
2407  sortrhstype.vals = matrixdata.rhscoef;
2408  sortrhstype.senses = matrixdata.rhssense;
2409  sortrhstype.nrhscoef = matrixdata.nrhscoef;
2410  SCIPsort(matrixdata.rhsidx, SYMsortRhsTypes, (void*) &sortrhstype, matrixdata.nrhscoef);
2411 
2412  /* create map for variables to indices */
2413  SCIP_CALL( SCIPhashtableCreate(&vartypemap, SCIPblkmem(scip), 5 * nvars, SYMhashGetKeyVartype, SYMhashKeyEQVartype, SYMhashKeyValVartype, (void*) scip) );
2414  assert( vartypemap != NULL );
2415 
2416  /* allocate space for mappings to colors */
2417  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.permvarcolors, nvars) );
2418  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef) );
2419  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef) );
2420  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &uniquevararray, nvars) );
2421 
2422  /* determine number of different coefficients */
2423 
2424  /* find non-equivalent variables: same objective, lower and upper bounds, and variable type */
2425  for (j = 0; j < nvars; ++j)
2426  {
2427  SCIP_VAR* var;
2428 
2429  var = vars[j];
2430  assert( var != NULL );
2431 
2432  /* if the variable type should be fixed, just increase the color */
2433  if ( SymmetryFixVar(fixedtype, var) || (nnlconss > 0 && SCIPhashsetExists(auxvars, (void*) var)) )
2434  {
2435  matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
2436 #ifdef SCIP_OUTPUT
2437  SCIPdebugMsg(scip, "Detected variable <%s> of fixed type %d - color %d.\n", SCIPvarGetName(var), SCIPvarGetType(var), matrixdata.nuniquevars - 1);
2438 #endif
2439  }
2440  else
2441  {
2442  SYM_VARTYPE* vt;
2443 
2444  vt = &uniquevararray[nuniquevararray];
2445  assert( nuniquevararray <= matrixdata.nuniquevars );
2446 
2447  vt->obj = SCIPvarGetObj(var);
2448  if ( local )
2449  {
2450  vt->lb = SCIPvarGetLbLocal(var);
2451  vt->ub = SCIPvarGetUbLocal(var);
2452  }
2453  else
2454  {
2455  vt->lb = SCIPvarGetLbGlobal(var);
2456  vt->ub = SCIPvarGetUbGlobal(var);
2457  }
2458  vt->type = SCIPvarGetType(var);
2459  vt->nconss = usecolumnsparsity ? nconssforvar[j] : 0; /*lint !e613*/
2460 
2461  if ( ! SCIPhashtableExists(vartypemap, (void*) vt) )
2462  {
2463  SCIP_CALL( SCIPhashtableInsert(vartypemap, (void*) vt) );
2464  vt->color = matrixdata.nuniquevars;
2465  matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
2466  ++nuniquevararray;
2467 #ifdef SCIP_OUTPUT
2468  SCIPdebugMsg(scip, "Detected variable <%s> of new type (probindex: %d, obj: %g, lb: %g, ub: %g, type: %d) - color %d.\n",
2469  SCIPvarGetName(var), SCIPvarGetProbindex(var), vt->obj, vt->lb, vt->ub, vt->type, matrixdata.nuniquevars - 1);
2470 #endif
2471  }
2472  else
2473  {
2474  SYM_VARTYPE* vtr;
2475 
2476  vtr = (SYM_VARTYPE*) SCIPhashtableRetrieve(vartypemap, (void*) vt);
2477  matrixdata.permvarcolors[j] = vtr->color;
2478  }
2479  }
2480  }
2481 
2482  /* If every variable is unique, terminate. -> no symmetries can be present */
2483  if ( matrixdata.nuniquevars == nvars )
2484  {
2485  *success = TRUE;
2486 
2487  /* free matrix data */
2488  SCIPfreeBlockMemoryArray(scip, &uniquevararray, nvars);
2489 
2490  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef);
2491  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef);
2492  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.permvarcolors, nvars);
2493  SCIPhashtableFree(&vartypemap);
2494 
2495  if ( usecolumnsparsity )
2496  SCIPfreeBlockMemoryArrayNull(scip, &nconssforvar, nvars);
2497 
2498  if ( nnlconss > 0 )
2499  {
2500  SCIPfreeExpriter(&it);
2501  SCIPhashsetFree(&auxvars, SCIPblkmem(scip));
2502  SCIPfreeBlockMemoryArrayNull(scip, isnonlinvar, nvars);
2503  }
2504 
2505  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2506  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2507  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2508  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2509  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2510  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef);
2511  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef);
2512 
2513  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
2514 
2515  return SCIP_OKAY;
2516  }
2517 
2518  /* find non-equivalent matrix entries (use sorting to avoid too many map calls) */
2519  for (j = 0; j < matrixdata.nmatcoef; ++j)
2520  {
2521  int idx;
2522 
2523  idx = matrixdata.matidx[j];
2524  assert( 0 <= idx && idx < matrixdata.nmatcoef );
2525 
2526  val = matrixdata.matcoef[idx];
2527  assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
2528 
2529  if ( ! SCIPisEQ(scip, val, oldcoef) )
2530  {
2531 #ifdef SCIP_OUTPUT
2532  SCIPdebugMsg(scip, "Detected new matrix entry type %f - color: %d\n.", val, matrixdata.nuniquemat);
2533 #endif
2534  matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat++;
2535  oldcoef = val;
2536  }
2537  else
2538  {
2539  assert( matrixdata.nuniquemat > 0 );
2540  matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat - 1;
2541  }
2542  }
2543 
2544  /* find non-equivalent rhs */
2545  oldcoef = SCIP_INVALID;
2546  for (j = 0; j < matrixdata.nrhscoef; ++j)
2547  {
2548  SYM_RHSSENSE sense;
2549  int idx;
2550 
2551  idx = matrixdata.rhsidx[j];
2552  assert( 0 <= idx && idx < matrixdata.nrhscoef );
2553  sense = matrixdata.rhssense[idx];
2554  val = matrixdata.rhscoef[idx];
2555 
2556  /* make sure that new senses are treated with new color */
2557  if ( sense != oldsense )
2558  oldcoef = SCIP_INVALID;
2559  oldsense = sense;
2560  assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
2561 
2562  /* assign new color to new type */
2563  if ( ! SCIPisEQ(scip, val, oldcoef) )
2564  {
2565 #ifdef SCIP_OUTPUT
2566  SCIPdebugMsg(scip, "Detected new rhs type %f, type: %u - color: %d\n", val, sense, matrixdata.nuniquerhs);
2567 #endif
2568  matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs++;
2569  oldcoef = val;
2570  }
2571  else
2572  {
2573  assert( matrixdata.nuniquerhs > 0 );
2574  matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs - 1;
2575  }
2576  }
2577  assert( 0 < matrixdata.nuniquevars && matrixdata.nuniquevars <= nvars );
2578  assert( 0 <= matrixdata.nuniquerhs && matrixdata.nuniquerhs <= matrixdata.nrhscoef );
2579  assert( 0 <= matrixdata.nuniquemat && matrixdata.nuniquemat <= matrixdata.nmatcoef );
2580 
2581  SCIPdebugMsg(scip, "Number of detected different variables: %d (total: %d).\n", matrixdata.nuniquevars, nvars);
2582  SCIPdebugMsg(scip, "Number of detected different rhs types: %d (total: %d).\n", matrixdata.nuniquerhs, matrixdata.nrhscoef);
2583  SCIPdebugMsg(scip, "Number of detected different matrix coefficients: %d (total: %d).\n", matrixdata.nuniquemat, matrixdata.nmatcoef);
2584 
2585  /* do not compute symmetry if all variables are non-equivalent (unique) or if all matrix coefficients are different */
2586  if ( matrixdata.nuniquevars < nvars && (matrixdata.nuniquemat == 0 || matrixdata.nuniquemat < matrixdata.nmatcoef) )
2587  {
2588  /* determine generators */
2589  SCIP_CALL( SYMcomputeSymmetryGenerators(scip, maxgenerators, &matrixdata, &exprdata, nperms, nmaxperms,
2590  perms, log10groupsize) );
2591  assert( *nperms <= *nmaxperms );
2592 
2593  /* SCIPisStopped() might call SCIPgetGap() which is only available after initpresolve */
2594  if ( checksymmetries && SCIPgetStage(scip) > SCIP_STAGE_INITPRESOLVE && ! SCIPisStopped(scip) )
2595  {
2596  SCIP_CALL( checkSymmetriesAreSymmetries(scip, fixedtype, &matrixdata, *nperms, *perms) );
2597  }
2598 
2599  if ( *nperms > 0 )
2600  {
2601  SCIP_CALL( setSymmetryData(scip, vars, nvars, nbinvars, permvars, npermvars, nbinpermvars, *perms, *nperms,
2602  nmovedvars, binvaraffected, compresssymmetries, compressthreshold, compressed) );
2603  }
2604  else
2605  {
2606  SCIPfreeBlockMemoryArrayNull(scip, isnonlinvar, nvars);
2607  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
2608  }
2609  }
2610  else
2611  {
2612  SCIPfreeBlockMemoryArrayNull(scip, isnonlinvar, nvars);
2613  SCIPfreeBlockMemoryArray(scip, &vars, nvars);
2614  }
2615  *success = TRUE;
2616 
2617  /* free matrix data */
2618  SCIPfreeBlockMemoryArray(scip, &uniquevararray, nvarsorig);
2619 
2620  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef);
2621  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef);
2622  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.permvarcolors, nvarsorig);
2623  SCIPhashtableFree(&vartypemap);
2624 
2625  if ( usecolumnsparsity )
2626  SCIPfreeBlockMemoryArrayNull(scip, &nconssforvar, nvarsorig);
2627 
2628  /* free cons expr specific data */
2629  if ( nnlconss > 0 )
2630  {
2631  assert( it != NULL );
2632  assert( auxvars != NULL );
2633 
2634  SCIPfreeExpriter(&it);
2635  SCIPhashsetFree(&auxvars, SCIPblkmem(scip));
2636  }
2637 
2638  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2639  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2640  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2641  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2642  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2643  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matidx, matrixdata.nmaxmatcoef);
2644  SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoef, matrixdata.nmaxmatcoef);
2645 
2646  return SCIP_OKAY;
2647 }
2648 
2649 
2650 /** determines symmetry */
2651 static
2653  SCIP* scip, /**< SCIP instance */
2654  SCIP_PROPDATA* propdata, /**< propagator data */
2655  SYM_SPEC symspecrequire, /**< symmetry specification for which we need to compute symmetries */
2656  SYM_SPEC symspecrequirefixed /**< symmetry specification of variables which must be fixed by symmetries */
2657  )
2658 { /*lint --e{641}*/
2659  SCIP_Bool successful;
2660  int maxgenerators;
2661  int nhandleconss;
2662  int nconss;
2663  unsigned int type = 0;
2664  int nvars;
2665  int j;
2666  int p;
2667 
2668  assert( scip != NULL );
2669  assert( propdata != NULL );
2670  assert( propdata->usesymmetry >= 0 );
2671  assert( propdata->ofenabled || propdata->symconsenabled || propdata->sstenabled );
2672 
2673  /* do not compute symmetry if reoptimization is enabled */
2674  if ( SCIPisReoptEnabled(scip) )
2675  {
2676  propdata->ofenabled = FALSE;
2677  propdata->symconsenabled = FALSE;
2678  propdata->sstenabled = FALSE;
2679  return SCIP_OKAY;
2680  }
2681 
2682  /* do not compute symmetry if Benders decomposition enabled */
2683  if ( SCIPgetNActiveBenders(scip) > 0 )
2684  {
2685  propdata->ofenabled = FALSE;
2686  propdata->symconsenabled = FALSE;
2687  propdata->sstenabled = FALSE;
2688  return SCIP_OKAY;
2689  }
2690 
2691  /* skip symmetry computation if no graph automorphism code was linked */
2692  if ( ! SYMcanComputeSymmetry() )
2693  {
2694  propdata->ofenabled = FALSE;
2695  propdata->symconsenabled = FALSE;
2696  propdata->sstenabled = FALSE;
2697 
2698  nconss = SCIPgetNActiveConss(scip);
2699  nhandleconss = getNSymhandableConss(scip, propdata->conshdlr_nonlinear);
2700 
2701  /* print verbMessage only if problem consists of symmetry handable constraints */
2702  assert( nhandleconss <= nconss );
2703  if ( nhandleconss < nconss )
2704  return SCIP_OKAY;
2705 
2707  " Deactivated symmetry handling methods, since SCIP was built without symmetry detector (SYM=none).\n");
2708 
2709  return SCIP_OKAY;
2710  }
2711 
2712  /* do not compute symmetry if there are active pricers */
2713  if ( SCIPgetNActivePricers(scip) > 0 )
2714  {
2715  propdata->ofenabled = FALSE;
2716  propdata->symconsenabled = FALSE;
2717  propdata->sstenabled = FALSE;
2718 
2719  return SCIP_OKAY;
2720  }
2721 
2722  /* avoid trivial cases */
2723  nvars = SCIPgetNVars(scip);
2724  if ( nvars <= 0 )
2725  {
2726  propdata->ofenabled = FALSE;
2727  propdata->symconsenabled = FALSE;
2728  propdata->sstenabled = FALSE;
2729 
2730  return SCIP_OKAY;
2731  }
2732 
2733  /* do not compute symmetry if there are no binary variables and non-binary variables cannot be handled, but only binary variables would be used */
2734  if ( propdata->onlybinarysymmetry && SCIPgetNBinVars(scip) == 0 )
2735  {
2736  propdata->ofenabled = FALSE;
2737  propdata->symconsenabled = FALSE;
2738 
2739  /* terminate if only Schreier Sims for binary variables is selected */
2740  if ( propdata->sstenabled )
2741  {
2742  if ( ! ((ISSSTINTACTIVE(propdata->sstleadervartype) && SCIPgetNIntVars(scip) > 0)
2743  || (ISSSTIMPLINTACTIVE(propdata->sstleadervartype) && SCIPgetNImplVars(scip) > 0)
2744  || (ISSSTCONTACTIVE(propdata->sstleadervartype) && SCIPgetNContVars(scip) > 0)) )
2745  return SCIP_OKAY;
2746  }
2747  else
2748  return SCIP_OKAY;
2749  }
2750 
2751  /* determine symmetry specification */
2752  if ( SCIPgetNBinVars(scip) > 0 )
2753  type |= (int) SYM_SPEC_BINARY;
2754  if ( SCIPgetNIntVars(scip) > 0 )
2755  type |= (int) SYM_SPEC_INTEGER;
2756  /* count implicit integer variables as real variables, since we cannot currently handle integral variables well */
2757  if ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2758  type |= (int) SYM_SPEC_REAL;
2759 
2760  /* skip symmetry computation if required variables are not present */
2761  if ( ! (type & symspecrequire) )
2762  {
2764  " (%.1fs) symmetry computation skipped: type (bin %c, int %c, cont %c) does not match requirements (bin %c, int %c, cont %c).\n",
2765  SCIPgetSolvingTime(scip),
2766  SCIPgetNBinVars(scip) > 0 ? '+' : '-',
2767  SCIPgetNIntVars(scip) > 0 ? '+' : '-',
2768  SCIPgetNContVars(scip) + SCIPgetNImplVars(scip) > 0 ? '+' : '-',
2769  (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2770  (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2771  (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2772 
2773  propdata->ofenabled = FALSE;
2774  propdata->symconsenabled = FALSE;
2775  propdata->sstenabled = FALSE;
2776 
2777  return SCIP_OKAY;
2778  }
2779 
2780  /* if a restart occured, possibly prepare symmetry data to be recomputed */
2781  if ( isSymmetryRecomputationRequired(scip, propdata) )
2782  {
2783  /* reset symmetry information */
2784  SCIP_CALL( delSymConss(scip, propdata) );
2785  SCIP_CALL( freeSymmetryData(scip, propdata) );
2786 
2787  propdata->lastrestart = SCIPgetNRuns(scip);
2788  propdata->offoundreduction = FALSE;
2789  }
2790 
2791  /* skip computation if symmetry has already been computed */
2792  if ( propdata->computedsymmetry )
2793  return SCIP_OKAY;
2794 
2795  /* skip symmetry computation if there are constraints that cannot be handled by symmetry */
2796  nconss = SCIPgetNActiveConss(scip);
2797  nhandleconss = getNSymhandableConss(scip, propdata->conshdlr_nonlinear);
2798  if ( nhandleconss < nconss )
2799  {
2801  " (%.1fs) symmetry computation skipped: there exist constraints that cannot be handled by symmetry methods.\n",
2802  SCIPgetSolvingTime(scip));
2803 
2804  propdata->ofenabled = FALSE;
2805  propdata->symconsenabled = FALSE;
2806  propdata->sstenabled = FALSE;
2807 
2808  return SCIP_OKAY;
2809  }
2810 
2811  assert( propdata->npermvars == 0 );
2812  assert( propdata->permvars == NULL );
2813 #ifndef NDEBUG
2814  assert( propdata->permvarsobj == NULL );
2815 #endif
2816  assert( propdata->nperms < 0 );
2817  assert( propdata->nmaxperms == 0 );
2818  assert( propdata->perms == NULL );
2819 
2820  /* output message */
2822  " (%.1fs) symmetry computation started: requiring (bin %c, int %c, cont %c), (fixed: bin %c, int %c, cont %c)\n",
2823  SCIPgetSolvingTime(scip),
2824  (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2825  (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2826  (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-',
2827  (symspecrequirefixed & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2828  (symspecrequirefixed & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2829  (symspecrequirefixed & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2830 
2831  /* output warning if we want to fix certain symmetry parts that we also want to compute */
2832  if ( symspecrequire & symspecrequirefixed )
2833  SCIPwarningMessage(scip, "Warning: some required symmetries must be fixed.\n");
2834 
2835  /* determine maximal number of generators depending on the number of variables */
2836  maxgenerators = propdata->maxgenerators;
2837  maxgenerators = MIN(maxgenerators, MAXGENNUMERATOR / nvars);
2838 
2839  /* actually compute (global) symmetry */
2840  SCIP_CALL( computeSymmetryGroup(scip, propdata->doubleequations, propdata->compresssymmetries, propdata->compressthreshold,
2841  maxgenerators, symspecrequirefixed, FALSE, propdata->checksymmetries, propdata->usecolumnsparsity, propdata->conshdlr_nonlinear,
2842  &propdata->npermvars, &propdata->nbinpermvars, &propdata->permvars, &propdata->nperms, &propdata->nmaxperms,
2843  &propdata->perms, &propdata->log10groupsize, &propdata->nmovedvars, &propdata->isnonlinvar,
2844  &propdata->binvaraffected, &propdata->compressed, &successful) );
2845 
2846  /* mark that we have computed the symmetry group */
2847  propdata->computedsymmetry = TRUE;
2848 
2849  /* store restart level */
2850  propdata->lastrestart = SCIPgetNRuns(scip);
2851 
2852  /* return if not successful */
2853  if ( ! successful )
2854  {
2855  assert( checkSymmetryDataFree(propdata) );
2856  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) could not compute symmetry\n", SCIPgetSolvingTime(scip));
2857 
2858  propdata->ofenabled = FALSE;
2859  propdata->symconsenabled = FALSE;
2860  propdata->sstenabled = FALSE;
2861 
2862  return SCIP_OKAY;
2863  }
2864 
2865  /* return if no symmetries found */
2866  if ( propdata->nperms == 0 )
2867  {
2868  assert( checkSymmetryDataFree(propdata) );
2869  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry present\n", SCIPgetSolvingTime(scip));
2870 
2871  propdata->ofenabled = FALSE;
2872  propdata->symconsenabled = FALSE;
2873  propdata->sstenabled = FALSE;
2874 
2875  return SCIP_OKAY;
2876  }
2877 
2878  /* display statistics */
2879  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) symmetry computation finished: %d generators found (max: ",
2880  SCIPgetSolvingTime(scip), propdata->nperms);
2881 
2882  /* display statistics: maximum number of generators */
2883  if ( maxgenerators == 0 )
2885  else
2886  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%d", maxgenerators);
2887 
2888  /* display statistics: log10 group size, number of affected vars*/
2889  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", log10 of symmetry group size: %.1f", propdata->log10groupsize);
2890 
2891  if ( propdata->displaynorbitvars )
2892  {
2893  if ( propdata->nmovedvars == -1 )
2894  {
2895  SCIP_CALL( SCIPdetermineNVarsAffectedSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2896  propdata->npermvars, &(propdata->nmovedvars)) );
2897  }
2898  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", number of affected variables: %d)\n", propdata->nmovedvars);
2899  }
2900  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ")\n");
2901 
2902  /* exit if no binary variables are affected by symmetry and we cannot handle non-binary symmetries */
2903  if ( ! propdata->binvaraffected )
2904  {
2905  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry on binary variables present.\n", SCIPgetSolvingTime(scip));
2906 
2907  /* disable OF and symmetry handling constraints based on symretopes */
2908  propdata->ofenabled = FALSE;
2909  propdata->symconsenabled = FALSE;
2910  if ( ! propdata->sstenabled ||
2911  ! ( ISSSTINTACTIVE(propdata->sstleadervartype) || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
2912  || ISSSTCONTACTIVE(propdata->sstleadervartype) ) )
2913  {
2914  propdata->sstenabled = FALSE;
2915 
2916  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) -> no handable symmetry found, free symmetry data.\n",
2917  SCIPgetSolvingTime(scip));
2918 
2919  /* free data and exit */
2920  SCIP_CALL( freeSymmetryData(scip, propdata) );
2921 
2922  return SCIP_OKAY;
2923  }
2924  }
2925 
2926  assert( propdata->nperms > 0 );
2927  assert( propdata->npermvars > 0 );
2928 
2929  /* compute components */
2930  assert( propdata->components == NULL );
2931  assert( propdata->componentbegins == NULL );
2932  assert( propdata->vartocomponent == NULL );
2933 
2934 #ifdef SCIP_OUTPUT_COMPONENT
2935  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation started\n", SCIPgetSolvingTime(scip));
2936 #endif
2937 
2938  /* we only need the components for orbital fixing, orbitope and subgroup detection, and Schreier Sims constraints */
2939  if ( propdata->ofenabled || ( propdata->symconsenabled && propdata->detectorbitopes )
2940  || propdata->detectsubgroups || propdata->sstenabled )
2941  {
2942  SCIP_CALL( SCIPcomputeComponentsSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2943  propdata->npermvars, FALSE, &propdata->components, &propdata->componentbegins,
2944  &propdata->vartocomponent, &propdata->componentblocked, &propdata->ncomponents) );
2945  }
2946 
2947 #ifdef SCIP_OUTPUT_COMPONENT
2948  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation finished\n", SCIPgetSolvingTime(scip));
2949 #endif
2950 
2951  /* set up data for OF */
2952  if ( propdata->ofenabled )
2953  {
2954  int componentidx;
2955  int v;
2956 
2957  /* transpose symmetries matrix here if necessary */
2958  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
2959  for (v = 0; v < propdata->npermvars; ++v)
2960  {
2961  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
2962  for (p = 0; p < propdata->nperms; ++p)
2963  {
2964  if ( SCIPvarIsBinary(propdata->permvars[v]) || propdata->sstenabled )
2965  propdata->permstrans[v][p] = propdata->perms[p][v];
2966  else
2967  propdata->permstrans[v][p] = v; /* ignore symmetry information on non-binary variables */
2968  }
2969  }
2970 
2971  /* prepare array for active permutations */
2972  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->inactiveperms, propdata->nperms) );
2973  for (v = 0; v < propdata->nperms; ++v)
2974  propdata->inactiveperms[v] = FALSE;
2975 
2976  /* create hashmap for storing the indices of variables */
2977  assert( propdata->permvarmap == NULL );
2978  SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
2979 
2980  /* prepare data structures */
2981  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permvarsevents, propdata->npermvars) );
2982  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg0, propdata->npermvars) );
2983  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg0list, propdata->npermvars) );
2984  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg1, propdata->npermvars) );
2985  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg1list, propdata->npermvars) );
2986 
2987  /* insert variables into hashmap */
2988  assert( propdata->nmovedpermvars == -1 );
2989  propdata->nmovedpermvars = 0;
2990  for (v = 0; v < propdata->npermvars; ++v)
2991  {
2992  SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
2993 
2994  propdata->bg0[v] = FALSE;
2995  propdata->bg1[v] = FALSE;
2996  propdata->permvarsevents[v] = -1;
2997 
2998  /* collect number of moved permvars */
2999  componentidx = propdata->vartocomponent[v];
3000  if ( componentidx > -1 && ! propdata->componentblocked[componentidx] )
3001  {
3002  propdata->nmovedpermvars += 1;
3003 
3004  if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_BINARY )
3005  ++propdata->nmovedbinpermvars;
3006  else if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_INTEGER )
3007  ++propdata->nmovedintpermvars;
3008  else if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_IMPLINT )
3009  ++propdata->nmovedimplintpermvars;
3010  else
3011  ++propdata->nmovedcontpermvars;
3012  }
3013 
3014  /* Only catch binary variables, since integer variables should be fixed pointwise; implicit integer variables
3015  * are not branched on. */
3016  if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_BINARY )
3017  {
3018  /* catch whether binary variables are globally fixed; also store filter position */
3020  propdata->eventhdlr, (SCIP_EVENTDATA*) propdata, &propdata->permvarsevents[v]) );
3021  }
3022  }
3023  assert( propdata->nbg1 == 0 );
3024  }
3025 
3026  /* set up data for Schreier Sims constraints or subgroup detection */
3027  if ( (propdata->sstenabled || propdata->detectsubgroups) && ! propdata->ofenabled )
3028  {
3029  int v;
3030 
3031  /* transpose symmetries matrix here if necessary */
3032  assert( propdata->permstrans == NULL );
3033  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
3034  for (v = 0; v < propdata->npermvars; ++v)
3035  {
3036  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
3037  for (p = 0; p < propdata->nperms; ++p)
3038  propdata->permstrans[v][p] = propdata->perms[p][v];
3039  }
3040 
3041  /* create hashmap for storing the indices of variables */
3042  assert( propdata->permvarmap == NULL );
3043  SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
3044 
3045  /* insert variables into hashmap */
3046  for (v = 0; v < propdata->npermvars; ++v)
3047  {
3048  SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
3049  }
3050  }
3051 
3052  /* handle several general aspects */
3053 #ifndef NDEBUG
3054  /* store objective coefficients for debug purposes */
3055  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permvarsobj, propdata->npermvars) );
3056  for (j = 0; j < propdata->npermvars; ++j)
3057  propdata->permvarsobj[j] = SCIPvarGetObj(propdata->permvars[j]);
3058 #endif
3059 
3060  /* capture symmetric variables and forbid multi aggregation */
3061 
3062  /* binary symmetries are always handled
3063  *
3064  * note: binary variables are in the beginning of permvars
3065  */
3066  if ( propdata->binvaraffected )
3067  {
3068  for (j = 0; j < propdata->nbinpermvars; ++j)
3069  {
3070  SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[j]) );
3071 
3072  if ( propdata->compressed )
3073  {
3074  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3075  }
3076  else
3077  {
3078  for (p = 0; p < propdata->nperms; ++p)
3079  {
3080  if ( propdata->perms[p][j] != j )
3081  {
3082  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3083  break;
3084  }
3085  }
3086  }
3087  }
3088  }
3089 
3090  /* if Schreier-Sims constraints are enabled, also capture symmetric variables and forbid multi aggregation of handable vars */
3091  if ( propdata->sstenabled && propdata->sstleadervartype != (int) SCIP_SSTTYPE_BINARY )
3092  {
3093  int cnt;
3094 
3095  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->nonbinpermvarcaptured,
3096  propdata->npermvars - propdata->nbinpermvars) );
3097  for (j = propdata->nbinpermvars, cnt = 0; j < propdata->npermvars; ++j, ++cnt)
3098  {
3099  if ( ! isLeadervartypeCompatible(propdata->permvars[j], propdata->sstleadervartype) )
3100  {
3101  propdata->nonbinpermvarcaptured[cnt] = FALSE;
3102  continue;
3103  }
3104 
3105  SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[j]) );
3106  propdata->nonbinpermvarcaptured[cnt] = TRUE;
3107 
3108  if ( propdata->compressed )
3109  {
3110  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3111  }
3112  else
3113  {
3114  for (p = 0; p < propdata->nperms; ++p)
3115  {
3116  if ( propdata->perms[p][j] != j )
3117  {
3118  SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3119  break;
3120  }
3121  }
3122  }
3123  }
3124  }
3125 
3126  /* free original perms matrix if no symmetry constraints are added */
3127  if ( ! propdata->symconsenabled && ! propdata->sstenabled )
3128  {
3129  for (p = 0; p < propdata->nperms; ++p)
3130  {
3131  SCIPfreeBlockMemoryArray(scip, &(propdata->perms)[p], propdata->npermvars);
3132  }
3133  SCIPfreeBlockMemoryArrayNull(scip, &propdata->perms, propdata->nmaxperms);
3134  }
3135 
3136  return SCIP_OKAY;
3137 }
3138 
3139 
3140 /*
3141  * Functions for symmetry constraints
3142  */
3143 
3144 
3145 /** Checks whether given set of 2-cycle permutations forms an orbitope and if so, builds the variable index matrix.
3146  *
3147  * If @p activevars == NULL, then the function assumes all permutations of the component are active and therefore all
3148  * moved vars are considered.
3149  *
3150  * We need to keep track of the number of generatored columns, because we might not be able to detect all orbitopes.
3151  * 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
3152  * in our construction need shape (1,2), (2,3), (3,4), (4,5).
3153  *
3154  * @pre @p orbitopevaridx has to be an initialized 2D array of size @p ntwocycles x @p nperms
3155  * @pre @p columnorder has to be an initialized array of size nperms
3156  * @pre @p nusedelems has to be an initialized array of size npermvars
3157  */
3158 static
3160  SCIP* scip, /**< SCIP instance */
3161  SCIP_VAR** permvars, /**< array of all permutation variables */
3162  int npermvars, /**< number of permutation variables */
3163  int** perms, /**< array of all permutations of the symmety group */
3164  int* activeperms, /**< indices of the relevant permutations in perms */
3165  int ntwocycles, /**< number of 2-cycles in the permutations */
3166  int nactiveperms, /**< number of active permutations */
3167  int** orbitopevaridx, /**< pointer to store variable index matrix */
3168  int* columnorder, /**< pointer to store column order */
3169  int* nusedelems, /**< pointer to store how often each element was used */
3170  int* nusedcols, /**< pointer to store numer of columns used in orbitope (or NULL) */
3171  SCIP_Shortbool* rowisbinary, /**< pointer to store which rows are binary (or NULL) */
3172  SCIP_Bool* isorbitope, /**< buffer to store result */
3173  SCIP_Shortbool* activevars /**< bitset to store whether a variable is active (or NULL) */
3174  )
3175 { /*lint --e{571}*/
3176  SCIP_Bool* usedperm;
3177  SCIP_Bool foundperm = FALSE;
3178  int nusedperms = 0;
3179  int nfilledcols;
3180  int coltoextend;
3181  int ntestedperms = 0;
3182  int row = 0;
3183  int j;
3184 
3185  assert( scip != NULL );
3186  assert( permvars != NULL );
3187  assert( perms != NULL );
3188  assert( activeperms != NULL );
3189  assert( orbitopevaridx != NULL );
3190  assert( columnorder != NULL );
3191  assert( nusedelems != NULL );
3192  assert( isorbitope != NULL );
3193  assert( nactiveperms > 0 );
3194  assert( ntwocycles > 0 );
3195  assert( npermvars > 0 );
3196  assert( activevars == NULL || (0 <= nactiveperms && nactiveperms < npermvars) );
3197 
3198  *isorbitope = TRUE;
3199  if ( nusedcols != NULL )
3200  *nusedcols = 0;
3201 
3202  /* whether a permutation was considered to contribute to orbitope */
3203  SCIP_CALL( SCIPallocClearBufferArray(scip, &usedperm, nactiveperms) );
3204 
3205  /* fill first two columns of orbitopevaridx matrix */
3206 
3207  /* look for the first active permutation which moves an active variable */
3208  while ( ! foundperm )
3209  {
3210  int permidx;
3211 
3212  assert( ntestedperms < nactiveperms );
3213 
3214  permidx = activeperms[ntestedperms];
3215 
3216  for (j = 0; j < npermvars; ++j)
3217  {
3218  if ( activevars != NULL && ! activevars[j] )
3219  continue;
3220 
3221  assert( activevars == NULL || activevars[perms[permidx][j]] );
3222 
3223  /* avoid adding the same 2-cycle twice */
3224  if ( perms[permidx][j] > j )
3225  {
3226  assert( SCIPvarIsBinary(permvars[j]) == SCIPvarIsBinary(permvars[perms[permidx][j]]) );
3227 
3228  if ( rowisbinary != NULL && SCIPvarIsBinary(permvars[j]) )
3229  rowisbinary[row] = TRUE;
3230 
3231  orbitopevaridx[row][0] = j;
3232  orbitopevaridx[row++][1] = perms[permidx][j];
3233  ++(nusedelems[j]);
3234  ++(nusedelems[perms[permidx][j]]);
3235 
3236  foundperm = TRUE;
3237  }
3238 
3239  if ( row == ntwocycles )
3240  break;
3241  }
3242 
3243  ++ntestedperms;
3244  }
3245 
3246  /* in the subgroup case it might happen that a generator has less than ntwocycles many 2-cyles */
3247  if ( row < ntwocycles )
3248  {
3249  *isorbitope = FALSE;
3250  SCIPfreeBufferArray(scip, &usedperm);
3251  return SCIP_OKAY;
3252  }
3253 
3254  usedperm[ntestedperms - 1] = TRUE;
3255  ++nusedperms;
3256  columnorder[0] = 0;
3257  columnorder[1] = 1;
3258  nfilledcols = 2;
3259 
3260  /* extend orbitopevaridx matrix to the left, i.e., iteratively find new permutations that
3261  * intersect the last added left column in each row in exactly one entry, starting with
3262  * column 0 */
3263  coltoextend = 0;
3264  for (j = ntestedperms; j < nactiveperms; ++j)
3265  { /* lint --e{850} */
3266  SCIP_Bool success = FALSE;
3267  SCIP_Bool infeasible = FALSE;
3268 
3269  if ( nusedperms == nactiveperms )
3270  break;
3271 
3272  if ( usedperm[j] )
3273  continue;
3274 
3275  SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
3276  perms[activeperms[j]], TRUE, &nusedelems, permvars, NULL, &success, &infeasible) );
3277 
3278  if ( infeasible )
3279  {
3280  *isorbitope = FALSE;
3281  break;
3282  }
3283  else if ( success )
3284  {
3285  usedperm[j] = TRUE;
3286  ++nusedperms;
3287  coltoextend = nfilledcols;
3288  columnorder[nfilledcols++] = -1; /* mark column to be filled from the left */
3289  j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
3290  }
3291  }
3292 
3293  if ( ! *isorbitope ) /*lint !e850*/
3294  {
3295  SCIPfreeBufferArray(scip, &usedperm);
3296  return SCIP_OKAY;
3297  }
3298 
3299  coltoextend = 1;
3300  for (j = ntestedperms; j < nactiveperms; ++j)
3301  { /*lint --e(850)*/
3302  SCIP_Bool success = FALSE;
3303  SCIP_Bool infeasible = FALSE;
3304 
3305  if ( nusedperms == nactiveperms )
3306  break;
3307 
3308  if ( usedperm[j] )
3309  continue;
3310 
3311  SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
3312  perms[activeperms[j]], FALSE, &nusedelems, permvars, NULL, &success, &infeasible) );
3313 
3314  if ( infeasible )
3315  {
3316  *isorbitope = FALSE;
3317  break;
3318  }
3319  else if ( success )
3320  {
3321  usedperm[j] = TRUE;
3322  ++nusedperms;
3323  coltoextend = nfilledcols;
3324  columnorder[nfilledcols] = 1; /* mark column to be filled from the right */
3325  ++nfilledcols;
3326  j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
3327  }
3328  }
3329 
3330  if ( activevars == NULL && nusedperms < nactiveperms ) /*lint !e850*/
3331  *isorbitope = FALSE;
3332 
3333  if ( nusedcols != NULL )
3334  *nusedcols = nfilledcols;
3335  assert( ! *isorbitope || activevars == NULL || nusedperms < nfilledcols );
3336 
3337  SCIPfreeBufferArray(scip, &usedperm);
3338 
3339  return SCIP_OKAY;
3340 }
3341 
3342 /** choose an order in which the generators should be added for subgroup detection */
3343 static
3345  SCIP* scip, /**< SCIP instance */
3346  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3347  int compidx, /**< index of component */
3348  int** genorder, /**< (initialized) buffer to store the resulting order of generator */
3349  int* ntwocycleperms /**< pointer to store the number of 2-cycle permutations in component compidx */
3350  )
3351 {
3352  int** perms;
3353  int* components;
3354  int* componentbegins;
3355  int* ntwocycles;
3356  int npermvars;
3357  int npermsincomp;
3358  int i;
3359 
3360  assert( scip != NULL );
3361  assert( propdata != NULL );
3362  assert( compidx >= 0 );
3363  assert( compidx < propdata->ncomponents );
3364  assert( genorder != NULL );
3365  assert( *genorder != NULL );
3366  assert( ntwocycleperms != NULL );
3367  assert( propdata->computedsymmetry );
3368  assert( propdata->nperms > 0 );
3369  assert( propdata->perms != NULL );
3370  assert( propdata->npermvars > 0 );
3371  assert( propdata->ncomponents > 0 );
3372  assert( propdata->components != NULL );
3373  assert( propdata->componentbegins != NULL );
3374 
3375  perms = propdata->perms;
3376  npermvars = propdata->npermvars;
3377  components = propdata->components;
3378  componentbegins = propdata->componentbegins;
3379  npermsincomp = componentbegins[compidx + 1] - componentbegins[compidx];
3380  *ntwocycleperms = npermsincomp;
3381 
3382  SCIP_CALL( SCIPallocBufferArray(scip, &ntwocycles, npermsincomp) );
3383 
3384  for (i = 0; i < npermsincomp; ++i)
3385  {
3386  int* perm;
3387  int nbincycles;
3388 
3389  perm = perms[components[componentbegins[compidx] + i]];
3390 
3391  SCIP_CALL( SCIPisInvolutionPerm(perm, propdata->permvars, npermvars, &(ntwocycles[i]), &nbincycles, FALSE) );
3392 
3393  /* we skip permutations which do not purely consist of 2-cycles */
3394  if ( ntwocycles[i] == 0 )
3395  {
3396  /* we change the number of two cycles for this perm so that it will be sorted to the end */
3397  if ( propdata->preferlessrows )
3398  ntwocycles[i] = npermvars;
3399  else
3400  ntwocycles[i] = 0;
3401  --(*ntwocycleperms);
3402  }
3403  else if ( ! propdata->preferlessrows )
3404  ntwocycles[i] = - ntwocycles[i];
3405  }
3406 
3407  SCIPsortIntInt(ntwocycles, *genorder, npermsincomp);
3408 
3409  SCIPfreeBufferArray(scip, &ntwocycles);
3410 
3411  return SCIP_OKAY;
3412 }
3413 
3414 
3415 /** builds the graph for symmetric subgroup detection from the given permutation of generators
3416  *
3417  * After execution, @p graphcomponents contains all permvars sorted by their color and component,
3418  * @p graphcompbegins points to the indices where new components in @p graphcomponents start and
3419  * @p compcolorbegins points to the indices where new colors in @p graphcompbegins start.
3420 */
3421 static
3423  SCIP* scip, /**< SCIP instance */
3424  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3425  int* genorder, /**< order in which the generators should be considered */
3426  int ntwocycleperms, /**< number of 2-cycle permutations in this component */
3427  int compidx, /**< index of the component */
3428  int** graphcomponents, /**< buffer to store the components of the graph (ordered var indices) */
3429  int** graphcompbegins, /**< buffer to store the indices of each new graph component */
3430  int** compcolorbegins, /**< buffer to store at which indices a new color begins */
3431  int* ngraphcomponents, /**< pointer to store the number of graph components */
3432  int* ncompcolors, /**< pointer to store the number of different colors */
3433  int** usedperms, /**< buffer to store the indices of permutations that were used */
3434  int* nusedperms, /**< pointer to store the number of used permutations in the graph */
3435  int usedpermssize, /**< initial size of usedperms */
3436  SCIP_Shortbool* permused /**< initialized buffer to store which permutations have been used
3437  * (identified by index in component) */
3438  )
3439 {
3440  SCIP_DISJOINTSET* vartocomponent;
3441  SCIP_DISJOINTSET* comptocolor;
3442  int** perms;
3443  int* components;
3444  int* componentbegins;
3445  int* componentslastperm;
3446  SYM_SORTGRAPHCOMPVARS graphcompvartype;
3447  int npermvars;
3448  int nextcolor;
3449  int nextcomp;
3450  int j;
3451  int k;
3452 
3453  assert( scip != NULL );
3454  assert( propdata != NULL );
3455  assert( graphcomponents != NULL );
3456  assert( graphcompbegins != NULL );
3457  assert( compcolorbegins != NULL );
3458  assert( ngraphcomponents != NULL );
3459  assert( ncompcolors != NULL );
3460  assert( genorder != NULL );
3461  assert( usedperms != NULL );
3462  assert( nusedperms != NULL );
3463  assert( usedpermssize > 0 );
3464  assert( permused != NULL );
3465  assert( ntwocycleperms >= 0 );
3466  assert( compidx >= 0 );
3467  assert( compidx < propdata->ncomponents );
3468  assert( propdata->computedsymmetry );
3469  assert( propdata->nperms > 0 );
3470  assert( propdata->perms != NULL );
3471  assert( propdata->npermvars > 0 );
3472  assert( propdata->ncomponents > 0 );
3473  assert( propdata->components != NULL );
3474  assert( propdata->componentbegins != NULL );
3475  assert( !propdata->componentblocked[compidx] );
3476 
3477  perms = propdata->perms;
3478  npermvars = propdata->npermvars;
3479  components = propdata->components;
3480  componentbegins = propdata->componentbegins;
3481  *nusedperms = 0;
3482 
3483  assert( ntwocycleperms <= componentbegins[compidx + 1] - componentbegins[compidx] );
3484 
3485  SCIP_CALL( SCIPcreateDisjointset(scip, &vartocomponent, npermvars) );
3486  SCIP_CALL( SCIPcreateDisjointset(scip, &comptocolor, npermvars) );
3487  SCIP_CALL( SCIPallocBufferArray( scip, &componentslastperm, npermvars) );
3488 
3489  for (k = 0; k < npermvars; ++k)
3490  componentslastperm[k] = -1;
3491 
3492  for (j = 0; j < ntwocycleperms; ++j)
3493  {
3494  int* perm;
3495  int firstcolor = -1;
3496 
3497  /* use given order of generators */
3498  perm = perms[components[componentbegins[compidx] + genorder[j]]];
3499  assert( perm != NULL );
3500 
3501  /* iteratively handle each swap of perm until an invalid one is found or all edges have been added */
3502  for (k = 0; k < npermvars; ++k)
3503  {
3504  int comp1;
3505  int comp2;
3506  int color1;
3507  int color2;
3508  int img;
3509 
3510  img = perm[k];
3511  assert( perm[img] == k );
3512 
3513  if ( img <= k )
3514  continue;
3515 
3516  comp1 = SCIPdisjointsetFind(vartocomponent, k);
3517  comp2 = SCIPdisjointsetFind(vartocomponent, img);
3518 
3519  if ( comp1 == comp2 )
3520  {
3521  /* another permutation has already merged these variables into one component; store its color */
3522  if ( firstcolor < 0 )
3523  {
3524  assert( SCIPdisjointsetFind(comptocolor, comp1) == SCIPdisjointsetFind(comptocolor, comp2) );
3525  firstcolor = SCIPdisjointsetFind(comptocolor, comp1);
3526  }
3527  componentslastperm[comp1] = j;
3528  continue;
3529  }
3530 
3531  /* if it is the second time that the component is used for this generator,
3532  * it is not guaranteed that the group acts like the symmetric group, so skip it
3533  */
3534  if ( componentslastperm[comp1] == j || componentslastperm[comp2] == j )
3535  break;
3536 
3537  color1 = SCIPdisjointsetFind(comptocolor, comp1);
3538  color2 = SCIPdisjointsetFind(comptocolor, comp2);
3539 
3540  /* a generator is not allowed to connect two components of the same color, since they depend on each other */
3541  if ( color1 == color2 )
3542  break;
3543 
3544  componentslastperm[comp1] = j;
3545  componentslastperm[comp2] = j;
3546 
3547  if ( firstcolor < 0 )
3548  firstcolor = color1;
3549  }
3550 
3551  /* if the generator is invalid, delete the newly added edges, go to next generator */
3552  if ( k < npermvars )
3553  continue;
3554 
3555  /* if the generator only acts on already existing components, we don't have to store it */
3556  if ( firstcolor == -1 )
3557  continue;
3558 
3559  /* check whether we need to resize */
3560  if ( *nusedperms >= usedpermssize )
3561  {
3562  int newsize = SCIPcalcMemGrowSize(scip, (*nusedperms) + 1);
3563  assert( newsize > usedpermssize );
3564 
3565  SCIP_CALL( SCIPreallocBufferArray(scip, usedperms, newsize) );
3566 
3567  usedpermssize = newsize;
3568  }
3569 
3570  (*usedperms)[*nusedperms] = components[componentbegins[compidx] + genorder[j]];
3571  ++(*nusedperms);
3572  permused[genorder[j]] = TRUE;
3573 
3574  /* if the generator can be added, update the datastructures for graph components and colors */
3575  for (k = 0; k < npermvars; ++k)
3576  {
3577  int comp1;
3578  int comp2;
3579  int color1;
3580  int color2;
3581  int img;
3582 
3583  img = perm[k];
3584  assert( perm[img] == k );
3585 
3586  if ( img <= k )
3587  continue;
3588 
3589  comp1 = SCIPdisjointsetFind(vartocomponent, k);
3590  comp2 = SCIPdisjointsetFind(vartocomponent, img);
3591 
3592  /* components and colors don't have to be updated if the components are the same */
3593  if ( comp1 == comp2 )
3594  continue;
3595 
3596  color1 = SCIPdisjointsetFind(comptocolor, comp1);
3597  color2 = SCIPdisjointsetFind(comptocolor, comp2);
3598 
3599  if ( color1 != color2 )
3600  {
3601  SCIPdisjointsetUnion(comptocolor, firstcolor, color1, TRUE);
3602  SCIPdisjointsetUnion(comptocolor, firstcolor, color2, TRUE);
3603  }
3604 
3605  SCIPdisjointsetUnion(vartocomponent, comp1, comp2, FALSE);
3606 
3607  assert( SCIPdisjointsetFind(vartocomponent, k) == SCIPdisjointsetFind(vartocomponent, img) );
3608  assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, k)) == firstcolor );
3609  assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, img)) == firstcolor );
3610  }
3611  }
3612 
3613  SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcomponents, npermvars) );
3614  SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.components), npermvars) );
3615  SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.colors), npermvars) );
3616 
3617  /*
3618  * At this point, we have built the colored graph. Now we transform the information in the
3619  * disjoint sets to the arrays graphcomponents, graphcompbegins, and compcolorbegins (see above).
3620  */
3621 
3622  /* build the struct graphcompvartype which is used to sort the graphcomponents array */
3623  for (j = 0; j < npermvars; ++j)
3624  {
3625  int comp;
3626 
3627  comp = SCIPdisjointsetFind(vartocomponent, j);
3628 
3629  graphcompvartype.components[j] = comp;
3630  graphcompvartype.colors[j] = SCIPdisjointsetFind(comptocolor, comp);
3631 
3632  (*graphcomponents)[j] = j;
3633  }
3634 
3635  /* sort graphcomponents first by color, then by component */
3636  SCIPsort(*graphcomponents, SYMsortGraphCompVars, (void*) &graphcompvartype, npermvars);
3637 
3638  *ngraphcomponents = SCIPdisjointsetGetComponentCount(vartocomponent);
3639  *ncompcolors = SCIPdisjointsetGetComponentCount(comptocolor);
3640  SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcompbegins, (*ngraphcomponents) + 1) );
3641  SCIP_CALL( SCIPallocBlockMemoryArray(scip, compcolorbegins, (*ncompcolors) + 1) );
3642 
3643  nextcolor = 1;
3644  nextcomp = 1;
3645  (*graphcompbegins)[0] = 0;
3646  (*compcolorbegins)[0] = 0;
3647 
3648  /* find the starting indices of new components and new colors */
3649  for (j = 1; j < npermvars; ++j)
3650  {
3651  int idx1;
3652  int idx2;
3653 
3654  idx1 = (*graphcomponents)[j];
3655  idx2 = (*graphcomponents)[j-1];
3656 
3657  assert( graphcompvartype.colors[idx1] >= graphcompvartype.colors[idx2] );
3658 
3659  if ( graphcompvartype.components[idx1] != graphcompvartype.components[idx2] )
3660  {
3661  (*graphcompbegins)[nextcomp] = j;
3662 
3663  if ( graphcompvartype.colors[idx1] > graphcompvartype.colors[idx2] )
3664  {
3665  (*compcolorbegins)[nextcolor] = nextcomp;
3666  ++nextcolor;
3667  }
3668 
3669  ++nextcomp;
3670  }
3671  }
3672  assert( nextcomp == *ngraphcomponents );
3673  assert( nextcolor == *ncompcolors );
3674 
3675  (*compcolorbegins)[nextcolor] = *ngraphcomponents;
3676  (*graphcompbegins)[nextcomp] = npermvars;
3677 
3678  SCIPfreeBufferArray(scip, &(graphcompvartype.colors));
3679  SCIPfreeBufferArray(scip, &(graphcompvartype.components));
3680  SCIPfreeBufferArray(scip, &componentslastperm);
3681  SCIPfreeDisjointset(scip, &comptocolor);
3682  SCIPfreeDisjointset(scip, &vartocomponent);
3683 
3684  return SCIP_OKAY;
3685 }
3686 
3687 /** adds an orbitope constraint for a suitable color of the subgroup graph */
3688 static
3690  SCIP* scip, /**< SCIP instance */
3691  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3692  int* usedperms, /**< array of the permutations that build the orbitope */
3693  int nusedperms, /**< number of permutations in usedperms */
3694  int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3695  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3696  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3697  int graphcoloridx, /**< index of the graph color */
3698  int nrows, /**< number of rows in the orbitope */
3699  int ncols, /**< number of columns in the orbitope */
3700  int* firstvaridx, /**< buffer to store the index of the largest variable (or NULL) */
3701  int* compidxfirstrow, /**< buffer to store the comp index for the first row (or NULL) */
3702  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3703  int* nvarslexorder, /**< number of variables in lexicographic order */
3704  int* maxnvarslexorder, /**< maximum number of variables in lexicographic order */
3705  SCIP_Bool mayinteract, /**< whether orbitope's symmetries might interact with other symmetries */
3706  SCIP_Bool* success /**< whether the orbitpe could be added */
3707  )
3708 { /*lint --e{571}*/
3709  char name[SCIP_MAXSTRLEN];
3710  SCIP_VAR*** orbitopevarmatrix;
3711  SCIP_Shortbool* activevars;
3712  int** orbitopevaridx;
3713  int* columnorder;
3714  int* nusedelems;
3715  SCIP_CONS* cons;
3716  SCIP_Bool isorbitope;
3717  SCIP_Bool infeasible = FALSE;
3718  int nactivevars = 0;
3719  int ngencols = 0;
3720  int k;
3721 
3722  assert( scip != NULL );
3723  assert( propdata != NULL );
3724  assert( usedperms != NULL );
3725  assert( compcolorbegins != NULL );
3726  assert( graphcompbegins != NULL );
3727  assert( graphcomponents != NULL );
3728  assert( nusedperms > 0 );
3729  assert( nrows > 0 );
3730  assert( ncols > 0 );
3731  assert( lexorder != NULL );
3732  assert( nvarslexorder != NULL );
3733  assert( maxnvarslexorder != NULL );
3734 
3735  *success = FALSE;
3736 
3737  /* create hashset to mark variables */
3738  SCIP_CALL( SCIPallocClearBufferArray(scip, &activevars, propdata->npermvars) );
3739 
3740  /* orbitope matrix for indices of variables in permvars array */
3741  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, nrows) );
3742  for (k = 0; k < nrows; ++k)
3743  {
3744  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[k], ncols) ); /*lint !e866*/
3745  }
3746 
3747  /* order of columns of orbitopevaridx */
3748  SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, ncols) );
3749  for (k = 0; k < ncols; ++k)
3750  columnorder[k] = ncols + 1;
3751 
3752  /* count how often an element was used in the potential orbitope */
3753  SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, propdata->npermvars) );
3754 
3755  /* mark variables in this subgroup orbitope */
3756  for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1]; ++k)
3757  {
3758  SCIP_VAR* firstvar;
3759  int compstart;
3760  int l;
3761 
3762  compstart = graphcompbegins[k];
3763  firstvar = propdata->permvars[graphcomponents[compstart]];
3764 
3765  if ( ! SCIPvarIsBinary(firstvar) )
3766  continue;
3767 
3768  for (l = 0; l < ncols; ++l)
3769  {
3770  int varidx;
3771 
3772  varidx = graphcomponents[compstart + l];
3773  assert( ! activevars[varidx] );
3774 
3775  activevars[varidx] = TRUE;
3776  ++nactivevars;
3777  }
3778  }
3779  assert( nactivevars == nrows * ncols );
3780 
3781  /* build the variable index matrix for the orbitope
3782  *
3783  * It is possible that we find an orbitope, but not using all possible columns. For example
3784  * (1,2), (2,3), (3,4), (3,5) defines the symmetric group on {1,2,3,4,5}, but the generators
3785  * we expect in our construction need shape (1,2), (2,3), (3,4), (4,5). For this reason,
3786  * we need to store how many columns have been generated.
3787  *
3788  * @todo ensure compatibility with more general generators
3789  */
3790  SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, propdata->permvars, propdata->npermvars,
3791  propdata->perms, usedperms, nrows, nusedperms, orbitopevaridx, columnorder,
3792  nusedelems, &ngencols, NULL, &isorbitope, activevars) );
3793 
3794  /* it might happen that we cannot detect the orbitope if it is generated by permutations with different
3795  * number of 2-cycles.
3796  */
3797  if ( ! isorbitope )
3798  {
3799  SCIPfreeBufferArray(scip, &nusedelems);
3800  SCIPfreeBufferArray(scip, &columnorder);
3801  SCIPfreeBufferArray(scip, &orbitopevaridx);
3802  SCIPfreeBufferArray(scip, &activevars);
3803 
3804  return SCIP_OKAY;
3805  }
3806 
3807  /* There are three possibilities for the structure of columnorder:
3808  * 1) [0, 1, -1, -1, ..., -1]
3809  * 2) [0, 1, 1, 1, ..., 1]
3810  * 3) [0, 1, -1, -1, ...., -1, 1, 1, ..., 1]
3811  *
3812  * The '1'-columns will be added to the matrix first and in the last 2
3813  * cases the method starts from the right. So to store the variable index
3814  * that will be in the upper-left corner, we need either the entryin the
3815  * second column (case 1) or the entry in the last column (cases 2 and 3).
3816  */
3817  if ( firstvaridx != NULL )
3818  {
3819  if ( columnorder[ngencols-1] > -1 )
3820  *firstvaridx = orbitopevaridx[0][ngencols-1];
3821  else
3822  *firstvaridx = orbitopevaridx[0][1];
3823  }
3824 
3825  /* find corresponding graphcomponent of first variable (needed for weak sbcs) */
3826  if ( compidxfirstrow != NULL && firstvaridx != NULL )
3827  {
3828  *compidxfirstrow = -1;
3829 
3830  for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1] && (*compidxfirstrow) < 0; ++k)
3831  {
3832  SCIP_VAR* firstvar;
3833  int compstart;
3834  int l;
3835 
3836  compstart = graphcompbegins[k];
3837  firstvar = propdata->permvars[graphcomponents[compstart]];
3838 
3839  if ( ! SCIPvarIsBinary(firstvar) )
3840  continue;
3841 
3842  /* iterate over all columns (elements in orbit), because we cannot see from ngencols which columns
3843  * have been left out
3844  */
3845  for (l = 0; l < ncols; ++l)
3846  {
3847  if ( graphcomponents[compstart + l] == *firstvaridx )
3848  {
3849  *compidxfirstrow = k;
3850  break;
3851  }
3852  }
3853  }
3854  assert( *compidxfirstrow > -1 );
3855  }
3856 
3857  /* prepare orbitope variable matrix */
3858  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix, nrows) );
3859  for (k = 0; k < nrows; ++k)
3860  {
3861  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix[k], ngencols) );
3862  }
3863 
3864  /* build the matrix containing the actual variables of the orbitope */
3865  SCIP_CALL( SCIPgenerateOrbitopeVarsMatrix(scip, &orbitopevarmatrix, nrows, ngencols,
3866  propdata->permvars, propdata->npermvars, orbitopevaridx, columnorder,
3867  nusedelems, NULL, &infeasible, TRUE, lexorder, nvarslexorder, maxnvarslexorder) );
3868 
3869  assert( ! infeasible );
3870  assert( firstvaridx == NULL || propdata->permvars[*firstvaridx] == orbitopevarmatrix[0][0] );
3871 
3872  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "suborbitope_%d_%d", graphcoloridx, propdata->norbitopes);
3873 
3874  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopevarmatrix,
3875  SCIP_ORBITOPETYPE_FULL, nrows, ngencols, FALSE, mayinteract, FALSE, FALSE, propdata->conssaddlp,
3876  TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3877 
3878  SCIP_CALL( SCIPaddCons(scip, cons) );
3879  *success = TRUE;
3880 
3881  /* do not release constraint here - will be done later */
3882  propdata->genorbconss[propdata->ngenorbconss++] = cons;
3883  ++propdata->norbitopes;
3884 
3885  for (k = nrows - 1; k >= 0; --k)
3886  SCIPfreeBufferArray(scip, &orbitopevarmatrix[k]);
3887  SCIPfreeBufferArray(scip, &orbitopevarmatrix);
3888  SCIPfreeBufferArray(scip, &nusedelems);
3889  SCIPfreeBufferArray(scip, &columnorder);
3890  for (k = nrows - 1; k >= 0; --k)
3891  SCIPfreeBufferArray(scip, &orbitopevaridx[k]);
3892  SCIPfreeBufferArray(scip, &orbitopevaridx);
3893  SCIPfreeBufferArray(scip, &activevars);
3894 
3895  return SCIP_OKAY;
3896 }
3897 
3898 /** adds strong SBCs for a suitable color of the subgroup graph */
3899 static
3901  SCIP* scip, /**< SCIP instance */
3902  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3903  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3904  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3905  int graphcompidx, /**< index of the graph component */
3906  SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3907  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3908  int* nvarsorder, /**< number of variables in lexicographic order */
3909  int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3910  )
3911 {
3912  int k;
3913 
3914  assert( scip != NULL );
3915  assert( propdata != NULL );
3916  assert( graphcompbegins != NULL );
3917  assert( graphcomponents != NULL );
3918  assert( graphcompidx >= 0 );
3919  assert( ! storelexorder || lexorder != NULL );
3920  assert( ! storelexorder || nvarsorder != NULL );
3921  assert( ! storelexorder || maxnvarsorder != NULL );
3922 
3923  /* possibly store lexicographic order defined by strong SBCs */
3924  if ( storelexorder )
3925  {
3926  if ( *maxnvarsorder == 0 )
3927  {
3928  *maxnvarsorder = graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx + 1];
3929  *nvarsorder = 0;
3930 
3931  SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
3932  }
3933  else
3934  {
3935  assert( *nvarsorder == *maxnvarsorder );
3936 
3937  *maxnvarsorder += graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx + 1];
3938 
3939  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
3940  }
3941 
3942  (*lexorder)[*nvarsorder++] = graphcomponents[graphcompbegins[graphcompidx]];
3943  }
3944 
3945  /* add strong SBCs (lex-max order) for chosen graph component */
3946  for (k = graphcompbegins[graphcompidx]+1; k < graphcompbegins[graphcompidx+1]; ++k)
3947  {
3948  char name[SCIP_MAXSTRLEN];
3949  SCIP_CONS* cons;
3950  SCIP_VAR* vars[2];
3951  SCIP_Real vals[2] = {1, -1};
3952 
3953  vars[0] = propdata->permvars[graphcomponents[k-1]];
3954  vars[1] = propdata->permvars[graphcomponents[k]];
3955 
3956  if ( storelexorder )
3957  (*lexorder)[*nvarsorder++] = graphcomponents[k];
3958 
3959  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "strong_sbcs_%s_%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3960 
3961  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3962  SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3963  FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3964 
3965  SCIP_CALL( SCIPaddCons(scip, cons) );
3966 
3967 #ifdef SCIP_MORE_DEBUG
3968  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3969  SCIPinfoMessage(scip, NULL, "\n");
3970 #endif
3971 
3972  /* check whether we need to resize */
3973  if ( propdata->ngenlinconss >= propdata->genlinconsssize )
3974  {
3975  int newsize;
3976 
3977  newsize = SCIPcalcMemGrowSize(scip, propdata->ngenlinconss + 1);
3978  assert( newsize > propdata->ngenlinconss );
3979 
3980  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize, newsize) );
3981 
3982  propdata->genlinconsssize = newsize;
3983  }
3984 
3985  propdata->genlinconss[propdata->ngenlinconss] = cons;
3986  ++propdata->ngenlinconss;
3987  }
3988 
3989  return SCIP_OKAY;
3990 }
3991 
3992 /** adds weak SBCs for a suitable color of the subgroup graph */
3993 static
3995  SCIP* scip, /**< SCIP instance */
3996  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3997  int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3998  int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3999  int* graphcomponents, /**< array of all variable indices sorted by color and comp */
4000  int ncompcolors, /**< number of colors in the graph */
4001  int* chosencomppercolor, /**< array indicating which comp was handled per color */
4002  int* firstvaridxpercolor,/**< array indicating the largest variable per color */
4003  int symgrpcompidx, /**< index of the component of the symmetry group */
4004  int* naddedconss, /**< buffer to store the number of added constraints */
4005  SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
4006  int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
4007  int* nvarsorder, /**< number of variables in lexicographic order */
4008  int* maxnvarsorder /**< maximum number of variables in lexicographic order */
4009  )
4010 { /*lint --e{571}*/
4011  SCIP_HASHMAP* varsinlexorder;
4012  SCIP_Shortbool* usedvars;
4013  SCIP_VAR* vars[2];
4014  SCIP_Real vals[2] = {1, -1};
4015  SCIP_Shortbool* varfound;
4016  int* orbit[2];
4017  int orbitsize[2] = {1, 1};
4018  int activeorb = 0;
4019  int chosencolor = -1;
4020  int j;
4021  int k;
4022 
4023  assert( scip != NULL );
4024  assert( propdata != NULL );
4025  assert( compcolorbegins != NULL );
4026  assert( graphcompbegins != NULL );
4027  assert( graphcomponents != NULL );
4028  assert( firstvaridxpercolor != NULL );
4029  assert( chosencomppercolor != NULL );
4030  assert( naddedconss != NULL );
4031  assert( symgrpcompidx >= 0 );
4032  assert( symgrpcompidx < propdata->ncomponents );
4033  assert( ! storelexorder || lexorder != NULL );
4034  assert( ! storelexorder || nvarsorder != NULL );
4035  assert( ! storelexorder || maxnvarsorder != NULL );
4036 
4037  *naddedconss = 0;
4038 
4039  SCIP_CALL( SCIPallocCleanBufferArray(scip, &usedvars, propdata->npermvars) );
4040  SCIP_CALL( SCIPallocClearBufferArray(scip, &varfound, propdata->npermvars) );
4041  SCIP_CALL( SCIPallocBufferArray(scip, &orbit[0], propdata->npermvars) );
4042  SCIP_CALL( SCIPallocBufferArray(scip, &orbit[1], propdata->npermvars) );
4043 
4044  /* Store the entries in lexorder in a hashmap, for fast lookups. */
4045  if ( lexorder == NULL || *lexorder == NULL )
4046  {
4047  /* Lexorder does not exist, so do not create hashmap. */
4048  varsinlexorder = NULL;
4049  }
4050  else
4051  {
4052  assert( *maxnvarsorder >= 0 );
4053  assert( *nvarsorder >= 0 );
4054 
4055  SCIP_CALL( SCIPhashmapCreate(&varsinlexorder, SCIPblkmem(scip), *maxnvarsorder) );
4056 
4057  for (k = 0; k < *nvarsorder; ++k)
4058  {
4059  /* add element from lexorder to hashmap.
4060  * Use insert, as duplicate entries in lexorder is not permitted. */
4061  assert( ! SCIPhashmapExists(varsinlexorder, (void*) (long) (*lexorder)[k]) ); /* Use int as pointer */
4062  SCIP_CALL( SCIPhashmapInsertInt(varsinlexorder, (void*) (long) (*lexorder)[k], k) );
4063  }
4064  }
4065 
4066  /* We will store the newest and the largest orbit and activeorb will be used to mark at which entry of the array
4067  * orbit the newly computed one will be stored. */
4068  for (j = 0; j < ncompcolors; ++j)
4069  {
4070  int graphcomp;
4071  int graphcompsize;
4072  int varidx;
4073 
4074  /* skip color for which we did not add anything */
4075  if ( chosencomppercolor[j] < 0 )
4076  continue;
4077 
4078  assert( firstvaridxpercolor[j] >= 0 );
4079 
4080  graphcomp = chosencomppercolor[j];
4081  graphcompsize = graphcompbegins[graphcomp+1] - graphcompbegins[graphcomp];
4082  varidx = firstvaridxpercolor[j];
4083 
4084  /* if the first variable was already contained in another orbit or if there are no variables left anyway, skip the
4085  * component */
4086  if ( varfound[varidx] || graphcompsize == propdata->npermvars )
4087  continue;
4088 
4089  /* If varidx is in lexorder, then it must be the first entry of lexorder. */
4090  if ( varsinlexorder != NULL
4091  && SCIPhashmapExists(varsinlexorder, (void*) (long) varidx)
4092  && lexorder != NULL && *lexorder != NULL && *maxnvarsorder > 0 && *nvarsorder > 0
4093  && (*lexorder)[0] != varidx )
4094  continue;
4095 
4096  /* mark all variables that have been used in strong SBCs */
4097  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
4098  {
4099  assert( 0 <= graphcomponents[k] && graphcomponents[k] < propdata->npermvars );
4100 
4101  usedvars[graphcomponents[k]] = TRUE;
4102  }
4103 
4104  SCIP_CALL( SCIPcomputeOrbitVar(scip, propdata->npermvars, propdata->perms,
4105  propdata->permstrans, propdata->components, propdata->componentbegins,
4106  usedvars, varfound, varidx, symgrpcompidx,
4107  orbit[activeorb], &orbitsize[activeorb]) );
4108 
4109  assert( orbit[activeorb][0] == varidx );
4110 
4111  if ( orbitsize[activeorb] > orbitsize[1 - activeorb] ) /*lint !e514*/
4112  {
4113  /* if the new orbit is larger then the old largest one, flip activeorb */
4114  activeorb = 1 - activeorb;
4115  chosencolor = j;
4116  }
4117 
4118  /* reset array */
4119  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
4120  usedvars[graphcomponents[k]] = FALSE;
4121  }
4122 
4123  /* check if we have found at least one non-empty orbit */
4124  if ( chosencolor > -1 )
4125  {
4126  /* flip activeorb again to avoid confusion, it is then at the largest orbit */
4127  activeorb = 1 - activeorb;
4128 
4129  assert( orbit[activeorb][0] == firstvaridxpercolor[chosencolor] );
4130  vars[0] = propdata->permvars[orbit[activeorb][0]];
4131 
4132  assert( chosencolor > -1 );
4133  SCIPdebugMsg(scip, " adding %d weak sbcs for enclosing orbit of color %d.\n", orbitsize[activeorb]-1, chosencolor);
4134 
4135  *naddedconss = orbitsize[activeorb] - 1;
4136 
4137  /* add weak SBCs for rest of enclosing orbit */
4138  for (j = 1; j < orbitsize[activeorb]; ++j)
4139  {
4140  SCIP_CONS* cons;
4141  char name[SCIP_MAXSTRLEN];
4142 
4143  vars[1] = propdata->permvars[orbit[activeorb][j]];
4144 
4145  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "weak_sbcs_%d_%s_%s", symgrpcompidx, SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
4146 
4147  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
4148  SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
4149  FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4150 
4151  SCIP_CALL( SCIPaddCons(scip, cons) );
4152 
4153 #ifdef SCIP_MORE_DEBUG
4154  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
4155  SCIPinfoMessage(scip, NULL, "\n");
4156 #endif
4157 
4158  /* check whether we need to resize */
4159  if ( propdata->ngenlinconss >= propdata->genlinconsssize )
4160  {
4161  int newsize;
4162 
4163  newsize = SCIPcalcMemGrowSize(scip, propdata->ngenlinconss + 1);
4164  assert( newsize > propdata->ngenlinconss );
4165 
4166  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize, newsize) );
4167 
4168  propdata->genlinconsssize = newsize;
4169  }
4170 
4171  propdata->genlinconss[propdata->ngenlinconss] = cons;
4172  ++propdata->ngenlinconss;
4173  }
4174 
4175  /* possibly store lexicographic order defined by weak SBCs */
4176  if ( storelexorder )
4177  {
4178  int varidx;
4179 
4180  varidx = orbit[activeorb][0];
4181 
4182  if ( *maxnvarsorder == 0 )
4183  {
4184  *maxnvarsorder = 1;
4185  *nvarsorder = 0;
4186 
4187  SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
4188  (*lexorder)[(*nvarsorder)++] = varidx;
4189  }
4190  else
4191  {
4192  assert( *nvarsorder == *maxnvarsorder );
4193  assert( varsinlexorder != NULL );
4194  assert( lexorder != NULL );
4195  assert( *lexorder != NULL );
4196 
4197  /* the leader of the weak inequalities has to be the first element in the lexicographic order */
4198  if ( varidx == (*lexorder)[0] )
4199  {
4200  /* lexorder is already ok!! */
4201  assert( SCIPhashmapExists(varsinlexorder, (void*) (long) varidx) );
4202  }
4203  else
4204  {
4205  /* Then varidx must not be in the lexorder,
4206  * We must add it at the front of the array, and maintain the current order. */
4207  assert( ! SCIPhashmapExists(varsinlexorder, (void*) (long) varidx) );
4208 
4209  ++(*maxnvarsorder);
4210  ++(*nvarsorder);
4211 
4212  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
4213 
4214  /* Shift array by one position to the right */
4215  for (k = *maxnvarsorder - 1; k >= 1; --k)
4216  (*lexorder)[k] = (*lexorder)[k - 1];
4217 
4218  (*lexorder)[0] = varidx;
4219  }
4220  }
4221  }
4222  }
4223  else
4224  SCIPdebugMsg(scip, " no further weak sbcs are valid\n");
4225 
4226  SCIPfreeBufferArray(scip, &orbit[1]);
4227  SCIPfreeBufferArray(scip, &orbit[0]);
4228  if ( varsinlexorder != NULL )
4229  SCIPhashmapFree(&varsinlexorder);
4230  SCIPfreeBufferArray(scip, &varfound);
4231  SCIPfreeCleanBufferArray(scip, &usedvars);
4232 
4233  return SCIP_OKAY;
4234 }
4235 
4236 
4237 /** temporarily adapt symmetry data to new variable order given by Schreier Sims */
4238 static
4240  SCIP* scip, /**< SCIP instance */
4241  int** origperms, /**< permutation matrix w.r.t. original variable ordering */
4242  int** modifiedperms, /**< memory for permutation matrix w.r.t. new variable ordering */
4243  int nperms, /**< number of permutations */
4244  SCIP_VAR** origpermvars, /**< array of permutation vars w.r.t. original variable ordering */
4245  SCIP_VAR** modifiedpermvars, /**< memory for array of permutation vars w.r.t. new variable ordering */
4246  int npermvars, /**< length or modifiedpermvars array */
4247  int* leaders, /**< leaders of Schreier Sims constraints */
4248  int nleaders /**< number of leaders */
4249  )
4250 {
4251  int* permvaridx;
4252  int* posinpermvar;
4253  int leader;
4254  int curposleader;
4255  int varidx;
4256  int lidx;
4257  int i;
4258  int l;
4259  int p;
4260 
4261  assert( scip != NULL );
4262  assert( origperms != NULL );
4263  assert( modifiedperms != NULL );
4264  assert( nperms > 0 );
4265  assert( origpermvars != NULL );
4266  assert( modifiedpermvars != NULL );
4267  assert( npermvars > 0 );
4268  assert( leaders != NULL );
4269  assert( nleaders > 0 );
4270 
4271  /* initialize map from position in lexicographic order to index of original permvar */
4272  SCIP_CALL( SCIPallocBufferArray(scip, &permvaridx, npermvars) );
4273  for (i = 0; i < npermvars; ++i)
4274  permvaridx[i] = i;
4275 
4276  /* initialize map from permvaridx to its current position in the reordered permvars array */
4277  SCIP_CALL( SCIPallocBufferArray(scip, &posinpermvar, npermvars) );
4278  for (i = 0; i < npermvars; ++i)
4279  posinpermvar[i] = i;
4280 
4281  /* Iterate over leaders and put the l-th leader to the l-th position of the lexicographic order.
4282  * We do this by swapping the l-th leader with the element at position l of the current permvars array. */
4283  for (l = 0; l < nleaders; ++l)
4284  {
4285  leader = leaders[l];
4286  curposleader = posinpermvar[leader];
4287  varidx = permvaridx[curposleader];
4288  lidx = permvaridx[l];
4289 
4290  /* swap the permvar at position l with the l-th leader */
4291  permvaridx[curposleader] = lidx;
4292  permvaridx[l] = varidx;
4293 
4294  /* update the position map */
4295  posinpermvar[lidx] = curposleader;
4296  posinpermvar[leader] = l;
4297  }
4298 
4299  /* update the permvars array to new variable order */
4300  for (i = 0; i < npermvars; ++i)
4301  modifiedpermvars[i] = origpermvars[permvaridx[i]];
4302 
4303  /* update the permutation to the new variable order */
4304  for (p = 0; p < nperms; ++p)
4305  {
4306  for (i = 0; i < npermvars; ++i)
4307  modifiedperms[p][i] = posinpermvar[origperms[p][permvaridx[i]]];
4308  }
4309 
4310  SCIPfreeBufferArray(scip, &permvaridx);
4311  SCIPfreeBufferArray(scip, &posinpermvar);
4312 
4313  return SCIP_OKAY;
4314 }
4315 
4316 
4317 /* returns the number of found orbitopes with at least three columns per graph component or 0
4318  * if the found orbitopes do not satisfy certain criteria for being used
4319  */
4320 static
4322  SCIP_VAR** permvars, /**< array of variables affected by symmetry */
4323  int* graphcomponents, /**< array of graph components */
4324  int* graphcompbegins, /**< array indicating starting position of graph components */
4325  int* compcolorbegins, /**< array indicating starting positions of potential orbitopes */
4326  int ncompcolors, /**< number of components encoded in compcolorbegins */
4327  int symcompsize /**< size of symmetry component for that we detect suborbitopes */
4328  )
4329 {
4330  SCIP_Bool oneorbitopecriterion = FALSE;
4331  SCIP_Bool multorbitopecriterion = FALSE;
4332  int norbitopes = 0;
4333  int j;
4334 
4335  assert( graphcompbegins != NULL );
4336  assert( compcolorbegins != NULL );
4337  assert( ncompcolors >= 0 );
4338  assert( symcompsize > 0 );
4339 
4340  for (j = 0; j < ncompcolors; ++j)
4341  {
4342  SCIP_VAR* firstvar;
4343  int largestcompsize = 0;
4344  int nbinrows= 0;
4345  int k;
4346 
4347  /* skip trivial components */
4348  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
4349  continue;
4350 
4351  /* check whether components of this color build an orbitope (with > 2 columns) */
4352  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4353  {
4354  int compsize;
4355 
4356  compsize = graphcompbegins[k+1] - graphcompbegins[k];
4357 
4358  /* the first component that we are looking at for this color */
4359  if ( largestcompsize < 1 )
4360  {
4361  if ( compsize < 3 )
4362  break;
4363 
4364  largestcompsize = compsize;
4365  }
4366  else if ( compsize != largestcompsize )
4367  break;
4368 
4369  firstvar = permvars[graphcomponents[graphcompbegins[k]]];
4370 
4371  /* count number of binary orbits (comps) */
4372  if ( SCIPvarIsBinary(firstvar) )
4373  ++nbinrows;
4374  }
4375 
4376  /* we have found an orbitope */
4377  if ( k == compcolorbegins[j+1] )
4378  {
4379  SCIP_Real threshold;
4380  int ncols;
4381 
4382  ++norbitopes;
4383  ncols = graphcompbegins[compcolorbegins[j] + 1] - graphcompbegins[compcolorbegins[j]];
4384 
4385  threshold = 0.7 * (SCIP_Real) symcompsize;
4386 
4387  /* check whether criteria for adding orbitopes are satisfied */
4388  if ( nbinrows <= 2 * ncols || (nbinrows <= 8 * ncols && nbinrows < 100) )
4389  multorbitopecriterion = TRUE;
4390  else if ( nbinrows <= 3 * ncols || (SCIP_Real) nbinrows * ncols >= threshold )
4391  oneorbitopecriterion = TRUE;
4392  }
4393  }
4394 
4395  if ( (norbitopes == 1 && oneorbitopecriterion) || (norbitopes >= 2 && multorbitopecriterion) )
4396  return norbitopes;
4397 
4398  return 0;
4399 }
4400 
4401 
4402 /** checks whether subgroups of the components are symmetric groups and adds SBCs for them */
4403 static
4405  SCIP* scip, /**< SCIP instance */
4406  SCIP_PROPDATA* propdata /**< pointer to data of symmetry propagator */
4407  )
4408 {
4409  int* genorder;
4410  int i;
4411 #ifdef SCIP_DEBUG
4412  int norbitopes = 0;
4413  int nstrongsbcs = 0;
4414  int nweaksbcs = 0;
4415 #endif
4416  int** modifiedperms;
4417  SCIP_VAR** modifiedpermvars;
4418  int* nvarsincomponent;
4419 
4420  assert( scip != NULL );
4421  assert( propdata != NULL );
4422  assert( propdata->computedsymmetry );
4423  assert( propdata->components != NULL );
4424  assert( propdata->componentbegins != NULL );
4425  assert( propdata->ncomponents > 0 );
4426  assert( propdata->nperms >= 0 );
4427 
4428  /* exit if no symmetry is present */
4429  if ( propdata->nperms == 0 )
4430  return SCIP_OKAY;
4431 
4432  /* exit if instance is too large */
4433  if ( SCIPgetNConss(scip) > propdata->maxnconsssubgroup )
4434  return SCIP_OKAY;
4435 
4436  assert( propdata->nperms > 0 );
4437  assert( propdata->perms != NULL );
4438  assert( propdata->npermvars > 0 );
4439  assert( propdata->permvars != NULL );
4440 
4441  /* create array for permutation order */
4442  SCIP_CALL( SCIPallocBufferArray(scip, &genorder, propdata->nperms) );
4443 
4444  /* create arrays for modified permutations in case we adapt the lexicographic order because of suborbitopes */
4445  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, propdata->nperms) );
4446  for (i = 0; i < propdata->nperms; ++i)
4447  {
4448  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[i], propdata->npermvars) );
4449  }
4450  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, propdata->npermvars) );
4451 
4452  SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarsincomponent, propdata->npermvars) );
4453  for (i = 0; i < propdata->npermvars; ++i)
4454  {
4455  if ( propdata->vartocomponent[i] >= 0 )
4456  ++nvarsincomponent[propdata->vartocomponent[i]];
4457  }
4458 
4459  SCIPdebugMsg(scip, "starting subgroup detection routine for %d components\n", propdata->ncomponents);
4460 
4461  /* iterate over components */
4462  for (i = 0; i < propdata->ncomponents; ++i)
4463  {
4464  int* graphcomponents;
4465  int* graphcompbegins;
4466  int* compcolorbegins;
4467  int* chosencomppercolor = NULL;
4468  int* firstvaridxpercolor = NULL;
4469  int* usedperms;
4470  int usedpermssize;
4471  int ngraphcomponents;
4472  int ncompcolors;
4473  int ntwocycleperms;
4474  int npermsincomp;
4475  int nusedperms;
4476  int ntrivialcolors = 0;
4477  int j;
4478  int* lexorder = NULL;
4479  int nvarslexorder = 0;
4480  int maxnvarslexorder = 0;
4481  SCIP_Shortbool* permused;
4482  SCIP_Bool allpermsused = FALSE;
4483  SCIP_Bool handlednonbinarysymmetry = FALSE;
4484  int norbitopesincomp;
4485 
4486  /* if component is blocked, skip it */
4487  if ( propdata->componentblocked[i] )
4488  {
4489  SCIPdebugMsg(scip, "component %d has already been handled and will be skipped\n", i);
4490  continue;
4491  }
4492 
4493  npermsincomp = propdata->componentbegins[i + 1] - propdata->componentbegins[i];
4494 
4495  /* set the first npermsincomp entries of genorder; the others are not used for this component */
4496  for (j = 0; j < npermsincomp; ++j)
4497  genorder[j] = j;
4498 
4499  SCIP_CALL( chooseOrderOfGenerators(scip, propdata, i, &genorder, &ntwocycleperms) );
4500 
4501  assert( ntwocycleperms >= 0 );
4502  assert( ntwocycleperms <= npermsincomp );
4503 
4504  SCIPdebugMsg(scip, "component %d has %d permutations consisting of 2-cycles\n", i, ntwocycleperms);
4505 
4506 #ifdef SCIP_MORE_DEBUG
4507  SCIP_Bool* used;
4508  int perm;
4509  int p;
4510  int k;
4511 
4512  SCIP_CALL( SCIPallocBufferArray(scip, &used, propdata->npermvars) );
4513  for (p = propdata->componentbegins[i]; p < propdata->componentbegins[i+1]; ++p)
4514  {
4515  perm = propdata->components[p];
4516 
4517  for (k = 0; k < propdata->npermvars; ++k)
4518  used[k] = FALSE;
4519 
4520  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "permutation %d\n", perm);
4521 
4522  for (k = 0; k < propdata->npermvars; ++k)
4523  {
4524  if ( used[k] )
4525  continue;
4526 
4527  j = propdata->perms[perm][k];
4528 
4529  if ( k == j )
4530  continue;
4531 
4532  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "(%s,", SCIPvarGetName(propdata->permvars[k]));
4533  used[k] = TRUE;
4534  while (j != k)
4535  {
4536  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%s,", SCIPvarGetName(propdata->permvars[j]));
4537  used[j] = TRUE;
4538 
4539  j = propdata->perms[perm][j];
4540  }
4542  }
4544  }
4545 
4546  SCIPfreeBufferArray(scip, &used);
4547 #endif
4548 
4549  if ( ntwocycleperms < 2 )
4550  {
4551  SCIPdebugMsg(scip, " --> skip\n");
4552  continue;
4553  }
4554 
4555  usedpermssize = ntwocycleperms / 2;
4556  SCIP_CALL( SCIPallocBufferArray(scip, &usedperms, usedpermssize) );
4557  SCIP_CALL( SCIPallocClearBufferArray(scip, &permused, npermsincomp) );
4558 
4559  SCIP_CALL( buildSubgroupGraph(scip, propdata, genorder, ntwocycleperms, i,
4560  &graphcomponents, &graphcompbegins, &compcolorbegins, &ngraphcomponents,
4561  &ncompcolors, &usedperms, &nusedperms, usedpermssize, permused) );
4562 
4563  SCIPdebugMsg(scip, " created subgroup detection graph using %d of the permutations\n", nusedperms);
4564 
4565  if ( nusedperms == npermsincomp )
4566  allpermsused = TRUE;
4567 
4568  assert( graphcomponents != NULL );
4569  assert( graphcompbegins != NULL );
4570  assert( compcolorbegins != NULL );
4571  assert( ngraphcomponents > 0 );
4572  assert( ncompcolors > 0 );
4573  assert( nusedperms <= ntwocycleperms );
4574  assert( ncompcolors < propdata->npermvars );
4575 
4576  if ( nusedperms == 0 )
4577  {
4578  SCIPdebugMsg(scip, " -> skipping component, since less no permutation was used\n");
4579 
4580  SCIPfreeBufferArray(scip, &permused);
4581  SCIPfreeBufferArray(scip, &usedperms);
4582 
4583  continue;
4584  }
4585 
4586  SCIPdebugMsg(scip, " number of different colors in the graph: %d\n", ncompcolors);
4587 
4588  if ( propdata->addstrongsbcs || propdata->addweaksbcs )
4589  {
4590  SCIP_CALL( SCIPallocBufferArray(scip, &chosencomppercolor, ncompcolors) );
4591  SCIP_CALL( SCIPallocBufferArray(scip, &firstvaridxpercolor, ncompcolors) );
4592 
4593  /* Initialize the arrays with -1 to encode that we have not added orbitopes/strong SBCs
4594  * yet. In case we do not modify this entry, no weak inequalities are added based on
4595  * this component.
4596  */
4597  for (j = 0; j < ncompcolors; ++j)
4598  {
4599  chosencomppercolor[j] = -1;
4600  firstvaridxpercolor[j] = -1;
4601  }
4602  }
4603 
4604  norbitopesincomp = getNOrbitopesInComp(propdata->permvars, graphcomponents, graphcompbegins, compcolorbegins,
4605  ncompcolors, nvarsincomponent[i]);
4606 
4607  /* if there is just one orbitope satisfying the requirements, handle the full component by symresacks */
4608  if ( norbitopesincomp == 1 )
4609  {
4610  int k;
4611 
4612  for (k = 0; k < npermsincomp; ++k)
4613  {
4614  SCIP_CONS* cons;
4615  char name[SCIP_MAXSTRLEN];
4616 
4617  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4618 
4619  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, propdata->perms[propdata->components[propdata->componentbegins[i] + k]],
4620  propdata->permvars, propdata->npermvars, FALSE,
4621  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4622  SCIP_CALL( SCIPaddCons(scip, cons));
4623 
4624  /* do not release constraint here - will be done later */
4625  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4626  ++propdata->nsymresacks;
4627 
4628  if ( ! propdata->componentblocked[i] )
4629  {
4630  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4631  ++propdata->ncompblocked;
4632  }
4633 
4634  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d\n", k, i);
4635  }
4636 
4637  goto FREELOOPMEMORY;
4638  }
4639 
4640  for (j = 0; j < ncompcolors; ++j)
4641  {
4642  int nbinarycomps = 0;
4643  int largestcolorcomp = -1;
4644  int largestcompsize = 0;
4645  int k;
4646  SCIP_Bool isorbitope = TRUE;
4647  SCIP_Bool orbitopeadded = FALSE;
4648  SCIP_Bool useorbitope;
4649 #ifdef SCIP_DEBUG
4650  SCIP_Bool binaffected = FALSE;
4651  SCIP_Bool intaffected = FALSE;
4652  SCIP_Bool contaffected = FALSE;
4653 #endif
4654 
4655  /* skip trivial components */
4656  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
4657  {
4658  if( chosencomppercolor != NULL )
4659  chosencomppercolor[j] = -1;
4660 
4661  ++ntrivialcolors;
4662  continue;
4663  }
4664 
4665  SCIPdebugMsg(scip, " color %d has %d components with overall %d variables\n", j, compcolorbegins[j+1] - compcolorbegins[j],
4666  graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]]);
4667 
4668  /* check whether components of this color might build an orbitope (with > 2 columns) */
4669  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4670  {
4671  SCIP_VAR* firstvar;
4672  int compsize;
4673 
4674  compsize = graphcompbegins[k+1] - graphcompbegins[k];
4675 
4676  /* the first component that we are looking at for this color */
4677  if ( largestcompsize < 1 )
4678  {
4679  if ( compsize < 3 )
4680  {
4681  isorbitope = FALSE;
4682  break;
4683  }
4684 
4685  largestcompsize = compsize;
4686  largestcolorcomp = k;
4687  }
4688  else if ( compsize != largestcompsize )
4689  {
4690  /* variable orbits (compsize) have not the same size, cannot define orbitope */
4691  isorbitope = FALSE;
4692  break;
4693  }
4694 
4695  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4696 
4697  /* count number of binary orbits (comps) */
4698  if ( SCIPvarIsBinary(firstvar) )
4699  ++nbinarycomps;
4700  }
4701 
4702 #ifdef SCIP_DEBUG
4703  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4704  {
4705  SCIP_VAR* firstvar;
4706 
4707  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4708 
4709  if ( SCIPvarIsBinary(firstvar) )
4710  binaffected = TRUE;
4711  else if (SCIPvarIsIntegral(firstvar) )
4712  intaffected = TRUE;
4713  else
4714  contaffected = TRUE;
4715  }
4716 
4717  SCIPdebugMsg(scip, " affected types (bin,int,cont): (%d,%d,%d)\n", binaffected, intaffected, contaffected);
4718 #endif
4719 
4720  /* only use the orbitope if there are binary rows */
4721  useorbitope = FALSE;
4722  if ( norbitopesincomp > 0 && nbinarycomps > 0 )
4723  useorbitope = TRUE;
4724 
4725  if ( isorbitope && useorbitope )
4726  {
4727  int firstvaridx;
4728  int chosencomp;
4729 
4730  SCIPdebugMsg(scip, " detected an orbitope with %d rows and %d columns\n", nbinarycomps, largestcompsize);
4731 
4732  assert( nbinarycomps > 0 );
4733  assert( largestcompsize > 2 );
4734 
4735  /* add the orbitope constraint for this color
4736  *
4737  * It might happen that we cannot generate the orbitope matrix if the orbitope is not generated by permutations
4738  * all having the same number of 2-cycles, e.g., the orbitope generated by (1,2)(4,5), (2,3), (5,6).
4739  */
4740  SCIP_CALL( addOrbitopeSubgroup(scip, propdata, usedperms, nusedperms, compcolorbegins,
4741  graphcompbegins, graphcomponents, j, nbinarycomps, largestcompsize, &firstvaridx, &chosencomp,
4742  &lexorder, &nvarslexorder, &maxnvarslexorder, allpermsused, &orbitopeadded) );
4743 
4744  if ( orbitopeadded )
4745  {
4746  if ( propdata->addstrongsbcs || propdata->addweaksbcs )
4747  {
4748  assert( chosencomppercolor != NULL );
4749  assert( firstvaridxpercolor != NULL );
4750 
4751  /* adapt the first variable per color to be compatible with the created orbiope (upper left variable) */
4752  assert( compcolorbegins[j] <= chosencomp && chosencomp < compcolorbegins[j+1] );
4753  assert( 0 <= firstvaridx && firstvaridx < propdata->npermvars );
4754 
4755  chosencomppercolor[j] = chosencomp;
4756  firstvaridxpercolor[j] = firstvaridx;
4757  }
4758 
4759  if ( ! propdata->componentblocked[i] )
4760  {
4761  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4762  ++propdata->ncompblocked;
4763  }
4764 
4765 #ifdef SCIP_DEBUG
4766  ++norbitopes;
4767 #endif
4768  }
4769  }
4770 
4771  /* if no (useable) orbitope was found, possibly add strong SBCs */
4772  if ( propdata->addstrongsbcs && ! orbitopeadded )
4773  {
4774  assert( largestcolorcomp >= 0 );
4775  assert( largestcolorcomp < ngraphcomponents );
4776  assert( largestcompsize > 0 );
4777 
4778  if( propdata->addweaksbcs )
4779  {
4780  assert( chosencomppercolor != NULL );
4781  assert( firstvaridxpercolor != NULL );
4782 
4783  chosencomppercolor[j] = largestcolorcomp;
4784  firstvaridxpercolor[j] = graphcomponents[graphcompbegins[largestcolorcomp]];
4785  }
4786 
4787  SCIPdebugMsg(scip, " choosing component %d with %d variables and adding strong SBCs\n",
4788  largestcolorcomp, graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp]);
4789 
4790  /* add the strong SBCs for the corresponding component */
4791  SCIP_CALL( addStrongSBCsSubgroup(scip, propdata, graphcompbegins, graphcomponents, largestcolorcomp,
4792  propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4793 
4794  /* store whether symmetries on non-binary symmetries have been handled */
4795  if ( ! SCIPvarIsBinary(propdata->permvars[graphcomponents[graphcompbegins[largestcolorcomp]]]) )
4796  handlednonbinarysymmetry = TRUE;
4797 
4798  if ( ! propdata->componentblocked[i] )
4799  {
4800  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4801  ++propdata->ncompblocked;
4802  }
4803 
4804 #ifdef SCIP_DEBUG
4805  nstrongsbcs += graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp] - 1;
4806 #endif
4807  }
4808  else if ( ! orbitopeadded )
4809  {
4810  SCIPdebugMsg(scip, " no useable orbitope found and no SBCs added\n");
4811 
4812  /* mark the color as not handled */
4813  if ( propdata->addweaksbcs )
4814  {
4815  assert( chosencomppercolor != NULL );
4816  chosencomppercolor[j] = -1; /*lint !e613*/
4817  }
4818  }
4819  }
4820 
4821  SCIPdebugMsg(scip, " skipped %d trivial colors\n", ntrivialcolors);
4822 
4823  /* possibly add weak SBCs for enclosing orbit of first component */
4824  if ( propdata->addweaksbcs && propdata->componentblocked[i] && nusedperms < npermsincomp )
4825  {
4826  int naddedconss;
4827 
4828  assert( firstvaridxpercolor != NULL );
4829  assert( chosencomppercolor != NULL );
4830 
4831  SCIP_CALL( addWeakSBCsSubgroup(scip, propdata, compcolorbegins, graphcompbegins,
4832  graphcomponents, ncompcolors, chosencomppercolor, firstvaridxpercolor,
4833  i, &naddedconss, propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4834 
4835  assert( naddedconss < propdata->npermvars );
4836 
4837 #ifdef SCIP_DEBUG
4838  nweaksbcs += naddedconss;
4839 #endif
4840  }
4841  else
4842  SCIPdebugMsg(scip, " don't add weak sbcs because all generators were used or the settings forbid it\n");
4843 
4844  /* if suborbitopes or strong group actions have been found, potentially add symresacks adapted to
4845  * variable order given by lexorder if no symmetries on non-binary variables have been handled
4846  */
4847  if ( nvarslexorder > 0 && propdata->addsymresacks && ! handlednonbinarysymmetry )
4848  {
4849  int k;
4850 
4851  SCIP_CALL( adaptSymmetryDataSST(scip, propdata->perms, modifiedperms, propdata->nperms,
4852  propdata->permvars, modifiedpermvars, propdata->npermvars, lexorder, nvarslexorder) );
4853 
4854  for (k = 0; k < npermsincomp; ++k)
4855  {
4856  SCIP_CONS* cons;
4857  char name[SCIP_MAXSTRLEN];
4858 
4859  /* skip permutations that have been used to build an orbitope */
4860  if ( permused[k] )
4861  continue;
4862 
4863  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4864 
4865  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[propdata->components[propdata->componentbegins[i] + k]],
4866  modifiedpermvars, propdata->npermvars, FALSE,
4867  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4868  SCIP_CALL( SCIPaddCons(scip, cons));
4869 
4870  /* do not release constraint here - will be done later */
4871  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4872  ++propdata->nsymresacks;
4873 
4874  if ( ! propdata->componentblocked[i] )
4875  {
4876  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4877  ++propdata->ncompblocked;
4878  }
4879 
4880  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d adapted to suborbitope lexorder\n", k, i);
4881  }
4882  }
4883 
4884  FREELOOPMEMORY:
4885  SCIPfreeBlockMemoryArrayNull(scip, &lexorder, maxnvarslexorder);
4886 
4887  SCIPfreeBufferArrayNull(scip, &firstvaridxpercolor);
4888  SCIPfreeBufferArrayNull(scip, &chosencomppercolor);
4889  SCIPfreeBlockMemoryArrayNull(scip, &compcolorbegins, ncompcolors + 1);
4890  SCIPfreeBlockMemoryArrayNull(scip, &graphcompbegins, ngraphcomponents + 1);
4891  SCIPfreeBlockMemoryArrayNull(scip, &graphcomponents, propdata->npermvars);
4892  SCIPfreeBufferArrayNull(scip, &permused);
4893  SCIPfreeBufferArrayNull(scip, &usedperms);
4894  }
4895 
4896 #ifdef SCIP_DEBUG
4897  SCIPdebugMsg(scip, "total number of added (sub-)orbitopes: %d\n", norbitopes);
4898  SCIPdebugMsg(scip, "total number of added strong sbcs: %d\n", nstrongsbcs);
4899  SCIPdebugMsg(scip, "total number of added weak sbcs: %d\n", nweaksbcs);
4900 #endif
4901 
4902  SCIPfreeBufferArray(scip, &nvarsincomponent);
4903 
4904  SCIPfreeBufferArray(scip, &modifiedpermvars);
4905  for (i = propdata->nperms - 1; i >= 0; --i)
4906  {
4907  SCIPfreeBufferArray(scip, &modifiedperms[i]);
4908  }
4909  SCIPfreeBufferArray(scip, &modifiedperms);
4910  SCIPfreeBufferArray(scip, &genorder);
4911 
4912  return SCIP_OKAY;
4913 }
4914 
4915 
4916 /*
4917  * Functions for symmetry constraints
4918  */
4919 
4920 
4921 /** sorts orbitope vars matrix such that rows are sorted increasingly w.r.t. minimum variable index in row;
4922  * columns are sorted such that first row is sorted increasingly w.r.t. variable indices
4923  */
4924 static
4926  SCIP* scip, /**< SCIP instance */
4927  int** orbitopevaridx, /**< variable index matrix of orbitope */
4928  SCIP_VAR*** vars, /**< variable matrix of orbitope */
4929  int nrows, /**< number of binary rows of orbitope */
4930  int ncols /**< number of columns of orbitope */
4931  )
4932 {
4933  SCIP_VAR** sortedrow;
4934  int* colorder;
4935  int* idcs;
4936  int arrlen;
4937  int minrowidx = INT_MAX;
4938  int minrow = INT_MAX;
4939  int i;
4940  int j;
4941 
4942  assert( scip != NULL );
4943  assert( orbitopevaridx != NULL );
4944  assert( vars != NULL );
4945  assert( nrows > 0 );
4946  assert( ncols > 0 );
4947 
4948  arrlen = MAX(nrows, ncols);
4949  SCIP_CALL( SCIPallocBufferArray(scip, &idcs, arrlen) );
4950 
4951  /* detect minimum index per row */
4952  for (i = 0; i < nrows; ++i)
4953  {
4954  int idx;
4955 
4956  idcs[i] = INT_MAX;
4957 
4958  for (j = 0; j < ncols; ++j)
4959  {
4960  idx = orbitopevaridx[i][j];
4961 
4962  if ( idx < idcs[i] )
4963  idcs[i] = idx;
4964 
4965  if ( idx < minrowidx )
4966  {
4967  minrowidx = idx;
4968  minrow = i;
4969  }
4970  }
4971  }
4972 
4973  /* sort rows increasingly w.r.t. minimum variable indices */
4974  SCIPsortIntPtr(idcs, (void**) vars, nrows);
4975 
4976  /* sort columns increasingly w.r.t. variable indices of first row */
4977  SCIP_CALL( SCIPallocBufferArray(scip, &colorder, ncols) );
4978  for (j = 0; j < ncols; ++j)
4979  {
4980  idcs[j] = orbitopevaridx[minrow][j];
4981  colorder[j] = j;
4982  }
4983 
4984  /* sort columns of first row and store new column order */
4985  SCIPsortIntIntPtr(idcs, colorder, (void**) vars[0], ncols);
4986 
4987  /* adapt rows 1, ..., nrows - 1 to new column order*/
4988  SCIP_CALL( SCIPallocBufferArray(scip, &sortedrow, ncols) );
4989  for (i = 1; i < nrows; ++i)
4990  {
4991  for (j = 0; j < ncols; ++j)
4992  sortedrow[j] = vars[i][colorder[j]];
4993  for (j = 0; j < ncols; ++j)
4994  vars[i][j] = sortedrow[j];
4995  }
4996 
4997  SCIPfreeBufferArray(scip, &sortedrow);
4998  SCIPfreeBufferArray(scip, &colorder);
4999  SCIPfreeBufferArray(scip, &idcs);
5000 
5001  return SCIP_OKAY;
5002 }
5003 
5004 
5005 /** checks whether components of the symmetry group can be completely handled by orbitopes */
5006 static
5008  SCIP* scip, /**< SCIP instance */
5009  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
5010  int* components, /**< array containing components of symmetry group */
5011  int* componentbegins, /**< array containing begin positions of components in components array */
5012  int ncomponents /**< number of components */
5013  )
5014 {
5015  SCIP_VAR** permvars;
5016  int** perms;
5017  int npermvars;
5018  int i;
5019 
5020  assert( scip != NULL );
5021  assert( propdata != NULL );
5022  assert( components != NULL );
5023  assert( componentbegins != NULL );
5024  assert( ncomponents > 0 );
5025  assert( propdata->nperms >= 0 );
5026 
5027  /* exit if no symmetry is present */
5028  if ( propdata->nperms == 0 )
5029  return SCIP_OKAY;
5030 
5031  assert( propdata->nperms > 0 );
5032  assert( propdata->perms != NULL );
5033  assert( propdata->nbinpermvars >= 0 );
5034  assert( propdata->npermvars >= 0 );
5035  assert( propdata->permvars != NULL );
5036 
5037  /* exit if no symmetry on binary variables is present */
5038  if ( propdata->nbinpermvars == 0 )
5039  {
5040  assert( ! propdata->binvaraffected );
5041  return SCIP_OKAY;
5042  }
5043 
5044  perms = propdata->perms;
5045  npermvars = propdata->npermvars;
5046  permvars = propdata->permvars;
5047 
5048  /* iterate over components */
5049  for (i = 0; i < ncomponents; ++i)
5050  {
5051  SCIP_VAR*** vars;
5052  SCIP_VAR*** varsallocorder;
5053  SCIP_CONS* cons;
5054  SCIP_Shortbool* rowisbinary;
5055  SCIP_Bool isorbitope = TRUE;
5056  SCIP_Bool infeasibleorbitope;
5057  int** orbitopevaridx;
5058  int* columnorder;
5059  int npermsincomponent;
5060  int ntwocyclescomp = INT_MAX;
5061  int nbincyclescomp = INT_MAX;
5062  int* nusedelems;
5063  int j;
5064  int cnt;
5065 
5066  /* orbitopes are detected first, so no component should be blocked */
5067  assert( ! propdata->componentblocked[i] );
5068 
5069  /* get properties of permutations */
5070  npermsincomponent = componentbegins[i + 1] - componentbegins[i];
5071  assert( npermsincomponent > 0 );
5072  for (j = componentbegins[i]; j < componentbegins[i + 1]; ++j)
5073  {
5074  int ntwocyclesperm = 0;
5075  int nbincyclesperm = 0;
5076 
5077  SCIP_CALL( SCIPisInvolutionPerm(perms[components[j]], permvars, npermvars,
5078  &ntwocyclesperm, &nbincyclesperm, FALSE) );
5079 
5080  if ( ntwocyclescomp == 0 )
5081  {
5082  isorbitope = FALSE;
5083  break;
5084  }
5085 
5086  /* if we are checking the first permutation */
5087  if ( ntwocyclescomp == INT_MAX )
5088  {
5089  ntwocyclescomp = ntwocyclesperm;
5090  nbincyclescomp = nbincyclesperm;
5091 
5092  /* if there are no binary rows */
5093  if ( nbincyclescomp == 0 )
5094  {
5095  isorbitope = FALSE;
5096  break;
5097  }
5098  }
5099 
5100  /* no or different number of 2-cycles or not all vars binary: permutations cannot generate orbitope */
5101  if ( ntwocyclescomp != ntwocyclesperm || nbincyclesperm != nbincyclescomp )
5102  {
5103  isorbitope = FALSE;
5104  break;
5105  }
5106  }
5107 
5108  /* if no orbitope was detected */
5109  if ( ! isorbitope )
5110  continue;
5111  assert( ntwocyclescomp > 0 );
5112  assert( ntwocyclescomp < INT_MAX );
5113 
5114  /* iterate over permutations and check whether for each permutation there exists
5115  * another permutation whose 2-cycles intersect pairwise in exactly one element */
5116 
5117  /* orbitope matrix for indices of variables in permvars array */
5118  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, ntwocyclescomp) );
5119  for (j = 0; j < ntwocyclescomp; ++j)
5120  {
5121  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[j], npermsincomponent + 1) ); /*lint !e866*/
5122  }
5123 
5124  /* order of columns of orbitopevaridx */
5125  SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, npermsincomponent + 1) );
5126  for (j = 0; j < npermsincomponent + 1; ++j)
5127  columnorder[j] = npermsincomponent + 2;
5128 
5129  /* count how often an element was used in the potential orbitope */
5130  SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, npermvars) );
5131 
5132  /* store whether a row of the potential orbitope contains only binary variables */
5133  SCIP_CALL( SCIPallocClearBufferArray(scip, &rowisbinary, ntwocyclescomp) );
5134 
5135  /* check if the permutations fulfill properties of an orbitope */
5136  SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, permvars, npermvars, perms,
5137  &(components[componentbegins[i]]), ntwocyclescomp, npermsincomponent,
5138  orbitopevaridx, columnorder, nusedelems, NULL, rowisbinary, &isorbitope, NULL) );
5139 
5140  if ( ! isorbitope )
5141  goto FREEDATASTRUCTURES;
5142 
5143  /* we have found a potential orbitope, prepare data for orbitope conshdlr */
5144  SCIP_CALL( SCIPallocBufferArray(scip, &vars, nbincyclescomp) );
5145  SCIP_CALL( SCIPallocBufferArray(scip, &varsallocorder, nbincyclescomp) );
5146  cnt = 0;
5147  for (j = 0; j < ntwocyclescomp; ++j)
5148  {
5149  if ( ! rowisbinary[j] )
5150  continue;
5151 
5152  SCIP_CALL( SCIPallocBufferArray(scip, &vars[cnt], npermsincomponent + 1) ); /*lint !e866*/
5153  varsallocorder[cnt] = vars[cnt]; /* to ensure that we can free the buffer in reverse order */
5154  ++cnt;
5155  }
5156  assert( cnt == nbincyclescomp );
5157 
5158  /* prepare variable matrix (reorder columns of orbitopevaridx) */
5159  infeasibleorbitope = FALSE;
5160  SCIP_CALL( SCIPgenerateOrbitopeVarsMatrix(scip, &vars, ntwocyclescomp, npermsincomponent + 1, permvars,
5161  npermvars, orbitopevaridx, columnorder, nusedelems, rowisbinary, &infeasibleorbitope, FALSE, NULL, NULL, NULL) );
5162 
5163  if ( ! infeasibleorbitope )
5164  {
5165  char name[SCIP_MAXSTRLEN];
5166 
5167  SCIPdebugMsg(scip, "found an orbitope of size %d x %d in component %d\n", ntwocyclescomp, npermsincomponent + 1, i);
5168 
5169  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_component%d", i);
5170 
5171  /* to ensure same orbitope is added if different sets of generators are found */
5172  SCIP_CALL( SCIPsortOrbitope(scip, orbitopevaridx, vars, nbincyclescomp, npermsincomponent + 1) );
5173 
5174  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, vars, SCIP_ORBITOPETYPE_FULL,
5175  nbincyclescomp, npermsincomponent + 1, propdata->usedynamicprop, FALSE, FALSE, FALSE,
5176  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5177 
5178  SCIP_CALL( SCIPaddCons(scip, cons) );
5179 
5180  /* do not release constraint here - will be done later */
5181  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5182  ++propdata->norbitopes;
5183 
5184  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5185  }
5186 
5187  /* free data structures */
5188  for (j = nbincyclescomp - 1; j >= 0; --j)
5189  {
5190  SCIPfreeBufferArray(scip, &varsallocorder[j]);
5191  }
5192  SCIPfreeBufferArray(scip, &varsallocorder);
5193  SCIPfreeBufferArray(scip, &vars);
5194 
5195  FREEDATASTRUCTURES:
5196  SCIPfreeBufferArray(scip, &rowisbinary);
5197  SCIPfreeBufferArray(scip, &nusedelems);
5198  SCIPfreeBufferArray(scip, &columnorder);
5199  for (j = ntwocyclescomp - 1; j >= 0; --j)
5200  {
5201  SCIPfreeBufferArray(scip, &orbitopevaridx[j]);
5202  }
5203  SCIPfreeBufferArray(scip, &orbitopevaridx);
5204  }
5205 
5206  return SCIP_OKAY;
5207 }
5208 
5209 
5210 /** update symmetry information of conflict graph */
5211 static
5213  SCIP* scip, /**< SCIP instance */
5214  SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
5215  SCIP_VAR** graphvars, /**< variables encoded in conflict graph (either all vars or permvars) */
5216  int ngraphvars, /**< number of nodes/vars in conflict graph */
5217  SCIP_VAR** permvars, /**< variables considered in permutations */
5218  int npermvars, /**< number of permvars */
5219  SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5220  SCIP_HASHMAP* varmap, /**< map from graphvar to node label in conflict graph
5221  * (or NULL if onlypermvars == TRUE) */
5222  int* orbits, /**< array of non-trivial orbits */
5223  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
5224  int norbits /**< number of non-trivial orbits */
5225  )
5226 {
5227  int i;
5228  int j;
5229 
5230  assert( scip != NULL );
5231  assert( conflictgraph != NULL );
5232  assert( graphvars != NULL );
5233  assert( ngraphvars > 0 );
5234  assert( permvars != NULL );
5235  assert( npermvars > 0 );
5236  assert( onlypermvars || varmap != NULL );
5237  assert( orbits != NULL );
5238  assert( orbitbegins != NULL );
5239  assert( norbits >= 0 );
5240 
5241  /* initialize/reset variable information of nodes in conflict graph */
5242  for (i = 0; i < ngraphvars; ++i)
5243  {
5244  SCIP_NODEDATA* nodedata;
5245 
5246  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5247 
5248  /* possibly create node data */
5249  if ( nodedata == NULL )
5250  {
5251  SCIP_CALL( SCIPallocBlockMemory(scip, &nodedata) );
5252  nodedata->var = graphvars[i];
5253  nodedata->active = TRUE;
5254  }
5255 
5256  /* (re-)set node data */
5257  nodedata->orbitidx = -1;
5258  nodedata->nconflictinorbit = 0;
5259  nodedata->orbitsize = -1;
5260  nodedata->posinorbit = -1;
5261 
5262  /* set node data */
5263  SCIPdigraphSetNodeData(conflictgraph, (void*) nodedata, i);
5264  }
5265 
5266  /* add orbit information to nodes of conflict graph */
5267  for (j = 0; j < norbits; ++j)
5268  {
5269  int posinorbit = 0;
5270  int orbitsize;
5271 
5272  orbitsize = orbitbegins[j + 1] - orbitbegins[j];
5273  assert( orbitsize >= 0 );
5274 
5275  for (i = orbitbegins[j]; i < orbitbegins[j + 1]; ++i)
5276  {
5277  SCIP_NODEDATA* nodedata;
5278  SCIP_VAR* var;
5279  int pos;
5280 
5281  /* get variable and position in conflict graph */
5282  if ( onlypermvars )
5283  {
5284  pos = orbits[i];
5285  var = permvars[pos];
5286  }
5287  else
5288  {
5289  var = permvars[orbits[i]];
5290  assert( var != NULL );
5291 
5292  assert( SCIPhashmapExists(varmap, var) );
5293  pos = SCIPhashmapGetImageInt(varmap, var);
5294  assert( pos != INT_MAX );
5295  }
5296 
5297  /* get node data */
5298  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, pos);
5299  assert( nodedata != NULL );
5300  assert( nodedata->var == var );
5301 
5302  nodedata->orbitidx = j;
5303  nodedata->orbitsize = orbitsize;
5304  nodedata->posinorbit = posinorbit++;
5305  }
5306  }
5307 
5308  /* add information on number of conflicts within orbit to conflict graph */
5309  for (i = 0; i < ngraphvars; ++i)
5310  {
5311  SCIP_NODEDATA* nodedata;
5312  SCIP_NODEDATA* nodedataconflict;
5313  int* conflictvaridx;
5314  int nconflictinorbit = 0;
5315  int curorbit;
5316 
5317  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5318  conflictvaridx = SCIPdigraphGetSuccessors(conflictgraph, i);
5319 
5320  assert( nodedata != NULL );
5321  assert( nodedata->nconflictinorbit == 0 );
5322  assert( conflictvaridx != NULL || SCIPdigraphGetNSuccessors(conflictgraph, i) == 0 );
5323 
5324  curorbit = nodedata->orbitidx;
5325 
5326  /* i-th variable is fixed by all permutations */
5327  if ( curorbit == -1 )
5328  {
5329  nodedata->nconflictinorbit = 0;
5330  continue;
5331  }
5332 
5333  /* get conflicts in orbit by couting the active neighbors of i in the same orbit */
5334  for (j = 0; j < SCIPdigraphGetNSuccessors(conflictgraph, i); ++j)
5335  {
5336  assert( conflictvaridx != NULL );
5337  nodedataconflict = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvaridx[j]);
5338  assert( nodedataconflict != NULL );
5339 
5340  if ( nodedataconflict->active && nodedataconflict->orbitidx == curorbit )
5341  ++nconflictinorbit;
5342  }
5343 
5344  nodedata->nconflictinorbit = nconflictinorbit;
5345  }
5346 
5347  return SCIP_OKAY;
5348 }
5349 
5350 
5351 /** create conflict graph either for symmetric or for all variables
5352  *
5353  * This routine just creates the graph, but does not add (symmetry) information to its nodes.
5354  * This has to be done separately by the routine updateSymInfoConflictGraphSST().
5355  */
5356 static
5358  SCIP* scip, /**< SCIP instance */
5359  SCIP_DIGRAPH** conflictgraph, /**< pointer to store conflict graph */
5360  SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5361  int ngraphvars, /**< number of vars encoded in conflict graph */
5362  SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5363  SCIP_HASHMAP* permvarmap, /**< map of variables to indices in permvars array (or NULL) */
5364  SCIP_Bool* success /**< pointer to store whether conflict graph could be created successfully */
5365  )
5366 {
5367  SCIP_CONSHDLR* setppcconshdlr;
5368  SCIP_CONS** setppcconss;
5369  SCIP_VAR** setppcvars;
5370  SCIP_CONS* cons;
5371  int nsetppcconss;
5372  int nsetppcvars;
5373  int nodei;
5374  int nodej;
5375  int c;
5376  int i;
5377  int j;
5378  int nedges = 0;
5379 
5380  assert( scip != NULL );
5381  assert( conflictgraph != NULL );
5382  assert( graphvars != NULL );
5383  assert( ngraphvars > 0 );
5384  assert( success != NULL );
5385 
5386  *success = FALSE;
5387 
5388  /* get setppcconss for creating conflict graph */
5389  setppcconshdlr = SCIPfindConshdlr(scip, "setppc");
5390  if ( setppcconshdlr == NULL )
5391  {
5392  SCIPdebugMsg(scip, "Could not find setppc conshdlr --> construction of conflict graph aborted.\n");
5393  return SCIP_OKAY;
5394  }
5395  assert( setppcconshdlr != NULL );
5396 
5397  setppcconss = SCIPconshdlrGetConss(setppcconshdlr);
5398  nsetppcconss = SCIPconshdlrGetNConss(setppcconshdlr);
5399  if ( nsetppcconss == 0 )
5400  {
5401  SCIPdebugMsg(scip, "No setppc constraints present --> construction of conflict graph aborted.\n");
5402  return SCIP_OKAY;
5403  }
5404 
5405  /* construct conflict graph */
5406  SCIP_CALL( SCIPcreateDigraph(scip, conflictgraph, ngraphvars) );
5407  *success = TRUE;
5408 
5409  SCIPdebugMsg(scip, "Construction of conflict graph:\n");
5410 
5411  for (c = 0; c < nsetppcconss; ++c)
5412  {
5413  cons = setppcconss[c];
5414  assert( cons != NULL );
5415 
5416  /* skip covering constraints */
5417  if ( SCIPgetTypeSetppc(scip, cons) == SCIP_SETPPCTYPE_COVERING )
5418  continue;
5419 
5420  setppcvars = SCIPgetVarsSetppc(scip, cons);
5421  nsetppcvars = SCIPgetNVarsSetppc(scip, cons);
5422  assert( setppcvars != NULL );
5423  assert( nsetppcvars > 0 );
5424 
5425  SCIPdebugMsg(scip, "\tAdd edges for constraint %s.\n", SCIPconsGetName(cons));
5426 
5427  /* iterate over pairs of variables in constraint and add bidirected arc
5428  * if both are affected by a symmetry or active */
5429  for (i = 0; i < nsetppcvars; ++i)
5430  {
5431  if ( onlypermvars )
5432  {
5433  nodei = SCIPhashmapGetImageInt(permvarmap, setppcvars[i]);
5434 
5435  /* skip variables that are not affected by symmetry */
5436  if ( nodei == INT_MAX )
5437  continue;
5438  }
5439  else
5440  {
5441  nodei = SCIPvarGetProbindex(setppcvars[i]);
5442 
5443  /* skip inactive variables */
5444  if ( nodei < 0 )
5445  continue;
5446  }
5447 
5448  for (j = i + 1; j < nsetppcvars; ++j)
5449  {
5450  if ( onlypermvars )
5451  {
5452  nodej = SCIPhashmapGetImageInt(permvarmap, setppcvars[j]);
5453 
5454  /* skip variables that are not affected by symmetyr */
5455  if ( nodej == INT_MAX )
5456  continue;
5457  }
5458  else
5459  {
5460  nodej = SCIPvarGetProbindex(setppcvars[j]);
5461 
5462  /* skip inactive variables */
5463  if ( nodej < 0 )
5464  continue;
5465  }
5466 
5467  SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodei, nodej, NULL) );
5468  SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodej, nodei, NULL) );
5469  ++nedges;
5470  }
5471  }
5472  }
5473  SCIPdebugMsg(scip, "Construction of conflict graph terminated; %d conflicts detected.\n", nedges);
5474 
5475  return SCIP_OKAY;
5476 }
5477 
5478 
5479 /** frees conflict graph */
5480 static
5482  SCIP* scip, /**< SCIP instance */
5483  SCIP_DIGRAPH** conflictgraph, /**< conflict graph */
5484  int nnodes /**< number of nodes in conflict graph */
5485  )
5486 {
5487  int i;
5488 
5489  assert( scip != NULL );
5490  assert( conflictgraph != NULL );
5491  assert( *conflictgraph != NULL );
5492  assert( nnodes > 0 );
5493 
5494  /* free node data */
5495  for (i = 0; i < nnodes; ++i)
5496  {
5497  SCIP_NODEDATA* nodedata;
5498 
5499  /* get node data */
5500  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(*conflictgraph, i);
5501 
5502  /* free node data (might not have been allocated if all components are already blocked) */
5503  if ( nodedata != NULL )
5504  {
5505  SCIPfreeBlockMemory(scip, &nodedata);
5506  }
5507  }
5508 
5509  /* free conflict graph */
5510  SCIPdigraphFree(conflictgraph);
5511 
5512  return SCIP_OKAY;
5513 }
5514 
5515 
5516 /** adds symresack constraints */
5517 static
5519  SCIP* scip, /**< SCIP instance */
5520  SCIP_PROP* prop, /**< symmetry breaking propagator */
5521  int* components, /**< array containing components of symmetry group */
5522  int* componentbegins, /**< array containing begin positions of components in components array */
5523  int ncomponents /**< number of components */
5524  )
5525 { /*lint --e{641}*/
5526  SCIP_PROPDATA* propdata;
5527  SCIP_VAR** permvars;
5528  SCIP_Bool conssaddlp;
5529  int** modifiedperms = NULL;
5530  SCIP_VAR** modifiedpermvars = NULL;
5531  int** perms;
5532  int nsymresackcons = 0;
5533  int npermvars;
5534  int nperms;
5535  int i;
5536  int p;
5537 
5538  assert( scip != NULL );
5539  assert( prop != NULL );
5540 
5541  propdata = SCIPpropGetData(prop);
5542  assert( propdata != NULL );
5543  assert( propdata->npermvars >= 0 );
5544  assert( propdata->nbinpermvars >= 0 );
5545 
5546  /* if no symmetries on binary variables are present */
5547  if ( propdata->nbinpermvars == 0 )
5548  {
5549  assert( propdata->binvaraffected == 0 );
5550  return SCIP_OKAY;
5551  }
5552 
5553  perms = propdata->perms;
5554  nperms = propdata->nperms;
5555  permvars = propdata->permvars;
5556  npermvars = propdata->npermvars;
5557  conssaddlp = propdata->conssaddlp;
5558 
5559  assert( nperms <= 0 || perms != NULL );
5560  assert( permvars != NULL );
5561  assert( npermvars > 0 );
5562 
5563  /* adapt natural variable order to a variable order that is compatible with Schreier Sims constraints */
5564  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5565  {
5566  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, nperms) );
5567  for (p = 0; p < nperms; ++p)
5568  {
5569  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[p], npermvars) );
5570  }
5571  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, npermvars) );
5572 
5573  for (i = 0; i < npermvars; ++i)
5574  modifiedpermvars[i] = permvars[i];
5575 
5576  SCIP_CALL( adaptSymmetryDataSST(scip, perms, modifiedperms, nperms, permvars, modifiedpermvars, npermvars,
5577  propdata->leaders, propdata->nleaders) );
5578  }
5579 
5580  /* if components have not been computed */
5581  if ( ncomponents == -1 )
5582  {
5583  assert( ! propdata->ofenabled );
5584  assert( ! propdata->detectorbitopes );
5585  assert( ! propdata->sstenabled );
5586 
5587  /* loop through perms and add symresack constraints */
5588  for (p = 0; p < propdata->nperms; ++p)
5589  {
5590  SCIP_CONS* cons;
5591  char name[SCIP_MAXSTRLEN];
5592 
5593  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_perm%d", p);
5594 
5595  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[p], permvars, npermvars, FALSE,
5596  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5597 
5598  SCIP_CALL( SCIPaddCons(scip, cons) );
5599 
5600  /* do not release constraint here - will be done later */
5601  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5602  ++propdata->nsymresacks;
5603  ++nsymresackcons;
5604  }
5605  }
5606  else
5607  {
5608  /* loop through components */
5609  for (i = 0; i < ncomponents; ++i)
5610  {
5611  SCIP_Bool sstcompatible = TRUE;
5612 
5613  if ( ISSSTINTACTIVE(propdata->sstleadervartype)
5614  || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
5615  || ISSSTCONTACTIVE(propdata->sstleadervartype) )
5616  sstcompatible = FALSE;
5617 
5618  /* skip components that were treated by incompatible symmetry handling techniques */
5619  if ( (propdata->componentblocked[i] & SYM_HANDLETYPE_SYMBREAK) != 0
5620  || (propdata->componentblocked[i] & SYM_HANDLETYPE_ORBITALFIXING) != 0
5621  || ((propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 && ! sstcompatible) )
5622  continue;
5623 
5624  /* loop through perms in component i and add symresack constraints */
5625  for (p = componentbegins[i]; p < componentbegins[i + 1]; ++p)
5626  {
5627  SCIP_CONS* cons;
5628  int permidx;
5629  char name[SCIP_MAXSTRLEN];
5630 
5631  permidx = components[p];
5632 
5633  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_component%d_perm%d", i, permidx);
5634 
5635  /* adapt permutation to leader */
5636  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5637  {
5638  assert( (propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 );
5639  assert( modifiedperms != NULL );
5640  assert( modifiedpermvars != NULL );
5641 
5642  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[permidx], modifiedpermvars, npermvars, FALSE,
5643  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5644  }
5645  else
5646  {
5647  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[permidx], permvars, npermvars, FALSE,
5648  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5649  }
5650  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5651  SCIP_CALL( SCIPaddCons(scip, cons) );
5652 
5653  /* do not release constraint here - will be done later */
5654  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5655  ++propdata->nsymresacks;
5656  ++nsymresackcons;
5657  }
5658  }
5659  }
5660 
5661  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5662  {
5663  assert( modifiedperms != NULL );
5664  assert( modifiedpermvars != NULL );
5665 
5666  SCIPfreeBufferArray(scip, &modifiedpermvars);
5667  for (p = nperms - 1; p >= 0; --p)
5668  {
5669  SCIPfreeBufferArray(scip, &modifiedperms[p]);
5670  }
5671  SCIPfreeBufferArray(scip, &modifiedperms);
5672  }
5673 
5674  SCIPdebugMsg(scip, "Added %d symresack constraints.\n", nsymresackcons);
5675 
5676  return SCIP_OKAY;
5677 }
5678 
5679 
5680 /** add Schreier Sims constraints for a specific orbit and update Schreier Sims table */
5681 static
5683  SCIP* scip, /**< SCIP instance */
5684  SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5685  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5686  SCIP_VAR** permvars, /**< permvars array */
5687  int* orbits, /**< symmetry orbits */
5688  int* orbitbegins, /**< array storing begin position for each orbit */
5689  int orbitidx, /**< index of orbit for Schreier Sims constraints */
5690  int orbitleaderidx, /**< index of leader variable for Schreier Sims constraints */
5691  SCIP_Shortbool* orbitvarinconflict, /**< indicator whether orbitvar is in conflict with orbit leader */
5692  int norbitvarinconflict, /**< number of variables in conflict with orbit leader */
5693  int* nchgbds, /**< pointer to store number of bound changes (or NULL) */
5694  SCIP_Bool useconflictgraph /**< whether conflict graph shall be used */
5695  )
5696 { /*lint --e{613,641}*/
5697  SCIP_CONS* cons;
5698  char name[SCIP_MAXSTRLEN];
5699  SCIP_VAR* vars[2];
5700  SCIP_Real vals[2];
5701  int orbitsize;
5702  int posleader;
5703  int poscur;
5704  int ncuts = 0;
5705  SCIP_Bool addcuts = FALSE;
5706  int i;
5707 #ifndef NDEBUG
5708  int j;
5709 #endif
5710 
5711  assert( scip != NULL );
5712  assert( conflictgraph != NULL || ! useconflictgraph );
5713  assert( propdata != NULL );
5714  assert( permvars != NULL );
5715  assert( orbits != NULL );
5716  assert( orbitbegins != NULL );
5717  assert( orbitidx >= 0 );
5718  assert( orbitleaderidx >= 0 );
5719  assert( orbitvarinconflict != NULL || ! useconflictgraph );
5720  assert( norbitvarinconflict >= 0 );
5721  assert( nchgbds != NULL );
5722 
5723  orbitsize = orbitbegins[orbitidx + 1] - orbitbegins[orbitidx];
5724 
5725  /* variables in conflict with leader are fixed and not treated by a cut; trailing -1 to not count the leader */
5726  if ( propdata->sstaddcuts )
5727  addcuts = TRUE;
5728  else if ( propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5729  || propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTS
5730  || propdata->ssttiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT )
5731  addcuts = propdata->addconflictcuts;
5732 
5733  if ( addcuts )
5734  ncuts = orbitsize - norbitvarinconflict - 1;
5735 
5736  /* (re-)allocate memory for Schreier Sims constraints and leaders */
5737  if ( ncuts > 0 )
5738  {
5739  if ( propdata->nsstconss == 0 )
5740  {
5741  assert( propdata->sstconss == NULL );
5742  assert( propdata->maxnsstconss == 0 );
5743  propdata->maxnsstconss = 2 * ncuts;
5744  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->sstconss), propdata->maxnsstconss) );
5745  }
5746  else if ( propdata->nsstconss + ncuts > propdata->maxnsstconss )
5747  {
5748  int newsize;
5749 
5750  newsize = SCIPcalcMemGrowSize(scip, propdata->maxnsstconss + 2 * ncuts);
5751  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(propdata->sstconss),
5752  propdata->maxnsstconss, newsize) );
5753  propdata->maxnsstconss = newsize;
5754  }
5755  }
5756 
5757  if ( propdata->nleaders == 0 )
5758  {
5759  propdata->maxnleaders = MIN(propdata->nperms, propdata->npermvars);
5760  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->leaders), propdata->maxnleaders) );
5761  }
5762  assert( propdata->nleaders < propdata->maxnleaders );
5763 
5764  /* add Schreier Sims constraints vars[0] >= vars[1], where vars[0] is always the leader */
5765  posleader = orbitbegins[orbitidx] + orbitleaderidx;
5766  vars[0] = permvars[orbits[posleader]];
5767  vals[0] = -1.0;
5768  vals[1] = 1.0;
5769  propdata->leaders[propdata->nleaders++] = orbits[posleader];
5770  *nchgbds = 0;
5771  for (i = 0, poscur = orbitbegins[orbitidx]; i < orbitsize; ++i, ++poscur)
5772  {
5773  if ( i == orbitleaderidx )
5774  {
5775  assert( orbitvarinconflict == NULL || ! orbitvarinconflict[i] );
5776  continue;
5777  }
5778 
5779  vars[1] = permvars[orbits[poscur]];
5780 #ifndef NDEBUG
5781  for (j = 0; j < propdata->nleaders - 1; ++j)
5782  {
5783  assert( propdata->leaders[j] != orbits[poscur] );
5784  }
5785 #endif
5786 
5787  /* if the i-th variable in the orbit is in a conflict with the leader, fix it to 0 */
5788  if ( useconflictgraph )
5789  {
5790  if ( orbitvarinconflict[i] )
5791  {
5792  assert( SCIPvarIsBinary(vars[1]) );
5793  assert( SCIPvarGetLbLocal(vars[1]) < 0.5 );
5794  assert( useconflictgraph );
5795 
5796  /* if variable is fixed */
5797  if ( SCIPvarGetUbLocal(vars[1]) > 0.5 )
5798  {
5799  SCIP_NODEDATA* nodedata;
5800 
5801  SCIP_CALL( SCIPchgVarUb(scip, vars[1], 0.0) );
5802  ++(*nchgbds);
5803 
5804  /* deactivate the fixed variable (cannot contribute to a conflict anymore) */
5805  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, orbits[poscur]);
5806  assert( nodedata != NULL );
5807  assert( nodedata->active );
5808 
5809  nodedata->active = FALSE;
5810  }
5811 
5812  /* reset value */
5813  orbitvarinconflict[i] = FALSE;
5814  }
5815  else if ( addcuts )
5816  {
5817  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5818  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5820 
5821  SCIP_CALL( SCIPaddCons(scip, cons) );
5822  propdata->sstconss[propdata->nsstconss++] = cons;
5823  }
5824  }
5825  else if ( addcuts )
5826  {
5827  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5828  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5830 
5831  SCIP_CALL( SCIPaddCons(scip, cons) );
5832  propdata->sstconss[propdata->nsstconss++] = cons;
5833  }
5834  }
5835 
5836  return SCIP_OKAY;
5837 }
5838 
5839 
5840 /** selection rule of next orbit/leader in orbit for Schreier Sims constraints */
5841 static
5843  SCIP* scip, /**< SCIP instance */
5844  SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5845  SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5846  int ngraphvars, /**< number of variables encoded in conflict graph */
5847  SCIP_HASHMAP* varmap, /**< map from variable to node label in conflict graph or NULL if useconflictgraph == FALSE */
5848  SCIP_VAR** permvars, /**< vars encoded in a permutation */
5849  int npermvars, /**< number of vars in a permutation */
5850  int* orbits, /**< orbits of stabilizer subgroup */
5851  int* orbitbegins, /**< array storing the begin position of each orbit in orbits */
5852  int norbits, /**< number of orbits */
5853  int leaderrule, /**< rule to select leader */
5854  int tiebreakrule, /**< tie break rule to select leader */
5855  SCIP_VARTYPE leadervartype, /**< variable type of leader */
5856  int* orbitidx, /**< pointer to index of selected orbit */
5857  int* leaderidx, /**< pointer to leader in orbit */
5858  SCIP_Shortbool* orbitvarinconflict, /**< array to store whether a var in the orbit is conflicting with leader */
5859  int* norbitvarinconflict, /**< pointer to store number of vars in the orbit in conflict with leader */
5860  SCIP_Bool useconflictgraph, /**< whether conflict graph shall be used */
5861  SCIP_Bool* success /**< pointer to store whether orbit cut be selected successfully */
5862  )
5863 {
5864  SCIP_NODEDATA* nodedata;
5865  int* conflictvars;
5866  int nconflictvars = 0;
5867  int varidx;
5868  int orbitcriterion;
5869  int curcriterion = INT_MIN;
5870  int orbitsize;
5871  int i;
5872  SCIP_NODEDATA* neighbordata;
5873  int leader = -1;
5874  int j;
5875 
5876  assert( scip != NULL );
5877  assert( conflictgraph != NULL || ! useconflictgraph );
5878  assert( graphvars != NULL );
5879  assert( ngraphvars > 0 );
5880  assert( varmap != NULL || ! useconflictgraph );
5881  assert( permvars != NULL );
5882  assert( npermvars > 0 );
5883  assert( orbits != NULL );
5884  assert( orbitbegins != NULL );
5885  assert( norbits > 0 );
5886  assert( orbitidx != NULL );
5887  assert( leaderidx != NULL );
5888  assert( orbitvarinconflict != NULL || ! useconflictgraph );
5889  assert( norbitvarinconflict != NULL );
5890  assert( success != NULL );
5891 
5892  *orbitidx = 0;
5893  *leaderidx = 0;
5894  *norbitvarinconflict = 0;
5895  *success = FALSE;
5896 
5897  /* terminate if leader or tiebreak rule cannot be checked */
5898  if ( ! useconflictgraph && (leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTS
5899  || leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5900  || tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
5901  return SCIP_OKAY;
5902 
5903  /* select the leader and its orbit */
5904  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT || leaderrule == (int) SCIP_LEADERRULE_LASTINORBIT )
5905  {
5906  orbitcriterion = INT_MIN;
5907 
5908  /* iterate over orbits and select the first one that meets the tiebreak rule */
5909  for (i = 0; i < norbits; ++i)
5910  {
5911  /* skip orbits containing vars different to the leader's vartype */
5912  if ( SCIPvarGetType(permvars[orbits[orbitbegins[i]]]) != leadervartype )
5913  continue;
5914 
5915  if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MINORBIT )
5916  curcriterion = orbitbegins[i] - orbitbegins[i + 1];
5917  else if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXORBIT )
5918  curcriterion = orbitbegins[i + 1] - orbitbegins[i];
5919  else
5920  {
5921  /* get first or last active variable in orbit */
5922  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
5923  {
5924  int cnt = orbitbegins[i];
5925 
5926  do
5927  {
5928  varidx = SCIPvarGetProbindex(permvars[orbits[cnt++]]);
5929  }
5930  while ( varidx == -1 && cnt < orbitbegins[i + 1]);
5931  }
5932  else
5933  {
5934  int cnt = orbitbegins[i + 1] - 1;
5935 
5936  do
5937  {
5938  varidx = SCIPvarGetProbindex(permvars[orbits[cnt--]]);
5939  }
5940  while ( varidx == -1 && cnt >= orbitbegins[i]);
5941  }
5942 
5943  /* skip inactive variables */
5944  if ( varidx == -1 )
5945  continue;
5946 
5947  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, varidx);
5948  assert( nodedata != NULL );
5949  assert( nodedata->orbitidx == i );
5950 
5951  if ( nodedata->nconflictinorbit > 0 )
5952  curcriterion = nodedata->nconflictinorbit;
5953  }
5954 
5955  /* update selected orbit */
5956  if ( curcriterion > orbitcriterion )
5957  {
5958  orbitcriterion = curcriterion;
5959  *orbitidx = i;
5960  *success = TRUE;
5961 
5962  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
5963  *leaderidx = 0;
5964  else
5965  *leaderidx = orbitbegins[i + 1] - orbitbegins[i] - 1;
5966  }
5967  }
5968 
5969  /* store variables in conflict with leader */
5970  if ( useconflictgraph )
5971  {
5972  leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
5973  assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
5974 
5975  nconflictvars = SCIPdigraphGetNSuccessors(conflictgraph, leader);
5976  }
5977 
5978  if ( *success && tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && nconflictvars > 0 )
5979  {
5980  SCIP_VAR* var;
5981  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
5982  assert( useconflictgraph );
5983  assert( leader >= 0 && leader < npermvars );
5984 
5985  conflictvars = SCIPdigraphGetSuccessors(conflictgraph, leader);
5986  assert( conflictvars != NULL );
5987  assert( orbitvarinconflict != NULL );
5988 
5989  for (i = 0; i < orbitsize; ++i)
5990  {
5991  /* skip the leader */
5992  if ( i == *leaderidx )
5993  continue;
5994 
5995  var = permvars[orbits[orbitbegins[*orbitidx] + i]];
5996 
5997  for (j = 0; j < nconflictvars; ++j)
5998  {
5999  neighbordata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvars[j]);
6000 
6001  assert( neighbordata != NULL );
6002 
6003  if ( neighbordata->var == var && neighbordata->active )
6004  {
6005  orbitvarinconflict[i] = TRUE;
6006  *norbitvarinconflict += 1;
6007  break;
6008  }
6009  }
6010  }
6011  }
6012  }
6013  else if ( useconflictgraph )
6014  {
6015  orbitcriterion = 0;
6016 
6017  /* iterate over variables and select the first one that meets the tiebreak rule */
6018  for (i = 0; i < ngraphvars; ++i)
6019  {
6020  /* skip vars different to the leader's vartype */
6021  if ( SCIPvarGetType(graphvars[i]) != leadervartype )
6022  continue;
6023 
6024  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
6025  assert( nodedata != NULL );
6026 
6027  /* skip variables not affected by symmetry */
6028  if ( nodedata->orbitidx == -1 )
6029  continue;
6030 
6031  if ( leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT )
6032  curcriterion = nodedata->nconflictinorbit;
6033  else
6034  curcriterion = SCIPdigraphGetNSuccessors(conflictgraph, i);
6035 
6036  if ( curcriterion > orbitcriterion )
6037  {
6038  orbitcriterion = curcriterion;
6039  *orbitidx = nodedata->orbitidx;
6040  *leaderidx = nodedata->posinorbit;
6041  *success = TRUE;
6042  }
6043  }
6044 
6045  /* store variables in conflict with leader */
6046  leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
6047  assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
6048  assert( norbitvarinconflict != NULL );
6049 
6050  nconflictvars = SCIPdigraphGetNSuccessors(conflictgraph, leader);
6051  if ( *success && nconflictvars > 0 )
6052  {
6053  SCIP_VAR* var;
6054  assert( orbitvarinconflict != NULL );
6055 
6056  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
6057 
6058  conflictvars = SCIPdigraphGetSuccessors(conflictgraph, leader);
6059  assert( conflictvars != NULL );
6060 
6061  for (i = 0; i < orbitsize; ++i)
6062  {
6063  /* skip the leader */
6064  if ( i == *leaderidx )
6065  continue;
6066 
6067  var = permvars[orbits[orbitbegins[*orbitidx] + i]];
6068 
6069  for (j = 0; j < nconflictvars; ++j)
6070  {
6071  neighbordata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvars[j]);
6072  assert( neighbordata != NULL );
6073 
6074  if ( neighbordata->var == var && neighbordata->active )
6075  {
6076  orbitvarinconflict[i] = TRUE;
6077  *norbitvarinconflict += 1;
6078  break;
6079  }
6080  }
6081  }
6082  }
6083  }
6084 
6085  return SCIP_OKAY;
6086 }
6087 
6088 
6089 /** add Schreier Sims constraints to the problem */
6090 static
6092  SCIP* scip, /**< SCIP instance */
6093  SCIP_PROPDATA* propdata, /**< datas of symmetry propagator */
6094  int* nchgbds /**< pointer to store number of bound changes (or NULL) */
6095  )
6096 { /*lint --e{641}*/
6097  SCIP_DIGRAPH* conflictgraph = NULL;
6098  SCIP_HASHMAP* varmap = NULL;
6099  SCIP_VAR** vars;
6100  int nvars;
6101 
6102  SCIP_HASHMAP* permvarmap;
6103  SCIP_VAR** permvars;
6104  int** permstrans;
6105  int npermvars;
6106  int nmovedpermvars;
6107  int nmovedbinpermvars;
6108  int nmovedintpermvars;
6109  int nmovedimplintpermvars;
6110  int nmovedcontpermvars;
6111  int nperms;
6112 
6113  int* orbits;
6114  int* orbitbegins;
6115  int norbits;
6116  int* components;
6117  int* componentbegins;
6118  int* vartocomponent;
6119  int ncomponents;
6120  unsigned* componentblocked;
6121 
6122  int orbitidx;
6123  int orbitleaderidx;
6124  SCIP_Shortbool* orbitvarinconflict = NULL;
6125  int norbitvarinconflict;
6126  SCIP_Shortbool* inactiveperms;
6127  int ninactiveperms;
6128  int posleader;
6129  int leaderrule;
6130  int tiebreakrule;
6131  int leadervartype;
6132  SCIP_VARTYPE selectedtype = SCIP_VARTYPE_CONTINUOUS;
6133  int nvarsselectedtype;
6134  SCIP_Bool conflictgraphcreated = FALSE;
6135  SCIP_Bool mixedcomponents;
6136  int* norbitleadercomponent;
6137 
6138  int c;
6139  int v;
6140  int p;
6141 
6142  assert( scip != NULL );
6143  assert( propdata != NULL );
6144 
6145  permvars = propdata->permvars;
6146  npermvars = propdata->npermvars;
6147  permvarmap = propdata->permvarmap;
6148  permstrans = propdata->permstrans;
6149  nperms = propdata->nperms;
6150  components = propdata->components;
6151  componentbegins = propdata->componentbegins;
6152  componentblocked = propdata->componentblocked;
6153  vartocomponent = propdata->vartocomponent;
6154  ncomponents = propdata->ncomponents;
6155  nmovedpermvars = propdata->nmovedpermvars;
6156  nmovedbinpermvars = propdata->nmovedbinpermvars;
6157  nmovedintpermvars = propdata->nmovedintpermvars;
6158  nmovedimplintpermvars = propdata->nmovedimplintpermvars;
6159  nmovedcontpermvars = propdata->nmovedcontpermvars;
6160 
6161  assert( permvars != NULL );
6162  assert( npermvars > 0 );
6163  assert( permvarmap != NULL );
6164  assert( permstrans != NULL );
6165  assert( nperms > 0 );
6166  assert( components != NULL );
6167  assert( componentbegins != NULL );
6168  assert( vartocomponent != NULL );
6169  assert( ncomponents > 0 );
6170  assert( nmovedpermvars > 0 || ! propdata->ofenabled );
6171  assert( nmovedbinpermvars > 0 || ! propdata->ofenabled );
6172 
6173  leaderrule = propdata->sstleaderrule;
6174  tiebreakrule = propdata->ssttiebreakrule;
6175  leadervartype = propdata->sstleadervartype;
6176  mixedcomponents = propdata->sstmixedcomponents;
6177 
6178  /* if not already computed, get number of affected vars */
6179  if ( nmovedpermvars == -1 )
6180  {
6181  nmovedpermvars = 0;
6182 
6183  for (v = 0; v < npermvars; ++v)
6184  {
6185  for (p = 0; p < nperms; ++p)
6186  {
6187  if ( permstrans[v][p] != v )
6188  {
6189  ++nmovedpermvars;
6190 
6191  switch ( SCIPvarGetType(permvars[v]) )
6192  {
6193  case SCIP_VARTYPE_BINARY:
6194  ++nmovedbinpermvars;
6195  break;
6196  case SCIP_VARTYPE_INTEGER:
6197  ++nmovedintpermvars;
6198  break;
6199  case SCIP_VARTYPE_IMPLINT:
6200  ++nmovedimplintpermvars;
6201  break;
6203  default:
6204  ++nmovedcontpermvars;
6205  }
6206  }
6207  }
6208  }
6209  }
6210  propdata->nmovedbinpermvars = nmovedbinpermvars;
6211  propdata->nmovedintpermvars = nmovedintpermvars;
6212  propdata->nmovedimplintpermvars = nmovedimplintpermvars;
6213  propdata->nmovedcontpermvars = nmovedcontpermvars;
6214 
6215  vars = SCIPgetVars(scip);
6216  nvars = SCIPgetNVars(scip);
6217 
6218  /* determine the leader's vartype */
6219  nvarsselectedtype = 0;
6220  if ( ISSSTBINACTIVE(leadervartype) && nmovedbinpermvars > nvarsselectedtype )
6221  {
6222  selectedtype = SCIP_VARTYPE_BINARY;
6223  nvarsselectedtype = nmovedbinpermvars;
6224  }
6225 
6226  if ( ISSSTINTACTIVE(leadervartype) && nmovedintpermvars > nvarsselectedtype )
6227  {
6228  selectedtype = SCIP_VARTYPE_INTEGER;
6229  nvarsselectedtype = nmovedintpermvars;
6230  }
6231 
6232  if ( ISSSTIMPLINTACTIVE(leadervartype) && nmovedimplintpermvars > nvarsselectedtype )
6233  {
6234  selectedtype = SCIP_VARTYPE_IMPLINT;
6235  nvarsselectedtype = nmovedimplintpermvars;
6236  }
6237 
6238  if ( ISSSTCONTACTIVE(leadervartype) && nmovedcontpermvars > nvarsselectedtype )
6239  {
6240  selectedtype = SCIP_VARTYPE_CONTINUOUS;
6241  nvarsselectedtype = nmovedcontpermvars;
6242  }
6243 
6244  /* terminate if no variables of a possible leader type is affected */
6245  if ( nvarsselectedtype == 0 )
6246  return SCIP_OKAY;
6247 
6248  /* possibly create conflict graph; graph is not created if no setppc conss are present */
6249  if ( selectedtype == SCIP_VARTYPE_BINARY && (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
6250  || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS
6251  || tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
6252  {
6253  SCIP_CALL( createConflictGraphSST(scip, &conflictgraph, vars, nvars, FALSE,
6254  permvarmap, &conflictgraphcreated) );
6255  }
6256 
6257  /* allocate data structures necessary for orbit computations and conflict graph */
6258  SCIP_CALL( SCIPallocBufferArray(scip, &inactiveperms, nperms) );
6259  SCIP_CALL( SCIPallocBufferArray(scip, &orbits, npermvars) );
6260  SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, npermvars) );
6261 
6262  if ( conflictgraphcreated )
6263  {
6264  SCIP_CALL( SCIPallocClearBufferArray(scip, &orbitvarinconflict, npermvars) );
6265  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), nvars) );
6266  for (v = 0; v < nvars; ++v)
6267  {
6268  assert( ! SCIPhashmapExists(varmap, vars[v]) );
6269  SCIP_CALL( SCIPhashmapInsertInt(varmap, vars[v], v) );
6270  }
6271  }
6272 
6273  SCIPdebugMsg(scip, "Start selection of orbits and leaders for Schreier Sims constraints.\n");
6274  SCIPdebugMsg(scip, "orbitidx\tleaderidx\torbitsize\n");
6275 
6276  if ( nchgbds != NULL )
6277  *nchgbds = 0;
6278 
6279  /* initialize array indicating whether permutations shall not be considered for orbit permutations */
6280  for (p = 0; p < nperms; ++p)
6281  inactiveperms[p] = TRUE;
6282 
6283  SCIP_CALL( SCIPallocBufferArray(scip, &norbitleadercomponent, ncomponents) );
6284  for (c = 0; c < ncomponents; ++c)
6285  norbitleadercomponent[c] = 0;
6286 
6287  /* iterate over components and compute orbits */
6288  for (c = 0; c < ncomponents; ++c)
6289  {
6290  SCIP_Bool success = TRUE;
6291 
6292  if ( componentblocked[c] )
6293  continue;
6294 
6295  for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6296  inactiveperms[components[p]] = FALSE;
6297  ninactiveperms = nperms - componentbegins[c + 1] + componentbegins[c];
6298 
6299  /* as long as the stabilizer is non-trivial, add Schreier Sims constraints */
6300  while ( ninactiveperms < nperms )
6301  {
6302  int nchanges = 0;
6303 
6304  /* compute orbits w.r.t. active perms */
6305  SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, npermvars, permstrans, nperms, inactiveperms,
6306  orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent,
6307  componentblocked, ncomponents, nmovedpermvars) );
6308 
6309  /* stop if we require pure components and a component contains variables of different types */
6310  if ( ! mixedcomponents )
6311  {
6312  for (p = 0; p < norbits; ++p)
6313  {
6314  /* stop if the first element of an orbits has the wrong vartype */
6315  if ( SCIPvarGetType(permvars[orbits[orbitbegins[p]]]) != selectedtype )
6316  {
6317  success = FALSE;
6318  break;
6319  }
6320  }
6321  }
6322 
6323  if ( ! success )
6324  break;
6325 
6326  /* update symmetry information of conflict graph */
6327  if ( conflictgraphcreated )
6328  {
6329  assert( conflictgraph != NULL );
6330  SCIP_CALL( updateSymInfoConflictGraphSST(scip, conflictgraph, vars, nvars, permvars, npermvars, FALSE,
6331  varmap, orbits, orbitbegins, norbits) );
6332  }
6333 
6334  /* possibly adapt the leader and tie-break rule */
6335  if ( (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS)
6336  && ! conflictgraphcreated )
6337  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
6338  if ( (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS)
6339  && selectedtype != SCIP_VARTYPE_BINARY )
6340  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
6341  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && ! conflictgraphcreated )
6342  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
6343  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && selectedtype != SCIP_VARTYPE_BINARY )
6344  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
6345 
6346  /* select orbit and leader */
6347  SCIP_CALL( selectOrbitLeaderSSTConss(scip, conflictgraph, vars, nvars, varmap,
6348  permvars, npermvars, orbits, orbitbegins, norbits, propdata->sstleaderrule, propdata->ssttiebreakrule, selectedtype,
6349  &orbitidx, &orbitleaderidx, orbitvarinconflict, &norbitvarinconflict, conflictgraphcreated, &success) );
6350 
6351  if ( ! success )
6352  break;
6353 
6354  assert( 0 <= orbitidx && orbitidx < norbits );
6355  assert( 0 <= orbitleaderidx && orbitleaderidx < orbitbegins[orbitidx + 1] - orbitbegins[orbitidx] );
6356  SCIPdebugMsg(scip, "%d\t\t%d\t\t%d\n", orbitidx, orbitleaderidx, orbitbegins[orbitidx + 1] - orbitbegins[orbitidx]);
6357 
6358  /* add Schreier Sims constraints for the selected orbit and update Schreier Sims table */
6359  SCIP_CALL( addSSTConssOrbitAndUpdateSST(scip, conflictgraph, propdata, permvars,
6360  orbits, orbitbegins, orbitidx, orbitleaderidx, orbitvarinconflict, norbitvarinconflict, &nchanges, conflictgraphcreated) );
6361 
6362  ++norbitleadercomponent[propdata->vartocomponent[orbits[orbitbegins[orbitidx] + orbitleaderidx]]];
6363 
6364  if ( nchgbds != NULL )
6365  *nchgbds += nchanges;
6366 
6367  /* deactivate permutations that move the orbit leader */
6368  posleader = orbits[orbitbegins[orbitidx] + orbitleaderidx];
6369  for (p = 0; p < nperms; ++p)
6370  {
6371  if ( inactiveperms[p] )
6372  continue;
6373 
6374  if ( permstrans[posleader][p] != posleader )
6375  {
6376  inactiveperms[p] = TRUE;
6377  ++ninactiveperms;
6378  }
6379  }
6380  }
6381 
6382  for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6383  inactiveperms[components[p]] = TRUE;
6384  }
6385 
6386  /* if Schreier Sims constraints have been added, store that Schreier Sims has been used for this component */
6387  for (c = 0; c < ncomponents; ++c)
6388  {
6389  if ( norbitleadercomponent[c] > 0 )
6390  componentblocked[c] |= SYM_HANDLETYPE_SST;
6391  }
6392  SCIPfreeBufferArray(scip, &norbitleadercomponent);
6393 
6394  if ( conflictgraphcreated )
6395  {
6396  SCIPhashmapFree(&varmap);
6397  SCIPfreeBufferArray(scip, &orbitvarinconflict);
6398  }
6399  SCIPfreeBufferArray(scip, &orbitbegins);
6400  SCIPfreeBufferArray(scip, &orbits);
6401  if ( conflictgraphcreated )
6402  {
6403  assert( conflictgraph != NULL );
6404  SCIP_CALL( freeConflictGraphSST(scip, &conflictgraph, nvars) );
6405  }
6406  SCIPfreeBufferArray(scip, &inactiveperms);
6407 
6408  return SCIP_OKAY;
6409 }
6410 
6411 
6412 /** finds problem symmetries */
6413 static
6415  SCIP* scip, /**< SCIP instance */
6416  SCIP_PROP* prop, /**< symmetry breaking propagator */
6417  int* nchgbds, /**< pointer to store number of bound changes (or NULL)*/
6418  SCIP_Bool* earlyterm /**< pointer to store whether we terminated early (or NULL) */
6419  )
6420 {
6421  SCIP_PROPDATA* propdata;
6422 
6423  assert( prop != NULL );
6424  assert( scip != NULL );
6425 
6426  propdata = SCIPpropGetData(prop);
6427  assert( propdata != NULL );
6428  assert( propdata->symconsenabled || propdata->sstenabled );
6429 
6430  /* if constraints have already been added */
6431  if ( propdata->triedaddconss && isSymmetryRecomputationRequired(scip, propdata) )
6432  {
6433  /* remove symmetry handling constraints to be prepared for a recomputation */
6434  SCIP_CALL( delSymConss(scip, propdata) );
6435  SCIP_CALL( freeSymmetryData(scip, propdata) );
6436 
6437  propdata->lastrestart = SCIPgetNRuns(scip);
6438  propdata->offoundreduction = FALSE;
6439  }
6440  else if ( propdata->triedaddconss )
6441  {
6442  assert( propdata->nperms > 0 );
6443 
6444  if ( earlyterm != NULL )
6445  *earlyterm = TRUE;
6446 
6447  return SCIP_OKAY;
6448  }
6449 
6450  /* possibly compute symmetry */
6451  if ( propdata->ofenabled && SCIPgetNBinVars(scip) > 1 )
6452  {
6453  SCIP_Bool oldsymconsenabled;
6454 
6455  oldsymconsenabled = propdata->symconsenabled;
6456 
6457  /* in the nonlinear case, all non-binary variables have to be fixed
6458  (fix non-binary potential branching variables)
6459  */
6460  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6461  {
6463  }
6464  else
6465  {
6467  }
6468 
6469  /* if there is no symmetry compatible with OF, check whether there are more general symmetries */
6470  if ( propdata->nperms == 0 && SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip) > 1 )
6471  {
6472  SCIP_CALL( freeSymmetryData(scip, propdata) );
6473  propdata->symconsenabled = oldsymconsenabled;
6474  propdata->ofenabled = FALSE;
6475  propdata->sstenabled = FALSE;
6476 
6478  }
6479  }
6480  else
6481  {
6483  }
6484  assert( propdata->binvaraffected || ! propdata->ofenabled || ! propdata->symconsenabled );
6485 
6486  if ( propdata->nperms <= 0 || (! propdata->symconsenabled && ! propdata->sstenabled) )
6487  return SCIP_OKAY;
6488 
6489  if ( ! propdata->binvaraffected )
6490  {
6491  SCIPdebugMsg(scip, "Symmetry propagator: problem is linear and no symmetry on binary variables has been found, turning symretope constraints off.\n");
6492  propdata->symconsenabled = FALSE;
6493  }
6494  assert( propdata->nperms > 0 );
6495  assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || propdata->sstenabled );
6496 
6497  propdata->triedaddconss = TRUE;
6498 
6499  if ( propdata->symconsenabled )
6500  {
6501  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms) );
6502 
6503  if ( propdata->detectorbitopes )
6504  {
6505  SCIP_CALL( detectOrbitopes(scip, propdata, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6506  }
6507  }
6508 
6509  /* disable orbital fixing if all components are handled by orbitopes */
6510  if ( propdata->ncomponents == propdata->norbitopes )
6511  propdata->ofenabled = FALSE;
6512 
6513  /* possibly stop */
6514  if ( SCIPisStopped(scip) )
6515  {
6516  if ( propdata->ngenorbconss == 0 )
6517  {
6518  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6519  }
6520  return SCIP_OKAY;
6521  }
6522 
6523  if ( propdata->ncompblocked < propdata->ncomponents && propdata->detectsubgroups && propdata->symconsenabled )
6524  {
6525  /* @TODO: create array only when needed */
6526  propdata->genlinconsssize = propdata->nperms;
6527  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize) );
6528 
6529  SCIP_CALL( detectAndHandleSubgroups(scip, propdata) );
6530  }
6531 
6532  if ( propdata->sstenabled )
6533  {
6534  SCIP_CALL( addSSTConss(scip, propdata, nchgbds) );
6535  }
6536 
6537  /* possibly stop */
6538  if ( SCIPisStopped(scip) || ! propdata->symconsenabled )
6539  return SCIP_OKAY;
6540 
6541  /* add symmetry breaking constraints if orbital fixing is not used outside orbitopes */
6542  if ( ! propdata->ofenabled )
6543  {
6544  /* exit if no or only trivial symmetry group is available */
6545  if ( propdata->nperms < 1 || ! propdata->binvaraffected )
6546  return SCIP_OKAY;
6547 
6548  if ( propdata->addsymresacks )
6549  {
6550  SCIP_CALL( addSymresackConss(scip, prop, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6551  }
6552 
6553  /* free symmetry conss if no orbitope/symresack constraints have been found (may happen if Schreier-Sims constraints are active) */
6554  if ( propdata->ngenorbconss == 0 )
6555  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6556  }
6557 
6558  return SCIP_OKAY;
6559 }
6560 
6561 
6562 
6563 /*
6564  * Local methods for orbital fixing
6565  */
6566 
6567 
6568 /** performs orbital fixing
6569  *
6570  * Note that we do not have to distinguish between variables that have been fixed or branched to 1, since the
6571  * stabilizer is with respect to the variables that have been branched to 1. Thus, if an orbit contains a variable that
6572  * has been branched to 1, the whole orbit only contains variables that have been branched to 1 - and nothing can be
6573  * fixed.
6574  */
6575 static
6577  SCIP* scip, /**< SCIP pointer */
6578  SCIP_VAR** permvars, /**< variables */
6579  int npermvars, /**< number of variables */
6580  int* orbits, /**< array of non-trivial orbits */
6581  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
6582  int norbits, /**< number of orbits */
6583  SCIP_Bool* infeasible, /**< pointer to store whether problem is infeasible */
6584  int* nfixedzero, /**< pointer to store number of variables fixed to 0 */
6585  int* nfixedone /**< pointer to store number of variables fixed to 1 */
6586  )
6587 {
6588  SCIP_Bool tightened;
6589  int i;
6590 
6591  assert( scip != NULL );
6592  assert( permvars != NULL );
6593  assert( orbits != NULL );
6594  assert( orbitbegins != NULL );
6595  assert( infeasible != NULL );
6596  assert( nfixedzero != NULL );
6597  assert( nfixedone != NULL );
6598  assert( norbits > 0 );
6599  assert( orbitbegins[0] == 0 );
6600 
6601  *infeasible = FALSE;
6602  *nfixedzero = 0;
6603  *nfixedone = 0;
6604 
6605  /* check all orbits */
6606  for (i = 0; i < norbits; ++i)
6607  {
6608  SCIP_Bool havefixedone = FALSE;
6609  SCIP_Bool havefixedzero = FALSE;
6610  SCIP_VAR* var;
6611  int j;
6612 
6613  /* we only have nontrivial orbits */
6614  assert( orbitbegins[i+1] - orbitbegins[i] >= 2 );
6615 
6616  /* check all variables in the orbit */
6617  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6618  {
6619  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6620  var = permvars[orbits[j]];
6621  assert( var != NULL );
6622 
6623  /* check whether variable is not binary (and not implicit integer!) */
6624  if ( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY )
6625  {
6626  /* skip orbit if there are non-binary variables */
6627  havefixedone = FALSE;
6628  havefixedzero = FALSE;
6629  break;
6630  }
6631 
6632  /* if variable is fixed to 1 -> can fix all variables in orbit to 1 */
6633  if ( SCIPvarGetLbLocal(var) > 0.5 )
6634  havefixedone = TRUE;
6635 
6636  /* check for zero-fixed variables */
6637  if ( SCIPvarGetUbLocal(var) < 0.5 )
6638  havefixedzero = TRUE;
6639  }
6640 
6641  /* check consistency */
6642  if ( havefixedone && havefixedzero )
6643  {
6644  *infeasible = TRUE;
6645  return SCIP_OKAY;
6646  }
6647 
6648  /* fix all variables to 0 if there is one variable fixed to 0 */
6649  if ( havefixedzero )
6650  {
6651  assert( ! havefixedone );
6652 
6653  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6654  {
6655  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6656  var = permvars[orbits[j]];
6657  assert( var != NULL );
6658 
6659  /* only variables that are not yet fixed to 0 */
6660  if ( SCIPvarGetUbLocal(var) > 0.5 )
6661  {
6662  SCIPdebugMsg(scip, "can fix <%s> (index %d) to 0.\n", SCIPvarGetName(var), orbits[j]);
6663  assert( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY );
6664  /* due to aggregation, var might already be fixed to 1, so do not put assert here */
6665 
6666  /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6667  SCIP_CALL( SCIPtightenVarUb(scip, var, 0.0, FALSE, infeasible, &tightened) );
6668  if ( *infeasible )
6669  return SCIP_OKAY;
6670  if ( tightened )
6671  ++(*nfixedzero);
6672  }
6673  }
6674  }
6675 
6676  /* fix all variables to 1 if there is one variable fixed to 1 */
6677  if ( havefixedone )
6678  {
6679  assert( ! havefixedzero );
6680 
6681  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6682  {
6683  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6684  var = permvars[orbits[j]];
6685  assert( var != NULL );
6686 
6687  /* only variables that are not yet fixed to 1 */
6688  if ( SCIPvarGetLbLocal(var) < 0.5)
6689  {
6690  SCIPdebugMsg(scip, "can fix <%s> (index %d) to 1.\n", SCIPvarGetName(var), orbits[j]);
6691  assert( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY );
6692  /* due to aggregation, var might already be fixed to 0, so do not put assert here */
6693 
6694  /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6695  SCIP_CALL( SCIPtightenVarLb(scip, var, 1.0, FALSE, infeasible, &tightened) );
6696  if ( *infeasible )
6697  return SCIP_OKAY;
6698  if ( tightened )
6699  ++(*nfixedone);
6700  }
6701  }
6702  }
6703  }
6704 
6705  return SCIP_OKAY;
6706 }
6707 
6708 
6709 /** Gets branching variables on the path to root
6710  *
6711  * The variables are added to bg1 and bg1list, which are prefilled with the variables globally fixed to 1.
6712  */
6713 static
6715  SCIP* scip, /**< SCIP pointer */
6716  int nvars, /**< number of variables */
6717  SCIP_HASHMAP* varmap, /**< map of variables to indices in vars array */
6718  SCIP_Shortbool* bg1, /**< bitset marking the variables globally fixed or branched to 1 */
6719  int* bg1list, /**< array to store the variable indices globally fixed or branched to 1 */
6720  int* nbg1 /**< pointer to store the number of variables in bg1 and bg1list */
6721  )
6722 {
6723  SCIP_NODE* node;
6724 
6725  assert( scip != NULL );
6726  assert( varmap != NULL );
6727  assert( bg1 != NULL );
6728  assert( bg1list != NULL );
6729  assert( nbg1 != NULL );
6730  assert( *nbg1 >= 0 );
6731 
6732  /* get current node */
6733  node = SCIPgetCurrentNode(scip);
6734 
6735 #ifdef SCIP_OUTPUT
6736  SCIP_CALL( SCIPprintNodeRootPath(scip, node, NULL) );
6737 #endif
6738 
6739  /* follow path to the root (in the root no domains were changed due to branching) */
6740  while ( SCIPnodeGetDepth(node) != 0 )
6741  {
6742  SCIP_BOUNDCHG* boundchg;
6743  SCIP_DOMCHG* domchg;
6744  SCIP_VAR* branchvar;
6745  int nboundchgs;
6746  int i;
6747 
6748  /* get domain changes of current node */
6749  domchg = SCIPnodeGetDomchg(node);
6750 
6751  /* If we stopped due to a solving limit, it might happen that a non-root node has no domain changes, in all other
6752  * cases domchg should not be NULL. */
6753  if ( domchg != NULL )
6754  {
6755  /* loop through all bound changes */
6756  nboundchgs = SCIPdomchgGetNBoundchgs(domchg);
6757  for (i = 0; i < nboundchgs; ++i)
6758  {
6759  /* get bound change info */
6760  boundchg = SCIPdomchgGetBoundchg(domchg, i);
6761  assert( boundchg != NULL );
6762 
6763  /* branching decisions have to be in the beginning of the bound change array */
6765  break;
6766 
6767  /* get corresponding branching variable */
6768  branchvar = SCIPboundchgGetVar(boundchg);
6769 
6770  /* we only consider binary variables */
6771  if ( SCIPvarGetType(branchvar) == SCIP_VARTYPE_BINARY )
6772  {
6773  /* if branching variable is not known (may have been created meanwhile,
6774  * e.g., by prop_inttobinary; may have been removed from symmetry data
6775  * due to compression), continue with parent node */
6776  if ( ! SCIPhashmapExists(varmap, (void*) branchvar) )
6777  break;
6778 
6779  if ( SCIPvarGetLbLocal(branchvar) > 0.5 )
6780  {
6781  int branchvaridx;
6782 
6783  branchvaridx = SCIPhashmapGetImageInt(varmap, (void*) branchvar);
6784  assert( branchvaridx < nvars );
6785 
6786  /* the variable might already be fixed to 1 */
6787  if ( ! bg1[branchvaridx] )
6788  {
6789  bg1[branchvaridx] = TRUE;
6790  bg1list[(*nbg1)++] = branchvaridx;
6791  }
6792  }
6793  }
6794  }
6795  }
6796 
6797  node = SCIPnodeGetParent(node);
6798  }
6799 
6800  return SCIP_OKAY;
6801 }
6802 
6803 
6804 /** propagates orbital fixing */
6805 static
6807  SCIP* scip, /**< SCIP pointer */
6808  SCIP_PROPDATA* propdata, /**< data of symmetry breaking propagator */
6809  SCIP_Bool* infeasible, /**< pointer to store whether the node is detected to be infeasible */
6810  int* nprop /**< pointer to store the number of propagations */
6811  )
6812 {
6813  SCIP_Shortbool* inactiveperms;
6814  SCIP_Shortbool* bg0;
6815  SCIP_Shortbool* bg1;
6816  SCIP_VAR** permvars;
6817  int* orbitbegins;
6818  int* orbits;
6819  int* components;
6820  int* componentbegins;
6821  int* vartocomponent;
6822  int ncomponents;
6823  int* bg0list;
6824  int nbg0;
6825  int* bg1list;
6826  int nbg1;
6827  int nactiveperms;
6828  int norbits;
6829  int npermvars;
6830  int nbinpermvars;
6831  int** permstrans;
6832  int nperms;
6833  int p;
6834  int v;
6835  int j;
6836  int componentidx;
6837 
6838  assert( scip != NULL );
6839  assert( propdata != NULL );
6840  assert( propdata->ofenabled );
6841  assert( infeasible != NULL );
6842  assert( nprop != NULL );
6843 
6844  *infeasible = FALSE;
6845  *nprop = 0;
6846 
6847  /* possibly compute symmetry; fix non-binary potential branching variables */
6848  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6849  {
6851  }
6852  else
6853  {
6855  }
6856  assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || ! propdata->ofenabled );
6857 
6858  /* return if there is no symmetry available */
6859  nperms = propdata->nperms;
6860  if ( nperms <= 0 || ! propdata->ofenabled )
6861  return SCIP_OKAY;
6862 
6863  assert( propdata->permvars != NULL );
6864  assert( propdata->npermvars > 0 );
6865  assert( propdata->permvarmap != NULL );
6866  assert( propdata->permstrans != NULL );
6867  assert( propdata->inactiveperms != NULL );
6868  assert( propdata->components != NULL );
6869  assert( propdata->componentbegins != NULL );
6870  assert( propdata->vartocomponent != NULL );
6871  assert( propdata->ncomponents > 0 );
6872 
6873  permvars = propdata->permvars;
6874  npermvars = propdata->npermvars;
6875  nbinpermvars = propdata->nbinpermvars;
6876  permstrans = propdata->permstrans;
6877  inactiveperms = propdata->inactiveperms;
6878  components = propdata->components;
6879  componentbegins = propdata->componentbegins;
6880  vartocomponent = propdata->vartocomponent;
6881  ncomponents = propdata->ncomponents;
6882 
6883  /* init bitset for marking variables (globally fixed or) branched to 1 */
6884  assert( propdata->bg1 != NULL );
6885  assert( propdata->bg1list != NULL );
6886  assert( propdata->nbg1 >= 0 );
6887  assert( propdata->nbg1 <= npermvars );
6888 
6889  bg1 = propdata->bg1;
6890  bg1list = propdata->bg1list;
6891  nbg1 = propdata->nbg1;
6892 
6893  /* get branching variables */
6894  SCIP_CALL( computeBranchingVariables(scip, npermvars, propdata->permvarmap, bg1, bg1list, &nbg1) );
6895  assert( nbg1 >= propdata->nbg1 );
6896 
6897  /* reset inactive permutations */
6898  nactiveperms = nperms;
6899  for (p = 0; p < nperms; ++p)
6900  propdata->inactiveperms[p] = FALSE;
6901 
6902  /* get pointers for bg0 */
6903  assert( propdata->bg0 != NULL );
6904  assert( propdata->bg0list != NULL );
6905  assert( propdata->nbg0 >= 0 );
6906  assert( propdata->nbg0 <= npermvars );
6907 
6908  bg0 = propdata->bg0;
6909  bg0list = propdata->bg0list;
6910  nbg0 = propdata->nbg0;
6911 
6912  /* filter out permutations that move variables that are fixed to 0 */
6913  for (j = 0; j < nbg0 && nactiveperms > 0; ++j)
6914  {
6915  int* pt;
6916 
6917  v = bg0list[j];
6918  assert( 0 <= v && v < npermvars );
6919  assert( bg0[v] );
6920 
6921  componentidx = vartocomponent[v];
6922 
6923  /* skip unaffected variables and blocked components */
6924  if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6925  continue;
6926 
6927  pt = permstrans[v];
6928  assert( pt != NULL );
6929 
6930  for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6931  {
6932  int img;
6933  int perm;
6934 
6935  perm = components[p];
6936 
6937  /* skip inactive permutations */
6938  if ( inactiveperms[perm] )
6939  continue;
6940 
6941  img = pt[perm];
6942 
6943  if ( img != v )
6944  {
6945 #ifndef NDEBUG
6946  SCIP_VAR* varv = permvars[v];
6947  SCIP_VAR* varimg = permvars[img];
6948 
6949  /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
6950  assert( SCIPvarGetType(varv) == SCIPvarGetType(varimg) ||
6951  (SCIPvarIsBinary(varv) && SCIPvarIsBinary(varimg)) ||
6953  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6954  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) ||
6956  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6957  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) );
6958  assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
6959 #endif
6960 
6961  /* we are moving a variable globally fixed to 0 to a variable not of this type */
6962  if ( ! bg0[img] )
6963  {
6964  inactiveperms[perm] = TRUE; /* mark as inactive */
6965  --nactiveperms;
6966  }
6967  }
6968  }
6969  }
6970 
6971  /* filter out permutations that move variables that are fixed to different values */
6972  for (j = 0; j < nbg1 && nactiveperms > 0; ++j)
6973  {
6974  int* pt;
6975 
6976  v = bg1list[j];
6977  assert( 0 <= v && v < npermvars );
6978  assert( bg1[v] );
6979 
6980  componentidx = vartocomponent[v];
6981 
6982  /* skip unaffected variables and blocked components */
6983  if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6984  continue;
6985 
6986  pt = permstrans[v];
6987  assert( pt != NULL );
6988 
6989  for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6990  {
6991  int img;
6992  int perm;
6993 
6994  perm = components[p];
6995 
6996  /* skip inactive permutations */
6997  if ( inactiveperms[perm] )
6998  continue;
6999 
7000  img = pt[perm];
7001 
7002  if ( img != v )
7003  {
7004 #ifndef NDEBUG
7005  SCIP_VAR* varv = permvars[v];
7006  SCIP_VAR* varimg = permvars[img];
7007 
7008  /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
7009  assert( SCIPvarGetType(varv) == SCIPvarGetType(varimg) ||
7010  (SCIPvarIsBinary(varv) && SCIPvarIsBinary(varimg)) ||
7012  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
7013  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) ||
7015  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
7016  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) );
7017  assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
7018 #endif
7019 
7020  /* we are moving a variable globally fixed or branched to 1 to a variable not of this type */
7021  if ( ! bg1[img] )
7022  {
7023  inactiveperms[perm] = TRUE; /* mark as inactive */
7024  --nactiveperms;
7025  }
7026  }
7027  }
7028  }
7029 
7030  /* Clean bg1 list - need to do this after the main loop! (Not needed for bg0.)
7031  * Note that variables globally fixed to 1 are not resetted, since the loop starts at propdata->nbg1. */
7032  for (j = propdata->nbg1; j < nbg1; ++j)
7033  bg1[bg1list[j]] = FALSE;
7034 
7035  /* exit if no active permuations left */
7036  if ( nactiveperms == 0 )
7037  return SCIP_OKAY;
7038 
7039  /* compute orbits of binary variables */
7040  SCIP_CALL( SCIPallocBufferArray(scip, &orbits, nbinpermvars) );
7041  SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, nbinpermvars) );
7042  SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, nbinpermvars, permstrans, nperms, inactiveperms,
7043  orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent, propdata->componentblocked, ncomponents, propdata->nmovedpermvars) );
7044 
7045  if ( norbits > 0 )
7046  {
7047  int nfixedzero = 0;
7048  int nfixedone = 0;
7049 
7050  SCIPdebugMsg(scip, "Perform orbital fixing on %d orbits (%d active perms).\n", norbits, nactiveperms);
7051  SCIP_CALL( performOrbitalFixing(scip, permvars, nbinpermvars, orbits, orbitbegins, norbits, infeasible, &nfixedzero, &nfixedone) );
7052 
7053  propdata->nfixedzero += nfixedzero;
7054  propdata->nfixedone += nfixedone;
7055  *nprop = nfixedzero + nfixedone;
7056 
7057  SCIPdebugMsg(scip, "Orbital fixings: %d 0s, %d 1s.\n", nfixedzero, nfixedone);
7058  }
7059 
7060  SCIPfreeBufferArray(scip, &orbitbegins);
7061  SCIPfreeBufferArray(scip, &orbits);
7062 
7063  return SCIP_OKAY;
7064 }
7065 
7066 
7067 
7068 /*
7069  * Callback methods of propagator
7070  */
7071 
7072 /** presolving initialization method of propagator (called when presolving is about to begin) */
7073 static
7074 SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
7075 { /*lint --e{715}*/
7076  SCIP_PROPDATA* propdata;
7077 
7078  assert( scip != NULL );
7079  assert( prop != NULL );
7080 
7081  propdata = SCIPpropGetData(prop);
7082  assert( propdata != NULL );
7083 
7084  /* get nonlinear conshdlr for future checks on whether there are nonlinear constraints */
7085  propdata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
7086 
7087  /* check whether we should run */
7088  if ( propdata->usesymmetry < 0 )
7089  {
7090  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
7092  }
7093  else if ( SCIPgetNRuns(scip) > propdata->lastrestart && isSymmetryRecomputationRequired(scip, propdata) )
7094  {
7095  assert( SCIPgetNRuns(scip) > 1 );
7096 
7098  }
7099 
7100  /* add symmetry handling constraints if required */
7101  if ( (propdata->symconsenabled || propdata->sstenabled) && propdata->addconsstiming == 0 )
7102  {
7103  SCIPdebugMsg(scip, "Try to add symmetry handling constraints before presolving.");
7104 
7106  }
7107  else if ( propdata->ofenabled && propdata->ofsymcomptiming == 0 )
7108  {
7109  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Symmetry computation before presolving:\n");
7110 
7111  /* otherwise compute symmetry if timing requests it; fix non-binary potential branching variables */
7112  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7113  {
7115  }
7116  else
7117  {
7119  }
7120  assert( propdata->binvaraffected || ! propdata->ofenabled );
7121  }
7122 
7123  return SCIP_OKAY;
7124 }
7125 
7126 
7127 /** presolving deinitialization method of propagator (called after presolving has been finished) */
7128 static
7129 SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
7130 { /*lint --e{715}*/
7131  SCIP_PROPDATA* propdata;
7132 
7133  assert( scip != NULL );
7134  assert( prop != NULL );
7135  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7136 
7137  SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
7138 
7139  propdata = SCIPpropGetData(prop);
7140  assert( propdata != NULL );
7141  assert( propdata->usesymmetry >= 0 );
7142 
7143  /* guarantee that symmetries are computed (and handled) if the solving process has not been interrupted
7144  * and even if presolving has been disabled */
7145  if ( (propdata->symconsenabled || propdata->sstenabled) && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7146  {
7148  }
7149 
7150  /* if timing requests it, guarantee that symmetries are computed even if presolving is disabled */
7151  if ( propdata->ofenabled && propdata->ofsymcomptiming <= 1 && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7152  {
7153  /* fix non-binary potential branching variables */
7154  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7155  {
7157  }
7158  else
7159  {
7161  }
7162  assert( propdata->binvaraffected || ! propdata->ofenabled );
7163  }
7164 
7165  return SCIP_OKAY;
7166 }
7167 
7168 
7169 /** presolving method of propagator */
7170 static
7171 SCIP_DECL_PROPPRESOL(propPresolSymmetry)
7172 { /*lint --e{715}*/
7173  SCIP_PROPDATA* propdata;
7174  int i;
7175 
7176  assert( scip != NULL );
7177  assert( prop != NULL );
7178  assert( result != NULL );
7179  assert( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING );
7180 
7181  *result = SCIP_DIDNOTRUN;
7182 
7183  propdata = SCIPpropGetData(prop);
7184  assert( propdata != NULL );
7185  assert( propdata->usesymmetry >= 0 );
7186 
7187  /* possibly create symmetry handling constraints */
7188  if ( propdata->symconsenabled || propdata->sstenabled )
7189  {
7190  int noldngenconns;
7191  int nchanges = 0;
7192  SCIP_Bool earlyterm = FALSE;
7193 
7194  /* skip presolving if we are not at the end if addconsstiming == 2 */
7195  assert( 0 <= propdata->addconsstiming && propdata->addconsstiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7196  if ( propdata->addconsstiming > SYM_COMPUTETIMING_DURINGPRESOL && ! SCIPisPresolveFinished(scip) )
7197  return SCIP_OKAY;
7198 
7199  /* possibly stop */
7200  if ( SCIPisStopped(scip) )
7201  return SCIP_OKAY;
7202 
7203  noldngenconns = propdata->ngenorbconss + propdata->nsstconss + propdata->ngenlinconss;
7204 
7205  SCIP_CALL( tryAddSymmetryHandlingConss(scip, prop, &nchanges, &earlyterm) );
7206 
7207  /* if we actually tried to add symmetry handling constraints */
7208  if ( ! earlyterm ) /*lint !e774*/
7209  {
7210  *result = SCIP_DIDNOTFIND;
7211 
7212  if ( nchanges > 0 )
7213  {
7214  *result = SCIP_SUCCESS;
7215  *nchgbds += nchanges;
7216  }
7217 
7218  /* if symmetry handling constraints have been added, presolve each */
7219  if ( propdata->ngenorbconss > 0 || propdata->ngenlinconss > 0 || propdata->nsstconss > 0 )
7220  {
7221  /* at this point, the symmetry group should be computed and nontrivial */
7222  assert( propdata->nperms > 0 );
7223  assert( propdata->triedaddconss );
7224 
7225  /* we have added at least one symmetry handling constraints, i.e., we were successful */
7226  *result = SCIP_SUCCESS;
7227 
7228  *naddconss += propdata->ngenorbconss + propdata->ngenlinconss + propdata->nsstconss - noldngenconns;
7229  SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", *naddconss);
7230 
7231  /* if constraints have been added, loop through generated constraints and presolve each */
7232  for (i = 0; i < propdata->ngenorbconss; ++i)
7233  {
7234  SCIP_CALL( SCIPpresolCons(scip, propdata->genorbconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7235  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7236  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7237 
7238  /* exit if cutoff or unboundedness has been detected */
7239  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7240  {
7241  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genorbconss[i]));
7242  return SCIP_OKAY;
7243  }
7244  }
7245 
7246  for (i = 0; i < propdata->ngenlinconss; ++i)
7247  {
7248  SCIP_CALL( SCIPpresolCons(scip, propdata->genlinconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7249  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7250  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7251 
7252  /* exit if cutoff or unboundedness has been detected */
7253  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7254  {
7255  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genlinconss[i]));
7256  return SCIP_OKAY;
7257  }
7258  }
7259  SCIPdebugMsg(scip, "Presolved %d generated constraints.\n",
7260  propdata->ngenorbconss + propdata->ngenlinconss);
7261 
7262  for (i = 0; i < propdata->nsstconss; ++i)
7263  {
7264  SCIP_CALL( SCIPpresolCons(scip, propdata->sstconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7265  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7266  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7267 
7268  /* exit if cutoff or unboundedness has been detected */
7269  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7270  {
7271  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->sstconss[i]));
7272  return SCIP_OKAY;
7273  }
7274  }
7275  SCIPdebugMsg(scip, "Presolved %d generated Schreier Sims constraints.\n", propdata->nsstconss);
7276  }
7277  }
7278  }
7279 
7280  /* run OF presolving */
7281  assert( 0 <= propdata->ofsymcomptiming && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7282  if ( propdata->ofenabled && propdata->performpresolving && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_DURINGPRESOL )
7283  {
7284  SCIP_Bool infeasible;
7285  int nprop;
7286 
7287  /* if we have not tried to add symmetry handling constraints */
7288  if ( *result == SCIP_DIDNOTRUN )
7289  *result = SCIP_DIDNOTFIND;
7290 
7291  SCIPdebugMsg(scip, "Presolving <%s>.\n", PROP_NAME);
7292 
7293  SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7294 
7295  if ( infeasible )
7296  {
7297  *result = SCIP_CUTOFF;
7298  propdata->offoundreduction = TRUE;
7299  }
7300  else if ( nprop > 0 )
7301  {
7302  *result = SCIP_SUCCESS;
7303  *nfixedvars += nprop;
7304  propdata->offoundreduction = TRUE;
7305  }
7306  }
7307  else if ( propdata->ofenabled && propdata->ofsymcomptiming == SYM_COMPUTETIMING_DURINGPRESOL )
7308  {
7309  /* otherwise compute symmetry early if timing requests it; fix non-binary potential branching variables */
7310  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7311  {
7313  }
7314  else
7315  {
7317  }
7318  assert( propdata->binvaraffected || ! propdata->ofenabled );
7319  }
7320 
7321  return SCIP_OKAY;
7322 }
7323 
7324 
7325 /** execution method of propagator */
7326 static
7327 SCIP_DECL_PROPEXEC(propExecSymmetry)
7328 { /*lint --e{715}*/
7329  SCIP_PROPDATA* propdata;
7330  SCIP_Bool infeasible = FALSE;
7331  SCIP_Longint nodenumber;
7332  int nprop = 0;
7333 
7334  assert( scip != NULL );
7335  assert( result != NULL );
7336 
7337  *result = SCIP_DIDNOTRUN;
7338 
7339  /* do not run if we are in the root or not yet solving */
7340  if ( SCIPgetDepth(scip) <= 0 || SCIPgetStage(scip) < SCIP_STAGE_SOLVING )
7341  return SCIP_OKAY;
7342 
7343  /* do nothing if we are in a probing node */
7344  if ( SCIPinProbing(scip) )
7345  return SCIP_OKAY;
7346 
7347  /* do not run again in repropagation, since the path to the root might have changed */
7348  if ( SCIPinRepropagation(scip) )
7349  return SCIP_OKAY;
7350 
7351  /* get data */
7352  propdata = SCIPpropGetData(prop);
7353  assert( propdata != NULL );
7354 
7355  /* if usesymmetry has not been read so far */
7356  if ( propdata->usesymmetry < 0 )
7357  {
7358  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
7360  }
7361  else if ( SCIPgetNRuns(scip) > propdata->lastrestart && isSymmetryRecomputationRequired(scip, propdata) )
7362  {
7363  assert( SCIPgetNRuns(scip) > 1 );
7364 
7366  }
7367 
7368  /* do not propagate if orbital fixing is not enabled */
7369  if ( ! propdata->ofenabled )
7370  return SCIP_OKAY;
7371 
7372  /* return if there is no symmetry available */
7373  if ( propdata->nperms == 0 )
7374  return SCIP_OKAY;
7375 
7376  /* return if we already ran in this node */
7377  nodenumber = SCIPnodeGetNumber(SCIPgetCurrentNode(scip));
7378  if ( nodenumber == propdata->nodenumber )
7379  return SCIP_OKAY;
7380  propdata->nodenumber = nodenumber;
7381 
7382  /* propagate */
7383  *result = SCIP_DIDNOTFIND;
7384 
7385  SCIPdebugMsg(scip, "Propagating <%s>.\n", SCIPpropGetName(prop));
7386 
7387  SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7388 
7389  if ( infeasible )
7390  {
7391  *result = SCIP_CUTOFF;
7392  propdata->offoundreduction = TRUE;
7393  }
7394  else if ( nprop > 0 )
7395  {
7396  *result = SCIP_REDUCEDDOM;
7397  propdata->offoundreduction = TRUE;
7398  }
7399 
7400  return SCIP_OKAY;
7401 }
7402 
7403 
7404 /** deinitialization method of propagator (called before transformed problem is freed) */
7405 static
7406 SCIP_DECL_PROPEXIT(propExitSymmetry)
7408  SCIP_PROPDATA* propdata;
7409 
7410  assert( scip != NULL );
7411  assert( prop != NULL );
7412  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7413 
7414  SCIPdebugMsg(scip, "Exiting propagator <%s>.\n", PROP_NAME);
7415 
7416  propdata = SCIPpropGetData(prop);
7417  assert( propdata != NULL );
7418 
7419  SCIP_CALL( freeSymmetryData(scip, propdata) );
7420 
7421  /* reset basic data */
7422  propdata->usesymmetry = -1;
7423  propdata->symconsenabled = FALSE;
7424  propdata->triedaddconss = FALSE;
7425  propdata->nsymresacks = 0;
7426  propdata->norbitopes = 0;
7427  propdata->ofenabled = FALSE;
7428  propdata->sstenabled = FALSE;
7429  propdata->lastrestart = 0;
7430  propdata->nfixedzero = 0;
7431  propdata->nfixedone = 0;
7432  propdata->nodenumber = -1;
7433  propdata->offoundreduction = FALSE;
7434 
7435  return SCIP_OKAY;
7436 }
7437 
7438 
7439 /** propagation conflict resolving method of propagator
7440  *
7441  * @todo Implement reverse propagation.
7442  *
7443  * Note that this is relatively difficult to obtain: One needs to include all bounds of variables that are responsible
7444  * for creating the orbit in which the variables that was propagated lies. This includes all variables that are moved
7445  * by the permutations which are involved in creating the orbit.
7446  */
7447 static
7448 SCIP_DECL_PROPRESPROP(propRespropSymmetry)
7449 { /*lint --e{715,818}*/
7450  assert( result != NULL );
7451 
7452  *result = SCIP_DIDNOTFIND;
7453 
7454  return SCIP_OKAY;
7455 }
7456 
7457 
7458 /** destructor of propagator to free user data (called when SCIP is exiting) */
7459 static
7460 SCIP_DECL_PROPFREE(propFreeSymmetry)
7461 { /*lint --e{715}*/
7462  SCIP_PROPDATA* propdata;
7463 
7464  assert( scip != NULL );
7465  assert( prop != NULL );
7466  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7467 
7468  SCIPdebugMsg(scip, "Freeing symmetry propagator.\n");
7469 
7470  propdata = SCIPpropGetData(prop);
7471  assert( propdata != NULL );
7472 
7473  SCIPfreeBlockMemory(scip, &propdata);
7474 
7475  return SCIP_OKAY;
7476 }
7477 
7478 
7479 /*
7480  * External methods
7481  */
7482 
7483 /** include symmetry propagator */
7485  SCIP* scip /**< SCIP data structure */
7486  )
7487 {
7488  SCIP_TABLEDATA* tabledata;
7489  SCIP_PROPDATA* propdata = NULL;
7490  SCIP_PROP* prop = NULL;
7491 
7492  SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
7493  assert( propdata != NULL );
7494 
7495  propdata->npermvars = 0;
7496  propdata->nbinpermvars = 0;
7497  propdata->permvars = NULL;
7498 #ifndef NDEBUG
7499  propdata->permvarsobj = NULL;
7500 #endif
7501  propdata->nperms = -1;
7502  propdata->nmaxperms = 0;
7503  propdata->perms = NULL;
7504  propdata->permstrans = NULL;
7505  propdata->permvarmap = NULL;
7506  propdata->nonbinpermvarcaptured = NULL;
7507 
7508  propdata->ncomponents = -1;
7509  propdata->ncompblocked = 0;
7510  propdata->components = NULL;
7511  propdata->componentbegins = NULL;
7512  propdata->vartocomponent = NULL;
7513  propdata->componentblocked = NULL;
7514 
7515  propdata->log10groupsize = -1.0;
7516  propdata->nmovedvars = -1;
7517  propdata->binvaraffected = FALSE;
7518  propdata->computedsymmetry = FALSE;
7519  propdata->conshdlr_nonlinear = NULL;
7520 
7521  propdata->usesymmetry = -1;
7522  propdata->symconsenabled = FALSE;
7523  propdata->triedaddconss = FALSE;
7524  propdata->genorbconss = NULL;
7525  propdata->genlinconss = NULL;
7526  propdata->ngenorbconss = 0;
7527  propdata->ngenlinconss = 0;
7528  propdata->genlinconsssize = 0;
7529  propdata->nsymresacks = 0;
7530  propdata->norbitopes = 0;
7531  propdata->isnonlinvar = NULL;
7532 
7533  propdata->ofenabled = FALSE;
7534  propdata->bg0 = NULL;
7535  propdata->bg0list = NULL;
7536  propdata->nbg0 = 0;
7537  propdata->bg1 = NULL;
7538  propdata->bg1list = NULL;
7539  propdata->nbg1 = 0;
7540  propdata->permvarsevents = NULL;
7541  propdata->inactiveperms = NULL;
7542  propdata->nmovedpermvars = -1;
7543  propdata->nmovedbinpermvars = 0;
7544  propdata->nmovedintpermvars = 0;
7545  propdata->nmovedimplintpermvars = 0;
7546  propdata->nmovedcontpermvars = 0;
7547  propdata->lastrestart = 0;
7548  propdata->nfixedzero = 0;
7549  propdata->nfixedone = 0;
7550  propdata->nodenumber = -1;
7551  propdata->offoundreduction = FALSE;
7552 
7553  propdata->sstenabled = FALSE;
7554  propdata->sstconss = NULL;
7555  propdata->nsstconss = 0;
7556  propdata->maxnsstconss = 0;
7557  propdata->leaders = NULL;
7558  propdata->nleaders = 0;
7559  propdata->maxnleaders = 0;
7560 
7561  /* create event handler */
7562  propdata->eventhdlr = NULL;
7564  eventExecSymmetry, NULL) );
7565  assert( propdata->eventhdlr != NULL );
7566 
7567  /* include constraint handler */
7569  PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING, propExecSymmetry, propdata) );
7570  assert( prop != NULL );
7571 
7572  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSymmetry) );
7573  SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSymmetry) );
7574  SCIP_CALL( SCIPsetPropInitpre(scip, prop, propInitpreSymmetry) );
7575  SCIP_CALL( SCIPsetPropExitpre(scip, prop, propExitpreSymmetry) );
7576  SCIP_CALL( SCIPsetPropResprop(scip, prop, propRespropSymmetry) );
7578 
7579  /* include table */
7580  SCIP_CALL( SCIPallocBlockMemory(scip, &tabledata) );
7581  tabledata->propdata = propdata;
7583  NULL, tableFreeOrbitalfixing, NULL, NULL, NULL, NULL, tableOutputOrbitalfixing,
7585 
7586  /* add parameters for computing symmetry */
7587  SCIP_CALL( SCIPaddIntParam(scip,
7588  "propagating/" PROP_NAME "/maxgenerators",
7589  "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
7590  &propdata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
7591 
7593  "propagating/" PROP_NAME "/checksymmetries",
7594  "Should all symmetries be checked after computation?",
7595  &propdata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
7596 
7598  "propagating/" PROP_NAME "/displaynorbitvars",
7599  "Should the number of variables affected by some symmetry be displayed?",
7600  &propdata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
7601 
7603  "propagating/" PROP_NAME "/doubleequations",
7604  "Double equations to positive/negative version?",
7605  &propdata->doubleequations, TRUE, DEFAULT_DOUBLEEQUATIONS, NULL, NULL) );
7606 
7607  /* add parameters for adding symmetry handling constraints */
7609  "propagating/" PROP_NAME "/conssaddlp",
7610  "Should the symmetry breaking constraints be added to the LP?",
7611  &propdata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
7612 
7614  "propagating/" PROP_NAME "/addsymresacks",
7615  "Add inequalities for symresacks for each generator?",
7616  &propdata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
7617 
7619  "propagating/" PROP_NAME "/detectorbitopes",
7620  "Should we check whether the components of the symmetry group can be handled by orbitopes?",
7621  &propdata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
7622 
7624  "propagating/" PROP_NAME "/detectsubgroups",
7625  "Should we try to detect symmetric subgroups of the symmetry group on binary variables?",
7626  &propdata->detectsubgroups, TRUE, DEFAULT_DETECTSUBGROUPS, NULL, NULL) );
7627 
7629  "propagating/" PROP_NAME "/addweaksbcs",
7630  "Should we add weak SBCs for enclosing orbit of symmetric subgroups?",
7631  &propdata->addweaksbcs, TRUE, DEFAULT_ADDWEAKSBCS, NULL, NULL) );
7632 
7633  SCIP_CALL( SCIPaddIntParam(scip,
7634  "propagating/" PROP_NAME "/addconsstiming",
7635  "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving)",
7636  &propdata->addconsstiming, TRUE, DEFAULT_ADDCONSSTIMING, 0, 2, NULL, NULL) );
7637 
7638  /* add parameters for orbital fixing */
7639  SCIP_CALL( SCIPaddIntParam(scip,
7640  "propagating/" PROP_NAME "/ofsymcomptiming",
7641  "timing of symmetry computation for orbital fixing (0 = before presolving, 1 = during presolving, 2 = at first call)",
7642  &propdata->ofsymcomptiming, TRUE, DEFAULT_OFSYMCOMPTIMING, 0, 2, NULL, NULL) );
7643 
7645  "propagating/" PROP_NAME "/performpresolving",
7646  "run orbital fixing during presolving?",
7647  &propdata->performpresolving, TRUE, DEFAULT_PERFORMPRESOLVING, NULL, NULL) );
7648 
7649  SCIP_CALL( SCIPaddIntParam(scip,
7650  "propagating/" PROP_NAME "/recomputerestart",
7651  "recompute symmetries after a restart has occured? (0 = never, 1 = always, 2 = if OF found reduction)",
7652  &propdata->recomputerestart, TRUE, DEFAULT_RECOMPUTERESTART, 0, 2, NULL, NULL) );
7653 
7655  "propagating/" PROP_NAME "/compresssymmetries",
7656  "Should non-affected variables be removed from permutation to save memory?",
7657  &propdata->compresssymmetries, TRUE, DEFAULT_COMPRESSSYMMETRIES, NULL, NULL) );
7658 
7660  "propagating/" PROP_NAME "/compressthreshold",
7661  "Compression is used if percentage of moved vars is at most the threshold.",
7662  &propdata->compressthreshold, TRUE, DEFAULT_COMPRESSTHRESHOLD, 0.0, 1.0, NULL, NULL) );
7663 
7665  "propagating/" PROP_NAME "/usecolumnsparsity",
7666  "Should the number of conss a variable is contained in be exploited in symmetry detection?",
7667  &propdata->usecolumnsparsity, TRUE, DEFAULT_USECOLUMNSPARSITY, NULL, NULL) );
7668 
7669  SCIP_CALL( SCIPaddIntParam(scip,
7670  "propagating/" PROP_NAME "/maxnconsssubgroup",
7671  "maximum number of constraints up to which subgroup structures are detected",
7672  &propdata->maxnconsssubgroup, TRUE, DEFAULT_MAXNCONSSSUBGROUP, 0, INT_MAX, NULL, NULL) );
7673 
7675  "propagating/" PROP_NAME "/usedynamicprop",
7676  "whether dynamic propagation should be used for full orbitopes",
7677  &propdata->usedynamicprop, TRUE, DEFAULT_USEDYNAMICPROP, NULL, NULL) );
7678 
7680  "propagating/" PROP_NAME "/addstrongsbcs",
7681  "Should strong SBCs for enclosing orbit of symmetric subgroups be added if orbitopes are not used?",
7682  &propdata->addstrongsbcs, TRUE, DEFAULT_ADDSTRONGSBCS, NULL, NULL) );
7683 
7684  SCIP_CALL( SCIPaddIntParam(scip,
7685  "propagating/" PROP_NAME "/ssttiebreakrule",
7686  "rule to select the orbit in Schreier Sims inequalities (variable in 0: minimum size orbit; 1: maximum size orbit; 2: orbit with most variables in conflict with leader)",
7687  &propdata->ssttiebreakrule, TRUE, DEFAULT_SSTTIEBREAKRULE, 0, 2, NULL, NULL) );
7688 
7689  SCIP_CALL( SCIPaddIntParam(scip,
7690  "propagating/" PROP_NAME "/sstleaderrule",
7691  "rule to select the leader in an orbit (0: first var; 1: last var; 2: var having most conflicting vars in orbit; 3: var having most conflicting vars in problem)",
7692  &propdata->sstleaderrule, TRUE, DEFAULT_SSTLEADERRULE, 0, 3, NULL, NULL) );
7693 
7694  SCIP_CALL( SCIPaddIntParam(scip,
7695  "propagating/" PROP_NAME "/sstleadervartype",
7696  "bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);" \
7697  "if multiple types are allowed, take the one with most affected vars",
7698  &propdata->sstleadervartype, TRUE, DEFAULT_SSTLEADERVARTYPE, 1, 15, NULL, NULL) );
7699 
7701  "propagating/" PROP_NAME "/addconflictcuts",
7702  "Should Schreier Sims constraints be added if we use a conflict based rule?",
7703  &propdata->addconflictcuts, TRUE, DEFAULT_ADDCONFLICTCUTS, NULL, NULL) );
7704 
7706  "propagating/" PROP_NAME "/sstaddcuts",
7707  "Should Schreier Sims constraints be added?",
7708  &propdata->sstaddcuts, TRUE, DEFAULT_SSTADDCUTS, NULL, NULL) );
7709 
7711  "propagating/" PROP_NAME "/sstmixedcomponents",
7712  "Should Schreier Sims constraints be added if a symmetry component contains variables of different types?",
7713  &propdata->sstmixedcomponents, TRUE, DEFAULT_SSTMIXEDCOMPONENTS, NULL, NULL) );
7714 
7716  "propagating/" PROP_NAME "/symfixnonbinaryvars",
7717  "Whether all non-binary variables shall be not affected by symmetries if OF is active?",
7718  &propdata->symfixnonbinaryvars, TRUE, DEFAULT_SYMFIXNONBINARYVARS, NULL, NULL) );
7719 
7721  "propagating/" PROP_NAME "/onlybinarysymmetry",
7722  "Is only symmetry on binary variables used?",
7723  &propdata->onlybinarysymmetry, TRUE, DEFAULT_ONLYBINARYSYMMETRY, NULL, NULL) );
7724 
7726  "propagating/" PROP_NAME "/preferlessrows",
7727  "Shall orbitopes with less rows be preferred in detection?",
7728  &propdata->preferlessrows, TRUE, DEFAULT_PREFERLESSROWS, NULL, NULL) );
7729 
7730  /* possibly add description */
7731  if ( SYMcanComputeSymmetry() )
7732  {
7734  }
7735 
7736  return SCIP_OKAY;
7737 }
7738 
7739 
7740 /** return currently available symmetry group information */
7742  SCIP* scip, /**< SCIP data structure */
7743  int* npermvars, /**< pointer to store number of variables for permutations */
7744  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
7745  SCIP_HASHMAP** permvarmap, /**< pointer to store hash map of permvars (or NULL) */
7746  int* nperms, /**< pointer to store number of permutations */
7747  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix (or NULL)*/
7748  int*** permstrans, /**< pointer to store permutation generators as (npermvars x nperms) matrix (or NULL)*/
7749  SCIP_Real* log10groupsize, /**< pointer to store log10 of group size (or NULL) */
7750  SCIP_Bool* binvaraffected, /**< pointer to store whether binary variables are affected (or NULL) */
7751  int** components, /**< pointer to store components of symmetry group (or NULL) */
7752  int** componentbegins, /**< pointer to store begin positions of components in components array (or NULL) */
7753  int** vartocomponent, /**< pointer to store assignment from variable to its component (or NULL) */
7754  int* ncomponents /**< pointer to store number of components (or NULL) */
7755  )
7756 {
7757  SCIP_PROPDATA* propdata;
7758  SCIP_PROP* prop;
7759 
7760  assert( scip != NULL );
7761  assert( npermvars != NULL );
7762  assert( permvars != NULL );
7763  assert( nperms != NULL );
7764  assert( perms != NULL || permstrans != NULL );
7765  assert( ncomponents != NULL || (components == NULL && componentbegins == NULL && vartocomponent == NULL) );
7766 
7767  /* find symmetry propagator */
7768  prop = SCIPfindProp(scip, "symmetry");
7769  if ( prop == NULL )
7770  {
7771  SCIPerrorMessage("Could not find symmetry propagator.\n");
7772  return SCIP_PLUGINNOTFOUND;
7773  }
7774  assert( prop != NULL );
7775  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7776 
7777  propdata = SCIPpropGetData(prop);
7778  assert( propdata != NULL );
7779 
7780  *npermvars = propdata->npermvars;
7781  *permvars = propdata->permvars;
7782 
7783  if ( permvarmap != NULL )
7784  *permvarmap = propdata->permvarmap;
7785 
7786  *nperms = propdata->nperms;
7787  if ( perms != NULL )
7788  {
7789  *perms = propdata->perms;
7790  assert( *perms != NULL || *nperms <= 0 );
7791  }
7792 
7793  if ( permstrans != NULL )
7794  {
7795  *permstrans = propdata->permstrans;
7796  assert( *permstrans != NULL || *nperms <= 0 );
7797  }
7798 
7799  if ( log10groupsize != NULL )
7800  *log10groupsize = propdata->log10groupsize;
7801 
7802  if ( binvaraffected != NULL )
7803  *binvaraffected = propdata->binvaraffected;
7804 
7805  if ( components != NULL )
7806  *components = propdata->components;
7807 
7808  if ( componentbegins != NULL )
7809  *componentbegins = propdata->componentbegins;
7810 
7811  if ( vartocomponent )
7812  *vartocomponent = propdata->vartocomponent;
7813 
7814  if ( ncomponents )
7815  *ncomponents = propdata->ncomponents;
7816 
7817  return SCIP_OKAY;
7818 }
7819 
7820 /** return whether orbital fixing is enabled */
7822  SCIP* scip /**< SCIP data structure */
7823  )
7824 {
7825  SCIP_PROP* prop;
7826  SCIP_PROPDATA* propdata;
7827 
7828  assert( scip != NULL );
7829 
7830  prop = SCIPfindProp(scip, PROP_NAME);
7831  if ( prop == NULL )
7832  return FALSE;
7833 
7834  propdata = SCIPpropGetData(prop);
7835  assert( propdata != NULL );
7836 
7837  return propdata->ofenabled;
7838 }
7839 
7840 /** return number of the symmetry group's generators */
7842  SCIP* scip /**< SCIP data structure */
7843  )
7844 {
7845  SCIP_PROP* prop;
7846  SCIP_PROPDATA* propdata;
7847 
7848  assert( scip != NULL );
7849 
7850  prop = SCIPfindProp(scip, PROP_NAME);
7851  if ( prop == NULL )
7852  return 0;
7853 
7854  propdata = SCIPpropGetData(prop);
7855  assert( propdata != NULL );
7856 
7857  if ( propdata->nperms < 0 )
7858  return 0;
7859  else
7860  return propdata->nperms;
7861 }
SCIP_RETCODE SCIPcomputeComponentsSym(SCIP *scip, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, SCIP_Bool transposed, int **components, int **componentbegins, int **vartocomponent, unsigned **componentblocked, int *ncomponents)
Definition: symmetry.c:757
SCIP_RETCODE SCIPsetPropPresol(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPPRESOL((*proppresol)), int presolpriority, int presolmaxrounds, SCIP_PRESOLTIMING presoltiming)
Definition: scip_prop.c:270
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:101
enum SCIP_BoundType SCIP_BOUNDTYPE
Definition: type_lp.h:50
int SCIPgetNIntVars(SCIP *scip)
Definition: scip_prob.c:2081
static SCIP_RETCODE addSSTConssOrbitAndUpdateSST(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_PROPDATA *propdata, SCIP_VAR **permvars, int *orbits, int *orbitbegins, int orbitidx, int orbitleaderidx, SCIP_Shortbool *orbitvarinconflict, int norbitvarinconflict, int *nchgbds, SCIP_Bool useconflictgraph)
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:90
SCIP_Bool SCIPinRepropagation(SCIP *scip)
Definition: scip_tree.c:137
SCIP_Real SCIPgetSolvingTime(SCIP *scip)
Definition: scip_timing.c:369
void SCIPsortIntIntPtr(int *intarray1, int *intarray2, void **ptrarray, int len)
#define DEFAULT_COMPRESSTHRESHOLD
SCIP_RETCODE SCIPexpriterInit(SCIP_EXPRITER *iterator, SCIP_EXPR *expr, SCIP_EXPRITER_TYPE type, SCIP_Bool allowrevisit)
Definition: expriter.c:491
static SCIP_RETCODE propagateOrbitalFixing(SCIP *scip, SCIP_PROPDATA *propdata, SCIP_Bool *infeasible, int *nprop)
#define DEFAULT_SSTMIXEDCOMPONENTS
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:84
SCIP_RETCODE SCIPtightenVarLb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition: scip_var.c:5200
SCIP_Real * matcoef
SCIP_RETCODE SCIPsimplifyExpr(SCIP *scip, SCIP_EXPR *rootexpr, SCIP_EXPR **simplified, SCIP_Bool *changed, SCIP_Bool *infeasible, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition: scip_expr.c:1762
SCIP_RETCODE SCIPincludeTable(SCIP *scip, const char *name, const char *desc, SCIP_Bool active, SCIP_DECL_TABLECOPY((*tablecopy)), SCIP_DECL_TABLEFREE((*tablefree)), SCIP_DECL_TABLEINIT((*tableinit)), SCIP_DECL_TABLEEXIT((*tableexit)), SCIP_DECL_TABLEINITSOL((*tableinitsol)), SCIP_DECL_TABLEEXITSOL((*tableexitsol)), SCIP_DECL_TABLEOUTPUT((*tableoutput)), SCIP_TABLEDATA *tabledata, int position, SCIP_STAGE earlieststage)
Definition: scip_table.c:47
SCIP_NODE * SCIPgetCurrentNode(SCIP *scip)
Definition: scip_tree.c:82
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:356
SCIP_Bool SCIPconsIsDynamic(SCIP_CONS *cons)
Definition: cons.c:8344
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition: misc.c:11175
#define DEFAULT_CHECKSYMMETRIES
Constraint handler for variable bound constraints .
static SCIP_RETCODE addSymresackConss(SCIP *scip, SCIP_PROP *prop, int *components, int *componentbegins, int ncomponents)
static SCIP_DECL_EVENTEXEC(eventExecSymmetry)
#define DEFAULT_SSTADDCUTS
SCIP_RETCODE SCIPhashtableInsert(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2487
static SCIP_RETCODE updateSymInfoConflictGraphSST(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_VAR **permvars, int npermvars, SCIP_Bool onlypermvars, SCIP_HASHMAP *varmap, int *orbits, int *orbitbegins, int norbits)
static SCIP_DECL_PROPPRESOL(propPresolSymmetry)
static SCIP_Bool isLeadervartypeCompatible(SCIP_VAR *var, int leadervartype)
SCIP_RETCODE SCIPcatchVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int *filterpos)
Definition: scip_event.c:345
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:877
#define DEFAULT_CONSSADDLP
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition: expr.c:3798
static SCIP_DECL_PROPEXEC(propExecSymmetry)
int SCIPgetNVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9395
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition: scip_mem.h:117
static SCIP_DECL_TABLEFREE(tableFreeOrbitalfixing)
SCIP_EXPR * SCIPexpriterGetParentDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:730
int SCIPgetNVarsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
SCIP_PROP * SCIPfindProp(SCIP *scip, const char *name)
Definition: scip_prop.c:320
SCIP_Real SCIPgetLhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:17910
int SCIPgetNVarsLogicor(SCIP *scip, SCIP_CONS *cons)
#define SCIP_MAXSTRLEN
Definition: def.h:293
SCIP_RETCODE SCIPgetSymmetry(SCIP *scip, int *npermvars, SCIP_VAR ***permvars, SCIP_HASHMAP **permvarmap, int *nperms, int ***perms, int ***permstrans, SCIP_Real *log10groupsize, SCIP_Bool *binvaraffected, int **components, int **componentbegins, int **vartocomponent, int *ncomponents)
SCIP_RETCODE SCIPcreateSymbreakCons(SCIP *scip, SCIP_CONS **cons, const char *name, int *perm, SCIP_VAR **vars, int nvars, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
#define ISSSTIMPLINTACTIVE(x)
#define SCIPallocClearBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:88
SCIP_RETCODE SCIPdelCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:2842
static SCIP_RETCODE addSSTConss(SCIP *scip, SCIP_PROPDATA *propdata, int *nchgbds)
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition: scip_mem.c:130
SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
SCIP_VAR ** SCIPgetVarsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:17966
int * SCIPdigraphGetSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition: misc.c:7721
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Real * SCIPgetBoundsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
static SCIP_RETCODE freeSymmetryData(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPincludeEventhdlrBasic(SCIP *scip, SCIP_EVENTHDLR **eventhdlrptr, const char *name, const char *desc, SCIP_DECL_EVENTEXEC((*eventexec)), SCIP_EVENTHDLRDATA *eventhdlrdata)
Definition: scip_event.c:95
SCIP_Real SCIPgetRhsNonlinear(SCIP_CONS *cons)
SCIP_NODE * SCIPnodeGetParent(SCIP_NODE *node)
Definition: tree.c:7714
#define PROP_TIMING
static SCIP_DECL_PROPRESPROP(propRespropSymmetry)
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1245
SCIP_Bool SCIPvarIsBinary(SCIP_VAR *var)
Definition: var.c:17431
static SCIP_RETCODE detectOrbitopes(SCIP *scip, SCIP_PROPDATA *propdata, int *components, int *componentbegins, int ncomponents)
SCIP_RETCODE SCIPcomputeOrbitsFilterSym(SCIP *scip, int npermvars, int **permstrans, int nperms, SCIP_Shortbool *inactiveperms, int *orbits, int *orbitbegins, int *norbits, int *components, int *componentbegins, int *vartocomponent, unsigned *componentblocked, int ncomponents, int nmovedpermvars)
Definition: symmetry.c:154
#define DEFAULT_DISPLAYNORBITVARS
#define ISSSTINTACTIVE(x)
SCIP_CONS ** SCIPconshdlrGetConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4547
#define FALSE
Definition: def.h:87
static SCIP_RETCODE freeConflictGraphSST(SCIP *scip, SCIP_DIGRAPH **conflictgraph, int nnodes)
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:3014
const char * SCIPeventhdlrGetName(SCIP_EVENTHDLR *eventhdlr)
Definition: event.c:315
int SCIPgetNActivePricers(SCIP *scip)
Definition: scip_pricer.c:339
SCIP_Real SCIPinfinity(SCIP *scip)
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:10755
#define TRUE
Definition: def.h:86
SCIP_RETCODE SCIPhashsetCreate(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem, int size)
Definition: misc.c:3699
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:54
SCIP_Bool SCIPconsIsStickingAtNode(SCIP_CONS *cons)
Definition: cons.c:8364
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition: misc.c:3132
#define SYM_HANDLETYPE_SYMBREAK
Definition: type_symmetry.h:60
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition: var.c:17600
SCIP_Bool SCIPhashsetExists(SCIP_HASHSET *hashset, void *element)
Definition: misc.c:3757
int SCIPgetSymmetryNGenerators(SCIP *scip)
#define SCIP_EVENTTYPE_GLBCHANGED
Definition: type_event.h:66
#define DEFAULT_RECOMPUTERESTART
static SCIP_RETCODE SCIPsortOrbitope(SCIP *scip, int **orbitopevaridx, SCIP_VAR ***vars, int nrows, int ncols)
SCIP_Bool SCIPconsIsTransformed(SCIP_CONS *cons)
Definition: cons.c:8394
int SCIPgetNActiveConss(SCIP *scip)
#define ISSSTBINACTIVE(x)
static GRAPHNODE ** active
SCIP_RETCODE SCIPtightenVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition: scip_var.c:5317
SCIP_VAR ** SCIPgetVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
#define SCIPfreeBlockMemory(scip, ptr)
Definition: scip_mem.h:99
Constraint handler for AND constraints, .
static SCIP_RETCODE detectAndHandleSubgroups(SCIP *scip, SCIP_PROPDATA *propdata)
#define SCIPduplicateBufferArray(scip, ptr, source, num)
Definition: scip_mem.h:123
SCIP_CONS ** SCIPgetConss(SCIP *scip)
Definition: scip_prob.c:3087
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
int SCIPnodeGetDepth(SCIP_NODE *node)
Definition: tree.c:7444
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:127
SCIP_VAR * SCIPgetVarVarbound(SCIP *scip, SCIP_CONS *cons)
Constraint handler for the set partitioning / packing / covering constraints .
#define DEFAULT_DETECTSUBGROUPS
static SCIP_RETCODE determineSymmetry(SCIP *scip, SCIP_PROPDATA *propdata, SYM_SPEC symspecrequire, SYM_SPEC symspecrequirefixed)
SCIP_VAR ** permvars
#define SCIP_EXPRITER_ENTEREXPR
Definition: type_expr.h:667
SCIP_Bool SCIPconsIsRemovable(SCIP_CONS *cons)
Definition: cons.c:8354
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
Definition: scip_message.c:111
static SCIP_RETCODE checkSymmetriesAreSymmetries(SCIP *scip, SYM_SPEC fixedtype, SYM_MATRIXDATA *matrixdata, int nperms, int **perms)
#define DEFAULT_SSTLEADERRULE
#define SCIPdebugMsg
Definition: scip_message.h:69
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:74
SCIP_Real SCIPgetRhsLinear(SCIP *scip, SCIP_CONS *cons)
#define TABLE_DESC_ORBITALFIXING
SCIP_EXPR * SCIPexpriterGetCurrent(SCIP_EXPRITER *iterator)
Definition: expriter.c:673
SCIP_Bool SCIPconsIsActive(SCIP_CONS *cons)
Definition: cons.c:8146
static SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
static SCIP_RETCODE getActiveVariables(SCIP *scip, SCIP_VAR ***vars, SCIP_Real **scalars, int *nvars, SCIP_Real *constant, SCIP_Bool transformed)
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
Definition: scip_message.c:199
int SCIPgetNContVars(SCIP *scip)
Definition: scip_prob.c:2171
SCIP_RETCODE SCIPcreateDigraph(SCIP *scip, SCIP_DIGRAPH **digraph, int nnodes)
#define DEFAULT_MAXNCONSSSUBGROUP
#define DEFAULT_COMPRESSSYMMETRIES
int SCIPgetNActiveBenders(SCIP *scip)
Definition: scip_benders.c:523
SCIP_RETCODE SCIPhashtableCreate(SCIP_HASHTABLE **hashtable, BMS_BLKMEM *blkmem, int tablesize, SCIP_DECL_HASHGETKEY((*hashgetkey)), SCIP_DECL_HASHKEYEQ((*hashkeyeq)), SCIP_DECL_HASHKEYVAL((*hashkeyval)), void *userptr)
Definition: misc.c:2236
static SCIP_RETCODE addWeakSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int ncompcolors, int *chosencomppercolor, int *firstvaridxpercolor, int symgrpcompidx, int *naddedconss, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
public functions to work with algebraic expressions
interface for symmetry computations
SCIP_Bool SCIPisReoptEnabled(SCIP *scip)
Definition: scip_solve.c:3610
int SCIPcompareExpr(SCIP *scip, SCIP_EXPR *expr1, SCIP_EXPR *expr2)
Definition: scip_expr.c:1723
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3363
#define DEFAULT_ADDWEAKSBCS
void SCIPsortIntPtr(int *intarray, void **ptrarray, int len)
int SCIPdigraphGetNNodes(SCIP_DIGRAPH *digraph)
Definition: misc.c:7648
constraint handler for (partitioning/packing/full) orbitope constraints w.r.t. the full symmetric gro...
constraint handler for symresack constraints
static SCIP_RETCODE checkTwoCyclePermsAreOrbitope(SCIP *scip, SCIP_VAR **permvars, int npermvars, int **perms, int *activeperms, int ntwocycles, int nactiveperms, int **orbitopevaridx, int *columnorder, int *nusedelems, int *nusedcols, SCIP_Shortbool *rowisbinary, SCIP_Bool *isorbitope, SCIP_Shortbool *activevars)
int SCIPgetNFixedVars(SCIP *scip)
Definition: scip_prob.c:2308
Constraint handler for "or" constraints, .
#define SCIPallocCleanBufferArray(scip, ptr, num)
Definition: scip_mem.h:133
static int getNSymhandableConss(SCIP *scip, SCIP_CONSHDLR *conshdlr_nonlinear)
SCIP_Longint SCIPnodeGetNumber(SCIP_NODE *node)
Definition: tree.c:7434
#define DEFAULT_USEDYNAMICPROP
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:17920
#define SYM_SPEC_BINARY
Definition: type_symmetry.h:34
#define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
Definition: scip_mem.h:96
Constraint handler for knapsack constraints of the form , x binary and .
SCIP_RETCODE SCIPsetPropExitpre(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPEXITPRE((*propexitpre)))
Definition: scip_prop.c:254
SCIP_Real ub
#define TABLE_EARLIEST_ORBITALFIXING
SCIP_Real SCIPeventGetNewbound(SCIP_EVENT *event)
Definition: event.c:1233
static const NodeData nodedata[]
Definition: gastrans.c:74
static SCIP_RETCODE chooseOrderOfGenerators(SCIP *scip, SCIP_PROPDATA *propdata, int compidx, int **genorder, int *ntwocycleperms)
SCIP_Real SCIPgetRhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPisPresolveFinished(SCIP *scip)
Definition: scip_general.c:603
SCIP_VAR ** SCIPgetVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5172
#define ISSSTCONTACTIVE(x)
#define ISORBITALFIXINGACTIVE(x)
#define SCIPhashFour(a, b, c, d)
Definition: pub_misc.h:515
SYM_RHSSENSE * rhssense
SCIP_Real * SCIPgetValsLinking(SCIP *scip, SCIP_CONS *cons)
#define SCIPerrorMessage
Definition: pub_message.h:55
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4175
SCIP_RETCODE SCIPgetConsNVars(SCIP *scip, SCIP_CONS *cons, int *nvars, SCIP_Bool *success)
Definition: scip_cons.c:2558
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:2769
SCIP_Bool SCIPisOrbitalfixingEnabled(SCIP *scip)
SCIP_VAR * SCIPgetResultantAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5197
void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
static SCIP_RETCODE computeBranchingVariables(SCIP *scip, int nvars, SCIP_HASHMAP *varmap, SCIP_Shortbool *bg1, int *bg1list, int *nbg1)
static SCIP_DECL_TABLEOUTPUT(tableOutputOrbitalfixing)
#define SYM_SPEC_INTEGER
Definition: type_symmetry.h:33
SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
Definition: expr_var.c:407
void * SCIPdigraphGetNodeData(SCIP_DIGRAPH *digraph, int node)
Definition: misc.c:7658
Constraint handler for logicor constraints (equivalent to set covering, but algorithms are suited fo...
#define ISSSTACTIVE(x)
SCIP_RETCODE SCIPpresolCons(SCIP *scip, SCIP_CONS *cons, int nrounds, SCIP_PRESOLTIMING presoltiming, int nnewfixedvars, int nnewaggrvars, int nnewchgvartypes, int nnewchgbds, int nnewholes, int nnewdelconss, int nnewaddconss, int nnewupgdconss, int nnewchgcoefs, int nnewchgsides, int *nfixedvars, int *naggrvars, int *nchgvartypes, int *nchgbds, int *naddholes, int *ndelconss, int *naddconss, int *nupgdconss, int *nchgcoefs, int *nchgsides, SCIP_RESULT *result)
Definition: scip_cons.c:2343
void SCIPhashsetFree(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem)
Definition: misc.c:3730
static INLINE uint32_t SCIPrealHashCode(double x)
Definition: pub_misc.h:535
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition: scip_mem.h:128
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip_general.c:474
void SCIPdigraphSetNodeData(SCIP_DIGRAPH *digraph, void *dataptr, int node)
Definition: misc.c:7674
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip_mem.c:48
SYM_RHSSENSE * senses
SCIP_RETCODE SCIPincludePropSymmetry(SCIP *scip)
#define DEFAULT_PERFORMPRESOLVING
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition: cons.c:8085
SCIP_RETCODE SCIPchgVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip_var.c:4763
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP *scip, SCIP_VAR *var)
Definition: scip_var.c:8712
static SCIP_DECL_PROPEXIT(propExitSymmetry)
SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1432
SCIP_Bool SCIPconsIsPropagated(SCIP_CONS *cons)
Definition: cons.c:8304
SCIP_VAR ** SCIPgetVarsLogicor(SCIP *scip, SCIP_CONS *cons)
#define PROP_PRESOL_MAXROUNDS
static SCIP_DECL_HASHGETKEY(SYMhashGetKeyVartype)
struct SCIP_EventData SCIP_EVENTDATA
Definition: type_event.h:164
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:17251
static SCIP_RETCODE addOrbitopeSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *usedperms, int nusedperms, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int graphcoloridx, int nrows, int ncols, int *firstvaridx, int *compidxfirstrow, int **lexorder, int *nvarslexorder, int *maxnvarslexorder, SCIP_Bool mayinteract, SCIP_Bool *success)
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition: misc.c:3048
SCIP_EXPR * SCIPgetExprNonlinear(SCIP_CONS *cons)
SCIP_DOMCHG * SCIPnodeGetDomchg(SCIP_NODE *node)
Definition: tree.c:7529
#define DEFAULT_ADDCONFLICTCUTS
#define DEFAULT_ADDSYMRESACKS
internal miscellaneous methods
#define PROP_PRESOL_PRIORITY
#define NULL
Definition: lpi_spx1.cpp:155
#define SCIP_Shortbool
Definition: def.h:92
#define EVENTHDLR_SYMMETRY_NAME
static SCIP_DECL_HASHKEYEQ(SYMhashKeyEQVartype)
SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
Definition: scip_param.c:260
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition: misc.c:11148
#define SCIP_PROPTIMING_ALWAYS
Definition: type_timing.h:64
#define PROP_FREQ
static SCIP_RETCODE addStrongSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *graphcompbegins, int *graphcomponents, int graphcompidx, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1443
#define SCIP_CALL(x)
Definition: def.h:384
#define TABLE_POSITION_ORBITALFIXING
#define DEFAULT_OFSYMCOMPTIMING
SCIP_RETCODE SCIPgetProbvarLinearSum(SCIP *scip, SCIP_VAR **vars, SCIP_Real *scalars, int *nvars, int varssize, SCIP_Real *constant, int *requiredsize, SCIP_Bool mergemultiples)
Definition: scip_var.c:1735
#define SCIP_SPECIALVAL
SCIP_VAR ** SCIPgetVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2192
propagator for symmetry handling
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:216
SCIP_Bool SCIPconsIsLocal(SCIP_CONS *cons)
Definition: cons.c:8324
Definition: graph_load.c:93
static SCIP_RETCODE selectOrbitLeaderSSTConss(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_HASHMAP *varmap, SCIP_VAR **permvars, int npermvars, int *orbits, int *orbitbegins, int norbits, int leaderrule, int tiebreakrule, SCIP_VARTYPE leadervartype, int *orbitidx, int *leaderidx, SCIP_Shortbool *orbitvarinconflict, int *norbitvarinconflict, SCIP_Bool useconflictgraph, SCIP_Bool *success)
static SCIP_RETCODE createConflictGraphSST(SCIP *scip, SCIP_DIGRAPH **conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_Bool onlypermvars, SCIP_HASHMAP *permvarmap, SCIP_Bool *success)
SCIP_RETCODE SCIPcreateDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset, int ncomponents)
SCIP_Real * vals
static SCIP_RETCODE delSymConss(SCIP *scip, SCIP_PROPDATA *propdata)
int SCIPdisjointsetGetComponentCount(SCIP_DISJOINTSET *djset)
Definition: misc.c:11245
static int getNOrbitopesInComp(SCIP_VAR **permvars, int *graphcomponents, int *graphcompbegins, int *compcolorbegins, int ncompcolors, int symcompsize)
SCIP_RETCODE SCIPgetConsVars(SCIP *scip, SCIP_CONS *cons, SCIP_VAR **vars, int varssize, SCIP_Bool *success)
Definition: scip_cons.c:2514
#define TABLE_NAME_ORBITALFIXING
int SCIPconshdlrGetNConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4590
SCIP_RETCODE SCIPcreateExpriter(SCIP *scip, SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2300
SCIP_Longint SCIPgetCapacityKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPgetConsCopy(SCIP *sourcescip, SCIP *targetscip, SCIP_CONS *sourcecons, SCIP_CONS **targetcons, SCIP_CONSHDLR *sourceconshdlr, SCIP_HASHMAP *varmap, SCIP_HASHMAP *consmap, const char *name, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode, SCIP_Bool global, SCIP_Bool *valid)
Definition: scip_copy.c:1577
SCIP_VAR * SCIPgetVbdvarVarbound(SCIP *scip, SCIP_CONS *cons)
constraint handler for linking binary variables to a linking (continuous or integer) variable ...
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:115
SCIP_RETCODE SCIPdigraphAddArcSafe(SCIP_DIGRAPH *digraph, int startnode, int endnode, void *data)
Definition: misc.c:7595
int SCIPdigraphGetNSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition: misc.c:7706
SCIP_VAR * SCIPeventGetVar(SCIP_EVENT *event)
Definition: event.c:1044
SCIP_RETCODE SCIPdetermineNVarsAffectedSym(SCIP *scip, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, int *nvarsaffected)
Definition: symmetry.c:575
#define SCIP_Bool
Definition: def.h:84
static SCIP_RETCODE setSymmetryData(SCIP *scip, SCIP_VAR **vars, int nvars, int nbinvars, SCIP_VAR ***permvars, int *npermvars, int *nbinpermvars, int **perms, int nperms, int *nmovedvars, SCIP_Bool *binvaraffected, SCIP_Bool usecompression, SCIP_Real compressthreshold, SCIP_Bool *compressed)
#define DEFAULT_PREFERLESSROWS
int SCIPgetNImplVars(SCIP *scip)
Definition: scip_prob.c:2126
SCIP_EVENTTYPE SCIPeventGetType(SCIP_EVENT *event)
Definition: event.c:1021
SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(SCIP_BOUNDCHG *boundchg)
Definition: var.c:17168
uint32_t SYM_SPEC
Definition: type_symmetry.h:37
int SCIPgetNVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2169
#define DEFAULT_SSTTIEBREAKRULE
SCIP_RETCODE SCIPhashmapRemoveAll(SCIP_HASHMAP *hashmap)
Definition: misc.c:3573
int SCIPgetDepth(SCIP *scip)
Definition: scip_tree.c:661
SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
Definition: scip_expr.c:1407
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9441
constraint handler for nonlinear constraints specified by algebraic expressions
static SCIP_Bool checkSymmetryDataFree(SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPisInvolutionPerm(int *perm, SCIP_VAR **vars, int nvars, int *ntwocyclesperm, int *nbincyclesperm, SCIP_Bool earlytermination)
Definition: symmetry.c:524
SCIP_RETCODE SCIPprintCons(SCIP *scip, SCIP_CONS *cons, FILE *file)
Definition: scip_cons.c:2473
#define MAX(x, y)
Definition: tclique_def.h:83
SCIP_Bool SYMcanComputeSymmetry(void)
SCIP_VAR * SCIPboundchgGetVar(SCIP_BOUNDCHG *boundchg)
Definition: var.c:17158
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition: cons.c:8105
SCIP_BOUNDCHG * SCIPdomchgGetBoundchg(SCIP_DOMCHG *domchg, int pos)
Definition: var.c:17206
SCIP_Real lb
#define DEFAULT_ONLYBINARYSYMMETRY
static SCIP_RETCODE adaptSymmetryDataSST(SCIP *scip, int **origperms, int **modifiedperms, int nperms, SCIP_VAR **origpermvars, SCIP_VAR **modifiedpermvars, int npermvars, int *leaders, int nleaders)
SCIP_Bool SCIPconsIsChecked(SCIP_CONS *cons)
Definition: cons.c:8284
SCIP_Bool SCIPconsIsInitial(SCIP_CONS *cons)
Definition: cons.c:8254
SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
Definition: var.c:17758
SCIP_RETCODE SCIPdropVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int filterpos)
Definition: scip_event.c:391
SCIP_EXPR * SCIPexpriterGetNext(SCIP_EXPRITER *iterator)
Definition: expriter.c:848
SCIP_Real SCIPgetVbdcoefVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_VARTYPE type
int SCIPgetNRuns(SCIP *scip)
Constraint handler for linear constraints in their most general form, .
void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
Definition: misc.c:2548
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPgetRhsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5985
SCIP_RETCODE SCIPvarGetOrigvarSum(SCIP_VAR **var, SCIP_Real *scalar, SCIP_Real *constant)
Definition: var.c:12773
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_MATRIXDATA *matrixdata, SYM_EXPRDATA *exprdata, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize)
SCIP_RETCODE SCIPgenerateOrbitopeVarsMatrix(SCIP *scip, SCIP_VAR ****vars, int nrows, int ncols, SCIP_VAR **permvars, int npermvars, int **orbitopevaridx, int *columnorder, int *nusedelems, SCIP_Shortbool *rowisbinary, SCIP_Bool *infeasible, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
Definition: symmetry.c:961
#define DEFAULT_MAXGENERATORS
#define PROP_NAME
int SCIPgetNBinVars(SCIP *scip)
Definition: scip_prob.c:2036
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4624
SCIP_Bool SCIPinProbing(SCIP *scip)
Definition: scip_probing.c:88
const char * SCIPpropGetName(SCIP_PROP *prop)
Definition: prop.c:932
SCIP_RETCODE SCIPextendSubOrbitope(int **suborbitope, int nrows, int nfilledcols, int coltoextend, int *perm, SCIP_Bool leftextension, int **nusedelems, SCIP_VAR **permvars, SCIP_Shortbool *rowisbinary, SCIP_Bool *success, SCIP_Bool *infeasible)
Definition: symmetry.c:627
SCIP_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9418
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition: misc.c:2286
int SCIPgetNVars(SCIP *scip)
Definition: scip_prob.c:1991
void SCIPexpriterSetStagesDFS(SCIP_EXPRITER *iterator, SCIP_EXPRITER_STAGE stopstages)
Definition: expriter.c:654
void SCIPfreeExpriter(SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2314
SCIP_VAR ** SCIPgetVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5939
enum SYM_Rhssense SYM_RHSSENSE
Definition: type_symmetry.h:56
SCIP_RETCODE SCIPcreateConsLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
#define COMPRESSNVARSLB
Constraint handler for XOR constraints, .
int SCIPgetNVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5916
#define DEFAULT_DOUBLEEQUATIONS
SCIP_RETCODE SCIPprintNodeRootPath(SCIP *scip, SCIP_NODE *node, FILE *file)
Definition: scip_tree.c:520
#define DEFAULT_ADDSTRONGSBCS
#define PROP_PRIORITY
#define SYM_HANDLETYPE_ORBITALFIXING
Definition: type_symmetry.h:61
SCIP_RETCODE SCIPsetPropResprop(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPRESPROP((*propresprop)))
Definition: scip_prop.c:303
#define PROP_DELAY
void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
Definition: misc.c:5440
#define PROP_DESC
static const SCIP_Real scalars[]
Definition: lp.c:5738
SCIP_VAR ** SCIPgetVarsLinear(SCIP *scip, SCIP_CONS *cons)
methods for handling symmetries
int SCIPdomchgGetNBoundchgs(SCIP_DOMCHG *domchg)
Definition: var.c:17198
SCIP_RETCODE SCIPgetBinvarsLinking(SCIP *scip, SCIP_CONS *cons, SCIP_VAR ***binvars, int *nbinvars)
const char * SYMsymmetryGetName(void)
SCIP_RETCODE SCIPcomputeOrbitVar(SCIP *scip, int npermvars, int **perms, int **permstrans, int *components, int *componentbegins, SCIP_Shortbool *ignoredvars, SCIP_Shortbool *varfound, int varidx, int component, int *orbit, int *orbitsize)
Definition: symmetry.c:302
SCIP_VAR * SCIPgetResultantOr(SCIP *scip, SCIP_CONS *cons)
Definition: cons_or.c:2215
int SCIPgetNConss(SCIP *scip)
Definition: scip_prob.c:3041
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip_cons.c:1110
SCIP_EXPRITER_STAGE SCIPexpriterGetStageDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:686
SCIP_BOUNDTYPE * SCIPgetBoundtypesBounddisjunction(SCIP *scip, SCIP_CONS *cons)
#define MAXGENNUMERATOR
#define SYM_SPEC_REAL
Definition: type_symmetry.h:35
SCIP_Real SCIPeventGetOldbound(SCIP_EVENT *event)
Definition: event.c:1209
#define ISSYMRETOPESACTIVE(x)
SCIP_VAR * SCIPgetLinkvarLinking(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1421
SCIP_RETCODE SCIPhashsetInsert(SCIP_HASHSET *hashset, BMS_BLKMEM *blkmem, void *element)
Definition: misc.c:3740
SCIP_VAR ** SCIPgetVars(SCIP *scip)
Definition: scip_prob.c:1946
SCIP_RETCODE SCIPcaptureVar(SCIP *scip, SCIP_VAR *var)
Definition: scip_var.c:1211
#define SCIP_Real
Definition: def.h:177
SCIP_Bool SCIPconsIsModifiable(SCIP_CONS *cons)
Definition: cons.c:8334
#define DEFAULT_SSTLEADERVARTYPE
#define SCIPfreeCleanBufferArray(scip, ptr)
Definition: scip_mem.h:137
struct SCIP_PropData SCIP_PROPDATA
Definition: type_prop.h:43
SCIP_Bool SCIPisStopped(SCIP *scip)
Definition: scip_general.c:694
#define SYM_COMPUTETIMING_AFTERPRESOL
Definition: type_symmetry.h:42
int SCIPgetNVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition: cons_and.c:5148
SCIP_TABLEDATA * SCIPtableGetData(SCIP_TABLE *table)
Definition: table.c:279
#define SYM_HANDLETYPE_SST
Definition: type_symmetry.h:62
int SCIPgetNVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPconsIsEnforced(SCIP_CONS *cons)
Definition: cons.c:8274
#define DEFAULT_SYMFIXNONBINARYVARS
public methods for data structures
#define SCIP_INVALID
Definition: def.h:197
#define DEFAULT_ADDCONSSTIMING
SCIP_RETCODE SCIPsetPropFree(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPFREE((*propfree)))
Definition: scip_prop.c:158
SCIP_Bool SCIPconsIsSeparated(SCIP_CONS *cons)
Definition: cons.c:8264
SCIP_PROPDATA * SCIPpropGetData(SCIP_PROP *prop)
Definition: prop.c:780
#define SCIP_Longint
Definition: def.h:162
static SCIP_RETCODE computeSymmetryGroup(SCIP *scip, SCIP_Bool doubleequations, SCIP_Bool compresssymmetries, SCIP_Real compressthreshold, int maxgenerators, SYM_SPEC fixedtype, SCIP_Bool local, SCIP_Bool checksymmetries, SCIP_Bool usecolumnsparsity, SCIP_CONSHDLR *conshdlr_nonlinear, int *npermvars, int *nbinpermvars, SCIP_VAR ***permvars, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, int *nmovedvars, SCIP_Bool **isnonlinvar, SCIP_Bool *binvaraffected, SCIP_Bool *compressed, SCIP_Bool *success)
const char * SYMsymmetryGetDesc(void)
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition: var.c:17416
SCIP_Bool SCIPhashtableExists(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2599
static SCIP_RETCODE performOrbitalFixing(SCIP *scip, SCIP_VAR **permvars, int npermvars, int *orbits, int *orbitbegins, int norbits, SCIP_Bool *infeasible, int *nfixedzero, int *nfixedone)
static SCIP_RETCODE collectCoefficients(SCIP *scip, SCIP_Bool doubleequations, SCIP_VAR **linvars, SCIP_Real *linvals, int nlinvars, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool istransformed, SYM_RHSSENSE rhssense, SYM_MATRIXDATA *matrixdata, int *nconssforvar)
static SCIP_Bool hasNonlinearConstraints(SCIP_PROPDATA *propdata)
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
Definition: scip_general.c:704
SCIP_RETCODE SCIPcreateConsOrbitope(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR ***vars, SCIP_ORBITOPETYPE orbitopetype, int nspcons, int nblocks, SCIP_Bool usedynamicprop, SCIP_Bool mayinteract, SCIP_Bool resolveprop, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
SCIP_Real * SCIPgetValsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
enum SCIP_Vartype SCIP_VARTYPE
Definition: type_var.h:60
#define EVENTHDLR_SYMMETRY_DESC
#define PROP_PRESOLTIMING
static SCIP_DECL_SORTINDCOMP(SYMsortRhsTypes)
SCIP_Bool SCIPconsIsConflict(SCIP_CONS *cons)
Definition: cons.c:8234
#define nnodes
Definition: gastrans.c:65
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:17976
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip_mem.h:102
static SCIP_RETCODE setSymmetryMethodEnabledValues(SCIP_PROPDATA *propdata)
SCIP_VAR * SCIPgetExprAuxVarNonlinear(SCIP_EXPR *expr)
SCIP_RETCODE SCIPhashmapInsert(SCIP_HASHMAP *hashmap, void *origin, void *image)
Definition: misc.c:3096
SCIP_Bool SCIPexpriterIsEnd(SCIP_EXPRITER *iterator)
Definition: expriter.c:959
SCIPallocBlockMemory(scip, subsol))
#define SCIP_EVENTTYPE_GUBCHANGED
Definition: type_event.h:67
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3221
SCIP_RETCODE SCIPsetPropInitpre(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPINITPRE((*propinitpre)))
Definition: scip_prop.c:238
constraint handler for bound disjunction constraints
void SCIPfreeDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset)
static SCIP_Bool SymmetryFixVar(SYM_SPEC fixedtype, SCIP_VAR *var)
void SCIPdigraphFree(SCIP_DIGRAPH **digraph)
Definition: misc.c:7470
SCIP_Longint * SCIPgetWeightsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPsetPropExit(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPEXIT((*propexit)))
Definition: scip_prop.c:190
#define SCIPABORT()
Definition: def.h:356
SCIP_Real obj
SCIP_Real * rhscoef
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition: var.c:17442
SCIP_Real SCIPgetLhsNonlinear(SCIP_CONS *cons)
static SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
int SCIPgetNVarsLinear(SCIP *scip, SCIP_CONS *cons)
static SCIP_DECL_HASHKEYVAL(SYMhashKeyValVartype)
#define DEFAULT_USECOLUMNSPARSITY
static SCIP_RETCODE tryAddSymmetryHandlingConss(SCIP *scip, SCIP_PROP *prop, int *nchgbds, SCIP_Bool *earlyterm)
#define DEFAULT_DETECTORBITOPES
static SCIP_Bool isSymmetryRecomputationRequired(SCIP *scip, SCIP_PROPDATA *propdata)
SCIP_Real SCIPgetLhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:130
SCIP_VAR * SCIPgetIntVarXor(SCIP *scip, SCIP_CONS *cons)
Definition: cons_xor.c:5962
static SCIP_RETCODE buildSubgroupGraph(SCIP *scip, SCIP_PROPDATA *propdata, int *genorder, int ntwocycleperms, int compidx, int **graphcomponents, int **graphcompbegins, int **compcolorbegins, int *ngraphcomponents, int *ncompcolors, int **usedperms, int *nusedperms, int usedpermssize, SCIP_Shortbool *permused)
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:48
#define SYM_COMPUTETIMING_DURINGPRESOL
Definition: type_symmetry.h:41
struct SCIP_TableData SCIP_TABLEDATA
Definition: type_table.h:49
#define SCIPreallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:119
SCIP_RETCODE SCIPincludePropBasic(SCIP *scip, SCIP_PROP **propptr, const char *name, const char *desc, int priority, int freq, SCIP_Bool delay, SCIP_PROPTIMING timingmask, SCIP_DECL_PROPEXEC((*propexec)), SCIP_PROPDATA *propdata)
Definition: scip_prop.c:105
static SCIP_DECL_PROPFREE(propFreeSymmetry)