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