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  graphcomp = chosencomppercolor[j];
4024  graphcompsize = graphcompbegins[graphcomp+1] - graphcompbegins[graphcomp];
4025  varidx = firstvaridxpercolor[j];
4026 
4027  /* if the first variable was already contained in another orbit or if there are no variables left anyway, skip the
4028  * component */
4029  if ( varfound[varidx] || graphcompsize == propdata->npermvars )
4030  continue;
4031 
4032  /* If varidx is in lexorder, then it must be the first entry of lexorder. */
4033  if ( varsinlexorder != NULL
4034  && SCIPhashmapExists(varsinlexorder, (void*) (long) varidx)
4035  && lexorder != NULL && *lexorder != NULL && *maxnvarsorder > 0 && *nvarsorder > 0
4036  && (*lexorder)[0] != varidx )
4037  continue;
4038 
4039  /* mark all variables that have been used in strong SBCs */
4040  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
4041  {
4042  assert( 0 <= graphcomponents[k] && graphcomponents[k] < propdata->npermvars );
4043 
4044  usedvars[graphcomponents[k]] = TRUE;
4045  }
4046 
4047  SCIP_CALL( SCIPcomputeOrbitVar(scip, propdata->npermvars, propdata->perms,
4048  propdata->permstrans, propdata->components, propdata->componentbegins,
4049  usedvars, varfound, varidx, symgrpcompidx,
4050  orbit[activeorb], &orbitsize[activeorb]) );
4051 
4052  assert( orbit[activeorb][0] == varidx );
4053 
4054  if ( orbitsize[activeorb] > orbitsize[1 - activeorb] ) /*lint !e514*/
4055  {
4056  /* if the new orbit is larger then the old largest one, flip activeorb */
4057  activeorb = 1 - activeorb;
4058  chosencolor = j;
4059  }
4060 
4061  /* reset array */
4062  for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
4063  usedvars[graphcomponents[k]] = FALSE;
4064  }
4065 
4066  /* check if we have found at least one non-empty orbit */
4067  if ( chosencolor > -1 )
4068  {
4069  /* flip activeorb again to avoid confusion, it is then at the largest orbit */
4070  activeorb = 1 - activeorb;
4071 
4072  assert( orbit[activeorb][0] == firstvaridxpercolor[chosencolor] );
4073  vars[0] = propdata->permvars[orbit[activeorb][0]];
4074 
4075  assert( chosencolor > -1 );
4076  SCIPdebugMsg(scip, " adding %d weak sbcs for enclosing orbit of color %d.\n", orbitsize[activeorb]-1, chosencolor);
4077 
4078  *naddedconss = orbitsize[activeorb] - 1;
4079 
4080  /* add weak SBCs for rest of enclosing orbit */
4081  for (j = 1; j < orbitsize[activeorb]; ++j)
4082  {
4083  SCIP_CONS* cons;
4084  char name[SCIP_MAXSTRLEN];
4085 
4086  vars[1] = propdata->permvars[orbit[activeorb][j]];
4087 
4088  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "weak_sbcs_%d_%s_%s", symgrpcompidx, SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
4089 
4090  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
4091  SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
4092  FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4093 
4094  SCIP_CALL( SCIPaddCons(scip, cons) );
4095 
4096 #ifdef SCIP_MORE_DEBUG
4097  SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
4098  SCIPinfoMessage(scip, NULL, "\n");
4099 #endif
4100 
4101  /* check whether we need to resize */
4102  if ( propdata->ngenlinconss >= propdata->genlinconsssize )
4103  {
4104  int newsize;
4105 
4106  newsize = SCIPcalcMemGrowSize(scip, propdata->ngenlinconss + 1);
4107  assert( newsize > propdata->ngenlinconss );
4108 
4109  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize, newsize) );
4110 
4111  propdata->genlinconsssize = newsize;
4112  }
4113 
4114  propdata->genlinconss[propdata->ngenlinconss] = cons;
4115  ++propdata->ngenlinconss;
4116  }
4117 
4118  /* possibly store lexicographic order defined by weak SBCs */
4119  if ( storelexorder )
4120  {
4121  int varidx;
4122 
4123  varidx = orbit[activeorb][0];
4124 
4125  if ( *maxnvarsorder == 0 )
4126  {
4127  *maxnvarsorder = 1;
4128  *nvarsorder = 0;
4129 
4130  SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
4131  (*lexorder)[(*nvarsorder)++] = varidx;
4132  }
4133  else
4134  {
4135  assert( *nvarsorder == *maxnvarsorder );
4136  assert( varsinlexorder != NULL );
4137  assert( lexorder != NULL );
4138  assert( *lexorder != NULL );
4139 
4140  /* the leader of the weak inequalities has to be the first element in the lexicographic order */
4141  if ( varidx == (*lexorder)[0] )
4142  {
4143  /* lexorder is already ok!! */
4144  assert( SCIPhashmapExists(varsinlexorder, (void*) (long) varidx) );
4145  }
4146  else
4147  {
4148  /* Then varidx must not be in the lexorder,
4149  * We must add it at the front of the array, and maintain the current order. */
4150  assert( ! SCIPhashmapExists(varsinlexorder, (void*) (long) varidx) );
4151 
4152  ++(*maxnvarsorder);
4153  ++(*nvarsorder);
4154 
4155  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
4156 
4157  /* Shift array by one position to the right */
4158  for (k = *maxnvarsorder - 1; k >= 1; --k)
4159  (*lexorder)[k] = (*lexorder)[k - 1];
4160 
4161  (*lexorder)[0] = varidx;
4162  }
4163  }
4164  }
4165  }
4166  else
4167  SCIPdebugMsg(scip, " no further weak sbcs are valid\n");
4168 
4169  SCIPfreeBufferArray(scip, &orbit[1]);
4170  SCIPfreeBufferArray(scip, &orbit[0]);
4171  if ( varsinlexorder != NULL )
4172  SCIPhashmapFree(&varsinlexorder);
4173  SCIPfreeBufferArray(scip, &varfound);
4174  SCIPfreeCleanBufferArray(scip, &usedvars);
4175 
4176  return SCIP_OKAY;
4177 }
4178 
4179 
4180 /** temporarily adapt symmetry data to new variable order given by Schreier Sims */
4181 static
4183  SCIP* scip, /**< SCIP instance */
4184  int** origperms, /**< permutation matrix w.r.t. original variable ordering */
4185  int** modifiedperms, /**< memory for permutation matrix w.r.t. new variable ordering */
4186  int nperms, /**< number of permutations */
4187  SCIP_VAR** origpermvars, /**< array of permutation vars w.r.t. original variable ordering */
4188  SCIP_VAR** modifiedpermvars, /**< memory for array of permutation vars w.r.t. new variable ordering */
4189  int npermvars, /**< length or modifiedpermvars array */
4190  int* leaders, /**< leaders of Schreier Sims constraints */
4191  int nleaders /**< number of leaders */
4192  )
4193 {
4194  int* permvaridx;
4195  int* posinpermvar;
4196  int leader;
4197  int curposleader;
4198  int varidx;
4199  int lidx;
4200  int i;
4201  int l;
4202  int p;
4203 
4204  assert( scip != NULL );
4205  assert( origperms != NULL );
4206  assert( modifiedperms != NULL );
4207  assert( nperms > 0 );
4208  assert( origpermvars != NULL );
4209  assert( modifiedpermvars != NULL );
4210  assert( npermvars > 0 );
4211  assert( leaders != NULL );
4212  assert( nleaders > 0 );
4213 
4214  /* initialize map from position in lexicographic order to index of original permvar */
4215  SCIP_CALL( SCIPallocBufferArray(scip, &permvaridx, npermvars) );
4216  for (i = 0; i < npermvars; ++i)
4217  permvaridx[i] = i;
4218 
4219  /* initialize map from permvaridx to its current position in the reordered permvars array */
4220  SCIP_CALL( SCIPallocBufferArray(scip, &posinpermvar, npermvars) );
4221  for (i = 0; i < npermvars; ++i)
4222  posinpermvar[i] = i;
4223 
4224  /* Iterate over leaders and put the l-th leader to the l-th position of the lexicographic order.
4225  * We do this by swapping the l-th leader with the element at position l of the current permvars array. */
4226  for (l = 0; l < nleaders; ++l)
4227  {
4228  leader = leaders[l];
4229  curposleader = posinpermvar[leader];
4230  varidx = permvaridx[curposleader];
4231  lidx = permvaridx[l];
4232 
4233  /* swap the permvar at position l with the l-th leader */
4234  permvaridx[curposleader] = lidx;
4235  permvaridx[l] = varidx;
4236 
4237  /* update the position map */
4238  posinpermvar[lidx] = curposleader;
4239  posinpermvar[leader] = l;
4240  }
4241 
4242  /* update the permvars array to new variable order */
4243  for (i = 0; i < npermvars; ++i)
4244  modifiedpermvars[i] = origpermvars[permvaridx[i]];
4245 
4246  /* update the permutation to the new variable order */
4247  for (p = 0; p < nperms; ++p)
4248  {
4249  for (i = 0; i < npermvars; ++i)
4250  modifiedperms[p][i] = posinpermvar[origperms[p][permvaridx[i]]];
4251  }
4252 
4253  SCIPfreeBufferArray(scip, &permvaridx);
4254  SCIPfreeBufferArray(scip, &posinpermvar);
4255 
4256  return SCIP_OKAY;
4257 }
4258 
4259 
4260 /* returns the number of found orbitopes with at least three columns per graph component or 0
4261  * if the found orbitopes do not satisfy certain criteria for being used
4262  */
4263 static
4265  SCIP_VAR** permvars, /**< array of variables affected by symmetry */
4266  int* graphcomponents, /**< array of graph components */
4267  int* graphcompbegins, /**< array indicating starting position of graph components */
4268  int* compcolorbegins, /**< array indicating starting positions of potential orbitopes */
4269  int ncompcolors, /**< number of components encoded in compcolorbegins */
4270  int symcompsize /**< size of symmetry component for that we detect suborbitopes */
4271  )
4272 {
4273  SCIP_Bool oneorbitopecriterion = FALSE;
4274  SCIP_Bool multorbitopecriterion = FALSE;
4275  int norbitopes = 0;
4276  int j;
4277 
4278  assert( graphcompbegins != NULL );
4279  assert( compcolorbegins != NULL );
4280  assert( ncompcolors >= 0 );
4281  assert( symcompsize > 0 );
4282 
4283  for (j = 0; j < ncompcolors; ++j)
4284  {
4285  SCIP_VAR* firstvar;
4286  int largestcompsize = 0;
4287  int nbinrows= 0;
4288  int k;
4289 
4290  /* skip trivial components */
4291  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
4292  continue;
4293 
4294  /* check whether components of this color build an orbitope (with > 2 columns) */
4295  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4296  {
4297  int compsize;
4298 
4299  compsize = graphcompbegins[k+1] - graphcompbegins[k];
4300 
4301  /* the first component that we are looking at for this color */
4302  if ( largestcompsize < 1 )
4303  {
4304  if ( compsize < 3 )
4305  break;
4306 
4307  largestcompsize = compsize;
4308  }
4309  else if ( compsize != largestcompsize )
4310  break;
4311 
4312  firstvar = permvars[graphcomponents[graphcompbegins[k]]];
4313 
4314  /* count number of binary orbits (comps) */
4315  if ( SCIPvarIsBinary(firstvar) )
4316  ++nbinrows;
4317  }
4318 
4319  /* we have found an orbitope */
4320  if ( k == compcolorbegins[j+1] )
4321  {
4322  SCIP_Real threshold;
4323  int ncols;
4324 
4325  ++norbitopes;
4326  ncols = graphcompbegins[compcolorbegins[j] + 1] - graphcompbegins[compcolorbegins[j]];
4327 
4328  threshold = 0.7 * (SCIP_Real) symcompsize;
4329 
4330  /* check whether criteria for adding orbitopes are satisfied */
4331  if ( nbinrows <= 2 * ncols || (nbinrows <= 8 * ncols && nbinrows < 100) )
4332  multorbitopecriterion = TRUE;
4333  else if ( nbinrows <= 3 * ncols || (SCIP_Real) nbinrows * ncols >= threshold )
4334  oneorbitopecriterion = TRUE;
4335  }
4336  }
4337 
4338  if ( (norbitopes == 1 && oneorbitopecriterion) || (norbitopes >= 2 && multorbitopecriterion) )
4339  return norbitopes;
4340 
4341  return 0;
4342 }
4343 
4344 
4345 /** checks whether subgroups of the components are symmetric groups and adds SBCs for them */
4346 static
4348  SCIP* scip, /**< SCIP instance */
4349  SCIP_PROPDATA* propdata /**< pointer to data of symmetry propagator */
4350  )
4351 {
4352  int* genorder;
4353  int i;
4354 #ifdef SCIP_DEBUG
4355  int norbitopes = 0;
4356  int nstrongsbcs = 0;
4357  int nweaksbcs = 0;
4358 #endif
4359  int** modifiedperms;
4360  SCIP_VAR** modifiedpermvars;
4361  int* nvarsincomponent;
4362 
4363  assert( scip != NULL );
4364  assert( propdata != NULL );
4365  assert( propdata->computedsymmetry );
4366  assert( propdata->components != NULL );
4367  assert( propdata->componentbegins != NULL );
4368  assert( propdata->ncomponents > 0 );
4369  assert( propdata->nperms >= 0 );
4370 
4371  /* exit if no symmetry is present */
4372  if ( propdata->nperms == 0 )
4373  return SCIP_OKAY;
4374 
4375  /* exit if instance is too large */
4376  if ( SCIPgetNConss(scip) > propdata->maxnconsssubgroup )
4377  return SCIP_OKAY;
4378 
4379  assert( propdata->nperms > 0 );
4380  assert( propdata->perms != NULL );
4381  assert( propdata->npermvars > 0 );
4382  assert( propdata->permvars != NULL );
4383 
4384  /* create array for permutation order */
4385  SCIP_CALL( SCIPallocBufferArray(scip, &genorder, propdata->nperms) );
4386 
4387  /* create arrays for modified permutations in case we adapt the lexicographic order because of suborbitopes */
4388  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, propdata->nperms) );
4389  for (i = 0; i < propdata->nperms; ++i)
4390  {
4391  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[i], propdata->npermvars) );
4392  }
4393  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, propdata->npermvars) );
4394 
4395  SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarsincomponent, propdata->npermvars) );
4396  for (i = 0; i < propdata->npermvars; ++i)
4397  {
4398  if ( propdata->vartocomponent[i] >= 0 )
4399  ++nvarsincomponent[propdata->vartocomponent[i]];
4400  }
4401 
4402  SCIPdebugMsg(scip, "starting subgroup detection routine for %d components\n", propdata->ncomponents);
4403 
4404  /* iterate over components */
4405  for (i = 0; i < propdata->ncomponents; ++i)
4406  {
4407  int* graphcomponents;
4408  int* graphcompbegins;
4409  int* compcolorbegins;
4410  int* chosencomppercolor = NULL;
4411  int* firstvaridxpercolor = NULL;
4412  int* usedperms;
4413  int usedpermssize;
4414  int ngraphcomponents;
4415  int ncompcolors;
4416  int ntwocycleperms;
4417  int npermsincomp;
4418  int nusedperms;
4419  int ntrivialcolors = 0;
4420  int j;
4421  int* lexorder = NULL;
4422  int nvarslexorder = 0;
4423  int maxnvarslexorder = 0;
4424  SCIP_Shortbool* permused;
4425  SCIP_Bool allpermsused = FALSE;
4426  SCIP_Bool handlednonbinarysymmetry = FALSE;
4427  int norbitopesincomp;
4428 
4429  /* if component is blocked, skip it */
4430  if ( propdata->componentblocked[i] )
4431  {
4432  SCIPdebugMsg(scip, "component %d has already been handled and will be skipped\n", i);
4433  continue;
4434  }
4435 
4436  npermsincomp = propdata->componentbegins[i + 1] - propdata->componentbegins[i];
4437 
4438  /* set the first npermsincomp entries of genorder; the others are not used for this component */
4439  for (j = 0; j < npermsincomp; ++j)
4440  genorder[j] = j;
4441 
4442  SCIP_CALL( chooseOrderOfGenerators(scip, propdata, i, &genorder, &ntwocycleperms) );
4443 
4444  assert( ntwocycleperms >= 0 );
4445  assert( ntwocycleperms <= npermsincomp );
4446 
4447  SCIPdebugMsg(scip, "component %d has %d permutations consisting of 2-cycles\n", i, ntwocycleperms);
4448 
4449 #ifdef SCIP_MORE_DEBUG
4450  SCIP_Bool* used;
4451  int perm;
4452  int p;
4453  int k;
4454 
4455  SCIP_CALL( SCIPallocBufferArray(scip, &used, propdata->npermvars) );
4456  for (p = propdata->componentbegins[i]; p < propdata->componentbegins[i+1]; ++p)
4457  {
4458  perm = propdata->components[p];
4459 
4460  for (k = 0; k < propdata->npermvars; ++k)
4461  used[k] = FALSE;
4462 
4463  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "permutation %d\n", perm);
4464 
4465  for (k = 0; k < propdata->npermvars; ++k)
4466  {
4467  if ( used[k] )
4468  continue;
4469 
4470  j = propdata->perms[perm][k];
4471 
4472  if ( k == j )
4473  continue;
4474 
4475  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "(%s,", SCIPvarGetName(propdata->permvars[k]));
4476  used[k] = TRUE;
4477  while (j != k)
4478  {
4479  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%s,", SCIPvarGetName(propdata->permvars[j]));
4480  used[j] = TRUE;
4481 
4482  j = propdata->perms[perm][j];
4483  }
4485  }
4487  }
4488 
4489  SCIPfreeBufferArray(scip, &used);
4490 #endif
4491 
4492  if ( ntwocycleperms < 2 )
4493  {
4494  SCIPdebugMsg(scip, " --> skip\n");
4495  continue;
4496  }
4497 
4498  usedpermssize = ntwocycleperms / 2;
4499  SCIP_CALL( SCIPallocBufferArray(scip, &usedperms, usedpermssize) );
4500  SCIP_CALL( SCIPallocClearBufferArray(scip, &permused, npermsincomp) );
4501 
4502  SCIP_CALL( buildSubgroupGraph(scip, propdata, genorder, ntwocycleperms, i,
4503  &graphcomponents, &graphcompbegins, &compcolorbegins, &ngraphcomponents,
4504  &ncompcolors, &usedperms, &nusedperms, usedpermssize, permused) );
4505 
4506  SCIPdebugMsg(scip, " created subgroup detection graph using %d of the permutations\n", nusedperms);
4507 
4508  if ( nusedperms == npermsincomp )
4509  allpermsused = TRUE;
4510 
4511  assert( graphcomponents != NULL );
4512  assert( graphcompbegins != NULL );
4513  assert( compcolorbegins != NULL );
4514  assert( ngraphcomponents > 0 );
4515  assert( ncompcolors > 0 );
4516  assert( nusedperms <= ntwocycleperms );
4517  assert( ncompcolors < propdata->npermvars );
4518 
4519  if ( nusedperms == 0 )
4520  {
4521  SCIPdebugMsg(scip, " -> skipping component, since less no permutation was used\n");
4522 
4523  SCIPfreeBufferArray(scip, &permused);
4524  SCIPfreeBufferArray(scip, &usedperms);
4525 
4526  continue;
4527  }
4528 
4529  SCIPdebugMsg(scip, " number of different colors in the graph: %d\n", ncompcolors);
4530 
4531  if ( propdata->addstrongsbcs || propdata->addweaksbcs )
4532  {
4533  SCIP_CALL( SCIPallocBufferArray(scip, &chosencomppercolor, ncompcolors) );
4534  SCIP_CALL( SCIPallocBufferArray(scip, &firstvaridxpercolor, ncompcolors) );
4535  }
4536 
4537  norbitopesincomp = getNOrbitopesInComp(propdata->permvars, graphcomponents, graphcompbegins, compcolorbegins,
4538  ncompcolors, nvarsincomponent[i]);
4539 
4540  /* if there is just one orbitope satisfying the requirements, handle the full component by symresacks */
4541  if ( norbitopesincomp == 1 )
4542  {
4543  int k;
4544 
4545  for (k = 0; k < npermsincomp; ++k)
4546  {
4547  SCIP_CONS* cons;
4548  char name[SCIP_MAXSTRLEN];
4549 
4550  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4551 
4552  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, propdata->perms[propdata->components[propdata->componentbegins[i] + k]],
4553  propdata->permvars, propdata->npermvars, FALSE,
4554  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4555  SCIP_CALL( SCIPaddCons(scip, cons));
4556 
4557  /* do not release constraint here - will be done later */
4558  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4559  ++propdata->nsymresacks;
4560 
4561  if ( ! propdata->componentblocked[i] )
4562  {
4563  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4564  ++propdata->ncompblocked;
4565  }
4566 
4567  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d\n", k, i);
4568  }
4569 
4570  goto FREELOOPMEMORY;
4571  }
4572 
4573  for (j = 0; j < ncompcolors; ++j)
4574  {
4575  int nbinarycomps = 0;
4576  int largestcolorcomp = -1;
4577  int largestcompsize = 0;
4578  int k;
4579  SCIP_Bool isorbitope = TRUE;
4580  SCIP_Bool orbitopeadded = FALSE;
4581  SCIP_Bool useorbitope;
4582 #ifdef SCIP_DEBUG
4583  SCIP_Bool binaffected = FALSE;
4584  SCIP_Bool intaffected = FALSE;
4585  SCIP_Bool contaffected = FALSE;
4586 #endif
4587 
4588  /* skip trivial components */
4589  if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
4590  {
4591  if( chosencomppercolor != NULL )
4592  chosencomppercolor[j] = -1;
4593 
4594  ++ntrivialcolors;
4595  continue;
4596  }
4597 
4598  SCIPdebugMsg(scip, " color %d has %d components with overall %d variables\n", j, compcolorbegins[j+1] - compcolorbegins[j],
4599  graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]]);
4600 
4601  /* check whether components of this color might build an orbitope (with > 2 columns) */
4602  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4603  {
4604  SCIP_VAR* firstvar;
4605  int compsize;
4606 
4607  compsize = graphcompbegins[k+1] - graphcompbegins[k];
4608 
4609  /* the first component that we are looking at for this color */
4610  if ( largestcompsize < 1 )
4611  {
4612  if ( compsize < 3 )
4613  {
4614  isorbitope = FALSE;
4615  break;
4616  }
4617 
4618  largestcompsize = compsize;
4619  largestcolorcomp = k;
4620  }
4621  else if ( compsize != largestcompsize )
4622  {
4623  /* variable orbits (compsize) have not the same size, cannot define orbitope */
4624  isorbitope = FALSE;
4625  break;
4626  }
4627 
4628  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4629 
4630  /* count number of binary orbits (comps) */
4631  if ( SCIPvarIsBinary(firstvar) )
4632  ++nbinarycomps;
4633  }
4634 
4635 #ifdef SCIP_DEBUG
4636  for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4637  {
4638  SCIP_VAR* firstvar;
4639 
4640  firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4641 
4642  if ( SCIPvarIsBinary(firstvar) )
4643  binaffected = TRUE;
4644  else if (SCIPvarIsIntegral(firstvar) )
4645  intaffected = TRUE;
4646  else
4647  contaffected = TRUE;
4648  }
4649 
4650  SCIPdebugMsg(scip, " affected types (bin,int,cont): (%d,%d,%d)\n", binaffected, intaffected, contaffected);
4651 #endif
4652 
4653  /* only use the orbitope if there are binary rows */
4654  useorbitope = FALSE;
4655  if ( norbitopesincomp > 0 && nbinarycomps > 0 )
4656  useorbitope = TRUE;
4657 
4658  if ( isorbitope && useorbitope )
4659  {
4660  int* firstvaridx;
4661  int* chosencomp;
4662 
4663  SCIPdebugMsg(scip, " detected an orbitope with %d rows and %d columns\n", nbinarycomps, largestcompsize);
4664 
4665  assert( nbinarycomps > 0 );
4666  assert( largestcompsize > 2 );
4667 
4668  firstvaridx = propdata->addweaksbcs ? &(firstvaridxpercolor[j]) : NULL; /*lint !e613*/
4669  chosencomp = propdata->addweaksbcs ? &(chosencomppercolor[j]) : NULL; /*lint !e613*/
4670 
4671  assert( firstvaridxpercolor == NULL || firstvaridxpercolor[j] >= 0 );
4672  assert( firstvaridxpercolor == NULL || firstvaridxpercolor[j] < propdata->npermvars );
4673 
4674  /* add the orbitope constraint for this color
4675  *
4676  * It might happen that we cannot generate the orbitope matrix if the orbitope is not generated by permutations
4677  * all having the same number of 2-cycles, e.g., the orbitope generated by (1,2)(4,5), (2,3), (5,6).
4678  */
4679  SCIP_CALL( addOrbitopeSubgroup(scip, propdata, usedperms, nusedperms, compcolorbegins,
4680  graphcompbegins, graphcomponents, j, nbinarycomps, largestcompsize, firstvaridx, chosencomp,
4681  &lexorder, &nvarslexorder, &maxnvarslexorder, allpermsused, &orbitopeadded) );
4682 
4683  if ( orbitopeadded )
4684  {
4685  if ( ! propdata->componentblocked[i] )
4686  {
4687  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4688  ++propdata->ncompblocked;
4689  }
4690 
4691 #ifdef SCIP_DEBUG
4692  ++norbitopes;
4693 #endif
4694  }
4695  }
4696 
4697  /* if no (useable) orbitope was found, possibly add strong SBCs */
4698  if ( propdata->addstrongsbcs && ! orbitopeadded )
4699  {
4700  assert( largestcolorcomp >= 0 );
4701  assert( largestcolorcomp < ngraphcomponents );
4702  assert( largestcompsize > 0 );
4703 
4704  if( propdata->addweaksbcs )
4705  {
4706  assert( chosencomppercolor != NULL );
4707  assert( firstvaridxpercolor != NULL );
4708 
4709  chosencomppercolor[j] = largestcolorcomp;
4710  firstvaridxpercolor[j] = graphcomponents[graphcompbegins[largestcolorcomp]];
4711  }
4712 
4713  SCIPdebugMsg(scip, " choosing component %d with %d variables and adding strong SBCs\n",
4714  largestcolorcomp, graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp]);
4715 
4716  /* add the strong SBCs for the corresponding component */
4717  SCIP_CALL( addStrongSBCsSubgroup(scip, propdata, graphcompbegins, graphcomponents, largestcolorcomp,
4718  propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4719 
4720  /* store whether symmetries on non-binary symmetries have been handled */
4721  if ( ! SCIPvarIsBinary(propdata->permvars[graphcomponents[graphcompbegins[largestcolorcomp]]]) )
4722  handlednonbinarysymmetry = TRUE;
4723 
4724  if ( ! propdata->componentblocked[i] )
4725  {
4726  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4727  ++propdata->ncompblocked;
4728  }
4729 
4730 #ifdef SCIP_DEBUG
4731  nstrongsbcs += graphcompbegins[largestcolorcomp+1] - graphcompbegins[largestcolorcomp] - 1;
4732 #endif
4733  }
4734  else if ( ! orbitopeadded )
4735  {
4736  SCIPdebugMsg(scip, " no useable orbitope found and no SBCs added\n");
4737 
4738  /* mark the color as not handled */
4739  if ( propdata->addweaksbcs )
4740  {
4741  assert( chosencomppercolor != NULL );
4742  chosencomppercolor[j] = -1; /*lint !e613*/
4743  }
4744  }
4745  }
4746 
4747  SCIPdebugMsg(scip, " skipped %d trivial colors\n", ntrivialcolors);
4748 
4749  /* possibly add weak SBCs for enclosing orbit of first component */
4750  if ( propdata->addweaksbcs && propdata->componentblocked[i] && nusedperms < npermsincomp )
4751  {
4752  int naddedconss;
4753 
4754  assert( firstvaridxpercolor != NULL );
4755  assert( chosencomppercolor != NULL );
4756 
4757  SCIP_CALL( addWeakSBCsSubgroup(scip, propdata, compcolorbegins, graphcompbegins,
4758  graphcomponents, ncompcolors, chosencomppercolor, firstvaridxpercolor,
4759  i, &naddedconss, propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4760 
4761  assert( naddedconss < propdata->npermvars );
4762 
4763 #ifdef SCIP_DEBUG
4764  nweaksbcs += naddedconss;
4765 #endif
4766  }
4767  else
4768  SCIPdebugMsg(scip, " don't add weak sbcs because all generators were used or the settings forbid it\n");
4769 
4770  /* if suborbitopes or strong group actions have been found, potentially add symresacks adapted to
4771  * variable order given by lexorder if no symmetries on non-binary variables have been handled
4772  */
4773  if ( nvarslexorder > 0 && propdata->addsymresacks && ! handlednonbinarysymmetry )
4774  {
4775  int k;
4776 
4777  SCIP_CALL( adaptSymmetryDataSST(scip, propdata->perms, modifiedperms, propdata->nperms,
4778  propdata->permvars, modifiedpermvars, propdata->npermvars, lexorder, nvarslexorder) );
4779 
4780  for (k = 0; k < npermsincomp; ++k)
4781  {
4782  SCIP_CONS* cons;
4783  char name[SCIP_MAXSTRLEN];
4784 
4785  /* skip permutations that have been used to build an orbitope */
4786  if ( permused[k] )
4787  continue;
4788 
4789  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4790 
4791  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[propdata->components[propdata->componentbegins[i] + k]],
4792  modifiedpermvars, propdata->npermvars, FALSE,
4793  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4794  SCIP_CALL( SCIPaddCons(scip, cons));
4795 
4796  /* do not release constraint here - will be done later */
4797  propdata->genorbconss[propdata->ngenorbconss++] = cons;
4798  ++propdata->nsymresacks;
4799 
4800  if ( ! propdata->componentblocked[i] )
4801  {
4802  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4803  ++propdata->ncompblocked;
4804  }
4805 
4806  SCIPdebugMsg(scip, " add symresack for permutation %d of component %d adapted to suborbitope lexorder\n", k, i);
4807  }
4808  }
4809 
4810  FREELOOPMEMORY:
4811  SCIPfreeBlockMemoryArrayNull(scip, &lexorder, maxnvarslexorder);
4812 
4813  SCIPfreeBufferArrayNull(scip, &firstvaridxpercolor);
4814  SCIPfreeBufferArrayNull(scip, &chosencomppercolor);
4815  SCIPfreeBlockMemoryArrayNull(scip, &compcolorbegins, ncompcolors + 1);
4816  SCIPfreeBlockMemoryArrayNull(scip, &graphcompbegins, ngraphcomponents + 1);
4817  SCIPfreeBlockMemoryArrayNull(scip, &graphcomponents, propdata->npermvars);
4818  SCIPfreeBufferArrayNull(scip, &permused);
4819  SCIPfreeBufferArrayNull(scip, &usedperms);
4820  }
4821 
4822 #ifdef SCIP_DEBUG
4823  SCIPdebugMsg(scip, "total number of added (sub-)orbitopes: %d\n", norbitopes);
4824  SCIPdebugMsg(scip, "total number of added strong sbcs: %d\n", nstrongsbcs);
4825  SCIPdebugMsg(scip, "total number of added weak sbcs: %d\n", nweaksbcs);
4826 #endif
4827 
4828  SCIPfreeBufferArray(scip, &nvarsincomponent);
4829 
4830  SCIPfreeBufferArray(scip, &modifiedpermvars);
4831  for (i = propdata->nperms - 1; i >= 0; --i)
4832  {
4833  SCIPfreeBufferArray(scip, &modifiedperms[i]);
4834  }
4835  SCIPfreeBufferArray(scip, &modifiedperms);
4836  SCIPfreeBufferArray(scip, &genorder);
4837 
4838  return SCIP_OKAY;
4839 }
4840 
4841 
4842 /*
4843  * Functions for symmetry constraints
4844  */
4845 
4846 
4847 /** sorts orbitope vars matrix such that rows are sorted increasingly w.r.t. minimum variable index in row;
4848  * columns are sorted such that first row is sorted increasingly w.r.t. variable indices
4849  */
4850 static
4852  SCIP* scip, /**< SCIP instance */
4853  int** orbitopevaridx, /**< variable index matrix of orbitope */
4854  SCIP_VAR*** vars, /**< variable matrix of orbitope */
4855  int nrows, /**< number of binary rows of orbitope */
4856  int ncols /**< number of columns of orbitope */
4857  )
4858 {
4859  SCIP_VAR** sortedrow;
4860  int* colorder;
4861  int* idcs;
4862  int arrlen;
4863  int minrowidx = INT_MAX;
4864  int minrow = INT_MAX;
4865  int i;
4866  int j;
4867 
4868  assert( scip != NULL );
4869  assert( orbitopevaridx != NULL );
4870  assert( vars != NULL );
4871  assert( nrows > 0 );
4872  assert( ncols > 0 );
4873 
4874  arrlen = MAX(nrows, ncols);
4875  SCIP_CALL( SCIPallocBufferArray(scip, &idcs, arrlen) );
4876 
4877  /* detect minimum index per row */
4878  for (i = 0; i < nrows; ++i)
4879  {
4880  int idx;
4881 
4882  idcs[i] = INT_MAX;
4883 
4884  for (j = 0; j < ncols; ++j)
4885  {
4886  idx = orbitopevaridx[i][j];
4887 
4888  if ( idx < idcs[i] )
4889  idcs[i] = idx;
4890 
4891  if ( idx < minrowidx )
4892  {
4893  minrowidx = idx;
4894  minrow = i;
4895  }
4896  }
4897  }
4898 
4899  /* sort rows increasingly w.r.t. minimum variable indices */
4900  SCIPsortIntPtr(idcs, (void**) vars, nrows);
4901 
4902  /* sort columns increasingly w.r.t. variable indices of first row */
4903  SCIP_CALL( SCIPallocBufferArray(scip, &colorder, ncols) );
4904  for (j = 0; j < ncols; ++j)
4905  {
4906  idcs[j] = orbitopevaridx[minrow][j];
4907  colorder[j] = j;
4908  }
4909 
4910  /* sort columns of first row and store new column order */
4911  SCIPsortIntIntPtr(idcs, colorder, (void**) vars[0], ncols);
4912 
4913  /* adapt rows 1, ..., nrows - 1 to new column order*/
4914  SCIP_CALL( SCIPallocBufferArray(scip, &sortedrow, ncols) );
4915  for (i = 1; i < nrows; ++i)
4916  {
4917  for (j = 0; j < ncols; ++j)
4918  sortedrow[j] = vars[i][colorder[j]];
4919  for (j = 0; j < ncols; ++j)
4920  vars[i][j] = sortedrow[j];
4921  }
4922 
4923  SCIPfreeBufferArray(scip, &sortedrow);
4924  SCIPfreeBufferArray(scip, &colorder);
4925  SCIPfreeBufferArray(scip, &idcs);
4926 
4927  return SCIP_OKAY;
4928 }
4929 
4930 
4931 /** checks whether components of the symmetry group can be completely handled by orbitopes */
4932 static
4934  SCIP* scip, /**< SCIP instance */
4935  SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
4936  int* components, /**< array containing components of symmetry group */
4937  int* componentbegins, /**< array containing begin positions of components in components array */
4938  int ncomponents /**< number of components */
4939  )
4940 {
4941  SCIP_VAR** permvars;
4942  int** perms;
4943  int npermvars;
4944  int i;
4945 
4946  assert( scip != NULL );
4947  assert( propdata != NULL );
4948  assert( components != NULL );
4949  assert( componentbegins != NULL );
4950  assert( ncomponents > 0 );
4951  assert( propdata->nperms >= 0 );
4952 
4953  /* exit if no symmetry is present */
4954  if ( propdata->nperms == 0 )
4955  return SCIP_OKAY;
4956 
4957  assert( propdata->nperms > 0 );
4958  assert( propdata->perms != NULL );
4959  assert( propdata->nbinpermvars >= 0 );
4960  assert( propdata->npermvars >= 0 );
4961  assert( propdata->permvars != NULL );
4962 
4963  /* exit if no symmetry on binary variables is present */
4964  if ( propdata->nbinpermvars == 0 )
4965  {
4966  assert( ! propdata->binvaraffected );
4967  return SCIP_OKAY;
4968  }
4969 
4970  perms = propdata->perms;
4971  npermvars = propdata->npermvars;
4972  permvars = propdata->permvars;
4973 
4974  /* iterate over components */
4975  for (i = 0; i < ncomponents; ++i)
4976  {
4977  SCIP_VAR*** vars;
4978  SCIP_VAR*** varsallocorder;
4979  SCIP_CONS* cons;
4980  SCIP_Shortbool* rowisbinary;
4981  SCIP_Bool isorbitope = TRUE;
4982  SCIP_Bool infeasibleorbitope;
4983  int** orbitopevaridx;
4984  int* columnorder;
4985  int npermsincomponent;
4986  int ntwocyclescomp = INT_MAX;
4987  int nbincyclescomp = INT_MAX;
4988  int* nusedelems;
4989  int j;
4990  int cnt;
4991 
4992  /* orbitopes are detected first, so no component should be blocked */
4993  assert( ! propdata->componentblocked[i] );
4994 
4995  /* get properties of permutations */
4996  npermsincomponent = componentbegins[i + 1] - componentbegins[i];
4997  assert( npermsincomponent > 0 );
4998  for (j = componentbegins[i]; j < componentbegins[i + 1]; ++j)
4999  {
5000  int ntwocyclesperm = 0;
5001  int nbincyclesperm = 0;
5002 
5003  SCIP_CALL( SCIPisInvolutionPerm(perms[components[j]], permvars, npermvars,
5004  &ntwocyclesperm, &nbincyclesperm, FALSE) );
5005 
5006  if ( ntwocyclescomp == 0 )
5007  {
5008  isorbitope = FALSE;
5009  break;
5010  }
5011 
5012  /* if we are checking the first permutation */
5013  if ( ntwocyclescomp == INT_MAX )
5014  {
5015  ntwocyclescomp = ntwocyclesperm;
5016  nbincyclescomp = nbincyclesperm;
5017 
5018  /* if there are no binary rows */
5019  if ( nbincyclescomp == 0 )
5020  {
5021  isorbitope = FALSE;
5022  break;
5023  }
5024  }
5025 
5026  /* no or different number of 2-cycles or not all vars binary: permutations cannot generate orbitope */
5027  if ( ntwocyclescomp != ntwocyclesperm || nbincyclesperm != nbincyclescomp )
5028  {
5029  isorbitope = FALSE;
5030  break;
5031  }
5032  }
5033 
5034  /* if no orbitope was detected */
5035  if ( ! isorbitope )
5036  continue;
5037  assert( ntwocyclescomp > 0 );
5038  assert( ntwocyclescomp < INT_MAX );
5039 
5040  /* iterate over permutations and check whether for each permutation there exists
5041  * another permutation whose 2-cycles intersect pairwise in exactly one element */
5042 
5043  /* orbitope matrix for indices of variables in permvars array */
5044  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, ntwocyclescomp) );
5045  for (j = 0; j < ntwocyclescomp; ++j)
5046  {
5047  SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[j], npermsincomponent + 1) ); /*lint !e866*/
5048  }
5049 
5050  /* order of columns of orbitopevaridx */
5051  SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, npermsincomponent + 1) );
5052  for (j = 0; j < npermsincomponent + 1; ++j)
5053  columnorder[j] = npermsincomponent + 2;
5054 
5055  /* count how often an element was used in the potential orbitope */
5056  SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, npermvars) );
5057 
5058  /* store whether a row of the potential orbitope contains only binary variables */
5059  SCIP_CALL( SCIPallocClearBufferArray(scip, &rowisbinary, ntwocyclescomp) );
5060 
5061  /* check if the permutations fulfill properties of an orbitope */
5062  SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, permvars, npermvars, perms,
5063  &(components[componentbegins[i]]), ntwocyclescomp, npermsincomponent,
5064  orbitopevaridx, columnorder, nusedelems, NULL, rowisbinary, &isorbitope, NULL) );
5065 
5066  if ( ! isorbitope )
5067  goto FREEDATASTRUCTURES;
5068 
5069  /* we have found a potential orbitope, prepare data for orbitope conshdlr */
5070  SCIP_CALL( SCIPallocBufferArray(scip, &vars, nbincyclescomp) );
5071  SCIP_CALL( SCIPallocBufferArray(scip, &varsallocorder, nbincyclescomp) );
5072  cnt = 0;
5073  for (j = 0; j < ntwocyclescomp; ++j)
5074  {
5075  if ( ! rowisbinary[j] )
5076  continue;
5077 
5078  SCIP_CALL( SCIPallocBufferArray(scip, &vars[cnt], npermsincomponent + 1) ); /*lint !e866*/
5079  varsallocorder[cnt] = vars[cnt]; /* to ensure that we can free the buffer in reverse order */
5080  ++cnt;
5081  }
5082  assert( cnt == nbincyclescomp );
5083 
5084  /* prepare variable matrix (reorder columns of orbitopevaridx) */
5085  infeasibleorbitope = FALSE;
5086  SCIP_CALL( SCIPgenerateOrbitopeVarsMatrix(scip, &vars, ntwocyclescomp, npermsincomponent + 1, permvars,
5087  npermvars, orbitopevaridx, columnorder, nusedelems, rowisbinary, &infeasibleorbitope, FALSE, NULL, NULL, NULL) );
5088 
5089  if ( ! infeasibleorbitope )
5090  {
5091  char name[SCIP_MAXSTRLEN];
5092 
5093  SCIPdebugMsg(scip, "found an orbitope of size %d x %d in component %d\n", ntwocyclescomp, npermsincomponent + 1, i);
5094 
5095  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_component%d", i);
5096 
5097  /* to ensure same orbitope is added if different sets of generators are found */
5098  SCIP_CALL( SCIPsortOrbitope(scip, orbitopevaridx, vars, nbincyclescomp, npermsincomponent + 1) );
5099 
5100  SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, vars, SCIP_ORBITOPETYPE_FULL,
5101  nbincyclescomp, npermsincomponent + 1, propdata->usedynamicprop, FALSE, FALSE, FALSE,
5102  propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5103 
5104  SCIP_CALL( SCIPaddCons(scip, cons) );
5105 
5106  /* do not release constraint here - will be done later */
5107  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5108  ++propdata->norbitopes;
5109 
5110  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5111  }
5112 
5113  /* free data structures */
5114  for (j = nbincyclescomp - 1; j >= 0; --j)
5115  {
5116  SCIPfreeBufferArray(scip, &varsallocorder[j]);
5117  }
5118  SCIPfreeBufferArray(scip, &varsallocorder);
5119  SCIPfreeBufferArray(scip, &vars);
5120 
5121  FREEDATASTRUCTURES:
5122  SCIPfreeBufferArray(scip, &rowisbinary);
5123  SCIPfreeBufferArray(scip, &nusedelems);
5124  SCIPfreeBufferArray(scip, &columnorder);
5125  for (j = ntwocyclescomp - 1; j >= 0; --j)
5126  {
5127  SCIPfreeBufferArray(scip, &orbitopevaridx[j]);
5128  }
5129  SCIPfreeBufferArray(scip, &orbitopevaridx);
5130  }
5131 
5132  return SCIP_OKAY;
5133 }
5134 
5135 
5136 /** update symmetry information of conflict graph */
5137 static
5139  SCIP* scip, /**< SCIP instance */
5140  SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
5141  SCIP_VAR** graphvars, /**< variables encoded in conflict graph (either all vars or permvars) */
5142  int ngraphvars, /**< number of nodes/vars in conflict graph */
5143  SCIP_VAR** permvars, /**< variables considered in permutations */
5144  int npermvars, /**< number of permvars */
5145  SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5146  SCIP_HASHMAP* varmap, /**< map from graphvar to node label in conflict graph
5147  * (or NULL if onlypermvars == TRUE) */
5148  int* orbits, /**< array of non-trivial orbits */
5149  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
5150  int norbits /**< number of non-trivial orbits */
5151  )
5152 {
5153  int i;
5154  int j;
5155 
5156  assert( scip != NULL );
5157  assert( conflictgraph != NULL );
5158  assert( graphvars != NULL );
5159  assert( ngraphvars > 0 );
5160  assert( permvars != NULL );
5161  assert( npermvars > 0 );
5162  assert( onlypermvars || varmap != NULL );
5163  assert( orbits != NULL );
5164  assert( orbitbegins != NULL );
5165  assert( norbits >= 0 );
5166 
5167  /* initialize/reset variable information of nodes in conflict graph */
5168  for (i = 0; i < ngraphvars; ++i)
5169  {
5170  SCIP_NODEDATA* nodedata;
5171 
5172  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5173 
5174  /* possibly create node data */
5175  if ( nodedata == NULL )
5176  {
5177  SCIP_CALL( SCIPallocBlockMemory(scip, &nodedata) );
5178  nodedata->var = graphvars[i];
5179  nodedata->active = TRUE;
5180  }
5181 
5182  /* (re-)set node data */
5183  nodedata->orbitidx = -1;
5184  nodedata->nconflictinorbit = 0;
5185  nodedata->orbitsize = -1;
5186  nodedata->posinorbit = -1;
5187 
5188  /* set node data */
5189  SCIPdigraphSetNodeData(conflictgraph, (void*) nodedata, i);
5190  }
5191 
5192  /* add orbit information to nodes of conflict graph */
5193  for (j = 0; j < norbits; ++j)
5194  {
5195  int posinorbit = 0;
5196  int orbitsize;
5197 
5198  orbitsize = orbitbegins[j + 1] - orbitbegins[j];
5199  assert( orbitsize >= 0 );
5200 
5201  for (i = orbitbegins[j]; i < orbitbegins[j + 1]; ++i)
5202  {
5203  SCIP_NODEDATA* nodedata;
5204  SCIP_VAR* var;
5205  int pos;
5206 
5207  /* get variable and position in conflict graph */
5208  if ( onlypermvars )
5209  {
5210  pos = orbits[i];
5211  var = permvars[pos];
5212  }
5213  else
5214  {
5215  var = permvars[orbits[i]];
5216  assert( var != NULL );
5217 
5218  assert( SCIPhashmapExists(varmap, var) );
5219  pos = SCIPhashmapGetImageInt(varmap, var);
5220  assert( pos != INT_MAX );
5221  }
5222 
5223  /* get node data */
5224  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, pos);
5225  assert( nodedata != NULL );
5226  assert( nodedata->var == var );
5227 
5228  nodedata->orbitidx = j;
5229  nodedata->orbitsize = orbitsize;
5230  nodedata->posinorbit = posinorbit++;
5231  }
5232  }
5233 
5234  /* add information on number of conflicts within orbit to conflict graph */
5235  for (i = 0; i < ngraphvars; ++i)
5236  {
5237  SCIP_NODEDATA* nodedata;
5238  SCIP_NODEDATA* nodedataconflict;
5239  int* conflictvaridx;
5240  int nconflictinorbit = 0;
5241  int curorbit;
5242 
5243  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5244  conflictvaridx = SCIPdigraphGetSuccessors(conflictgraph, i);
5245 
5246  assert( nodedata != NULL );
5247  assert( nodedata->nconflictinorbit == 0 );
5248  assert( conflictvaridx != NULL || SCIPdigraphGetNSuccessors(conflictgraph, i) == 0 );
5249 
5250  curorbit = nodedata->orbitidx;
5251 
5252  /* i-th variable is fixed by all permutations */
5253  if ( curorbit == -1 )
5254  {
5255  nodedata->nconflictinorbit = 0;
5256  continue;
5257  }
5258 
5259  /* get conflicts in orbit by couting the active neighbors of i in the same orbit */
5260  for (j = 0; j < SCIPdigraphGetNSuccessors(conflictgraph, i); ++j)
5261  {
5262  assert( conflictvaridx != NULL );
5263  nodedataconflict = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvaridx[j]);
5264  assert( nodedataconflict != NULL );
5265 
5266  if ( nodedataconflict->active && nodedataconflict->orbitidx == curorbit )
5267  ++nconflictinorbit;
5268  }
5269 
5270  nodedata->nconflictinorbit = nconflictinorbit;
5271  }
5272 
5273  return SCIP_OKAY;
5274 }
5275 
5276 
5277 /** create conflict graph either for symmetric or for all variables
5278  *
5279  * This routine just creates the graph, but does not add (symmetry) information to its nodes.
5280  * This has to be done separately by the routine updateSymInfoConflictGraphSST().
5281  */
5282 static
5284  SCIP* scip, /**< SCIP instance */
5285  SCIP_DIGRAPH** conflictgraph, /**< pointer to store conflict graph */
5286  SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5287  int ngraphvars, /**< number of vars encoded in conflict graph */
5288  SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5289  SCIP_HASHMAP* permvarmap, /**< map of variables to indices in permvars array (or NULL) */
5290  SCIP_Bool* success /**< pointer to store whether conflict graph could be created successfully */
5291  )
5292 {
5293  SCIP_CONSHDLR* setppcconshdlr;
5294  SCIP_CONS** setppcconss;
5295  SCIP_VAR** setppcvars;
5296  SCIP_CONS* cons;
5297  int nsetppcconss;
5298  int nsetppcvars;
5299  int nodei;
5300  int nodej;
5301  int c;
5302  int i;
5303  int j;
5304  int nedges = 0;
5305 
5306  assert( scip != NULL );
5307  assert( conflictgraph != NULL );
5308  assert( graphvars != NULL );
5309  assert( ngraphvars > 0 );
5310  assert( success != NULL );
5311 
5312  *success = FALSE;
5313 
5314  /* get setppcconss for creating conflict graph */
5315  setppcconshdlr = SCIPfindConshdlr(scip, "setppc");
5316  if ( setppcconshdlr == NULL )
5317  {
5318  SCIPdebugMsg(scip, "Could not find setppc conshdlr --> construction of conflict graph aborted.\n");
5319  return SCIP_OKAY;
5320  }
5321  assert( setppcconshdlr != NULL );
5322 
5323  setppcconss = SCIPconshdlrGetConss(setppcconshdlr);
5324  nsetppcconss = SCIPconshdlrGetNConss(setppcconshdlr);
5325  if ( nsetppcconss == 0 )
5326  {
5327  SCIPdebugMsg(scip, "No setppc constraints present --> construction of conflict graph aborted.\n");
5328  return SCIP_OKAY;
5329  }
5330 
5331  /* construct conflict graph */
5332  SCIP_CALL( SCIPcreateDigraph(scip, conflictgraph, ngraphvars) );
5333  *success = TRUE;
5334 
5335  SCIPdebugMsg(scip, "Construction of conflict graph:\n");
5336 
5337  for (c = 0; c < nsetppcconss; ++c)
5338  {
5339  cons = setppcconss[c];
5340  assert( cons != NULL );
5341 
5342  /* skip covering constraints */
5343  if ( SCIPgetTypeSetppc(scip, cons) == SCIP_SETPPCTYPE_COVERING )
5344  continue;
5345 
5346  setppcvars = SCIPgetVarsSetppc(scip, cons);
5347  nsetppcvars = SCIPgetNVarsSetppc(scip, cons);
5348  assert( setppcvars != NULL );
5349  assert( nsetppcvars > 0 );
5350 
5351  SCIPdebugMsg(scip, "\tAdd edges for constraint %s.\n", SCIPconsGetName(cons));
5352 
5353  /* iterate over pairs of variables in constraint and add bidirected arc
5354  * if both are affected by a symmetry or active */
5355  for (i = 0; i < nsetppcvars; ++i)
5356  {
5357  if ( onlypermvars )
5358  {
5359  nodei = SCIPhashmapGetImageInt(permvarmap, setppcvars[i]);
5360 
5361  /* skip variables that are not affected by symmetry */
5362  if ( nodei == INT_MAX )
5363  continue;
5364  }
5365  else
5366  {
5367  nodei = SCIPvarGetProbindex(setppcvars[i]);
5368 
5369  /* skip inactive variables */
5370  if ( nodei < 0 )
5371  continue;
5372  }
5373 
5374  for (j = i + 1; j < nsetppcvars; ++j)
5375  {
5376  if ( onlypermvars )
5377  {
5378  nodej = SCIPhashmapGetImageInt(permvarmap, setppcvars[j]);
5379 
5380  /* skip variables that are not affected by symmetyr */
5381  if ( nodej == INT_MAX )
5382  continue;
5383  }
5384  else
5385  {
5386  nodej = SCIPvarGetProbindex(setppcvars[j]);
5387 
5388  /* skip inactive variables */
5389  if ( nodej < 0 )
5390  continue;
5391  }
5392 
5393  SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodei, nodej, NULL) );
5394  SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodej, nodei, NULL) );
5395  ++nedges;
5396  }
5397  }
5398  }
5399  SCIPdebugMsg(scip, "Construction of conflict graph terminated; %d conflicts detected.\n", nedges);
5400 
5401  return SCIP_OKAY;
5402 }
5403 
5404 
5405 /** frees conflict graph */
5406 static
5408  SCIP* scip, /**< SCIP instance */
5409  SCIP_DIGRAPH** conflictgraph, /**< conflict graph */
5410  int nnodes /**< number of nodes in conflict graph */
5411  )
5412 {
5413  int i;
5414 
5415  assert( scip != NULL );
5416  assert( conflictgraph != NULL );
5417  assert( *conflictgraph != NULL );
5418  assert( nnodes > 0 );
5419 
5420  /* free node data */
5421  for (i = 0; i < nnodes; ++i)
5422  {
5423  SCIP_NODEDATA* nodedata;
5424 
5425  /* get node data */
5426  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(*conflictgraph, i);
5427 
5428  /* free node data (might not have been allocated if all components are already blocked) */
5429  if ( nodedata != NULL )
5430  {
5431  SCIPfreeBlockMemory(scip, &nodedata);
5432  }
5433  }
5434 
5435  /* free conflict graph */
5436  SCIPdigraphFree(conflictgraph);
5437 
5438  return SCIP_OKAY;
5439 }
5440 
5441 
5442 /** adds symresack constraints */
5443 static
5445  SCIP* scip, /**< SCIP instance */
5446  SCIP_PROP* prop, /**< symmetry breaking propagator */
5447  int* components, /**< array containing components of symmetry group */
5448  int* componentbegins, /**< array containing begin positions of components in components array */
5449  int ncomponents /**< number of components */
5450  )
5451 { /*lint --e{641}*/
5452  SCIP_PROPDATA* propdata;
5453  SCIP_VAR** permvars;
5454  SCIP_Bool conssaddlp;
5455  int** modifiedperms = NULL;
5456  SCIP_VAR** modifiedpermvars = NULL;
5457  int** perms;
5458  int nsymresackcons = 0;
5459  int npermvars;
5460  int nperms;
5461  int i;
5462  int p;
5463 
5464  assert( scip != NULL );
5465  assert( prop != NULL );
5466 
5467  propdata = SCIPpropGetData(prop);
5468  assert( propdata != NULL );
5469  assert( propdata->npermvars >= 0 );
5470  assert( propdata->nbinpermvars >= 0 );
5471 
5472  /* if no symmetries on binary variables are present */
5473  if ( propdata->nbinpermvars == 0 )
5474  {
5475  assert( propdata->binvaraffected == 0 );
5476  return SCIP_OKAY;
5477  }
5478 
5479  perms = propdata->perms;
5480  nperms = propdata->nperms;
5481  permvars = propdata->permvars;
5482  npermvars = propdata->npermvars;
5483  conssaddlp = propdata->conssaddlp;
5484 
5485  assert( nperms <= 0 || perms != NULL );
5486  assert( permvars != NULL );
5487  assert( npermvars > 0 );
5488 
5489  /* adapt natural variable order to a variable order that is compatible with Schreier Sims constraints */
5490  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5491  {
5492  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, nperms) );
5493  for (p = 0; p < nperms; ++p)
5494  {
5495  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[p], npermvars) );
5496  }
5497  SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, npermvars) );
5498 
5499  for (i = 0; i < npermvars; ++i)
5500  modifiedpermvars[i] = permvars[i];
5501 
5502  SCIP_CALL( adaptSymmetryDataSST(scip, perms, modifiedperms, nperms, permvars, modifiedpermvars, npermvars,
5503  propdata->leaders, propdata->nleaders) );
5504  }
5505 
5506  /* if components have not been computed */
5507  if ( ncomponents == -1 )
5508  {
5509  assert( ! propdata->ofenabled );
5510  assert( ! propdata->detectorbitopes );
5511  assert( ! propdata->sstenabled );
5512 
5513  /* loop through perms and add symresack constraints */
5514  for (p = 0; p < propdata->nperms; ++p)
5515  {
5516  SCIP_CONS* cons;
5517  char name[SCIP_MAXSTRLEN];
5518 
5519  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_perm%d", p);
5520 
5521  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[p], permvars, npermvars, FALSE,
5522  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5523 
5524  SCIP_CALL( SCIPaddCons(scip, cons) );
5525 
5526  /* do not release constraint here - will be done later */
5527  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5528  ++propdata->nsymresacks;
5529  ++nsymresackcons;
5530  }
5531  }
5532  else
5533  {
5534  /* loop through components */
5535  for (i = 0; i < ncomponents; ++i)
5536  {
5537  SCIP_Bool sstcompatible = TRUE;
5538 
5539  if ( ISSSTINTACTIVE(propdata->sstleadervartype)
5540  || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
5541  || ISSSTCONTACTIVE(propdata->sstleadervartype) )
5542  sstcompatible = FALSE;
5543 
5544  /* skip components that were treated by incompatible symmetry handling techniques */
5545  if ( (propdata->componentblocked[i] & SYM_HANDLETYPE_SYMBREAK) != 0
5546  || (propdata->componentblocked[i] & SYM_HANDLETYPE_ORBITALFIXING) != 0
5547  || ((propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 && ! sstcompatible) )
5548  continue;
5549 
5550  /* loop through perms in component i and add symresack constraints */
5551  for (p = componentbegins[i]; p < componentbegins[i + 1]; ++p)
5552  {
5553  SCIP_CONS* cons;
5554  int permidx;
5555  char name[SCIP_MAXSTRLEN];
5556 
5557  permidx = components[p];
5558 
5559  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_component%d_perm%d", i, permidx);
5560 
5561  /* adapt permutation to leader */
5562  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5563  {
5564  assert( (propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 );
5565  assert( modifiedperms != NULL );
5566  assert( modifiedpermvars != NULL );
5567 
5568  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[permidx], modifiedpermvars, npermvars, FALSE,
5569  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5570  }
5571  else
5572  {
5573  SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[permidx], permvars, npermvars, FALSE,
5574  conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5575  }
5576  propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5577  SCIP_CALL( SCIPaddCons(scip, cons) );
5578 
5579  /* do not release constraint here - will be done later */
5580  propdata->genorbconss[propdata->ngenorbconss++] = cons;
5581  ++propdata->nsymresacks;
5582  ++nsymresackcons;
5583  }
5584  }
5585  }
5586 
5587  if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5588  {
5589  assert( modifiedperms != NULL );
5590  assert( modifiedpermvars != NULL );
5591 
5592  SCIPfreeBufferArray(scip, &modifiedpermvars);
5593  for (p = nperms - 1; p >= 0; --p)
5594  {
5595  SCIPfreeBufferArray(scip, &modifiedperms[p]);
5596  }
5597  SCIPfreeBufferArray(scip, &modifiedperms);
5598  }
5599 
5600  SCIPdebugMsg(scip, "Added %d symresack constraints.\n", nsymresackcons);
5601 
5602  return SCIP_OKAY;
5603 }
5604 
5605 
5606 /** add Schreier Sims constraints for a specific orbit and update Schreier Sims table */
5607 static
5609  SCIP* scip, /**< SCIP instance */
5610  SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5611  SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5612  SCIP_VAR** permvars, /**< permvars array */
5613  int* orbits, /**< symmetry orbits */
5614  int* orbitbegins, /**< array storing begin position for each orbit */
5615  int orbitidx, /**< index of orbit for Schreier Sims constraints */
5616  int orbitleaderidx, /**< index of leader variable for Schreier Sims constraints */
5617  SCIP_Shortbool* orbitvarinconflict, /**< indicator whether orbitvar is in conflict with orbit leader */
5618  int norbitvarinconflict, /**< number of variables in conflict with orbit leader */
5619  int* nchgbds, /**< pointer to store number of bound changes (or NULL) */
5620  SCIP_Bool useconflictgraph /**< whether conflict graph shall be used */
5621  )
5622 { /*lint --e{613,641}*/
5623  SCIP_CONS* cons;
5624  char name[SCIP_MAXSTRLEN];
5625  SCIP_VAR* vars[2];
5626  SCIP_Real vals[2];
5627  int orbitsize;
5628  int posleader;
5629  int poscur;
5630  int ncuts = 0;
5631  SCIP_Bool addcuts = FALSE;
5632  int i;
5633 #ifndef NDEBUG
5634  int j;
5635 #endif
5636 
5637  assert( scip != NULL );
5638  assert( conflictgraph != NULL || ! useconflictgraph );
5639  assert( propdata != NULL );
5640  assert( permvars != NULL );
5641  assert( orbits != NULL );
5642  assert( orbitbegins != NULL );
5643  assert( orbitidx >= 0 );
5644  assert( orbitleaderidx >= 0 );
5645  assert( orbitvarinconflict != NULL || ! useconflictgraph );
5646  assert( norbitvarinconflict >= 0 );
5647  assert( nchgbds != NULL );
5648 
5649  orbitsize = orbitbegins[orbitidx + 1] - orbitbegins[orbitidx];
5650 
5651  /* variables in conflict with leader are fixed and not treated by a cut; trailing -1 to not count the leader */
5652  if ( propdata->sstaddcuts )
5653  addcuts = TRUE;
5654  else if ( propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5655  || propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTS
5656  || propdata->ssttiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT )
5657  addcuts = propdata->addconflictcuts;
5658 
5659  if ( addcuts )
5660  ncuts = orbitsize - norbitvarinconflict - 1;
5661 
5662  /* (re-)allocate memory for Schreier Sims constraints and leaders */
5663  if ( ncuts > 0 )
5664  {
5665  if ( propdata->nsstconss == 0 )
5666  {
5667  assert( propdata->sstconss == NULL );
5668  assert( propdata->maxnsstconss == 0 );
5669  propdata->maxnsstconss = 2 * ncuts;
5670  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->sstconss), propdata->maxnsstconss) );
5671  }
5672  else if ( propdata->nsstconss + ncuts > propdata->maxnsstconss )
5673  {
5674  int newsize;
5675 
5676  newsize = SCIPcalcMemGrowSize(scip, propdata->maxnsstconss + 2 * ncuts);
5677  SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(propdata->sstconss),
5678  propdata->maxnsstconss, newsize) );
5679  propdata->maxnsstconss = newsize;
5680  }
5681  }
5682 
5683  if ( propdata->nleaders == 0 )
5684  {
5685  propdata->maxnleaders = MIN(propdata->nperms, propdata->npermvars);
5686  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->leaders), propdata->maxnleaders) );
5687  }
5688  assert( propdata->nleaders < propdata->maxnleaders );
5689 
5690  /* add Schreier Sims constraints vars[0] >= vars[1], where vars[0] is always the leader */
5691  posleader = orbitbegins[orbitidx] + orbitleaderidx;
5692  vars[0] = permvars[orbits[posleader]];
5693  vals[0] = -1.0;
5694  vals[1] = 1.0;
5695  propdata->leaders[propdata->nleaders++] = orbits[posleader];
5696  *nchgbds = 0;
5697  for (i = 0, poscur = orbitbegins[orbitidx]; i < orbitsize; ++i, ++poscur)
5698  {
5699  if ( i == orbitleaderidx )
5700  {
5701  assert( orbitvarinconflict == NULL || ! orbitvarinconflict[i] );
5702  continue;
5703  }
5704 
5705  vars[1] = permvars[orbits[poscur]];
5706 #ifndef NDEBUG
5707  for (j = 0; j < propdata->nleaders - 1; ++j)
5708  {
5709  assert( propdata->leaders[j] != orbits[poscur] );
5710  }
5711 #endif
5712 
5713  /* if the i-th variable in the orbit is in a conflict with the leader, fix it to 0 */
5714  if ( useconflictgraph )
5715  {
5716  if ( orbitvarinconflict[i] )
5717  {
5718  assert( SCIPvarIsBinary(vars[1]) );
5719  assert( SCIPvarGetLbLocal(vars[1]) < 0.5 );
5720  assert( useconflictgraph );
5721 
5722  /* if variable is fixed */
5723  if ( SCIPvarGetUbLocal(vars[1]) > 0.5 )
5724  {
5725  SCIP_NODEDATA* nodedata;
5726 
5727  SCIP_CALL( SCIPchgVarUb(scip, vars[1], 0.0) );
5728  ++(*nchgbds);
5729 
5730  /* deactivate the fixed variable (cannot contribute to a conflict anymore) */
5731  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, orbits[poscur]);
5732  assert( nodedata != NULL );
5733  assert( nodedata->active );
5734 
5735  nodedata->active = FALSE;
5736  }
5737 
5738  /* reset value */
5739  orbitvarinconflict[i] = FALSE;
5740  }
5741  else if ( addcuts )
5742  {
5743  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5744  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5746 
5747  SCIP_CALL( SCIPaddCons(scip, cons) );
5748  propdata->sstconss[propdata->nsstconss++] = cons;
5749  }
5750  }
5751  else if ( addcuts )
5752  {
5753  (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5754  SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5756 
5757  SCIP_CALL( SCIPaddCons(scip, cons) );
5758  propdata->sstconss[propdata->nsstconss++] = cons;
5759  }
5760  }
5761 
5762  return SCIP_OKAY;
5763 }
5764 
5765 
5766 /** selection rule of next orbit/leader in orbit for Schreier Sims constraints */
5767 static
5769  SCIP* scip, /**< SCIP instance */
5770  SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5771  SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5772  int ngraphvars, /**< number of variables encoded in conflict graph */
5773  SCIP_HASHMAP* varmap, /**< map from variable to node label in conflict graph or NULL if useconflictgraph == FALSE */
5774  SCIP_VAR** permvars, /**< vars encoded in a permutation */
5775  int npermvars, /**< number of vars in a permutation */
5776  int* orbits, /**< orbits of stabilizer subgroup */
5777  int* orbitbegins, /**< array storing the begin position of each orbit in orbits */
5778  int norbits, /**< number of orbits */
5779  int leaderrule, /**< rule to select leader */
5780  int tiebreakrule, /**< tie break rule to select leader */
5781  SCIP_VARTYPE leadervartype, /**< variable type of leader */
5782  int* orbitidx, /**< pointer to index of selected orbit */
5783  int* leaderidx, /**< pointer to leader in orbit */
5784  SCIP_Shortbool* orbitvarinconflict, /**< array to store whether a var in the orbit is conflicting with leader */
5785  int* norbitvarinconflict, /**< pointer to store number of vars in the orbit in conflict with leader */
5786  SCIP_Bool useconflictgraph, /**< whether conflict graph shall be used */
5787  SCIP_Bool* success /**< pointer to store whether orbit cut be selected successfully */
5788  )
5789 {
5790  SCIP_NODEDATA* nodedata;
5791  int* conflictvars;
5792  int nconflictvars = 0;
5793  int varidx;
5794  int orbitcriterion;
5795  int curcriterion = INT_MIN;
5796  int orbitsize;
5797  int i;
5798  SCIP_NODEDATA* neighbordata;
5799  int leader = -1;
5800  int j;
5801 
5802  assert( scip != NULL );
5803  assert( conflictgraph != NULL || ! useconflictgraph );
5804  assert( graphvars != NULL );
5805  assert( ngraphvars > 0 );
5806  assert( varmap != NULL || ! useconflictgraph );
5807  assert( permvars != NULL );
5808  assert( npermvars > 0 );
5809  assert( orbits != NULL );
5810  assert( orbitbegins != NULL );
5811  assert( norbits > 0 );
5812  assert( orbitidx != NULL );
5813  assert( leaderidx != NULL );
5814  assert( orbitvarinconflict != NULL || ! useconflictgraph );
5815  assert( norbitvarinconflict != NULL );
5816  assert( success != NULL );
5817 
5818  *orbitidx = 0;
5819  *leaderidx = 0;
5820  *norbitvarinconflict = 0;
5821  *success = FALSE;
5822 
5823  /* terminate if leader or tiebreak rule cannot be checked */
5824  if ( ! useconflictgraph && (leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTS
5825  || leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5826  || tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
5827  return SCIP_OKAY;
5828 
5829  /* select the leader and its orbit */
5830  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT || leaderrule == (int) SCIP_LEADERRULE_LASTINORBIT )
5831  {
5832  orbitcriterion = INT_MIN;
5833 
5834  /* iterate over orbits and select the first one that meets the tiebreak rule */
5835  for (i = 0; i < norbits; ++i)
5836  {
5837  /* skip orbits containing vars different to the leader's vartype */
5838  if ( SCIPvarGetType(permvars[orbits[orbitbegins[i]]]) != leadervartype )
5839  continue;
5840 
5841  if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MINORBIT )
5842  curcriterion = orbitbegins[i] - orbitbegins[i + 1];
5843  else if ( tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXORBIT )
5844  curcriterion = orbitbegins[i + 1] - orbitbegins[i];
5845  else
5846  {
5847  /* get first or last active variable in orbit */
5848  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
5849  {
5850  int cnt = orbitbegins[i];
5851 
5852  do
5853  {
5854  varidx = SCIPvarGetProbindex(permvars[orbits[cnt++]]);
5855  }
5856  while ( varidx == -1 && cnt < orbitbegins[i + 1]);
5857  }
5858  else
5859  {
5860  int cnt = orbitbegins[i + 1] - 1;
5861 
5862  do
5863  {
5864  varidx = SCIPvarGetProbindex(permvars[orbits[cnt--]]);
5865  }
5866  while ( varidx == -1 && cnt >= orbitbegins[i]);
5867  }
5868 
5869  /* skip inactive variables */
5870  if ( varidx == -1 )
5871  continue;
5872 
5873  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, varidx);
5874  assert( nodedata != NULL );
5875  assert( nodedata->orbitidx == i );
5876 
5877  if ( nodedata->nconflictinorbit > 0 )
5878  curcriterion = nodedata->nconflictinorbit;
5879  }
5880 
5881  /* update selected orbit */
5882  if ( curcriterion > orbitcriterion )
5883  {
5884  orbitcriterion = curcriterion;
5885  *orbitidx = i;
5886  *success = TRUE;
5887 
5888  if ( leaderrule == (int) SCIP_LEADERRULE_FIRSTINORBIT )
5889  *leaderidx = 0;
5890  else
5891  *leaderidx = orbitbegins[i + 1] - orbitbegins[i] - 1;
5892  }
5893  }
5894 
5895  /* store variables in conflict with leader */
5896  if ( useconflictgraph )
5897  {
5898  leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
5899  assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
5900 
5901  nconflictvars = SCIPdigraphGetNSuccessors(conflictgraph, leader);
5902  }
5903 
5904  if ( *success && tiebreakrule == (int) SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && nconflictvars > 0 )
5905  {
5906  SCIP_VAR* var;
5907  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
5908  assert( useconflictgraph );
5909  assert( leader >= 0 && leader < npermvars );
5910 
5911  conflictvars = SCIPdigraphGetSuccessors(conflictgraph, leader);
5912  assert( conflictvars != NULL );
5913  assert( orbitvarinconflict != NULL );
5914 
5915  for (i = 0; i < orbitsize; ++i)
5916  {
5917  /* skip the leader */
5918  if ( i == *leaderidx )
5919  continue;
5920 
5921  var = permvars[orbits[orbitbegins[*orbitidx] + i]];
5922 
5923  for (j = 0; j < nconflictvars; ++j)
5924  {
5925  neighbordata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvars[j]);
5926 
5927  assert( neighbordata != NULL );
5928 
5929  if ( neighbordata->var == var && neighbordata->active )
5930  {
5931  orbitvarinconflict[i] = TRUE;
5932  *norbitvarinconflict += 1;
5933  break;
5934  }
5935  }
5936  }
5937  }
5938  }
5939  else if ( useconflictgraph )
5940  {
5941  orbitcriterion = 0;
5942 
5943  /* iterate over variables and select the first one that meets the tiebreak rule */
5944  for (i = 0; i < ngraphvars; ++i)
5945  {
5946  /* skip vars different to the leader's vartype */
5947  if ( SCIPvarGetType(graphvars[i]) != leadervartype )
5948  continue;
5949 
5950  nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5951  assert( nodedata != NULL );
5952 
5953  /* skip variables not affected by symmetry */
5954  if ( nodedata->orbitidx == -1 )
5955  continue;
5956 
5957  if ( leaderrule == (int) SCIP_LEADERRULE_MAXCONFLICTSINORBIT )
5958  curcriterion = nodedata->nconflictinorbit;
5959  else
5960  curcriterion = SCIPdigraphGetNSuccessors(conflictgraph, i);
5961 
5962  if ( curcriterion > orbitcriterion )
5963  {
5964  orbitcriterion = curcriterion;
5965  *orbitidx = nodedata->orbitidx;
5966  *leaderidx = nodedata->posinorbit;
5967  *success = TRUE;
5968  }
5969  }
5970 
5971  /* store variables in conflict with leader */
5972  leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
5973  assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
5974  assert( norbitvarinconflict != NULL );
5975 
5976  nconflictvars = SCIPdigraphGetNSuccessors(conflictgraph, leader);
5977  if ( *success && nconflictvars > 0 )
5978  {
5979  SCIP_VAR* var;
5980  assert( orbitvarinconflict != NULL );
5981 
5982  orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
5983 
5984  conflictvars = SCIPdigraphGetSuccessors(conflictgraph, leader);
5985  assert( conflictvars != NULL );
5986 
5987  for (i = 0; i < orbitsize; ++i)
5988  {
5989  /* skip the leader */
5990  if ( i == *leaderidx )
5991  continue;
5992 
5993  var = permvars[orbits[orbitbegins[*orbitidx] + i]];
5994 
5995  for (j = 0; j < nconflictvars; ++j)
5996  {
5997  neighbordata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, conflictvars[j]);
5998  assert( neighbordata != NULL );
5999 
6000  if ( neighbordata->var == var && neighbordata->active )
6001  {
6002  orbitvarinconflict[i] = TRUE;
6003  *norbitvarinconflict += 1;
6004  break;
6005  }
6006  }
6007  }
6008  }
6009  }
6010 
6011  return SCIP_OKAY;
6012 }
6013 
6014 
6015 /** add Schreier Sims constraints to the problem */
6016 static
6018  SCIP* scip, /**< SCIP instance */
6019  SCIP_PROPDATA* propdata, /**< datas of symmetry propagator */
6020  int* nchgbds /**< pointer to store number of bound changes (or NULL) */
6021  )
6022 { /*lint --e{641}*/
6023  SCIP_DIGRAPH* conflictgraph = NULL;
6024  SCIP_HASHMAP* varmap = NULL;
6025  SCIP_VAR** vars;
6026  int nvars;
6027 
6028  SCIP_HASHMAP* permvarmap;
6029  SCIP_VAR** permvars;
6030  int** permstrans;
6031  int npermvars;
6032  int nmovedpermvars;
6033  int nmovedbinpermvars;
6034  int nmovedintpermvars;
6035  int nmovedimplintpermvars;
6036  int nmovedcontpermvars;
6037  int nperms;
6038 
6039  int* orbits;
6040  int* orbitbegins;
6041  int norbits;
6042  int* components;
6043  int* componentbegins;
6044  int* vartocomponent;
6045  int ncomponents;
6046  unsigned* componentblocked;
6047 
6048  int orbitidx;
6049  int orbitleaderidx;
6050  SCIP_Shortbool* orbitvarinconflict = NULL;
6051  int norbitvarinconflict;
6052  SCIP_Shortbool* inactiveperms;
6053  int ninactiveperms;
6054  int posleader;
6055  int leaderrule;
6056  int tiebreakrule;
6057  int leadervartype;
6058  SCIP_VARTYPE selectedtype = SCIP_VARTYPE_CONTINUOUS;
6059  int nvarsselectedtype;
6060  SCIP_Bool conflictgraphcreated = FALSE;
6061  SCIP_Bool mixedcomponents;
6062  int* norbitleadercomponent;
6063 
6064  int c;
6065  int v;
6066  int p;
6067 
6068  assert( scip != NULL );
6069  assert( propdata != NULL );
6070 
6071  permvars = propdata->permvars;
6072  npermvars = propdata->npermvars;
6073  permvarmap = propdata->permvarmap;
6074  permstrans = propdata->permstrans;
6075  nperms = propdata->nperms;
6076  components = propdata->components;
6077  componentbegins = propdata->componentbegins;
6078  componentblocked = propdata->componentblocked;
6079  vartocomponent = propdata->vartocomponent;
6080  ncomponents = propdata->ncomponents;
6081  nmovedpermvars = propdata->nmovedpermvars;
6082  nmovedbinpermvars = propdata->nmovedbinpermvars;
6083  nmovedintpermvars = propdata->nmovedintpermvars;
6084  nmovedimplintpermvars = propdata->nmovedimplintpermvars;
6085  nmovedcontpermvars = propdata->nmovedcontpermvars;
6086 
6087  assert( permvars != NULL );
6088  assert( npermvars > 0 );
6089  assert( permvarmap != NULL );
6090  assert( permstrans != NULL );
6091  assert( nperms > 0 );
6092  assert( components != NULL );
6093  assert( componentbegins != NULL );
6094  assert( vartocomponent != NULL );
6095  assert( ncomponents > 0 );
6096  assert( nmovedpermvars > 0 || ! propdata->ofenabled );
6097  assert( nmovedbinpermvars > 0 || ! propdata->ofenabled );
6098 
6099  leaderrule = propdata->sstleaderrule;
6100  tiebreakrule = propdata->ssttiebreakrule;
6101  leadervartype = propdata->sstleadervartype;
6102  mixedcomponents = propdata->sstmixedcomponents;
6103 
6104  /* if not already computed, get number of affected vars */
6105  if ( nmovedpermvars == -1 )
6106  {
6107  nmovedpermvars = 0;
6108 
6109  for (v = 0; v < npermvars; ++v)
6110  {
6111  for (p = 0; p < nperms; ++p)
6112  {
6113  if ( permstrans[v][p] != v )
6114  {
6115  ++nmovedpermvars;
6116 
6117  switch ( SCIPvarGetType(permvars[v]) )
6118  {
6119  case SCIP_VARTYPE_BINARY:
6120  ++nmovedbinpermvars;
6121  break;
6122  case SCIP_VARTYPE_INTEGER:
6123  ++nmovedintpermvars;
6124  break;
6125  case SCIP_VARTYPE_IMPLINT:
6126  ++nmovedimplintpermvars;
6127  break;
6129  default:
6130  ++nmovedcontpermvars;
6131  }
6132  }
6133  }
6134  }
6135  }
6136  propdata->nmovedbinpermvars = nmovedbinpermvars;
6137  propdata->nmovedintpermvars = nmovedintpermvars;
6138  propdata->nmovedimplintpermvars = nmovedimplintpermvars;
6139  propdata->nmovedcontpermvars = nmovedcontpermvars;
6140 
6141  vars = SCIPgetVars(scip);
6142  nvars = SCIPgetNVars(scip);
6143 
6144  /* determine the leader's vartype */
6145  nvarsselectedtype = 0;
6146  if ( ISSSTBINACTIVE(leadervartype) && nmovedbinpermvars > nvarsselectedtype )
6147  {
6148  selectedtype = SCIP_VARTYPE_BINARY;
6149  nvarsselectedtype = nmovedbinpermvars;
6150  }
6151 
6152  if ( ISSSTINTACTIVE(leadervartype) && nmovedintpermvars > nvarsselectedtype )
6153  {
6154  selectedtype = SCIP_VARTYPE_INTEGER;
6155  nvarsselectedtype = nmovedintpermvars;
6156  }
6157 
6158  if ( ISSSTIMPLINTACTIVE(leadervartype) && nmovedimplintpermvars > nvarsselectedtype )
6159  {
6160  selectedtype = SCIP_VARTYPE_IMPLINT;
6161  nvarsselectedtype = nmovedimplintpermvars;
6162  }
6163 
6164  if ( ISSSTCONTACTIVE(leadervartype) && nmovedcontpermvars > nvarsselectedtype )
6165  {
6166  selectedtype = SCIP_VARTYPE_CONTINUOUS;
6167  nvarsselectedtype = nmovedcontpermvars;
6168  }
6169 
6170  /* terminate if no variables of a possible leader type is affected */
6171  if ( nvarsselectedtype == 0 )
6172  return SCIP_OKAY;
6173 
6174  /* possibly create conflict graph; graph is not created if no setppc conss are present */
6175  if ( selectedtype == SCIP_VARTYPE_BINARY && (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
6176  || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS
6177  || tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT) )
6178  {
6179  SCIP_CALL( createConflictGraphSST(scip, &conflictgraph, vars, nvars, FALSE,
6180  permvarmap, &conflictgraphcreated) );
6181  }
6182 
6183  /* allocate data structures necessary for orbit computations and conflict graph */
6184  SCIP_CALL( SCIPallocBufferArray(scip, &inactiveperms, nperms) );
6185  SCIP_CALL( SCIPallocBufferArray(scip, &orbits, npermvars) );
6186  SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, npermvars) );
6187 
6188  if ( conflictgraphcreated )
6189  {
6190  SCIP_CALL( SCIPallocClearBufferArray(scip, &orbitvarinconflict, npermvars) );
6191  SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), nvars) );
6192  for (v = 0; v < nvars; ++v)
6193  {
6194  assert( ! SCIPhashmapExists(varmap, vars[v]) );
6195  SCIP_CALL( SCIPhashmapInsertInt(varmap, vars[v], v) );
6196  }
6197  }
6198 
6199  SCIPdebugMsg(scip, "Start selection of orbits and leaders for Schreier Sims constraints.\n");
6200  SCIPdebugMsg(scip, "orbitidx\tleaderidx\torbitsize\n");
6201 
6202  if ( nchgbds != NULL )
6203  *nchgbds = 0;
6204 
6205  /* initialize array indicating whether permutations shall not be considered for orbit permutations */
6206  for (p = 0; p < nperms; ++p)
6207  inactiveperms[p] = TRUE;
6208 
6209  SCIP_CALL( SCIPallocBufferArray(scip, &norbitleadercomponent, ncomponents) );
6210  for (c = 0; c < ncomponents; ++c)
6211  norbitleadercomponent[c] = 0;
6212 
6213  /* iterate over components and compute orbits */
6214  for (c = 0; c < ncomponents; ++c)
6215  {
6216  SCIP_Bool success = TRUE;
6217 
6218  if ( componentblocked[c] )
6219  continue;
6220 
6221  for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6222  inactiveperms[components[p]] = FALSE;
6223  ninactiveperms = nperms - componentbegins[c + 1] + componentbegins[c];
6224 
6225  /* as long as the stabilizer is non-trivial, add Schreier Sims constraints */
6226  while ( ninactiveperms < nperms )
6227  {
6228  int nchanges = 0;
6229 
6230  /* compute orbits w.r.t. active perms */
6231  SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, npermvars, permstrans, nperms, inactiveperms,
6232  orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent,
6233  componentblocked, ncomponents, nmovedpermvars) );
6234 
6235  /* stop if we require pure components and a component contains variables of different types */
6236  if ( ! mixedcomponents )
6237  {
6238  for (p = 0; p < norbits; ++p)
6239  {
6240  /* stop if the first element of an orbits has the wrong vartype */
6241  if ( SCIPvarGetType(permvars[orbits[orbitbegins[p]]]) != selectedtype )
6242  {
6243  success = FALSE;
6244  break;
6245  }
6246  }
6247  }
6248 
6249  if ( ! success )
6250  break;
6251 
6252  /* update symmetry information of conflict graph */
6253  if ( conflictgraphcreated )
6254  {
6255  assert( conflictgraph != NULL );
6256  SCIP_CALL( updateSymInfoConflictGraphSST(scip, conflictgraph, vars, nvars, permvars, npermvars, FALSE,
6257  varmap, orbits, orbitbegins, norbits) );
6258  }
6259 
6260  /* possibly adapt the leader and tie-break rule */
6261  if ( (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS)
6262  && ! conflictgraphcreated )
6263  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
6264  if ( (leaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT || leaderrule == SCIP_LEADERRULE_MAXCONFLICTS)
6265  && selectedtype != SCIP_VARTYPE_BINARY )
6266  leaderrule = SCIP_LEADERRULE_FIRSTINORBIT;
6267  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && ! conflictgraphcreated )
6268  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
6269  if ( tiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT && selectedtype != SCIP_VARTYPE_BINARY )
6270  tiebreakrule = SCIP_LEADERTIEBREAKRULE_MAXORBIT;
6271 
6272  /* select orbit and leader */
6273  SCIP_CALL( selectOrbitLeaderSSTConss(scip, conflictgraph, vars, nvars, varmap,
6274  permvars, npermvars, orbits, orbitbegins, norbits, propdata->sstleaderrule, propdata->ssttiebreakrule, selectedtype,
6275  &orbitidx, &orbitleaderidx, orbitvarinconflict, &norbitvarinconflict, conflictgraphcreated, &success) );
6276 
6277  if ( ! success )
6278  break;
6279 
6280  assert( 0 <= orbitidx && orbitidx < norbits );
6281  assert( 0 <= orbitleaderidx && orbitleaderidx < orbitbegins[orbitidx + 1] - orbitbegins[orbitidx] );
6282  SCIPdebugMsg(scip, "%d\t\t%d\t\t%d\n", orbitidx, orbitleaderidx, orbitbegins[orbitidx + 1] - orbitbegins[orbitidx]);
6283 
6284  /* add Schreier Sims constraints for the selected orbit and update Schreier Sims table */
6285  SCIP_CALL( addSSTConssOrbitAndUpdateSST(scip, conflictgraph, propdata, permvars,
6286  orbits, orbitbegins, orbitidx, orbitleaderidx, orbitvarinconflict, norbitvarinconflict, &nchanges, conflictgraphcreated) );
6287 
6288  ++norbitleadercomponent[propdata->vartocomponent[orbits[orbitbegins[orbitidx] + orbitleaderidx]]];
6289 
6290  if ( nchgbds != NULL )
6291  *nchgbds += nchanges;
6292 
6293  /* deactivate permutations that move the orbit leader */
6294  posleader = orbits[orbitbegins[orbitidx] + orbitleaderidx];
6295  for (p = 0; p < nperms; ++p)
6296  {
6297  if ( inactiveperms[p] )
6298  continue;
6299 
6300  if ( permstrans[posleader][p] != posleader )
6301  {
6302  inactiveperms[p] = TRUE;
6303  ++ninactiveperms;
6304  }
6305  }
6306  }
6307 
6308  for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6309  inactiveperms[components[p]] = TRUE;
6310  }
6311 
6312  /* if Schreier Sims constraints have been added, store that Schreier Sims has been used for this component */
6313  for (c = 0; c < ncomponents; ++c)
6314  {
6315  if ( norbitleadercomponent[c] > 0 )
6316  componentblocked[c] |= SYM_HANDLETYPE_SST;
6317  }
6318  SCIPfreeBufferArray(scip, &norbitleadercomponent);
6319 
6320  if ( conflictgraphcreated )
6321  {
6322  SCIPhashmapFree(&varmap);
6323  SCIPfreeBufferArray(scip, &orbitvarinconflict);
6324  }
6325  SCIPfreeBufferArray(scip, &orbitbegins);
6326  SCIPfreeBufferArray(scip, &orbits);
6327  if ( conflictgraphcreated )
6328  {
6329  assert( conflictgraph != NULL );
6330  SCIP_CALL( freeConflictGraphSST(scip, &conflictgraph, nvars) );
6331  }
6332  SCIPfreeBufferArray(scip, &inactiveperms);
6333 
6334  return SCIP_OKAY;
6335 }
6336 
6337 
6338 /** finds problem symmetries */
6339 static
6341  SCIP* scip, /**< SCIP instance */
6342  SCIP_PROP* prop, /**< symmetry breaking propagator */
6343  int* nchgbds, /**< pointer to store number of bound changes (or NULL)*/
6344  SCIP_Bool* earlyterm /**< pointer to store whether we terminated early (or NULL) */
6345  )
6346 {
6347  SCIP_PROPDATA* propdata;
6348 
6349  assert( prop != NULL );
6350  assert( scip != NULL );
6351 
6352  propdata = SCIPpropGetData(prop);
6353  assert( propdata != NULL );
6354  assert( propdata->symconsenabled || propdata->sstenabled );
6355 
6356  /* if constraints have already been added */
6357  if ( propdata->triedaddconss )
6358  {
6359  assert( propdata->nperms > 0 );
6360 
6361  if ( earlyterm != NULL )
6362  *earlyterm = TRUE;
6363 
6364  return SCIP_OKAY;
6365  }
6366 
6367  /* possibly compute symmetry */
6368  if ( propdata->ofenabled && SCIPgetNBinVars(scip) > 1 )
6369  {
6370  SCIP_Bool oldsymconsenabled;
6371 
6372  oldsymconsenabled = propdata->symconsenabled;
6373 
6374  /* in the nonlinear case, all non-binary variables have to be fixed
6375  (fix non-binary potential branching variables)
6376  */
6377  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6378  {
6380  }
6381  else
6382  {
6384  }
6385 
6386  /* if there is no symmetry compatible with OF, check whether there are more general symmetries */
6387  if ( propdata->nperms == 0 && SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip) > 1 )
6388  {
6389  SCIP_CALL( freeSymmetryData(scip, propdata) );
6390  propdata->symconsenabled = oldsymconsenabled;
6391  propdata->ofenabled = FALSE;
6392  propdata->sstenabled = FALSE;
6393 
6395  }
6396  }
6397  else
6398  {
6400  }
6401  assert( propdata->binvaraffected || ! propdata->ofenabled || ! propdata->symconsenabled );
6402 
6403  if ( propdata->nperms <= 0 || (! propdata->symconsenabled && ! propdata->sstenabled) )
6404  return SCIP_OKAY;
6405 
6406  if ( ! propdata->binvaraffected )
6407  {
6408  SCIPdebugMsg(scip, "Symmetry propagator: problem is linear and no symmetry on binary variables has been found, turning symretope constraints off.\n");
6409  propdata->symconsenabled = FALSE;
6410  }
6411  assert( propdata->nperms > 0 );
6412  assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || propdata->sstenabled );
6413 
6414  propdata->triedaddconss = TRUE;
6415 
6416  if ( propdata->symconsenabled )
6417  {
6418  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms) );
6419 
6420  if ( propdata->detectorbitopes )
6421  {
6422  SCIP_CALL( detectOrbitopes(scip, propdata, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6423  }
6424  }
6425 
6426  /* disable orbital fixing if all components are handled by orbitopes */
6427  if ( propdata->ncomponents == propdata->norbitopes )
6428  propdata->ofenabled = FALSE;
6429 
6430  /* possibly stop */
6431  if ( SCIPisStopped(scip) )
6432  {
6433  if ( propdata->ngenorbconss == 0 )
6434  {
6435  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6436  }
6437  return SCIP_OKAY;
6438  }
6439 
6440  if ( propdata->ncompblocked < propdata->ncomponents && propdata->detectsubgroups && propdata->symconsenabled )
6441  {
6442  /* @TODO: create array only when needed */
6443  propdata->genlinconsssize = propdata->nperms;
6444  SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize) );
6445 
6446  SCIP_CALL( detectAndHandleSubgroups(scip, propdata) );
6447  }
6448 
6449  if ( propdata->sstenabled )
6450  {
6451  SCIP_CALL( addSSTConss(scip, propdata, nchgbds) );
6452  }
6453 
6454  /* possibly stop */
6455  if ( SCIPisStopped(scip) || ! propdata->symconsenabled )
6456  return SCIP_OKAY;
6457 
6458  /* add symmetry breaking constraints if orbital fixing is not used outside orbitopes */
6459  if ( ! propdata->ofenabled )
6460  {
6461  /* exit if no or only trivial symmetry group is available */
6462  if ( propdata->nperms < 1 || ! propdata->binvaraffected )
6463  return SCIP_OKAY;
6464 
6465  if ( propdata->addsymresacks )
6466  {
6467  SCIP_CALL( addSymresackConss(scip, prop, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6468  }
6469 
6470  /* free symmetry conss if no orbitope/symresack constraints have been found (may happen if Schreier-Sims constraints are active) */
6471  if ( propdata->ngenorbconss == 0 )
6472  SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6473  }
6474 
6475  return SCIP_OKAY;
6476 }
6477 
6478 
6479 
6480 /*
6481  * Local methods for orbital fixing
6482  */
6483 
6484 
6485 /** performs orbital fixing
6486  *
6487  * Note that we do not have to distinguish between variables that have been fixed or branched to 1, since the
6488  * stabilizer is with respect to the variables that have been branched to 1. Thus, if an orbit contains a variable that
6489  * has been branched to 1, the whole orbit only contains variables that have been branched to 1 - and nothing can be
6490  * fixed.
6491  */
6492 static
6494  SCIP* scip, /**< SCIP pointer */
6495  SCIP_VAR** permvars, /**< variables */
6496  int npermvars, /**< number of variables */
6497  int* orbits, /**< array of non-trivial orbits */
6498  int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
6499  int norbits, /**< number of orbits */
6500  SCIP_Bool* infeasible, /**< pointer to store whether problem is infeasible */
6501  int* nfixedzero, /**< pointer to store number of variables fixed to 0 */
6502  int* nfixedone /**< pointer to store number of variables fixed to 1 */
6503  )
6504 {
6505  SCIP_Bool tightened;
6506  int i;
6507 
6508  assert( scip != NULL );
6509  assert( permvars != NULL );
6510  assert( orbits != NULL );
6511  assert( orbitbegins != NULL );
6512  assert( infeasible != NULL );
6513  assert( nfixedzero != NULL );
6514  assert( nfixedone != NULL );
6515  assert( norbits > 0 );
6516  assert( orbitbegins[0] == 0 );
6517 
6518  *infeasible = FALSE;
6519  *nfixedzero = 0;
6520  *nfixedone = 0;
6521 
6522  /* check all orbits */
6523  for (i = 0; i < norbits; ++i)
6524  {
6525  SCIP_Bool havefixedone = FALSE;
6526  SCIP_Bool havefixedzero = FALSE;
6527  SCIP_VAR* var;
6528  int j;
6529 
6530  /* we only have nontrivial orbits */
6531  assert( orbitbegins[i+1] - orbitbegins[i] >= 2 );
6532 
6533  /* check all variables in the orbit */
6534  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6535  {
6536  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6537  var = permvars[orbits[j]];
6538  assert( var != NULL );
6539 
6540  /* check whether variable is not binary (and not implicit integer!) */
6541  if ( SCIPvarGetType(var) != SCIP_VARTYPE_BINARY )
6542  {
6543  /* skip orbit if there are non-binary variables */
6544  havefixedone = FALSE;
6545  havefixedzero = FALSE;
6546  break;
6547  }
6548 
6549  /* if variable is fixed to 1 -> can fix all variables in orbit to 1 */
6550  if ( SCIPvarGetLbLocal(var) > 0.5 )
6551  havefixedone = TRUE;
6552 
6553  /* check for zero-fixed variables */
6554  if ( SCIPvarGetUbLocal(var) < 0.5 )
6555  havefixedzero = TRUE;
6556  }
6557 
6558  /* check consistency */
6559  if ( havefixedone && havefixedzero )
6560  {
6561  *infeasible = TRUE;
6562  return SCIP_OKAY;
6563  }
6564 
6565  /* fix all variables to 0 if there is one variable fixed to 0 */
6566  if ( havefixedzero )
6567  {
6568  assert( ! havefixedone );
6569 
6570  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6571  {
6572  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6573  var = permvars[orbits[j]];
6574  assert( var != NULL );
6575 
6576  /* only variables that are not yet fixed to 0 */
6577  if ( SCIPvarGetUbLocal(var) > 0.5 )
6578  {
6579  SCIPdebugMsg(scip, "can fix <%s> (index %d) to 0.\n", SCIPvarGetName(var), orbits[j]);
6580  assert( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY );
6581  /* due to aggregation, var might already be fixed to 1, so do not put assert here */
6582 
6583  /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6584  SCIP_CALL( SCIPtightenVarUb(scip, var, 0.0, FALSE, infeasible, &tightened) );
6585  if ( *infeasible )
6586  return SCIP_OKAY;
6587  if ( tightened )
6588  ++(*nfixedzero);
6589  }
6590  }
6591  }
6592 
6593  /* fix all variables to 1 if there is one variable fixed to 1 */
6594  if ( havefixedone )
6595  {
6596  assert( ! havefixedzero );
6597 
6598  for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6599  {
6600  assert( 0 <= orbits[j] && orbits[j] < npermvars );
6601  var = permvars[orbits[j]];
6602  assert( var != NULL );
6603 
6604  /* only variables that are not yet fixed to 1 */
6605  if ( SCIPvarGetLbLocal(var) < 0.5)
6606  {
6607  SCIPdebugMsg(scip, "can fix <%s> (index %d) to 1.\n", SCIPvarGetName(var), orbits[j]);
6608  assert( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY );
6609  /* due to aggregation, var might already be fixed to 0, so do not put assert here */
6610 
6611  /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6612  SCIP_CALL( SCIPtightenVarLb(scip, var, 1.0, FALSE, infeasible, &tightened) );
6613  if ( *infeasible )
6614  return SCIP_OKAY;
6615  if ( tightened )
6616  ++(*nfixedone);
6617  }
6618  }
6619  }
6620  }
6621 
6622  return SCIP_OKAY;
6623 }
6624 
6625 
6626 /** Gets branching variables on the path to root
6627  *
6628  * The variables are added to bg1 and bg1list, which are prefilled with the variables globally fixed to 1.
6629  */
6630 static
6632  SCIP* scip, /**< SCIP pointer */
6633  int nvars, /**< number of variables */
6634  SCIP_HASHMAP* varmap, /**< map of variables to indices in vars array */
6635  SCIP_Shortbool* bg1, /**< bitset marking the variables globally fixed or branched to 1 */
6636  int* bg1list, /**< array to store the variable indices globally fixed or branched to 1 */
6637  int* nbg1 /**< pointer to store the number of variables in bg1 and bg1list */
6638  )
6639 {
6640  SCIP_NODE* node;
6641 
6642  assert( scip != NULL );
6643  assert( varmap != NULL );
6644  assert( bg1 != NULL );
6645  assert( bg1list != NULL );
6646  assert( nbg1 != NULL );
6647  assert( *nbg1 >= 0 );
6648 
6649  /* get current node */
6650  node = SCIPgetCurrentNode(scip);
6651 
6652 #ifdef SCIP_OUTPUT
6653  SCIP_CALL( SCIPprintNodeRootPath(scip, node, NULL) );
6654 #endif
6655 
6656  /* follow path to the root (in the root no domains were changed due to branching) */
6657  while ( SCIPnodeGetDepth(node) != 0 )
6658  {
6659  SCIP_BOUNDCHG* boundchg;
6660  SCIP_DOMCHG* domchg;
6661  SCIP_VAR* branchvar;
6662  int nboundchgs;
6663  int i;
6664 
6665  /* get domain changes of current node */
6666  domchg = SCIPnodeGetDomchg(node);
6667 
6668  /* If we stopped due to a solving limit, it might happen that a non-root node has no domain changes, in all other
6669  * cases domchg should not be NULL. */
6670  if ( domchg != NULL )
6671  {
6672  /* loop through all bound changes */
6673  nboundchgs = SCIPdomchgGetNBoundchgs(domchg);
6674  for (i = 0; i < nboundchgs; ++i)
6675  {
6676  /* get bound change info */
6677  boundchg = SCIPdomchgGetBoundchg(domchg, i);
6678  assert( boundchg != NULL );
6679 
6680  /* branching decisions have to be in the beginning of the bound change array */
6682  break;
6683 
6684  /* get corresponding branching variable */
6685  branchvar = SCIPboundchgGetVar(boundchg);
6686 
6687  /* we only consider binary variables */
6688  if ( SCIPvarGetType(branchvar) == SCIP_VARTYPE_BINARY )
6689  {
6690  /* if branching variable is not known (may have been created meanwhile,
6691  * e.g., by prop_inttobinary; may have been removed from symmetry data
6692  * due to compression), continue with parent node */
6693  if ( ! SCIPhashmapExists(varmap, (void*) branchvar) )
6694  break;
6695 
6696  if ( SCIPvarGetLbLocal(branchvar) > 0.5 )
6697  {
6698  int branchvaridx;
6699 
6700  branchvaridx = SCIPhashmapGetImageInt(varmap, (void*) branchvar);
6701  assert( branchvaridx < nvars );
6702 
6703  /* the variable might already be fixed to 1 */
6704  if ( ! bg1[branchvaridx] )
6705  {
6706  bg1[branchvaridx] = TRUE;
6707  bg1list[(*nbg1)++] = branchvaridx;
6708  }
6709  }
6710  }
6711  }
6712  }
6713 
6714  node = SCIPnodeGetParent(node);
6715  }
6716 
6717  return SCIP_OKAY;
6718 }
6719 
6720 
6721 /** propagates orbital fixing */
6722 static
6724  SCIP* scip, /**< SCIP pointer */
6725  SCIP_PROPDATA* propdata, /**< data of symmetry breaking propagator */
6726  SCIP_Bool* infeasible, /**< pointer to store whether the node is detected to be infeasible */
6727  int* nprop /**< pointer to store the number of propagations */
6728  )
6729 {
6730  SCIP_Shortbool* inactiveperms;
6731  SCIP_Shortbool* bg0;
6732  SCIP_Shortbool* bg1;
6733  SCIP_VAR** permvars;
6734  int* orbitbegins;
6735  int* orbits;
6736  int* components;
6737  int* componentbegins;
6738  int* vartocomponent;
6739  int ncomponents;
6740  int* bg0list;
6741  int nbg0;
6742  int* bg1list;
6743  int nbg1;
6744  int nactiveperms;
6745  int norbits;
6746  int npermvars;
6747  int nbinpermvars;
6748  int** permstrans;
6749  int nperms;
6750  int p;
6751  int v;
6752  int j;
6753  int componentidx;
6754 
6755  assert( scip != NULL );
6756  assert( propdata != NULL );
6757  assert( propdata->ofenabled );
6758  assert( infeasible != NULL );
6759  assert( nprop != NULL );
6760 
6761  *infeasible = FALSE;
6762  *nprop = 0;
6763 
6764  /* possibly compute symmetry; fix non-binary potential branching variables */
6765  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6766  {
6768  }
6769  else
6770  {
6772  }
6773  assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || ! propdata->ofenabled );
6774 
6775  /* return if there is no symmetry available */
6776  nperms = propdata->nperms;
6777  if ( nperms <= 0 || ! propdata->ofenabled )
6778  return SCIP_OKAY;
6779 
6780  assert( propdata->permvars != NULL );
6781  assert( propdata->npermvars > 0 );
6782  assert( propdata->permvarmap != NULL );
6783  assert( propdata->permstrans != NULL );
6784  assert( propdata->inactiveperms != NULL );
6785  assert( propdata->components != NULL );
6786  assert( propdata->componentbegins != NULL );
6787  assert( propdata->vartocomponent != NULL );
6788  assert( propdata->ncomponents > 0 );
6789 
6790  permvars = propdata->permvars;
6791  npermvars = propdata->npermvars;
6792  nbinpermvars = propdata->nbinpermvars;
6793  permstrans = propdata->permstrans;
6794  inactiveperms = propdata->inactiveperms;
6795  components = propdata->components;
6796  componentbegins = propdata->componentbegins;
6797  vartocomponent = propdata->vartocomponent;
6798  ncomponents = propdata->ncomponents;
6799 
6800  /* init bitset for marking variables (globally fixed or) branched to 1 */
6801  assert( propdata->bg1 != NULL );
6802  assert( propdata->bg1list != NULL );
6803  assert( propdata->nbg1 >= 0 );
6804  assert( propdata->nbg1 <= npermvars );
6805 
6806  bg1 = propdata->bg1;
6807  bg1list = propdata->bg1list;
6808  nbg1 = propdata->nbg1;
6809 
6810  /* get branching variables */
6811  SCIP_CALL( computeBranchingVariables(scip, npermvars, propdata->permvarmap, bg1, bg1list, &nbg1) );
6812  assert( nbg1 >= propdata->nbg1 );
6813 
6814  /* reset inactive permutations */
6815  nactiveperms = nperms;
6816  for (p = 0; p < nperms; ++p)
6817  propdata->inactiveperms[p] = FALSE;
6818 
6819  /* get pointers for bg0 */
6820  assert( propdata->bg0 != NULL );
6821  assert( propdata->bg0list != NULL );
6822  assert( propdata->nbg0 >= 0 );
6823  assert( propdata->nbg0 <= npermvars );
6824 
6825  bg0 = propdata->bg0;
6826  bg0list = propdata->bg0list;
6827  nbg0 = propdata->nbg0;
6828 
6829  /* filter out permutations that move variables that are fixed to 0 */
6830  for (j = 0; j < nbg0 && nactiveperms > 0; ++j)
6831  {
6832  int* pt;
6833 
6834  v = bg0list[j];
6835  assert( 0 <= v && v < npermvars );
6836  assert( bg0[v] );
6837 
6838  componentidx = vartocomponent[v];
6839 
6840  /* skip unaffected variables and blocked components */
6841  if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6842  continue;
6843 
6844  pt = permstrans[v];
6845  assert( pt != NULL );
6846 
6847  for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6848  {
6849  int img;
6850  int perm;
6851 
6852  perm = components[p];
6853 
6854  /* skip inactive permutations */
6855  if ( inactiveperms[perm] )
6856  continue;
6857 
6858  img = pt[perm];
6859 
6860  if ( img != v )
6861  {
6862 #ifndef NDEBUG
6863  SCIP_VAR* varv = permvars[v];
6864  SCIP_VAR* varimg = permvars[img];
6865 
6866  /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
6867  assert( SCIPvarGetType(varv) == SCIPvarGetType(varimg) ||
6868  (SCIPvarIsBinary(varv) && SCIPvarIsBinary(varimg)) ||
6870  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6871  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) ||
6873  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6874  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) );
6875  assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
6876 #endif
6877 
6878  /* we are moving a variable globally fixed to 0 to a variable not of this type */
6879  if ( ! bg0[img] )
6880  {
6881  inactiveperms[perm] = TRUE; /* mark as inactive */
6882  --nactiveperms;
6883  }
6884  }
6885  }
6886  }
6887 
6888  /* filter out permutations that move variables that are fixed to different values */
6889  for (j = 0; j < nbg1 && nactiveperms > 0; ++j)
6890  {
6891  int* pt;
6892 
6893  v = bg1list[j];
6894  assert( 0 <= v && v < npermvars );
6895  assert( bg1[v] );
6896 
6897  componentidx = vartocomponent[v];
6898 
6899  /* skip unaffected variables and blocked components */
6900  if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6901  continue;
6902 
6903  pt = permstrans[v];
6904  assert( pt != NULL );
6905 
6906  for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6907  {
6908  int img;
6909  int perm;
6910 
6911  perm = components[p];
6912 
6913  /* skip inactive permutations */
6914  if ( inactiveperms[perm] )
6915  continue;
6916 
6917  img = pt[perm];
6918 
6919  if ( img != v )
6920  {
6921 #ifndef NDEBUG
6922  SCIP_VAR* varv = permvars[v];
6923  SCIP_VAR* varimg = permvars[img];
6924 
6925  /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
6926  assert( SCIPvarGetType(varv) == SCIPvarGetType(varimg) ||
6927  (SCIPvarIsBinary(varv) && SCIPvarIsBinary(varimg)) ||
6929  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6930  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) ||
6932  SCIPisEQ(scip, SCIPvarGetLbGlobal(varv), SCIPvarGetLbGlobal(varimg)) &&
6933  SCIPisEQ(scip, SCIPvarGetUbGlobal(varv), SCIPvarGetUbGlobal(varimg))) );
6934  assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
6935 #endif
6936 
6937  /* we are moving a variable globally fixed or branched to 1 to a variable not of this type */
6938  if ( ! bg1[img] )
6939  {
6940  inactiveperms[perm] = TRUE; /* mark as inactive */
6941  --nactiveperms;
6942  }
6943  }
6944  }
6945  }
6946 
6947  /* Clean bg1 list - need to do this after the main loop! (Not needed for bg0.)
6948  * Note that variables globally fixed to 1 are not resetted, since the loop starts at propdata->nbg1. */
6949  for (j = propdata->nbg1; j < nbg1; ++j)
6950  bg1[bg1list[j]] = FALSE;
6951 
6952  /* exit if no active permuations left */
6953  if ( nactiveperms == 0 )
6954  return SCIP_OKAY;
6955 
6956  /* compute orbits of binary variables */
6957  SCIP_CALL( SCIPallocBufferArray(scip, &orbits, nbinpermvars) );
6958  SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, nbinpermvars) );
6959  SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, nbinpermvars, permstrans, nperms, inactiveperms,
6960  orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent, propdata->componentblocked, ncomponents, propdata->nmovedpermvars) );
6961 
6962  if ( norbits > 0 )
6963  {
6964  int nfixedzero = 0;
6965  int nfixedone = 0;
6966 
6967  SCIPdebugMsg(scip, "Perform orbital fixing on %d orbits (%d active perms).\n", norbits, nactiveperms);
6968  SCIP_CALL( performOrbitalFixing(scip, permvars, nbinpermvars, orbits, orbitbegins, norbits, infeasible, &nfixedzero, &nfixedone) );
6969 
6970  propdata->nfixedzero += nfixedzero;
6971  propdata->nfixedone += nfixedone;
6972  *nprop = nfixedzero + nfixedone;
6973 
6974  SCIPdebugMsg(scip, "Orbital fixings: %d 0s, %d 1s.\n", nfixedzero, nfixedone);
6975  }
6976 
6977  SCIPfreeBufferArray(scip, &orbitbegins);
6978  SCIPfreeBufferArray(scip, &orbits);
6979 
6980  return SCIP_OKAY;
6981 }
6982 
6983 
6984 
6985 /*
6986  * Callback methods of propagator
6987  */
6988 
6989 /** presolving initialization method of propagator (called when presolving is about to begin) */
6990 static
6991 SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
6992 { /*lint --e{715}*/
6993  SCIP_PROPDATA* propdata;
6994 
6995  assert( scip != NULL );
6996  assert( prop != NULL );
6997 
6998  propdata = SCIPpropGetData(prop);
6999  assert( propdata != NULL );
7000 
7001  /* get nonlinear conshdlr for future checks on whether there are nonlinear constraints */
7002  propdata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
7003 
7004  /* check whether we should run */
7005  if ( propdata->usesymmetry < 0 )
7006  {
7007  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
7008 
7009  if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
7010  propdata->symconsenabled = TRUE;
7011  else
7012  propdata->symconsenabled = FALSE;
7013 
7014  if ( ISORBITALFIXINGACTIVE(propdata->usesymmetry) )
7015  propdata->ofenabled = TRUE;
7016  else
7017  propdata->ofenabled = FALSE;
7018 
7019  if ( ISSSTACTIVE(propdata->usesymmetry) )
7020  propdata->sstenabled = TRUE;
7021  else
7022  propdata->sstenabled = FALSE;
7023  }
7024 
7025  /* add symmetry handling constraints if required */
7026  if ( (propdata->symconsenabled || propdata->sstenabled) && propdata->addconsstiming == 0 )
7027  {
7028  SCIPdebugMsg(scip, "Try to add symmetry handling constraints before presolving.");
7029 
7031  }
7032  else if ( propdata->ofenabled && propdata->ofsymcomptiming == 0 )
7033  {
7034  SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Symmetry computation before presolving:\n");
7035 
7036  /* otherwise compute symmetry if timing requests it; fix non-binary potential branching variables */
7037  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7038  {
7040  }
7041  else
7042  {
7044  }
7045  assert( propdata->binvaraffected || ! propdata->ofenabled );
7046  }
7047 
7048  return SCIP_OKAY;
7049 }
7050 
7051 
7052 /** presolving deinitialization method of propagator (called after presolving has been finished) */
7053 static
7054 SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
7055 { /*lint --e{715}*/
7056  SCIP_PROPDATA* propdata;
7057 
7058  assert( scip != NULL );
7059  assert( prop != NULL );
7060  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7061 
7062  SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
7063 
7064  propdata = SCIPpropGetData(prop);
7065  assert( propdata != NULL );
7066  assert( propdata->usesymmetry >= 0 );
7067 
7068  /* guarantee that symmetries are computed (and handled) if the solving process has not been interrupted
7069  * and even if presolving has been disabled */
7070  if ( (propdata->symconsenabled || propdata->sstenabled) && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7071  {
7073  }
7074 
7075  /* if timing requests it, guarantee that symmetries are computed even if presolving is disabled */
7076  if ( propdata->ofenabled && propdata->ofsymcomptiming <= 1 && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7077  {
7078  /* fix non-binary potential branching variables */
7079  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7080  {
7082  }
7083  else
7084  {
7086  }
7087  assert( propdata->binvaraffected || ! propdata->ofenabled );
7088  }
7089 
7090  return SCIP_OKAY;
7091 }
7092 
7093 
7094 /** presolving method of propagator */
7095 static
7096 SCIP_DECL_PROPPRESOL(propPresolSymmetry)
7097 { /*lint --e{715}*/
7098  SCIP_PROPDATA* propdata;
7099  int i;
7100 
7101  assert( scip != NULL );
7102  assert( prop != NULL );
7103  assert( result != NULL );
7104  assert( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING );
7105 
7106  *result = SCIP_DIDNOTRUN;
7107 
7108  propdata = SCIPpropGetData(prop);
7109  assert( propdata != NULL );
7110  assert( propdata->usesymmetry >= 0 );
7111 
7112  /* possibly create symmetry handling constraints */
7113  if ( propdata->symconsenabled || propdata->sstenabled )
7114  {
7115  int noldngenconns;
7116  int nchanges = 0;
7117  SCIP_Bool earlyterm = FALSE;
7118 
7119  /* skip presolving if we are not at the end if addconsstiming == 2 */
7120  assert( 0 <= propdata->addconsstiming && propdata->addconsstiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7121  if ( propdata->addconsstiming > SYM_COMPUTETIMING_DURINGPRESOL && ! SCIPisPresolveFinished(scip) )
7122  return SCIP_OKAY;
7123 
7124  /* possibly stop */
7125  if ( SCIPisStopped(scip) )
7126  return SCIP_OKAY;
7127 
7128  noldngenconns = propdata->ngenorbconss + propdata->nsstconss + propdata->ngenlinconss;
7129 
7130  SCIP_CALL( tryAddSymmetryHandlingConss(scip, prop, &nchanges, &earlyterm) );
7131 
7132  /* if we actually tried to add symmetry handling constraints */
7133  if ( ! earlyterm )
7134  {
7135  *result = SCIP_DIDNOTFIND;
7136 
7137  if ( nchanges > 0 )
7138  {
7139  *result = SCIP_SUCCESS;
7140  *nchgbds += nchanges;
7141  }
7142 
7143  /* if symmetry handling constraints have been added, presolve each */
7144  if ( propdata->ngenorbconss > 0 || propdata->ngenlinconss > 0 || propdata->nsstconss > 0 )
7145  {
7146  /* at this point, the symmetry group should be computed and nontrivial */
7147  assert( propdata->nperms > 0 );
7148  assert( propdata->triedaddconss );
7149 
7150  /* we have added at least one symmetry handling constraints, i.e., we were successful */
7151  *result = SCIP_SUCCESS;
7152 
7153  *naddconss += propdata->ngenorbconss + propdata->ngenlinconss + propdata->nsstconss - noldngenconns;
7154  SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", *naddconss);
7155 
7156  /* if constraints have been added, loop through generated constraints and presolve each */
7157  for (i = 0; i < propdata->ngenorbconss; ++i)
7158  {
7159  SCIP_CALL( SCIPpresolCons(scip, propdata->genorbconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7160  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7161  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7162 
7163  /* exit if cutoff or unboundedness has been detected */
7164  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7165  {
7166  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genorbconss[i]));
7167  return SCIP_OKAY;
7168  }
7169  }
7170 
7171  for (i = 0; i < propdata->ngenlinconss; ++i)
7172  {
7173  SCIP_CALL( SCIPpresolCons(scip, propdata->genlinconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7174  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7175  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7176 
7177  /* exit if cutoff or unboundedness has been detected */
7178  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7179  {
7180  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genlinconss[i]));
7181  return SCIP_OKAY;
7182  }
7183  }
7184  SCIPdebugMsg(scip, "Presolved %d generated constraints.\n",
7185  propdata->ngenorbconss + propdata->ngenlinconss);
7186 
7187  for (i = 0; i < propdata->nsstconss; ++i)
7188  {
7189  SCIP_CALL( SCIPpresolCons(scip, propdata->sstconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
7190  nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
7191  nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7192 
7193  /* exit if cutoff or unboundedness has been detected */
7194  if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7195  {
7196  SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->sstconss[i]));
7197  return SCIP_OKAY;
7198  }
7199  }
7200  SCIPdebugMsg(scip, "Presolved %d generated Schreier Sims constraints.\n", propdata->nsstconss);
7201  }
7202  }
7203  }
7204 
7205  /* run OF presolving */
7206  assert( 0 <= propdata->ofsymcomptiming && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7207  if ( propdata->ofenabled && propdata->performpresolving && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_DURINGPRESOL )
7208  {
7209  SCIP_Bool infeasible;
7210  int nprop;
7211 
7212  /* if we have not tried to add symmetry handling constraints */
7213  if ( *result == SCIP_DIDNOTRUN )
7214  *result = SCIP_DIDNOTFIND;
7215 
7216  SCIPdebugMsg(scip, "Presolving <%s>.\n", PROP_NAME);
7217 
7218  SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7219 
7220  if ( infeasible )
7221  {
7222  *result = SCIP_CUTOFF;
7223  propdata->offoundreduction = TRUE;
7224  }
7225  else if ( nprop > 0 )
7226  {
7227  *result = SCIP_SUCCESS;
7228  *nfixedvars += nprop;
7229  propdata->offoundreduction = TRUE;
7230  }
7231  }
7232  else if ( propdata->ofenabled && propdata->ofsymcomptiming == SYM_COMPUTETIMING_DURINGPRESOL )
7233  {
7234  /* otherwise compute symmetry early if timing requests it; fix non-binary potential branching variables */
7235  if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7236  {
7238  }
7239  else
7240  {
7242  }
7243  assert( propdata->binvaraffected || ! propdata->ofenabled );
7244  }
7245 
7246  return SCIP_OKAY;
7247 }
7248 
7249 
7250 /** execution method of propagator */
7251 static
7252 SCIP_DECL_PROPEXEC(propExecSymmetry)
7253 { /*lint --e{715}*/
7254  SCIP_PROPDATA* propdata;
7255  SCIP_Bool infeasible = FALSE;
7256  SCIP_Longint nodenumber;
7257  int nprop = 0;
7258 
7259  assert( scip != NULL );
7260  assert( result != NULL );
7261 
7262  *result = SCIP_DIDNOTRUN;
7263 
7264  /* do not run if we are in the root or not yet solving */
7265  if ( SCIPgetDepth(scip) <= 0 || SCIPgetStage(scip) < SCIP_STAGE_SOLVING )
7266  return SCIP_OKAY;
7267 
7268  /* do nothing if we are in a probing node */
7269  if ( SCIPinProbing(scip) )
7270  return SCIP_OKAY;
7271 
7272  /* do not run again in repropagation, since the path to the root might have changed */
7273  if ( SCIPinRepropagation(scip) )
7274  return SCIP_OKAY;
7275 
7276  /* get data */
7277  propdata = SCIPpropGetData(prop);
7278  assert( propdata != NULL );
7279 
7280  /* if usesymmetry has not been read so far */
7281  if ( propdata->usesymmetry < 0 )
7282  {
7283  SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
7284  if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
7285  propdata->symconsenabled = TRUE;
7286  else
7287  propdata->symconsenabled = FALSE;
7288 
7289  if ( ISORBITALFIXINGACTIVE(propdata->usesymmetry) )
7290  propdata->ofenabled = TRUE;
7291  else
7292  propdata->ofenabled = FALSE;
7293 
7294  if ( ISSSTACTIVE(propdata->usesymmetry) )
7295  propdata->sstenabled = TRUE;
7296  else
7297  propdata->sstenabled = FALSE;
7298  }
7299 
7300  /* do not propagate if orbital fixing is not enabled */
7301  if ( ! propdata->ofenabled )
7302  return SCIP_OKAY;
7303 
7304  /* return if there is no symmetry available */
7305  if ( propdata->nperms == 0 )
7306  return SCIP_OKAY;
7307 
7308  /* return if we already ran in this node */
7309  nodenumber = SCIPnodeGetNumber(SCIPgetCurrentNode(scip));
7310  if ( nodenumber == propdata->nodenumber )
7311  return SCIP_OKAY;
7312  propdata->nodenumber = nodenumber;
7313 
7314  /* propagate */
7315  *result = SCIP_DIDNOTFIND;
7316 
7317  SCIPdebugMsg(scip, "Propagating <%s>.\n", SCIPpropGetName(prop));
7318 
7319  SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7320 
7321  if ( infeasible )
7322  {
7323  *result = SCIP_CUTOFF;
7324  propdata->offoundreduction = TRUE;
7325  }
7326  else if ( nprop > 0 )
7327  {
7328  *result = SCIP_REDUCEDDOM;
7329  propdata->offoundreduction = TRUE;
7330  }
7331 
7332  return SCIP_OKAY;
7333 }
7334 
7335 
7336 /** deinitialization method of propagator (called before transformed problem is freed) */
7337 static
7338 SCIP_DECL_PROPEXIT(propExitSymmetry)
7340  SCIP_PROPDATA* propdata;
7341 
7342  assert( scip != NULL );
7343  assert( prop != NULL );
7344  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7345 
7346  SCIPdebugMsg(scip, "Exiting propagator <%s>.\n", PROP_NAME);
7347 
7348  propdata = SCIPpropGetData(prop);
7349  assert( propdata != NULL );
7350 
7351  SCIP_CALL( freeSymmetryData(scip, propdata) );
7352 
7353  /* reset basic data */
7354  propdata->usesymmetry = -1;
7355  propdata->symconsenabled = FALSE;
7356  propdata->triedaddconss = FALSE;
7357  propdata->nsymresacks = 0;
7358  propdata->norbitopes = 0;
7359  propdata->ofenabled = FALSE;
7360  propdata->sstenabled = FALSE;
7361  propdata->lastrestart = 0;
7362  propdata->nfixedzero = 0;
7363  propdata->nfixedone = 0;
7364  propdata->nodenumber = -1;
7365  propdata->offoundreduction = FALSE;
7366 
7367  return SCIP_OKAY;
7368 }
7369 
7370 
7371 /** propagation conflict resolving method of propagator
7372  *
7373  * @todo Implement reverse propagation.
7374  *
7375  * Note that this is relatively difficult to obtain: One needs to include all bounds of variables that are responsible
7376  * for creating the orbit in which the variables that was propagated lies. This includes all variables that are moved
7377  * by the permutations which are involved in creating the orbit.
7378  */
7379 static
7380 SCIP_DECL_PROPRESPROP(propRespropSymmetry)
7381 { /*lint --e{715,818}*/
7382  assert( result != NULL );
7383 
7384  *result = SCIP_DIDNOTFIND;
7385 
7386  return SCIP_OKAY;
7387 }
7388 
7389 
7390 /** destructor of propagator to free user data (called when SCIP is exiting) */
7391 static
7392 SCIP_DECL_PROPFREE(propFreeSymmetry)
7393 { /*lint --e{715}*/
7394  SCIP_PROPDATA* propdata;
7395 
7396  assert( scip != NULL );
7397  assert( prop != NULL );
7398  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7399 
7400  SCIPdebugMsg(scip, "Freeing symmetry propagator.\n");
7401 
7402  propdata = SCIPpropGetData(prop);
7403  assert( propdata != NULL );
7404 
7405  SCIPfreeBlockMemory(scip, &propdata);
7406 
7407  return SCIP_OKAY;
7408 }
7409 
7410 
7411 /*
7412  * External methods
7413  */
7414 
7415 /** include symmetry propagator */
7417  SCIP* scip /**< SCIP data structure */
7418  )
7419 {
7420  SCIP_TABLEDATA* tabledata;
7421  SCIP_PROPDATA* propdata = NULL;
7422  SCIP_PROP* prop = NULL;
7423 
7424  SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
7425  assert( propdata != NULL );
7426 
7427  propdata->npermvars = 0;
7428  propdata->nbinpermvars = 0;
7429  propdata->permvars = NULL;
7430 #ifndef NDEBUG
7431  propdata->permvarsobj = NULL;
7432 #endif
7433  propdata->nperms = -1;
7434  propdata->nmaxperms = 0;
7435  propdata->perms = NULL;
7436  propdata->permstrans = NULL;
7437  propdata->permvarmap = NULL;
7438  propdata->nonbinpermvarcaptured = NULL;
7439 
7440  propdata->ncomponents = -1;
7441  propdata->ncompblocked = 0;
7442  propdata->components = NULL;
7443  propdata->componentbegins = NULL;
7444  propdata->vartocomponent = NULL;
7445  propdata->componentblocked = NULL;
7446 
7447  propdata->log10groupsize = -1.0;
7448  propdata->nmovedvars = -1;
7449  propdata->binvaraffected = FALSE;
7450  propdata->computedsymmetry = FALSE;
7451  propdata->conshdlr_nonlinear = NULL;
7452 
7453  propdata->usesymmetry = -1;
7454  propdata->symconsenabled = FALSE;
7455  propdata->triedaddconss = FALSE;
7456  propdata->genorbconss = NULL;
7457  propdata->genlinconss = NULL;
7458  propdata->ngenorbconss = 0;
7459  propdata->ngenlinconss = 0;
7460  propdata->genlinconsssize = 0;
7461  propdata->nsymresacks = 0;
7462  propdata->norbitopes = 0;
7463  propdata->isnonlinvar = NULL;
7464 
7465  propdata->ofenabled = FALSE;
7466  propdata->bg0 = NULL;
7467  propdata->bg0list = NULL;
7468  propdata->nbg0 = 0;
7469  propdata->bg1 = NULL;
7470  propdata->bg1list = NULL;
7471  propdata->nbg1 = 0;
7472  propdata->permvarsevents = NULL;
7473  propdata->inactiveperms = NULL;
7474  propdata->nmovedpermvars = -1;
7475  propdata->nmovedbinpermvars = 0;
7476  propdata->nmovedintpermvars = 0;
7477  propdata->nmovedimplintpermvars = 0;
7478  propdata->nmovedcontpermvars = 0;
7479  propdata->lastrestart = 0;
7480  propdata->nfixedzero = 0;
7481  propdata->nfixedone = 0;
7482  propdata->nodenumber = -1;
7483  propdata->offoundreduction = FALSE;
7484 
7485  propdata->sstenabled = FALSE;
7486  propdata->sstconss = NULL;
7487  propdata->nsstconss = 0;
7488  propdata->maxnsstconss = 0;
7489  propdata->leaders = NULL;
7490  propdata->nleaders = 0;
7491  propdata->maxnleaders = 0;
7492 
7493  /* create event handler */
7494  propdata->eventhdlr = NULL;
7496  eventExecSymmetry, NULL) );
7497  assert( propdata->eventhdlr != NULL );
7498 
7499  /* include constraint handler */
7501  PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING, propExecSymmetry, propdata) );
7502  assert( prop != NULL );
7503 
7504  SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSymmetry) );
7505  SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSymmetry) );
7506  SCIP_CALL( SCIPsetPropInitpre(scip, prop, propInitpreSymmetry) );
7507  SCIP_CALL( SCIPsetPropExitpre(scip, prop, propExitpreSymmetry) );
7508  SCIP_CALL( SCIPsetPropResprop(scip, prop, propRespropSymmetry) );
7510 
7511  /* include table */
7512  SCIP_CALL( SCIPallocBlockMemory(scip, &tabledata) );
7513  tabledata->propdata = propdata;
7515  NULL, tableFreeOrbitalfixing, NULL, NULL, NULL, NULL, tableOutputOrbitalfixing,
7517 
7518  /* add parameters for computing symmetry */
7519  SCIP_CALL( SCIPaddIntParam(scip,
7520  "propagating/" PROP_NAME "/maxgenerators",
7521  "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
7522  &propdata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
7523 
7525  "propagating/" PROP_NAME "/checksymmetries",
7526  "Should all symmetries be checked after computation?",
7527  &propdata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
7528 
7530  "propagating/" PROP_NAME "/displaynorbitvars",
7531  "Should the number of variables affected by some symmetry be displayed?",
7532  &propdata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
7533 
7535  "propagating/" PROP_NAME "/doubleequations",
7536  "Double equations to positive/negative version?",
7537  &propdata->doubleequations, TRUE, DEFAULT_DOUBLEEQUATIONS, NULL, NULL) );
7538 
7539  /* add parameters for adding symmetry handling constraints */
7541  "propagating/" PROP_NAME "/conssaddlp",
7542  "Should the symmetry breaking constraints be added to the LP?",
7543  &propdata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
7544 
7546  "propagating/" PROP_NAME "/addsymresacks",
7547  "Add inequalities for symresacks for each generator?",
7548  &propdata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
7549 
7551  "propagating/" PROP_NAME "/detectorbitopes",
7552  "Should we check whether the components of the symmetry group can be handled by orbitopes?",
7553  &propdata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
7554 
7556  "propagating/" PROP_NAME "/detectsubgroups",
7557  "Should we try to detect symmetric subgroups of the symmetry group on binary variables?",
7558  &propdata->detectsubgroups, TRUE, DEFAULT_DETECTSUBGROUPS, NULL, NULL) );
7559 
7561  "propagating/" PROP_NAME "/addweaksbcs",
7562  "Should we add weak SBCs for enclosing orbit of symmetric subgroups?",
7563  &propdata->addweaksbcs, TRUE, DEFAULT_ADDWEAKSBCS, NULL, NULL) );
7564 
7565  SCIP_CALL( SCIPaddIntParam(scip,
7566  "propagating/" PROP_NAME "/addconsstiming",
7567  "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving)",
7568  &propdata->addconsstiming, TRUE, DEFAULT_ADDCONSSTIMING, 0, 2, NULL, NULL) );
7569 
7570  /* add parameters for orbital fixing */
7571  SCIP_CALL( SCIPaddIntParam(scip,
7572  "propagating/" PROP_NAME "/ofsymcomptiming",
7573  "timing of symmetry computation for orbital fixing (0 = before presolving, 1 = during presolving, 2 = at first call)",
7574  &propdata->ofsymcomptiming, TRUE, DEFAULT_OFSYMCOMPTIMING, 0, 2, NULL, NULL) );
7575 
7577  "propagating/" PROP_NAME "/performpresolving",
7578  "run orbital fixing during presolving?",
7579  &propdata->performpresolving, TRUE, DEFAULT_PERFORMPRESOLVING, NULL, NULL) );
7580 
7581  SCIP_CALL( SCIPaddIntParam(scip,
7582  "propagating/" PROP_NAME "/recomputerestart",
7583  "recompute symmetries after a restart has occured? (0 = never, 1 = always, 2 = if OF found reduction)",
7584  &propdata->recomputerestart, TRUE, DEFAULT_RECOMPUTERESTART, 0, 2, NULL, NULL) );
7585 
7587  "propagating/" PROP_NAME "/compresssymmetries",
7588  "Should non-affected variables be removed from permutation to save memory?",
7589  &propdata->compresssymmetries, TRUE, DEFAULT_COMPRESSSYMMETRIES, NULL, NULL) );
7590 
7592  "propagating/" PROP_NAME "/compressthreshold",
7593  "Compression is used if percentage of moved vars is at most the threshold.",
7594  &propdata->compressthreshold, TRUE, DEFAULT_COMPRESSTHRESHOLD, 0.0, 1.0, NULL, NULL) );
7595 
7597  "propagating/" PROP_NAME "/usecolumnsparsity",
7598  "Should the number of conss a variable is contained in be exploited in symmetry detection?",
7599  &propdata->usecolumnsparsity, TRUE, DEFAULT_USECOLUMNSPARSITY, NULL, NULL) );
7600 
7601  SCIP_CALL( SCIPaddIntParam(scip,
7602  "propagating/" PROP_NAME "/maxnconsssubgroup",
7603  "maximum number of constraints up to which subgroup structures are detected",
7604  &propdata->maxnconsssubgroup, TRUE, DEFAULT_MAXNCONSSSUBGROUP, 0, INT_MAX, NULL, NULL) );
7605 
7607  "propagating/" PROP_NAME "/usedynamicprop",
7608  "whether dynamic propagation should be used for full orbitopes",
7609  &propdata->usedynamicprop, TRUE, DEFAULT_USEDYNAMICPROP, NULL, NULL) );
7610 
7612  "propagating/" PROP_NAME "/addstrongsbcs",
7613  "Should strong SBCs for enclosing orbit of symmetric subgroups be added if orbitopes are not used?",
7614  &propdata->addstrongsbcs, TRUE, DEFAULT_ADDSTRONGSBCS, NULL, NULL) );
7615 
7616  SCIP_CALL( SCIPaddIntParam(scip,
7617  "propagating/" PROP_NAME "/ssttiebreakrule",
7618  "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)",
7619  &propdata->ssttiebreakrule, TRUE, DEFAULT_SSTTIEBREAKRULE, 0, 2, NULL, NULL) );
7620 
7621  SCIP_CALL( SCIPaddIntParam(scip,
7622  "propagating/" PROP_NAME "/sstleaderrule",
7623  "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)",
7624  &propdata->sstleaderrule, TRUE, DEFAULT_SSTLEADERRULE, 0, 3, NULL, NULL) );
7625 
7626  SCIP_CALL( SCIPaddIntParam(scip,
7627  "propagating/" PROP_NAME "/sstleadervartype",
7628  "bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);" \
7629  "if multiple types are allowed, take the one with most affected vars",
7630  &propdata->sstleadervartype, TRUE, DEFAULT_SSTLEADERVARTYPE, 1, 15, NULL, NULL) );
7631 
7633  "propagating/" PROP_NAME "/addconflictcuts",
7634  "Should Schreier Sims constraints be added if we use a conflict based rule?",
7635  &propdata->addconflictcuts, TRUE, DEFAULT_ADDCONFLICTCUTS, NULL, NULL) );
7636 
7638  "propagating/" PROP_NAME "/sstaddcuts",
7639  "Should Schreier Sims constraints be added?",
7640  &propdata->sstaddcuts, TRUE, DEFAULT_SSTADDCUTS, NULL, NULL) );
7641 
7643  "propagating/" PROP_NAME "/sstmixedcomponents",
7644  "Should Schreier Sims constraints be added if a symmetry component contains variables of different types?",
7645  &propdata->sstmixedcomponents, TRUE, DEFAULT_SSTMIXEDCOMPONENTS, NULL, NULL) );
7646 
7648  "propagating/" PROP_NAME "/symfixnonbinaryvars",
7649  "Whether all non-binary variables shall be not affected by symmetries if OF is active?",
7650  &propdata->symfixnonbinaryvars, TRUE, DEFAULT_SYMFIXNONBINARYVARS, NULL, NULL) );
7651 
7653  "propagating/" PROP_NAME "/onlybinarysymmetry",
7654  "Is only symmetry on binary variables used?",
7655  &propdata->onlybinarysymmetry, TRUE, DEFAULT_ONLYBINARYSYMMETRY, NULL, NULL) );
7656 
7658  "propagating/" PROP_NAME "/preferlessrows",
7659  "Shall orbitopes with less rows be preferred in detection?",
7660  &propdata->preferlessrows, TRUE, DEFAULT_PREFERLESSROWS, NULL, NULL) );
7661 
7662  /* possibly add description */
7663  if ( SYMcanComputeSymmetry() )
7664  {
7666  }
7667 
7668  return SCIP_OKAY;
7669 }
7670 
7671 
7672 /** return currently available symmetry group information */
7674  SCIP* scip, /**< SCIP data structure */
7675  int* npermvars, /**< pointer to store number of variables for permutations */
7676  SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
7677  SCIP_HASHMAP** permvarmap, /**< pointer to store hash map of permvars (or NULL) */
7678  int* nperms, /**< pointer to store number of permutations */
7679  int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix (or NULL)*/
7680  int*** permstrans, /**< pointer to store permutation generators as (npermvars x nperms) matrix (or NULL)*/
7681  SCIP_Real* log10groupsize, /**< pointer to store log10 of group size (or NULL) */
7682  SCIP_Bool* binvaraffected, /**< pointer to store whether binary variables are affected (or NULL) */
7683  int** components, /**< pointer to store components of symmetry group (or NULL) */
7684  int** componentbegins, /**< pointer to store begin positions of components in components array (or NULL) */
7685  int** vartocomponent, /**< pointer to store assignment from variable to its component (or NULL) */
7686  int* ncomponents /**< pointer to store number of components (or NULL) */
7687  )
7688 {
7689  SCIP_PROPDATA* propdata;
7690  SCIP_PROP* prop;
7691 
7692  assert( scip != NULL );
7693  assert( npermvars != NULL );
7694  assert( permvars != NULL );
7695  assert( nperms != NULL );
7696  assert( perms != NULL || permstrans != NULL );
7697  assert( ncomponents != NULL || (components == NULL && componentbegins == NULL && vartocomponent == NULL) );
7698 
7699  /* find symmetry propagator */
7700  prop = SCIPfindProp(scip, "symmetry");
7701  if ( prop == NULL )
7702  {
7703  SCIPerrorMessage("Could not find symmetry propagator.\n");
7704  return SCIP_PLUGINNOTFOUND;
7705  }
7706  assert( prop != NULL );
7707  assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7708 
7709  propdata = SCIPpropGetData(prop);
7710  assert( propdata != NULL );
7711 
7712  *npermvars = propdata->npermvars;
7713  *permvars = propdata->permvars;
7714 
7715  if ( permvarmap != NULL )
7716  *permvarmap = propdata->permvarmap;
7717 
7718  *nperms = propdata->nperms;
7719  if ( perms != NULL )
7720  {
7721  *perms = propdata->perms;
7722  assert( *perms != NULL || *nperms <= 0 );
7723  }
7724 
7725  if ( permstrans != NULL )
7726  {
7727  *permstrans = propdata->permstrans;
7728  assert( *permstrans != NULL || *nperms <= 0 );
7729  }
7730 
7731  if ( log10groupsize != NULL )
7732  *log10groupsize = propdata->log10groupsize;
7733 
7734  if ( binvaraffected != NULL )
7735  *binvaraffected = propdata->binvaraffected;
7736 
7737  if ( components != NULL )
7738  *components = propdata->components;
7739 
7740  if ( componentbegins != NULL )
7741  *componentbegins = propdata->componentbegins;
7742 
7743  if ( vartocomponent )
7744  *vartocomponent = propdata->vartocomponent;
7745 
7746  if ( ncomponents )
7747  *ncomponents = propdata->ncomponents;
7748 
7749  return SCIP_OKAY;
7750 }
7751 
7752 /** return whether orbital fixing is enabled */
7754  SCIP* scip /**< SCIP data structure */
7755  )
7756 {
7757  SCIP_PROP* prop;
7758  SCIP_PROPDATA* propdata;
7759 
7760  assert( scip != NULL );
7761 
7762  prop = SCIPfindProp(scip, PROP_NAME);
7763  if ( prop == NULL )
7764  return FALSE;
7765 
7766  propdata = SCIPpropGetData(prop);
7767  assert( propdata != NULL );
7768 
7769  return propdata->ofenabled;
7770 }
7771 
7772 /** return number of the symmetry group's generators */
7774  SCIP* scip /**< SCIP data structure */
7775  )
7776 {
7777  SCIP_PROP* prop;
7778  SCIP_PROPDATA* propdata;
7779 
7780  assert( scip != NULL );
7781 
7782  prop = SCIPfindProp(scip, PROP_NAME);
7783  if ( prop == NULL )
7784  return 0;
7785 
7786  propdata = SCIPpropGetData(prop);
7787  assert( propdata != NULL );
7788 
7789  if ( propdata->nperms < 0 )
7790  return 0;
7791  else
7792  return propdata->nperms;
7793 }
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:2080
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:9394
#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:2841
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:3086
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:2170
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:2307
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:2768
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:403
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:2125
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:9440
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:2035
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:9417
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition: misc.c:2286
int SCIPgetNVars(SCIP *scip)
Definition: scip_prob.c:1990
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:3040
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:1945
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)