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