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 );