Scippy

SCIP

Solving Constraint Integer Programs

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