Scippy

SCIP

Solving Constraint Integer Programs

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