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->nmaxperms = 0;
1073 propdata->nmovedpermvars = -1;
1074 propdata->nmovedbinpermvars = 0;
1075 propdata->nmovedintpermvars = 0;
1076 propdata->nmovedimplintpermvars = 0;
1077 propdata->nmovedcontpermvars = 0;
1078 propdata->nmovedvars = -1;
1079 propdata->log10groupsize = -1.0;
1080 propdata->binvaraffected = FALSE;
1081 propdata->isnonlinvar = NULL;
1082 }
1083 propdata->nperms = -1;
1084
1085 assert( checkSymmetryDataFree(propdata) );
1086
1087 propdata->computedsymmetry = FALSE;
1088 propdata->compressed = FALSE;
1089
1090 return SCIP_OKAY;
1091}
1092
1093
1094/** makes sure that the constraint array (potentially NULL) of given array size is sufficiently large */
1095static
1097 SCIP* scip, /**< SCIP pointer */
1098 SCIP_CONS*** consarrptr, /**< constraint array pointer */
1099 int* consarrsizeptr, /**< constraint array size pointer */
1100 int consarrsizereq /**< constraint array size required */
1101 )
1102{
1103 int newsize;
1104
1105 assert( scip != NULL );
1106 assert( consarrptr != NULL );
1107 assert( consarrsizeptr != NULL );
1108 assert( consarrsizereq > 0 );
1109 assert( *consarrsizeptr >= 0 );
1110 assert( (*consarrsizeptr == 0) == (*consarrptr == NULL) );
1111
1112 /* array is already sufficiently large */
1113 if ( consarrsizereq <= *consarrsizeptr )
1114 return SCIP_OKAY;
1115
1116 /* compute new size */
1117 newsize = SCIPcalcMemGrowSize(scip, consarrsizereq);
1118 assert( newsize > *consarrsizeptr );
1119
1120 /* allocate or reallocate */
1121 if ( *consarrptr == NULL )
1122 {
1123 SCIP_CALL( SCIPallocBlockMemoryArray(scip, consarrptr, newsize) );
1124 }
1125 else
1126 {
1127 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, consarrptr, *consarrsizeptr, newsize) );
1128 }
1129
1130 *consarrsizeptr = newsize;
1131
1132 return SCIP_OKAY;
1133}
1134
1135/** set symmetry data */
1136static
1138 SCIP* scip, /**< SCIP pointer */
1139 SYM_SYMTYPE symtype, /**< type of symmetries in perms */
1140 SCIP_VAR** vars, /**< vars present at time of symmetry computation */
1141 int nvars, /**< number of vars present at time of symmetry computation */
1142 int nbinvars, /**< number of binary vars present at time of symmetry computation */
1143 SCIP_VAR*** permvars, /**< pointer to permvars array */
1144 int* npermvars, /**< pointer to store number of permvars */
1145 int* nbinpermvars, /**< pointer to store number of binary permvars */
1146 SCIP_Real** permvardomaincenter, /**< pointer to store center points of variable domains */
1147 int** perms, /**< permutations matrix (nperms x nvars) */
1148 int nperms, /**< number of permutations */
1149 int* nmovedvars, /**< pointer to store number of vars affected by symmetry (if usecompression) or NULL */
1150 SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1151 SCIP_Bool usecompression, /**< whether symmetry data shall be compressed */
1152 SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1153 SCIP_Bool* compressed /**< pointer to store whether compression has been performed */
1154 )
1155{
1156 SCIP_Real ub;
1157 SCIP_Real lb;
1158 int i;
1159 int p;
1160
1161 assert( scip != NULL );
1162 assert( vars != NULL );
1163 assert( nvars > 0 );
1164 assert( permvars != NULL );
1165 assert( npermvars != NULL );
1166 assert( nbinpermvars != NULL );
1167 assert( perms != NULL );
1168 assert( nperms > 0 );
1169 assert( binvaraffected != NULL );
1170 assert( SCIPisGE(scip, compressthreshold, 0.0) );
1171 assert( SCIPisLE(scip, compressthreshold, 1.0) );
1172 assert( compressed != NULL );
1173
1174 /* set default return values */
1175 *permvars = vars;
1176 *npermvars = nvars;
1177 *nbinpermvars = nbinvars;
1178 *binvaraffected = FALSE;
1179 *compressed = FALSE;
1180
1181 /* if we possibly perform compression */
1182 if ( usecompression && SCIPgetNVars(scip) >= COMPRESSNVARSLB )
1183 {
1184 SCIP_Real percentagemovedvars;
1185 int* labelmovedvars;
1186 int* labeltopermvaridx;
1187 int nbinvarsaffected = 0;
1188
1189 assert( nmovedvars != NULL );
1190
1191 *nmovedvars = 0;
1192
1193 /* detect number of moved vars and label moved vars */
1194 SCIP_CALL( SCIPallocBufferArray(scip, &labelmovedvars, nvars) );
1195 SCIP_CALL( SCIPallocBufferArray(scip, &labeltopermvaridx, nvars) );
1196 for (i = 0; i < nvars; ++i)
1197 {
1198 labelmovedvars[i] = -1;
1199
1200 for (p = 0; p < nperms; ++p)
1201 {
1202 if ( perms[p][i] != i )
1203 {
1204 labeltopermvaridx[*nmovedvars] = i;
1205 labelmovedvars[i] = (*nmovedvars)++;
1206
1207 if ( SCIPvarIsBinary(vars[i]) )
1208 ++nbinvarsaffected;
1209 break;
1210 }
1211 }
1212 }
1213
1214 if ( nbinvarsaffected > 0 )
1215 *binvaraffected = TRUE;
1216
1217 /* check whether compression should be performed */
1218 percentagemovedvars = (SCIP_Real) *nmovedvars / (SCIP_Real) nvars;
1219 if ( *nmovedvars > 0 && SCIPisLE(scip, percentagemovedvars, compressthreshold) )
1220 {
1221 /* remove variables from permutations that are not affected by any permutation */
1222 for (p = 0; p < nperms; ++p)
1223 {
1224 /* iterate over labels and adapt permutation (possibly taking signed permutations into account) */
1225 for (i = 0; i < *nmovedvars; ++i)
1226 {
1227 assert( i <= labeltopermvaridx[i] );
1228 if ( perms[p][labeltopermvaridx[i]] >= nvars )
1229 {
1230 int imgvaridx;
1231
1232 assert( symtype == SYM_SYMTYPE_SIGNPERM );
1233
1234 imgvaridx = perms[p][labeltopermvaridx[i]] - nvars;
1235 perms[p][i] = labelmovedvars[imgvaridx] + *nmovedvars;
1236
1237 assert( 0 <= perms[p][i] && perms[p][i] < 2 * (*nmovedvars) );
1238 }
1239 else
1240 perms[p][i] = labelmovedvars[perms[p][labeltopermvaridx[i]]];
1241 }
1242
1243 if ( symtype == SYM_SYMTYPE_SIGNPERM )
1244 {
1245 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], 2 * nvars, 2 * (*nmovedvars)) );
1246 }
1247 else
1248 {
1249 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], nvars, *nmovedvars) );
1250 }
1251 }
1252
1253 /* remove variables from permvars array that are not affected by any symmetry */
1254 SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvars, *nmovedvars) );
1255 for (i = 0; i < *nmovedvars; ++i)
1256 {
1257 (*permvars)[i] = vars[labeltopermvaridx[i]];
1258 }
1259 *npermvars = *nmovedvars;
1260 *nbinpermvars = nbinvarsaffected;
1261 *compressed = TRUE;
1262
1263 SCIPfreeBlockMemoryArray(scip, &vars, nvars);
1264 }
1265 SCIPfreeBufferArray(scip, &labeltopermvaridx);
1266 SCIPfreeBufferArray(scip, &labelmovedvars);
1267 }
1268 else
1269 {
1270 /* detect whether binary variable is affected by symmetry and count number of binary permvars */
1271 for (i = 0; i < nbinvars; ++i)
1272 {
1273 for (p = 0; p < nperms && ! *binvaraffected; ++p)
1274 {
1275 if ( perms[p][i] != i )
1276 {
1277 if ( SCIPvarIsBinary(vars[i]) )
1278 *binvaraffected = TRUE;
1279 break;
1280 }
1281 }
1282 }
1283 }
1284
1285 /* store center points of variable domains */
1286 SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvardomaincenter, *npermvars) );
1287 for (i = 0; i < *npermvars; ++i)
1288 {
1289 ub = SCIPvarGetUbGlobal((*permvars)[i]);
1290 lb = SCIPvarGetLbGlobal((*permvars)[i]);
1291
1292 (*permvardomaincenter)[i] = 0.5 * (ub + lb);
1293 }
1294
1295 return SCIP_OKAY;
1296}
1297
1298/** returns whether a constraint handler can provide required symmetry information */
1299static
1301 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
1302 SYM_SYMTYPE symtype /**< type of symmetries for which information are needed */
1303 )
1304{
1305 assert( conshdlr != NULL );
1306
1307 switch ( symtype )
1308 {
1309 case SYM_SYMTYPE_PERM:
1310 return SCIPconshdlrSupportsPermsymDetection(conshdlr);
1311 default:
1312 assert( symtype == SYM_SYMTYPE_SIGNPERM );
1314 } /*lint !e788*/
1315}
1316
1317/** returns whether all constraint handlers with constraints can provide symmetry information */
1318static
1320 SCIP* scip, /**< SCIP pointer */
1321 SYM_SYMTYPE symtype /**< type of symmetries for which information are needed */
1322 )
1323{
1324 SCIP_CONSHDLR** conshdlrs;
1325 SCIP_CONSHDLR* conshdlr;
1326 int nconshdlrs;
1327 int c;
1328
1329 conshdlrs = SCIPgetConshdlrs(scip);
1330 assert( conshdlrs != NULL );
1331
1332 nconshdlrs = SCIPgetNConshdlrs(scip);
1333 for (c = 0; c < nconshdlrs; ++c)
1334 {
1335 conshdlr = conshdlrs[c];
1336 assert( conshdlr != NULL );
1337
1338 if ( ! conshdlrCanProvideSymInformation(conshdlr, symtype) && SCIPconshdlrGetNConss(conshdlr) > 0 )
1339 {
1340 char name[SCIP_MAXSTRLEN];
1341
1342 if ( symtype == SYM_SYMTYPE_PERM )
1343 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "CONSGETPERMSYMGRAPH");
1344 else
1345 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "CONSGETSIGNEDPERMSYMGRAPH");
1346
1348 " Symmetry detection interrupted: constraints of type %s do not provide symmetry information.\n"
1349 " If symmetries shall be detected, implement the %s callback.\n",
1350 SCIPconshdlrGetName(conshdlr), name);
1351
1352 return FALSE;
1353 }
1354 }
1355
1356 /* check whether all expressions provide sufficient symmetry information */
1357 conshdlr = SCIPfindConshdlr(scip, "nonlinear");
1358 if ( conshdlr != NULL && SCIPconshdlrGetNConss(conshdlr) > 0 )
1359 {
1360 SCIP_EXPRHDLR* exprhdlr;
1361
1362 for (c = 0; c < SCIPgetNExprhdlrs(scip); ++c)
1363 {
1364 SCIP_Bool found = FALSE;
1365 exprhdlr = SCIPgetExprhdlrs(scip)[c];
1366
1367 if ( SCIPexprhdlrHasGetSymData(exprhdlr) )
1368 continue;
1369
1370 /* check whether exprhdlr is known by SCIP (and handles symmetries correctly) */
1371 if ( strcmp(SCIPexprhdlrGetName(exprhdlr), "var") == 0
1372 || strcmp(SCIPexprhdlrGetName(exprhdlr), "sum") == 0
1373 || strcmp(SCIPexprhdlrGetName(exprhdlr), "product") == 0
1374 || strcmp(SCIPexprhdlrGetName(exprhdlr), "val") == 0
1375 || strcmp(SCIPexprhdlrGetName(exprhdlr), "pow") == 0
1376 || strcmp(SCIPexprhdlrGetName(exprhdlr), "signpow") == 0
1377 || strcmp(SCIPexprhdlrGetName(exprhdlr), "exp") == 0
1378 || strcmp(SCIPexprhdlrGetName(exprhdlr), "log") == 0
1379 || strcmp(SCIPexprhdlrGetName(exprhdlr), "abs") == 0
1380 || strcmp(SCIPexprhdlrGetName(exprhdlr), "sin") == 0
1381 || strcmp(SCIPexprhdlrGetName(exprhdlr), "cos") == 0
1382 || strcmp(SCIPexprhdlrGetName(exprhdlr), "entropy") == 0
1383 || strcmp(SCIPexprhdlrGetName(exprhdlr), "erf") == 0
1384 || strcmp(SCIPexprhdlrGetName(exprhdlr), "varidx") == 0 )
1385 found = TRUE;
1386
1387 /* there exists an unknown expression handler that does not provide symmetry information */
1388 if ( ! found )
1389 {
1390 SCIPwarningMessage(scip, "Expression handler %s does not implement the EXPRGETSYMDATA callback.\n"
1391 "Computed symmetries might be incorrect if the expression uses different constants or assigns\n"
1392 "different coefficients to its children.\n", SCIPexprhdlrGetName(SCIPgetExprhdlrs(scip)[c]));
1393 }
1394 }
1395 }
1396
1397 return TRUE;
1398}
1399
1400/** provides estimates for the number of nodes and edges in a symmetry detection graph */
1401static
1403 SCIP* scip, /**< SCIP pointer */
1404 int* nopnodes, /**< pointer to store estimate for number of operator nodes */
1405 int* nvalnodes, /**< pointer to store estimate for number of value nodes */
1406 int* nconsnodes, /**< pointer to store estimate for number of constraint nodes */
1407 int* nedges /**< pointer to store estimate for number of edges */
1408 )
1409{
1410 SCIP_CONS** conss;
1411 SCIP_Bool success;
1412 int nvars;
1413 int nconss;
1414 int num;
1415 int c;
1416
1417 assert( scip != NULL );
1418 assert( nopnodes != NULL );
1419 assert( nvalnodes != NULL );
1420 assert( nconsnodes != NULL );
1421 assert( nedges != NULL );
1422
1423 nvars = SCIPgetNVars(scip);
1424 nconss = SCIPgetNConss(scip);
1425 conss = SCIPgetConss(scip);
1426 assert( conss != NULL || nconss == 0 );
1427
1428 *nconsnodes = nconss;
1429
1430 /* get estimate from different types of constraints */
1431 *nopnodes = 0;
1432 *nvalnodes = 0;
1433 for (c = 0; c < nconss; ++c)
1434 {
1435 if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "bounddisjunction") == 0 )
1436 {
1437 SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1438
1439 if ( success )
1440 {
1441 *nopnodes += num;
1442 *nvalnodes += num;
1443 }
1444 }
1445 else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "indicator") == 0 )
1446 {
1447 *nopnodes += 3;
1448 *nvalnodes += 1;
1449 }
1450 else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "nonlinear") == 0 )
1451 {
1452 SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1453
1454 /* use binary trees as a proxy for an expression tree */
1455 if ( success )
1456 {
1457 int depth;
1458 int numnodes;
1459 int expval;
1460
1461 depth = (int) log2((double) num);
1462 expval = (int) exp2((double) (depth + 1));
1463 numnodes = MIN(expval, 100);
1464
1465 *nopnodes += numnodes;
1466 *nvalnodes += MAX((int) 0.1 * numnodes, 1);
1467 }
1468 }
1469 else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "SOS1") == 0 )
1470 {
1471 SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1472
1473 if ( success )
1474 *nopnodes += num;
1475 }
1476 else if ( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(conss[c])), "SOS2") == 0 )
1477 {
1478 SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &num, &success) );
1479
1480 if ( success )
1481 *nopnodes += num - 1;
1482 }
1483 }
1484
1485 /* use a staggered scheme for the number of edges since this can become large
1486 *
1487 * In most cases, edges represent variable coefficients from linear constraints.
1488 * For this reason, use number of variables as proxy.
1489 */
1490 if ( nvars <= 100000 )
1491 *nedges = 100 * nvars;
1492 else if ( nvars <= 1000000 )
1493 *nedges = 32 * nvars;
1494 else if ( nvars <= 16700000 )
1495 *nedges = 16 * nvars;
1496 else
1497 *nedges = INT_MAX / 10;
1498
1499 return SCIP_OKAY;
1500}
1501
1502/** checks whether computed symmetries are indeed symmetries */
1503static
1505 SCIP* scip, /**< SCIP pointer */
1506 SYM_SYMTYPE symtype, /**< type of symmetries to be checked */
1507 int** perms, /**< array of permutations */
1508 int nperms, /**< number of permutations */
1509 int npermvars, /**< number of variables permutations act on */
1510 SYM_SPEC fixedtype /**< variable types that must be fixed by symmetries */
1511 )
1512{
1513 SYM_GRAPH** graphs;
1514 SCIP_CONS** conss;
1515 SCIP_VAR** symvars;
1516 SCIP_Bool success;
1517 int* graphperm;
1518 int* groupbegins;
1519 int ngroups = 1;
1520 int nsymvars;
1521 int nconss;
1522 int p;
1523 int c;
1524 int g;
1525#ifdef SCIP_DISPLAY_SYM_CHECK
1526 int permlen;
1527 SCIP_Bool* covered;
1528#endif
1529
1530 assert( scip != NULL );
1531 assert( perms != NULL );
1532 assert( nperms > 0 );
1533 assert( npermvars > 0 );
1534
1535 /* get symmetry detection graphs for all constraints */
1536 nconss = SCIPgetNConss(scip);
1537 conss = SCIPgetConss(scip);
1538 assert( conss != NULL );
1539
1540 symvars = SCIPgetVars(scip);
1541 nsymvars = SCIPgetNVars(scip);
1542 assert( nsymvars == npermvars );
1543
1544 SCIP_CALL( SCIPallocBufferArray(scip, &graphs, nconss) );
1545
1546 for (c = 0; c < nconss; ++c)
1547 {
1548 SCIP_CALL( SCIPcreateSymgraph(scip, symtype, &graphs[c], symvars, nsymvars, 10, 10, 1, 100) );
1549
1550 success = FALSE;
1551 switch ( symtype )
1552 {
1553 case SYM_SYMTYPE_PERM:
1554 SCIP_CALL( SCIPgetConsPermsymGraph(scip, conss[c], graphs[c], &success) );
1555 break;
1556 default:
1557 assert( symtype == SYM_SYMTYPE_SIGNPERM );
1558 SCIP_CALL( SCIPgetConsSignedPermsymGraph(scip, conss[c], graphs[c], &success) );
1559 } /*lint !e788*/
1560
1561 SCIP_CALL( SCIPcomputeSymgraphColors(scip, graphs[c], fixedtype) );
1562
1563 assert( success );
1564 }
1565
1566 /* sort graphs for quicker comparisons */
1567 SCIP_CALL( SCIPallocBufferArray(scip, &graphperm, nconss) );
1568 SCIP_CALL( SCIPallocBufferArray(scip, &groupbegins, nconss + 1) );
1569 for (c = 0; c < nconss; ++c)
1570 {
1572 }
1573
1574 SCIPsort(graphperm, SYMsortSymgraphs, graphs, nconss);
1575
1576 groupbegins[0] = 0;
1577 for (c = 1; c < nconss; ++c)
1578 {
1579 if ( compareSymgraphs(scip, graphs[graphperm[c]], graphs[graphperm[c-1]]) != 0 )
1580 groupbegins[ngroups++] = c;
1581 }
1582 groupbegins[ngroups] = nconss;
1583
1584 /* remove information from symmetry detection graph that is not needed anymore */
1585 for (c = 0; c < nconss; ++c)
1586 {
1588 }
1589
1590#ifdef SCIP_DISPLAY_SYM_CHECK
1591 permlen = symtype == SYM_SYMTYPE_SIGNPERM ? 2 * npermvars : npermvars;
1592 SCIP_CALL( SCIPallocClearBufferArray(scip, &covered, permlen) );
1593#endif
1594
1595 /* iterate over all permutations and check whether they define symmetries */
1596 for (p = 0; p < nperms; ++p)
1597 {
1598 SYM_GRAPH* graph;
1599 SCIP_Bool found = TRUE;
1600 int d;
1601#ifdef SCIP_DISPLAY_SYM_CHECK
1602 int i;
1603
1604 SCIPinfoMessage(scip, NULL, "Check whether permutation %d is a symmetry:\n", p);
1605 for (i = 0; i < permlen; ++i)
1606 {
1607 SCIP_CALL( displayCycleOfSymmetry(scip, perms[p], symtype, i, covered, npermvars, SCIPgetVars(scip)) );
1608 }
1609
1610 for (i = 0; i < permlen; ++i)
1611 covered[i] = FALSE;
1612 SCIPinfoMessage(scip, NULL, "Check whether every constraint has a symmetric counterpart.\n");
1613#endif
1614
1615 /* for every constraint, create permuted graph by copying nodes and edges */
1616 for (g = 0; g < ngroups; ++g)
1617 {
1618 for (c = groupbegins[g]; c < groupbegins[g+1]; ++c)
1619 {
1620#ifdef SCIP_DISPLAY_SYM_CHECK
1621 SCIPinfoMessage(scip, NULL, "Check whether constraint %d has a symmetric counterpart:\n",
1622 graphperm[c]);
1623 SCIP_CALL( SCIPprintCons(scip, conss[graphperm[c]], NULL) );
1624 SCIPinfoMessage(scip, NULL, "\n");
1625#endif
1626 SCIP_CALL( SCIPcopySymgraph(scip, &graph, graphs[graphperm[c]], perms[p], fixedtype) );
1627
1628 /* if adapted graph is equivalent to original graph, we don't need to check further graphs */
1629 if ( SYMcheckGraphsAreIdentical(scip, symtype, graph, graphs[graphperm[c]]) )
1630 {
1631#ifdef SCIP_DISPLAY_SYM_CHECK
1632 SCIPinfoMessage(scip, NULL, "\tconstraint is symmetric to itself\n");
1633#endif
1634 SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1635 continue;
1636 }
1637
1638 /* check whether graph has an isomorphic counterpart */
1639 found = FALSE;
1640 for (d = groupbegins[g]; d < groupbegins[g+1] && ! found; ++d)
1641 {
1642 found = SYMcheckGraphsAreIdentical(scip, symtype, graph, graphs[graphperm[d]]);
1643
1644#ifdef SCIP_DISPLAY_SYM_CHECK
1645 SCIPinfoMessage(scip, NULL, "\tconstraint is %ssymmetric to constraint %d\n\t", !found ? "not " : "", d);
1646 SCIP_CALL( SCIPprintCons(scip, conss[graphperm[d]], NULL) );
1647 SCIPinfoMessage(scip, NULL, "\n");
1648#endif
1649 }
1650
1651 SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1652
1653 if ( ! found )
1654 {
1655#ifdef SCIP_DISPLAY_SYM_CHECK
1656 SCIPfreeBufferArray(scip, &covered);
1657#endif
1658 SCIPerrorMessage("permutation %d is not a symmetry\n", p);
1659 return SCIP_ERROR;
1660 }
1661 }
1662 }
1663 }
1664
1665#ifdef SCIP_DISPLAY_SYM_CHECK
1666 SCIPfreeBufferArray(scip, &covered);
1667#endif
1668
1669 SCIPfreeBufferArray(scip, &groupbegins);
1670 SCIPfreeBufferArray(scip, &graphperm);
1671
1672 for (c = nconss - 1; c >= 0; --c)
1673 {
1674 SCIP_CALL( SCIPfreeSymgraph(scip, &graphs[c]) );
1675 }
1676 SCIPfreeBufferArray(scip, &graphs);
1677
1678 return SCIP_OKAY;
1679}
1680
1681/** computes symmetry group of a CIP */
1682static
1684 SCIP* scip, /**< SCIP pointer */
1685 SYM_SYMTYPE symtype, /**< type of symmetries to be computed */
1686 SCIP_Bool compresssymmetries, /**< Should non-affected variables be removed from permutation to save memory? */
1687 SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1688 int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
1689 SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1690 SCIP_Bool checksymmetries, /**< Should all symmetries be checked after computation? */
1691 SCIP_VAR*** permvars, /**< pointer to permvars array */
1692 int* npermvars, /**< pointer to store number of permvars */
1693 int* nbinpermvars, /**< pointer to store number of binary permvars */
1694 SCIP_Real** permvardomaincenter, /**< pointer to store center points of variable domains */
1695 int*** perms, /**< pointer to store permutation matrix (nperms x nvars) */
1696 int* nperms, /**< pointer to store number of permutations */
1697 int* nmaxperms, /**< pointer to store maximal number of permutations
1698 * (needed for freeing storage) */
1699 int* nmovedvars, /**< pointer to store number of vars affected
1700 * by symmetry (if usecompression) or NULL */
1701 SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1702 SCIP_Bool* compressed, /**< pointer to store whether compression has been performed */
1703 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
1704 SCIP_Real* symcodetime, /**< pointer to store the time for symmetry code */
1705 SCIP_Bool* success /**< pointer to store whether symmetry computation was successful */
1706 )
1707{
1708 SCIP_CONS** conss;
1709 SYM_GRAPH* graph;
1710 int nconsnodes = 0;
1711 int nvalnodes = 0;
1712 int nopnodes = 0;
1713 int nedges = 0;
1714 int nconss;
1715 int c;
1716
1717 assert( scip != NULL );
1718 assert( permvars != NULL );
1719 assert( npermvars != NULL );
1720 assert( nbinpermvars != NULL );
1721 assert( perms != NULL );
1722 assert( nperms != NULL );
1723 assert( nmaxperms != NULL );
1724 assert( nmovedvars != NULL );
1725 assert( binvaraffected != NULL );
1726 assert( compressed != NULL );
1727 assert( log10groupsize != NULL );
1728 assert( symcodetime != NULL );
1729 assert( success != NULL );
1730
1731 /* init pointers */
1732 *permvars = NULL;
1733 *npermvars = 0;
1734 *nbinpermvars = 0;
1735 *perms = NULL;
1736 *nperms = 0;
1737 *nmaxperms = 0;
1738 *nmovedvars = -1;
1739 *binvaraffected = FALSE;
1740 *compressed = FALSE;
1741 *log10groupsize = 0;
1742 *success = FALSE;
1743 *symcodetime = 0.0;
1744
1745 /* check whether all constraints can provide symmetry information */
1746 if ( ! conshdlrsCanProvideSymInformation(scip, symtype) )
1747 return SCIP_OKAY;
1748
1749 /* get symmetry detection graphs from constraints */
1750 conss = SCIPgetConss(scip);
1751 nconss = SCIPgetNConss(scip);
1752
1753 assert( conss != NULL || nconss == 0 );
1754
1755 /* exit if no constraints or no variables are available */
1756 if ( nconss == 0 || SCIPgetNVars(scip) == 0 )
1757 {
1758 *success = TRUE;
1759 return SCIP_OKAY;
1760 }
1761
1762 /* get an estimate for the number of nodes and edges */
1763 SCIP_CALL( estimateSymgraphSize(scip, &nopnodes, &nvalnodes, &nconsnodes, &nedges) );
1764
1765 /* create graph */
1767 nopnodes, nvalnodes, nconsnodes, nedges) );
1768
1769 *success = TRUE;
1770 for (c = 0; c < nconss && *success; ++c)
1771 {
1772 if ( symtype == SYM_SYMTYPE_PERM )
1773 {
1774 SCIP_CALL( SCIPgetConsPermsymGraph(scip, conss[c], graph, success) );
1775 }
1776 else
1777 {
1778 assert( symtype == SYM_SYMTYPE_SIGNPERM );
1779 SCIP_CALL( SCIPgetConsSignedPermsymGraph(scip, conss[c], graph, success) );
1780 }
1781
1782 /* terminate early if graph could not be returned */
1783 if ( ! *success )
1784 {
1785 SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1786
1787 return SCIP_OKAY;
1788 }
1789 }
1790
1791 SCIP_CALL( SCIPcomputeSymgraphColors(scip, graph, fixedtype) );
1792
1793 /* terminate early in case all variables are different */
1794 if ( (symtype == SYM_SYMTYPE_PERM && SCIPgetSymgraphNVarcolors(graph) == SCIPgetNVars(scip))
1795 || (symtype == SYM_SYMTYPE_SIGNPERM && SCIPgetSymgraphNVarcolors(graph) == 2 * SCIPgetNVars(scip)) )
1796 {
1797 SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1798 return SCIP_OKAY;
1799 }
1800
1801 /*
1802 * actually compute symmetries
1803 */
1804 SCIP_CALL( SYMcomputeSymmetryGenerators(scip, maxgenerators, graph, nperms, nmaxperms,
1805 perms, log10groupsize, symcodetime) );
1806
1807 if ( checksymmetries && *nperms > 0 )
1808 {
1809 SCIP_CALL( checkSymmetriesAreSymmetries(scip, symtype, *perms, *nperms, SCIPgetNVars(scip), fixedtype) );
1810 }
1811
1812 /* potentially store symmetries */
1813 if ( *nperms > 0 )
1814 {
1815 SCIP_VAR** vars;
1816 int nvars;
1817
1818 nvars = SCIPgetNVars(scip);
1819 SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &vars, SCIPgetVars(scip), nvars) ); /*lint !e666*/
1820
1821 SCIP_CALL( setSymmetryData(scip, symtype, vars, nvars, SCIPgetNBinVars(scip), permvars, npermvars, nbinpermvars,
1822 permvardomaincenter, *perms, *nperms, nmovedvars, binvaraffected,
1823 compresssymmetries, compressthreshold, compressed) );
1824 }
1825
1826 /* free symmetry graph */
1827 SCIP_CALL( SCIPfreeSymgraph(scip, &graph) );
1828
1829 return SCIP_OKAY;
1830}
1831
1832/** returns whether a symmetry is a non-standard permutation */
1833static
1835 SCIP* scip, /**< SCIP instance */
1836 int* symmetry, /**< a symmetry encoded as a signed permutation */
1837 SCIP_VAR** vars, /**< array of variables the symmetry acts on */
1838 int nvars /**< number of variables in vars */
1839 )
1840{
1841 int v;
1842
1843 assert( symmetry != NULL );
1844 assert( vars != NULL );
1845 assert( nvars > 0 );
1846
1847 for (v = 0; v < nvars; ++v)
1848 {
1849 /* the symmetry is signed */
1850 if ( symmetry[v] >= nvars )
1851 return TRUE;
1852
1853 /* the domain of symmetric variables is different */
1854 if ( !SCIPisEQ(scip, SCIPvarGetLbLocal(vars[v]), SCIPvarGetLbLocal(vars[symmetry[v]]))
1855 || !SCIPisEQ(scip, SCIPvarGetUbLocal(vars[v]), SCIPvarGetUbLocal(vars[symmetry[v]])) )
1856 {
1857 assert( SCIPisEQ(scip, SCIPvarGetUbLocal(vars[v]) - SCIPvarGetLbLocal(vars[v]),
1858 SCIPvarGetUbLocal(vars[symmetry[v]]) - SCIPvarGetLbLocal(vars[symmetry[v]])) );
1859 return TRUE;
1860 }
1861 }
1862
1863 return FALSE;
1864}
1865
1866/** checks whether component contains non-standard permutations
1867 *
1868 * If all symmetries are standard permutations, stores them as such.
1869 */
1870static
1872 SCIP* scip, /**< SCIP instance */
1873 SCIP_PROPDATA* propdata /**< propagator data */
1874 )
1875{
1876 int* components;
1877 int* componentbegins;
1878 int ncomponents;
1879 int i;
1880 int c;
1881
1882 assert( scip != NULL );
1883 assert( propdata != NULL );
1884 assert( propdata->ncomponents > 0 );
1885 assert( propdata->components != NULL );
1886 assert( propdata->componentbegins != NULL );
1887
1888 components = propdata->components;
1889 componentbegins = propdata->componentbegins;
1890 ncomponents = propdata->ncomponents;
1891
1892 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &(propdata->componenthassignedperm), ncomponents) );
1893
1894 /* stop if no non-standard permutations can exist */
1895 if ( (SYM_SYMTYPE) propdata->symtype == SYM_SYMTYPE_PERM )
1896 return SCIP_OKAY;
1897
1898 /* for each component, check whether it has a non-standard permutation */
1899 for (c = 0; c < ncomponents; ++c)
1900 {
1901 for (i = componentbegins[c]; i < componentbegins[c + 1]; ++i)
1902 {
1903 if ( isNonstandardPerm(scip, propdata->perms[components[i]], propdata->permvars, propdata->npermvars) )
1904 {
1905 propdata->componenthassignedperm[c] = TRUE;
1906 break;
1907 }
1908 }
1909 }
1910
1911 return SCIP_OKAY;
1912}
1913
1914/** ensures that the symmetry components are already computed */
1915static
1917 SCIP* scip, /**< SCIP instance */
1918 SCIP_PROPDATA* propdata /**< propagator data */
1919 )
1920{
1921 assert( scip != NULL );
1922 assert( propdata != NULL );
1923
1924 /* symmetries must have been determined */
1925 assert( propdata->nperms >= 0 );
1926
1927 /* stop if already computed */
1928 if ( propdata->ncomponents >= 0 )
1929 return SCIP_OKAY;
1930
1931 /* compute components */
1932 assert( propdata->ncomponents == -1 );
1933 assert( propdata->components == NULL );
1934 assert( propdata->componentbegins == NULL );
1935 assert( propdata->vartocomponent == NULL );
1936
1937#ifdef SCIP_OUTPUT_COMPONENT
1938 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation started\n", SCIPgetSolvingTime(scip));
1939#endif
1940
1941 SCIP_CALL( SCIPcomputeComponentsSym(scip, (SYM_SYMTYPE) propdata->symtype, propdata->perms, propdata->nperms,
1942 propdata->permvars, propdata->npermvars, FALSE, &propdata->components, &propdata->componentbegins,
1943 &propdata->vartocomponent, &propdata->componentblocked, &propdata->ncomponents) );
1944
1945#ifdef SCIP_OUTPUT_COMPONENT
1946 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation finished\n", SCIPgetSolvingTime(scip));
1947#endif
1948
1949 assert( propdata->components != NULL );
1950 assert( propdata->componentbegins != NULL );
1951 assert( propdata->ncomponents > 0 );
1952
1953 /* structure of symmetries can be simplified if they are standard permutations */
1955 assert( propdata->componenthassignedperm != NULL );
1956
1957 return SCIP_OKAY;
1958}
1959
1960
1961/** ensures that permvarmap is initialized */
1962static
1964 SCIP* scip, /**< SCIP instance */
1965 SCIP_PROPDATA* propdata /**< propagator data */
1966 )
1967{
1968 int v;
1969
1970 assert( scip != NULL );
1971 assert( propdata != NULL );
1972
1973 /* symmetries must have been determined */
1974 assert( propdata->nperms >= 0 );
1975
1976 /* stop if already computed */
1977 if ( propdata->permvarmap != NULL )
1978 return SCIP_OKAY;
1979
1980 /* create hashmap for storing the indices of variables */
1981 SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
1982
1983 /* insert variables into hashmap */
1984 for (v = 0; v < propdata->npermvars; ++v)
1985 {
1986 SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
1987 }
1988
1989 return SCIP_OKAY;
1990}
1991
1992
1993/** ensures that permstrans is initialized */
1994static
1996 SCIP* scip, /**< SCIP instance */
1997 SCIP_PROPDATA* propdata /**< propagator data */
1998 )
1999{
2000 int v;
2001 int p;
2002
2003 assert( scip != NULL );
2004 assert( propdata != NULL );
2005
2006 /* symmetries must have been determined */
2007 assert( propdata->nperms >= 0 );
2008
2009 /* stop if already computed */
2010 if ( propdata->permstrans != NULL )
2011 return SCIP_OKAY;
2012
2013 /* transpose symmetries matrix here */
2014 assert( propdata->permstrans == NULL );
2015 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
2016 for (v = 0; v < propdata->npermvars; ++v)
2017 {
2018 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
2019 for (p = 0; p < propdata->nperms; ++p)
2020 propdata->permstrans[v][p] = propdata->perms[p][v];
2021 }
2022
2023 return SCIP_OKAY;
2024}
2025
2026
2027/** ensures that movedpermvarscounts is initialized */
2028static
2030 SCIP* scip, /**< SCIP instance */
2031 SCIP_PROPDATA* propdata /**< propagator data */
2032 )
2033{
2034 int v;
2035 int p;
2036
2037 assert( scip != NULL );
2038 assert( propdata != NULL );
2039
2040 /* symmetries must have been determined */
2041 assert( propdata->nperms >= 0 );
2042
2043 /* stop if already computed */
2044 if ( propdata->nmovedpermvars >= 0 )
2045 return SCIP_OKAY;
2046 assert( propdata->nmovedpermvars == -1 );
2047
2048 propdata->nmovedpermvars = 0;
2049 propdata->nmovedbinpermvars = 0;
2050 propdata->nmovedintpermvars = 0;
2051 propdata->nmovedimplintpermvars = 0;
2052 propdata->nmovedcontpermvars = 0;
2053
2054 for (v = 0; v < propdata->npermvars; ++v)
2055 {
2056 for (p = 0; p < propdata->nperms; ++p)
2057 {
2058 if ( propdata->perms[p][v] != v )
2059 {
2060 ++propdata->nmovedpermvars;
2061
2062 switch ( SCIPvarGetType(propdata->permvars[v]) )
2063 {
2065 ++propdata->nmovedbinpermvars;
2066 break;
2068 ++propdata->nmovedintpermvars;
2069 break;
2071 ++propdata->nmovedimplintpermvars;
2072 break;
2074 ++propdata->nmovedcontpermvars;
2075 break;
2076 default:
2077 SCIPerrorMessage("Variable provided with unknown vartype\n");
2078 return SCIP_ERROR;
2079 }
2080 break;
2081 }
2082 }
2083 }
2084
2085 return SCIP_OKAY;
2086}
2087
2088
2089/** returns whether any allowed symmetry handling method is effective for the problem instance */
2090static
2092 SCIP* scip, /**< SCIP instance */
2093 SCIP_PROPDATA* propdata /**< propagator data */
2094 )
2095{
2096 /* must always compute symmetry if it is enforced */
2097 if ( propdata->enforcecomputesymmetry )
2098 return TRUE;
2099
2100 /* for dynamic symmetry handling or orbital reduction, branching must be possible */
2101 if ( propdata->usedynamicprop || ISORBITALREDUCTIONACTIVE(propdata->usesymmetry) )
2102 {
2103 /* @todo a proper test whether variables can be branched on or not */
2104 if ( SCIPgetNBinVars(scip) > 0 )
2105 return TRUE;
2106 if ( SCIPgetNIntVars(scip) > 0 )
2107 return TRUE;
2108 /* continuous variables can be branched on if nonlinear constraints exist */
2109 if ( ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2110 && SCIPconshdlrGetNActiveConss(propdata->conshdlr_nonlinear) > 0 )
2111 return TRUE;
2112 }
2113
2114 /* for SST, matching leadervartypes */
2115 if ( ISSSTACTIVE(propdata->usesymmetry) )
2116 {
2117 if ( ISSSTBINACTIVE(propdata->sstleadervartype) && SCIPgetNBinVars(scip) > 0 ) /*lint !e641*/
2118 return TRUE;
2119 if ( ISSSTINTACTIVE(propdata->sstleadervartype) && SCIPgetNIntVars(scip) > 0 ) /*lint !e641*/
2120 return TRUE;
2121 if ( ISSSTIMPLINTACTIVE(propdata->sstleadervartype) && SCIPgetNImplVars(scip) > 0 ) /*lint !e641*/
2122 return TRUE;
2123 if ( ISSSTCONTACTIVE(propdata->sstleadervartype) && SCIPgetNContVars(scip) > 0 ) /*lint !e641*/
2124 return TRUE;
2125 }
2126
2127 /* for static symmetry handling constraints, binary variables must be present */
2128 if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
2129 {
2130 if ( SCIPgetNBinVars(scip) > 0 )
2131 return TRUE;
2132 }
2133
2134 /* if all tests above fail, then the symmetry handling methods cannot achieve anything */
2135 return FALSE;
2136}
2137
2138/** determines symmetry */
2139static
2141 SCIP* scip, /**< SCIP instance */
2142 SCIP_PROPDATA* propdata, /**< propagator data */
2143 SYM_SPEC symspecrequire, /**< symmetry specification for which we need to compute symmetries */
2144 SYM_SPEC symspecrequirefixed /**< symmetry specification of variables which must be fixed by symmetries */
2145 )
2146{ /*lint --e{641}*/
2147 SCIP_Bool successful;
2148 SCIP_Real symcodetime = 0.0;
2149 int maxgenerators;
2150 unsigned int type = 0;
2151 int nvars;
2152 int i;
2153
2154 assert( scip != NULL );
2155 assert( propdata != NULL );
2156 assert( propdata->usesymmetry >= 0 );
2157
2158 /* do not compute symmetry if reoptimization is enabled */
2159 if ( SCIPisReoptEnabled(scip) )
2160 return SCIP_OKAY;
2161
2162 /* do not compute symmetry if Benders decomposition enabled */
2163 if ( SCIPgetNActiveBenders(scip) > 0 )
2164 return SCIP_OKAY;
2165
2166 /* skip symmetry computation if no graph automorphism code was linked */
2167 if ( ! SYMcanComputeSymmetry() )
2168 {
2170 " Deactivated symmetry handling methods, since SCIP was built without symmetry detector (SYM=none).\n");
2171
2172 return SCIP_OKAY;
2173 }
2174
2175 /* do not compute symmetry if there are active pricers */
2176 if ( SCIPgetNActivePricers(scip) > 0 )
2177 return SCIP_OKAY;
2178
2179 /* avoid trivial cases */
2180 nvars = SCIPgetNVars(scip);
2181 if ( nvars <= 0 )
2182 return SCIP_OKAY;
2183
2184 /* do not compute symmetry if we cannot handle it */
2185 if ( !testSymmetryComputationRequired(scip, propdata) )
2186 return SCIP_OKAY;
2187
2188 /* determine symmetry specification */
2189 if ( SCIPgetNBinVars(scip) > 0 )
2190 type |= (int) SYM_SPEC_BINARY;
2191 if ( SCIPgetNIntVars(scip) > 0 )
2192 type |= (int) SYM_SPEC_INTEGER;
2193 /* count implicit integer variables as real variables, since we cannot currently handle integral variables well */
2194 if ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2195 type |= (int) SYM_SPEC_REAL;
2196
2197 /* skip symmetry computation if required variables are not present */
2198 if ( ! (type & symspecrequire) )
2199 {
2201 " (%.1fs) symmetry computation skipped: type (bin %c, int %c, cont %c) does not match requirements (bin %c, int %c, cont %c).\n",
2203 SCIPgetNBinVars(scip) > 0 ? '+' : '-',
2204 SCIPgetNIntVars(scip) > 0 ? '+' : '-',
2205 SCIPgetNContVars(scip) + SCIPgetNImplVars(scip) > 0 ? '+' : '-',
2206 (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2207 (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2208 (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2209
2210 return SCIP_OKAY;
2211 }
2212
2213 /* skip computation if symmetry has already been computed */
2214 if ( propdata->computedsymmetry )
2215 return SCIP_OKAY;
2216
2217 assert( propdata->npermvars == 0 );
2218 assert( propdata->permvars == NULL );
2219 assert( propdata->nperms < 0 );
2220 assert( propdata->nmaxperms == 0 );
2221 assert( propdata->perms == NULL );
2222
2223 /* output message */
2225 " (%.1fs) symmetry computation started: requiring (bin %c, int %c, cont %c), (fixed: bin %c, int %c, cont %c)\n",
2227 (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2228 (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2229 (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-',
2230 (symspecrequirefixed & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2231 (symspecrequirefixed & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2232 (symspecrequirefixed & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2233
2234 /* output warning if we want to fix certain symmetry parts that we also want to compute */
2235 if ( symspecrequire & symspecrequirefixed )
2236 SCIPwarningMessage(scip, "Warning: some required symmetries must be fixed.\n");
2237
2238 /* determine maximal number of generators depending on the number of variables */
2239 maxgenerators = propdata->maxgenerators;
2240 maxgenerators = MIN(maxgenerators, MAXGENNUMERATOR / nvars);
2241
2242 /* actually compute (global) symmetry */
2243 SCIP_CALL( computeSymmetryGroup(scip, (SYM_SYMTYPE) propdata->symtype,
2244 propdata->compresssymmetries, propdata->compressthreshold,
2245 maxgenerators, symspecrequirefixed, propdata->checksymmetries, &propdata->permvars,
2246 &propdata->npermvars, &propdata->nbinpermvars, &propdata->permvardomaincenter,
2247 &propdata->perms, &propdata->nperms, &propdata->nmaxperms,
2248 &propdata->nmovedvars, &propdata->binvaraffected, &propdata->compressed,
2249 &propdata->log10groupsize, &symcodetime, &successful) );
2250
2251 /* mark that we have computed the symmetry group */
2252 propdata->computedsymmetry = TRUE;
2253
2254 /* store restart level */
2255 propdata->lastrestart = SCIPgetNRuns(scip);
2256
2257 /* return if not successful */
2258 if ( ! successful )
2259 {
2260 assert( checkSymmetryDataFree(propdata) );
2261 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) could not compute symmetry\n", SCIPgetSolvingTime(scip));
2262
2263 return SCIP_OKAY;
2264 }
2265
2266 /* return if no symmetries found */
2267 if ( propdata->nperms == 0 )
2268 {
2269 assert( checkSymmetryDataFree(propdata) );
2270 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry present (symcode time: %.2f)\n", SCIPgetSolvingTime(scip), symcodetime);
2271
2272 return SCIP_OKAY;
2273 }
2274 assert( propdata->nperms > 0 );
2275 assert( propdata->npermvars > 0 );
2276
2277 /* display statistics */
2278 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) symmetry computation finished: %d generators found (max: ",
2279 SCIPgetSolvingTime(scip), propdata->nperms);
2280
2281 /* display statistics: maximum number of generators */
2282 if ( maxgenerators == 0 )
2284 else
2285 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%d", maxgenerators);
2286
2287 /* display statistics: log10 group size, number of affected vars*/
2288 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", log10 of symmetry group size: %.2f", propdata->log10groupsize);
2289
2290 if ( propdata->displaynorbitvars )
2291 {
2292 if ( propdata->nmovedvars == -1 )
2293 {
2294 SCIP_CALL( SCIPdetermineNVarsAffectedSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2295 propdata->npermvars, &(propdata->nmovedvars)) );
2296 }
2297 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", number of affected variables: %d)\n", propdata->nmovedvars);
2298 }
2299 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ") (symcode time: %.2f)\n", symcodetime);
2300
2301 /* capture all variables while they are in the permvars array */
2302 for (i = 0; i < propdata->npermvars; ++i)
2303 {
2304 SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[i]) );
2305 }
2306
2307 return SCIP_OKAY;
2308}
2309
2310
2311/*
2312 * Functions for symmetry constraints
2313 */
2314
2315
2316/** Checks whether given set of 2-cycle permutations forms an orbitope and if so, builds the variable index matrix.
2317 *
2318 * If @p activevars == NULL, then the function assumes all permutations of the component are active and therefore all
2319 * moved vars are considered.
2320 *
2321 * We need to keep track of the number of generated columns, because we might not be able to detect all orbitopes.
2322 * 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
2323 * in our construction need shape (1,2), (2,3), (3,4), (4,5).
2324 *
2325 * @pre @p orbitopevaridx has to be an initialized 2D array of size @p ntwocycles x @p nperms
2326 * @pre @p columnorder has to be an initialized array of size nperms
2327 * @pre @p nusedelems has to be an initialized array of size npermvars
2328 */
2329static
2331 SCIP* scip, /**< SCIP instance */
2332 SCIP_VAR** permvars, /**< array of all permutation variables */
2333 int npermvars, /**< number of permutation variables */
2334 int** perms, /**< array of all permutations of the symmetry group */
2335 int* activeperms, /**< indices of the relevant permutations in perms */
2336 int ntwocycles, /**< number of 2-cycles in the permutations */
2337 int nactiveperms, /**< number of active permutations */
2338 int** orbitopevaridx, /**< pointer to store variable index matrix */
2339 int* columnorder, /**< pointer to store column order */
2340 int* nusedelems, /**< pointer to store how often each element was used */
2341 int* nusedcols, /**< pointer to store number of columns used in orbitope (or NULL) */
2342 SCIP_Shortbool* rowisbinary, /**< pointer to store which rows are binary (or NULL) */
2343 SCIP_Bool* isorbitope, /**< buffer to store result */
2344 SCIP_Shortbool* activevars /**< bitset to store whether a variable is active (or NULL) */
2345 )
2346{ /*lint --e{571}*/
2347 SCIP_Bool* usedperm;
2348 SCIP_Bool foundperm = FALSE;
2349 int nusedperms = 0;
2350 int nfilledcols;
2351 int coltoextend;
2352 int ntestedperms = 0;
2353 int row = 0;
2354 int j;
2355
2356 assert( scip != NULL );
2357 assert( permvars != NULL );
2358 assert( perms != NULL );
2359 assert( activeperms != NULL );
2360 assert( orbitopevaridx != NULL );
2361 assert( columnorder != NULL );
2362 assert( nusedelems != NULL );
2363 assert( isorbitope != NULL );
2364 assert( nactiveperms > 0 );
2365 assert( ntwocycles > 0 );
2366 assert( npermvars > 0 );
2367 assert( activevars == NULL || (0 <= nactiveperms && nactiveperms < npermvars) );
2368
2369 *isorbitope = TRUE;
2370 if ( nusedcols != NULL )
2371 *nusedcols = 0;
2372
2373 /* whether a permutation was considered to contribute to orbitope */
2374 SCIP_CALL( SCIPallocClearBufferArray(scip, &usedperm, nactiveperms) );
2375
2376 /* fill first two columns of orbitopevaridx matrix */
2377
2378 /* look for the first active permutation which moves an active variable */
2379 while ( ! foundperm )
2380 {
2381 int permidx;
2382
2383 assert( ntestedperms < nactiveperms );
2384
2385 permidx = activeperms[ntestedperms];
2386
2387 for (j = 0; j < npermvars; ++j)
2388 {
2389 if ( activevars != NULL && ! activevars[j] )
2390 continue;
2391
2392 assert( activevars == NULL || activevars[perms[permidx][j]] );
2393
2394 /* avoid adding the same 2-cycle twice */
2395 if ( perms[permidx][j] > j )
2396 {
2397 assert( SCIPvarIsBinary(permvars[j]) == SCIPvarIsBinary(permvars[perms[permidx][j]]) );
2398
2399 if ( rowisbinary != NULL && SCIPvarIsBinary(permvars[j]) )
2400 rowisbinary[row] = TRUE;
2401
2402 orbitopevaridx[row][0] = j;
2403 orbitopevaridx[row++][1] = perms[permidx][j];
2404 ++(nusedelems[j]);
2405 ++(nusedelems[perms[permidx][j]]);
2406
2407 foundperm = TRUE;
2408 }
2409
2410 if ( row == ntwocycles )
2411 break;
2412 }
2413
2414 ++ntestedperms;
2415 }
2416
2417 /* in the subgroup case it might happen that a generator has less than ntwocycles many 2-cyles */
2418 if ( row != ntwocycles )
2419 {
2420 *isorbitope = FALSE;
2421 SCIPfreeBufferArray(scip, &usedperm);
2422 return SCIP_OKAY;
2423 }
2424
2425 usedperm[ntestedperms - 1] = TRUE;
2426 ++nusedperms;
2427 columnorder[0] = 0;
2428 columnorder[1] = 1;
2429 nfilledcols = 2;
2430
2431 /* extend orbitopevaridx matrix to the left, i.e., iteratively find new permutations that
2432 * intersect the last added left column in each row in exactly one entry, starting with
2433 * column 0 */
2434 coltoextend = 0;
2435 for (j = ntestedperms; j < nactiveperms; ++j)
2436 { /* lint --e{850} */
2437 SCIP_Bool success = FALSE;
2438 SCIP_Bool infeasible = FALSE;
2439
2440 if ( nusedperms == nactiveperms )
2441 break;
2442
2443 if ( usedperm[j] )
2444 continue;
2445
2446 SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
2447 perms[activeperms[j]], TRUE, &nusedelems, permvars, NULL, &success, &infeasible) );
2448
2449 if ( infeasible )
2450 {
2451 *isorbitope = FALSE;
2452 break;
2453 }
2454 else if ( success )
2455 {
2456 usedperm[j] = TRUE;
2457 ++nusedperms;
2458 coltoextend = nfilledcols;
2459 columnorder[nfilledcols++] = -1; /* mark column to be filled from the left */
2460 j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
2461 }
2462 }
2463
2464 if ( ! *isorbitope ) /*lint !e850*/
2465 {
2466 SCIPfreeBufferArray(scip, &usedperm);
2467 return SCIP_OKAY;
2468 }
2469
2470 coltoextend = 1;
2471 for (j = ntestedperms; j < nactiveperms; ++j)
2472 { /*lint --e(850)*/
2473 SCIP_Bool success = FALSE;
2474 SCIP_Bool infeasible = FALSE;
2475
2476 if ( nusedperms == nactiveperms )
2477 break;
2478
2479 if ( usedperm[j] )
2480 continue;
2481
2482 SCIP_CALL( SCIPextendSubOrbitope(orbitopevaridx, ntwocycles, nfilledcols, coltoextend,
2483 perms[activeperms[j]], FALSE, &nusedelems, permvars, NULL, &success, &infeasible) );
2484
2485 if ( infeasible )
2486 {
2487 *isorbitope = FALSE;
2488 break;
2489 }
2490 else if ( success )
2491 {
2492 usedperm[j] = TRUE;
2493 ++nusedperms;
2494 coltoextend = nfilledcols;
2495 columnorder[nfilledcols] = 1; /* mark column to be filled from the right */
2496 ++nfilledcols;
2497 j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
2498 }
2499 }
2500
2501 if ( activevars == NULL && nusedperms < nactiveperms ) /*lint !e850*/
2502 *isorbitope = FALSE;
2503
2504 if ( nusedcols != NULL )
2505 *nusedcols = nfilledcols;
2506 assert( ! *isorbitope || activevars == NULL || nusedperms < nfilledcols );
2507
2508 SCIPfreeBufferArray(scip, &usedperm);
2509
2510 return SCIP_OKAY;
2511}
2512
2513/** choose an order in which the generators should be added for subgroup detection */
2514static
2516 SCIP* scip, /**< SCIP instance */
2517 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2518 int compidx, /**< index of component */
2519 int** genorder, /**< (initialized) buffer to store the resulting order of generator */
2520 int* ntwocycleperms /**< pointer to store the number of 2-cycle permutations in component compidx */
2521 )
2522{
2523 int** perms;
2524 int* components;
2525 int* componentbegins;
2526 int* ntwocycles;
2527 int npermvars;
2528 int npermsincomp;
2529 int i;
2530
2531 assert( scip != NULL );
2532 assert( propdata != NULL );
2533 assert( compidx >= 0 );
2534 assert( compidx < propdata->ncomponents );
2535 assert( genorder != NULL );
2536 assert( *genorder != NULL );
2537 assert( ntwocycleperms != NULL );
2538 assert( propdata->computedsymmetry );
2539 assert( propdata->nperms > 0 );
2540 assert( propdata->perms != NULL );
2541 assert( propdata->npermvars > 0 );
2542 assert( propdata->ncomponents > 0 );
2543 assert( propdata->components != NULL );
2544 assert( propdata->componentbegins != NULL );
2545
2546 perms = propdata->perms;
2547 npermvars = propdata->npermvars;
2548 components = propdata->components;
2549 componentbegins = propdata->componentbegins;
2550 npermsincomp = componentbegins[compidx + 1] - componentbegins[compidx];
2551 *ntwocycleperms = npermsincomp;
2552
2553 SCIP_CALL( SCIPallocBufferArray(scip, &ntwocycles, npermsincomp) );
2554
2555 for (i = 0; i < npermsincomp; ++i)
2556 {
2557 int* perm;
2558 int nbincycles;
2559
2560 perm = perms[components[componentbegins[compidx] + i]];
2561
2562 SCIP_CALL( SCIPisInvolutionPerm(perm, propdata->permvars, npermvars, &(ntwocycles[i]), &nbincycles, FALSE) );
2563
2564 /* we skip permutations which do not purely consist of 2-cycles */
2565 if ( ntwocycles[i] == 0 )
2566 {
2567 /* we change the number of two cycles for this perm so that it will be sorted to the end */
2568 if ( propdata->preferlessrows )
2569 ntwocycles[i] = npermvars;
2570 else
2571 ntwocycles[i] = 0;
2572 --(*ntwocycleperms);
2573 }
2574 else if ( ! propdata->preferlessrows )
2575 ntwocycles[i] = - ntwocycles[i];
2576 }
2577
2578 SCIPsortIntInt(ntwocycles, *genorder, npermsincomp);
2579
2580 SCIPfreeBufferArray(scip, &ntwocycles);
2581
2582 return SCIP_OKAY;
2583}
2584
2585
2586/** builds the graph for symmetric subgroup detection from the given permutation of generators
2587 *
2588 * After execution, @p graphcomponents contains all permvars sorted by their color and component,
2589 * @p graphcompbegins points to the indices where new components in @p graphcomponents start and
2590 * @p compcolorbegins points to the indices where new colors in @p graphcompbegins start.
2591*/
2592static
2594 SCIP* scip, /**< SCIP instance */
2595 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2596 int* genorder, /**< order in which the generators should be considered */
2597 int ntwocycleperms, /**< number of 2-cycle permutations in this component */
2598 int compidx, /**< index of the component */
2599 int** graphcomponents, /**< buffer to store the components of the graph (ordered var indices) */
2600 int** graphcompbegins, /**< buffer to store the indices of each new graph component */
2601 int** compcolorbegins, /**< buffer to store at which indices a new color begins */
2602 int* ngraphcomponents, /**< pointer to store the number of graph components */
2603 int* ncompcolors, /**< pointer to store the number of different colors */
2604 int** usedperms, /**< buffer to store the indices of permutations that were used */
2605 int* nusedperms, /**< pointer to store the number of used permutations in the graph */
2606 int usedpermssize, /**< initial size of usedperms */
2607 SCIP_Shortbool* permused /**< initialized buffer to store which permutations have been used
2608 * (identified by index in component) */
2609 )
2610{
2611 SCIP_DISJOINTSET* vartocomponent;
2612 SCIP_DISJOINTSET* comptocolor;
2613 int** perms;
2614 int* components;
2615 int* componentbegins;
2616 int* componentslastperm;
2617 SYM_SORTGRAPHCOMPVARS graphcompvartype;
2618 int npermvars;
2619 int nextcolor;
2620 int nextcomp;
2621 int j;
2622 int k;
2623
2624 assert( scip != NULL );
2625 assert( propdata != NULL );
2626 assert( graphcomponents != NULL );
2627 assert( graphcompbegins != NULL );
2628 assert( compcolorbegins != NULL );
2629 assert( ngraphcomponents != NULL );
2630 assert( ncompcolors != NULL );
2631 assert( genorder != NULL );
2632 assert( usedperms != NULL );
2633 assert( nusedperms != NULL );
2634 assert( usedpermssize > 0 );
2635 assert( permused != NULL );
2636 assert( ntwocycleperms >= 0 );
2637 assert( compidx >= 0 );
2638 assert( compidx < propdata->ncomponents );
2639 assert( propdata->computedsymmetry );
2640 assert( propdata->nperms > 0 );
2641 assert( propdata->perms != NULL );
2642 assert( propdata->npermvars > 0 );
2643 assert( propdata->ncomponents > 0 );
2644 assert( propdata->components != NULL );
2645 assert( propdata->componentbegins != NULL );
2646 assert( ! propdata->componentblocked[compidx] );
2647
2648 perms = propdata->perms;
2649 npermvars = propdata->npermvars;
2650 components = propdata->components;
2651 componentbegins = propdata->componentbegins;
2652 *nusedperms = 0;
2653
2654 assert( ntwocycleperms <= componentbegins[compidx + 1] - componentbegins[compidx] );
2655
2656 SCIP_CALL( SCIPcreateDisjointset(scip, &vartocomponent, npermvars) );
2657 SCIP_CALL( SCIPcreateDisjointset(scip, &comptocolor, npermvars) );
2658 SCIP_CALL( SCIPallocBufferArray( scip, &componentslastperm, npermvars) );
2659
2660 for (k = 0; k < npermvars; ++k)
2661 componentslastperm[k] = -1;
2662
2663 for (j = 0; j < ntwocycleperms; ++j)
2664 {
2665 int* perm;
2666 int firstcolor = -1;
2667
2668 /* use given order of generators */
2669 perm = perms[components[componentbegins[compidx] + genorder[j]]];
2670 assert( perm != NULL );
2671
2672 /* iteratively handle each swap of perm until an invalid one is found or all edges have been added */
2673 for (k = 0; k < npermvars; ++k)
2674 {
2675 int comp1;
2676 int comp2;
2677 int color1;
2678 int color2;
2679 int img;
2680
2681 img = perm[k];
2682 assert( perm[img] == k );
2683
2684 if ( img <= k )
2685 continue;
2686
2687 comp1 = SCIPdisjointsetFind(vartocomponent, k);
2688 comp2 = SCIPdisjointsetFind(vartocomponent, img);
2689
2690 if ( comp1 == comp2 )
2691 {
2692 /* another permutation has already merged these variables into one component; store its color */
2693 if ( firstcolor < 0 )
2694 {
2695 assert( SCIPdisjointsetFind(comptocolor, comp1) == SCIPdisjointsetFind(comptocolor, comp2) );
2696 firstcolor = SCIPdisjointsetFind(comptocolor, comp1);
2697 }
2698 componentslastperm[comp1] = j;
2699 continue;
2700 }
2701
2702 /* if it is the second time that the component is used for this generator,
2703 * it is not guaranteed that the group acts like the symmetric group, so skip it
2704 */
2705 if ( componentslastperm[comp1] == j || componentslastperm[comp2] == j )
2706 break;
2707
2708 color1 = SCIPdisjointsetFind(comptocolor, comp1);
2709 color2 = SCIPdisjointsetFind(comptocolor, comp2);
2710
2711 /* a generator is not allowed to connect two components of the same color, since they depend on each other */
2712 if ( color1 == color2 )
2713 break;
2714
2715 componentslastperm[comp1] = j;
2716 componentslastperm[comp2] = j;
2717
2718 if ( firstcolor < 0 )
2719 firstcolor = color1;
2720 }
2721
2722 /* if the generator is invalid, delete the newly added edges, go to next generator */
2723 if ( k < npermvars )
2724 continue;
2725
2726 /* if the generator only acts on already existing components, we don't have to store it */
2727 if ( firstcolor == -1 )
2728 continue;
2729
2730 /* check whether we need to resize */
2731 if ( *nusedperms >= usedpermssize )
2732 {
2733 int newsize = SCIPcalcMemGrowSize(scip, (*nusedperms) + 1);
2734 assert( newsize > usedpermssize );
2735
2736 SCIP_CALL( SCIPreallocBufferArray(scip, usedperms, newsize) );
2737
2738 usedpermssize = newsize;
2739 }
2740
2741 (*usedperms)[*nusedperms] = components[componentbegins[compidx] + genorder[j]];
2742 ++(*nusedperms);
2743 permused[genorder[j]] = TRUE;
2744
2745 /* if the generator can be added, update the datastructures for graph components and colors */
2746 for (k = 0; k < npermvars; ++k)
2747 {
2748 int comp1;
2749 int comp2;
2750 int color1;
2751 int color2;
2752 int img;
2753
2754 img = perm[k];
2755 assert( perm[img] == k );
2756
2757 if ( img <= k )
2758 continue;
2759
2760 comp1 = SCIPdisjointsetFind(vartocomponent, k);
2761 comp2 = SCIPdisjointsetFind(vartocomponent, img);
2762
2763 /* components and colors don't have to be updated if the components are the same */
2764 if ( comp1 == comp2 )
2765 continue;
2766
2767 color1 = SCIPdisjointsetFind(comptocolor, comp1);
2768 color2 = SCIPdisjointsetFind(comptocolor, comp2);
2769
2770 if ( color1 != color2 )
2771 {
2772 SCIPdisjointsetUnion(comptocolor, firstcolor, color1, TRUE);
2773 SCIPdisjointsetUnion(comptocolor, firstcolor, color2, TRUE);
2774 }
2775
2776 SCIPdisjointsetUnion(vartocomponent, comp1, comp2, FALSE);
2777
2778 assert( SCIPdisjointsetFind(vartocomponent, k) == SCIPdisjointsetFind(vartocomponent, img) );
2779 assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, k)) == firstcolor );
2780 assert( SCIPdisjointsetFind(comptocolor, SCIPdisjointsetFind(vartocomponent, img)) == firstcolor );
2781 }
2782 }
2783
2784 SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcomponents, npermvars) );
2785 SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.components), npermvars) );
2786 SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.colors), npermvars) );
2787
2788 /*
2789 * At this point, we have built the colored graph. Now we transform the information in the
2790 * disjoint sets to the arrays graphcomponents, graphcompbegins, and compcolorbegins (see above).
2791 */
2792
2793 /* build the struct graphcompvartype which is used to sort the graphcomponents array */
2794 for (j = 0; j < npermvars; ++j)
2795 {
2796 int comp;
2797
2798 comp = SCIPdisjointsetFind(vartocomponent, j);
2799
2800 graphcompvartype.components[j] = comp;
2801 graphcompvartype.colors[j] = SCIPdisjointsetFind(comptocolor, comp);
2802
2803 (*graphcomponents)[j] = j;
2804 }
2805
2806 /* sort graphcomponents first by color, then by component */
2807 SCIPsort(*graphcomponents, SYMsortGraphCompVars, (void*) &graphcompvartype, npermvars);
2808
2809 *ngraphcomponents = SCIPdisjointsetGetComponentCount(vartocomponent);
2810 *ncompcolors = SCIPdisjointsetGetComponentCount(comptocolor);
2811 SCIP_CALL( SCIPallocBlockMemoryArray(scip, graphcompbegins, (*ngraphcomponents) + 1) );
2812 SCIP_CALL( SCIPallocBlockMemoryArray(scip, compcolorbegins, (*ncompcolors) + 1) );
2813
2814 nextcolor = 1;
2815 nextcomp = 1;
2816 (*graphcompbegins)[0] = 0;
2817 (*compcolorbegins)[0] = 0;
2818
2819 /* find the starting indices of new components and new colors */
2820 for (j = 1; j < npermvars; ++j)
2821 {
2822 int idx1;
2823 int idx2;
2824
2825 idx1 = (*graphcomponents)[j];
2826 idx2 = (*graphcomponents)[j-1];
2827
2828 assert( graphcompvartype.colors[idx1] >= graphcompvartype.colors[idx2] );
2829
2830 if ( graphcompvartype.components[idx1] != graphcompvartype.components[idx2] )
2831 {
2832 (*graphcompbegins)[nextcomp] = j;
2833
2834 if ( graphcompvartype.colors[idx1] > graphcompvartype.colors[idx2] )
2835 {
2836 (*compcolorbegins)[nextcolor] = nextcomp;
2837 ++nextcolor;
2838 }
2839
2840 ++nextcomp;
2841 }
2842 }
2843 assert( nextcomp == *ngraphcomponents );
2844 assert( nextcolor == *ncompcolors );
2845
2846 (*compcolorbegins)[nextcolor] = *ngraphcomponents;
2847 (*graphcompbegins)[nextcomp] = npermvars;
2848
2849 SCIPfreeBufferArray(scip, &(graphcompvartype.colors));
2850 SCIPfreeBufferArray(scip, &(graphcompvartype.components));
2851 SCIPfreeBufferArray(scip, &componentslastperm);
2852 SCIPfreeDisjointset(scip, &comptocolor);
2853 SCIPfreeDisjointset(scip, &vartocomponent);
2854
2855 return SCIP_OKAY;
2856}
2857
2858/** adds an orbitope constraint for a suitable color of the subgroup graph */
2859static
2861 SCIP* scip, /**< SCIP instance */
2862 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
2863 int* usedperms, /**< array of the permutations that build the orbitope */
2864 int nusedperms, /**< number of permutations in usedperms */
2865 int* compcolorbegins, /**< array indicating where a new graphcolor begins */
2866 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
2867 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
2868 int graphcoloridx, /**< index of the graph color */
2869 int nrows, /**< number of rows in the orbitope */
2870 int ncols, /**< number of columns in the orbitope */
2871 int* firstvaridx, /**< buffer to store the index of the largest variable (or NULL) */
2872 int* compidxfirstrow, /**< buffer to store the comp index for the first row (or NULL) */
2873 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
2874 int* nvarslexorder, /**< number of variables in lexicographic order */
2875 int* maxnvarslexorder, /**< maximum number of variables in lexicographic order */
2876 SCIP_Bool mayinteract, /**< whether orbitope's symmetries might interact with other symmetries */
2877 SCIP_Bool* success /**< whether the orbitope could be added */
2878 )
2879{ /*lint --e{571}*/
2880 char name[SCIP_MAXSTRLEN];
2881 SCIP_VAR*** orbitopevarmatrix;
2882 SCIP_Shortbool* activevars;
2883 int** orbitopevaridx;
2884 int* columnorder;
2885 int* nusedelems;
2886 SCIP_CONS* cons;
2887 SCIP_Bool isorbitope;
2888 SCIP_Bool infeasible = FALSE;
2889#ifndef NDEBUG
2890 int nactivevars = 0;
2891#endif
2892 int ngencols = 0;
2893 int k;
2894
2895 assert( scip != NULL );
2896 assert( propdata != NULL );
2897 assert( usedperms != NULL );
2898 assert( compcolorbegins != NULL );
2899 assert( graphcompbegins != NULL );
2900 assert( graphcomponents != NULL );
2901 assert( nusedperms > 0 );
2902 assert( nrows > 0 );
2903 assert( ncols > 0 );
2904 assert( lexorder != NULL );
2905 assert( nvarslexorder != NULL );
2906 assert( maxnvarslexorder != NULL );
2907
2908 *success = FALSE;
2909
2910 /* create hashset to mark variables */
2911 SCIP_CALL( SCIPallocClearBufferArray(scip, &activevars, propdata->npermvars) );
2912
2913 /* orbitope matrix for indices of variables in permvars array */
2914 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx, nrows) );
2915 for (k = 0; k < nrows; ++k)
2916 {
2917 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[k], ncols) ); /*lint !e866*/
2918 }
2919
2920 /* order of columns of orbitopevaridx */
2921 SCIP_CALL( SCIPallocBufferArray(scip, &columnorder, ncols) );
2922 for (k = 0; k < ncols; ++k)
2923 columnorder[k] = ncols + 1;
2924
2925 /* count how often an element was used in the potential orbitope */
2926 SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, propdata->npermvars) );
2927
2928 /* mark variables in this subgroup orbitope */
2929 for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1]; ++k)
2930 {
2931 SCIP_VAR* firstvar;
2932 int compstart;
2933 int l;
2934
2935 compstart = graphcompbegins[k];
2936 firstvar = propdata->permvars[graphcomponents[compstart]];
2937
2938 if ( ! SCIPvarIsBinary(firstvar) )
2939 continue;
2940
2941 for (l = 0; l < ncols; ++l)
2942 {
2943 int varidx;
2944
2945 varidx = graphcomponents[compstart + l];
2946 assert( ! activevars[varidx] );
2947
2948 activevars[varidx] = TRUE;
2949#ifndef NDEBUG
2950 ++nactivevars;
2951#endif
2952 }
2953 }
2954 assert( nactivevars == nrows * ncols );
2955
2956 /* build the variable index matrix for the orbitope
2957 *
2958 * It is possible that we find an orbitope, but not using all possible columns. For example
2959 * (1,2), (2,3), (3,4), (3,5) defines the symmetric group on {1,2,3,4,5}, but the generators
2960 * we expect in our construction need shape (1,2), (2,3), (3,4), (4,5). For this reason,
2961 * we need to store how many columns have been generated.
2962 *
2963 * @todo ensure compatibility with more general generators
2964 */
2965 SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, propdata->permvars, propdata->npermvars,
2966 propdata->perms, usedperms, nrows, nusedperms, orbitopevaridx, columnorder,
2967 nusedelems, &ngencols, NULL, &isorbitope, activevars) );
2968
2969 /* it might happen that we cannot detect the orbitope if it is generated by permutations with different
2970 * number of 2-cycles.
2971 */
2972 if ( ! isorbitope )
2973 {
2974 SCIPfreeBufferArray(scip, &nusedelems);
2975 SCIPfreeBufferArray(scip, &columnorder);
2976 for (k = nrows - 1; k >= 0; --k)
2977 {
2978 SCIPfreeBufferArray(scip, &orbitopevaridx[k]);
2979 }
2980 SCIPfreeBufferArray(scip, &orbitopevaridx);
2981 SCIPfreeBufferArray(scip, &activevars);
2982
2983 return SCIP_OKAY;
2984 }
2985
2986 /* There are three possibilities for the structure of columnorder:
2987 * 1) [0, 1, -1, -1, ..., -1]
2988 * 2) [0, 1, 1, 1, ..., 1]
2989 * 3) [0, 1, -1, -1, ...., -1, 1, 1, ..., 1]
2990 *
2991 * The '1'-columns will be added to the matrix first and in the last 2
2992 * cases the method starts from the right. So to store the variable index
2993 * that will be in the upper-left corner, we need either the entryin the
2994 * second column (case 1) or the entry in the last column (cases 2 and 3).
2995 */
2996 if ( firstvaridx != NULL )
2997 {
2998 if ( columnorder[ngencols-1] > -1 )
2999 *firstvaridx = orbitopevaridx[0][ngencols-1];
3000 else
3001 *firstvaridx = orbitopevaridx[0][1];
3002 }
3003
3004 /* find corresponding graphcomponent of first variable (needed for weak sbcs) */
3005 if ( compidxfirstrow != NULL && firstvaridx != NULL )
3006 {
3007 *compidxfirstrow = -1;
3008
3009 for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1] && (*compidxfirstrow) < 0; ++k)
3010 {
3011 SCIP_VAR* firstvar;
3012 int compstart;
3013 int l;
3014
3015 compstart = graphcompbegins[k];
3016 firstvar = propdata->permvars[graphcomponents[compstart]];
3017
3018 if ( ! SCIPvarIsBinary(firstvar) )
3019 continue;
3020
3021 /* iterate over all columns (elements in orbit), because we cannot see from ngencols which columns
3022 * have been left out
3023 */
3024 for (l = 0; l < ncols; ++l)
3025 {
3026 if ( graphcomponents[compstart + l] == *firstvaridx )
3027 {
3028 *compidxfirstrow = k;
3029 break;
3030 }
3031 }
3032 }
3033 assert( *compidxfirstrow > -1 );
3034 }
3035
3036 /* prepare orbitope variable matrix */
3037 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix, nrows) );
3038 for (k = 0; k < nrows; ++k)
3039 {
3040 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevarmatrix[k], ngencols) );
3041 }
3042
3043 /* build the matrix containing the actual variables of the orbitope */
3044 SCIP_CALL( SCIPgenerateOrbitopeVarsMatrix(scip, &orbitopevarmatrix, nrows, ngencols,
3045 propdata->permvars, propdata->npermvars, orbitopevaridx, columnorder,
3046 nusedelems, NULL, &infeasible, TRUE, lexorder, nvarslexorder, maxnvarslexorder) );
3047
3048 assert( ! infeasible );
3049 assert( firstvaridx == NULL || propdata->permvars[*firstvaridx] == orbitopevarmatrix[0][0] );
3050
3051 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "suborbitope_%d_%d", graphcoloridx, propdata->norbitopes);
3052
3053 SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopevarmatrix,
3054 SCIP_ORBITOPETYPE_FULL, nrows, ngencols, FALSE, mayinteract, FALSE, FALSE, propdata->conssaddlp,
3056
3057 SCIP_CALL( SCIPaddCons(scip, cons) );
3058 *success = TRUE;
3059
3060 /* do not release constraint here - will be done later */
3062 &propdata->genorbconsssize, propdata->ngenorbconss + 1) );
3063 propdata->genorbconss[propdata->ngenorbconss++] = cons;
3064 ++propdata->norbitopes;
3065
3066 for (k = nrows - 1; k >= 0; --k)
3067 SCIPfreeBufferArray(scip, &orbitopevarmatrix[k]);
3068 SCIPfreeBufferArray(scip, &orbitopevarmatrix);
3069 SCIPfreeBufferArray(scip, &nusedelems);
3070 SCIPfreeBufferArray(scip, &columnorder);
3071 for (k = nrows - 1; k >= 0; --k)
3072 SCIPfreeBufferArray(scip, &orbitopevaridx[k]);
3073 SCIPfreeBufferArray(scip, &orbitopevaridx);
3074 SCIPfreeBufferArray(scip, &activevars);
3075
3076 return SCIP_OKAY;
3077}
3078
3079/** adds strong SBCs for a suitable color of the subgroup graph */
3080static
3082 SCIP* scip, /**< SCIP instance */
3083 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3084 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3085 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3086 int graphcompidx, /**< index of the graph component */
3087 SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3088 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3089 int* nvarsorder, /**< number of variables in lexicographic order */
3090 int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3091 )
3092{
3093 int k;
3094
3095 assert( scip != NULL );
3096 assert( propdata != NULL );
3097 assert( graphcompbegins != NULL );
3098 assert( graphcomponents != NULL );
3099 assert( graphcompidx >= 0 );
3100 assert( ! storelexorder || lexorder != NULL );
3101 assert( ! storelexorder || nvarsorder != NULL );
3102 assert( ! storelexorder || maxnvarsorder != NULL );
3103
3104 /* possibly store lexicographic order defined by strong SBCs */
3105 if ( storelexorder )
3106 {
3107 if ( *maxnvarsorder == 0 )
3108 {
3109 *maxnvarsorder = graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx];
3110 *nvarsorder = 0;
3111
3112 SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
3113 }
3114 else
3115 {
3116 assert( *nvarsorder == *maxnvarsorder );
3117
3118 *maxnvarsorder += graphcompbegins[graphcompidx + 1] - graphcompbegins[graphcompidx];
3119
3120 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
3121 }
3122
3123 (*lexorder)[*nvarsorder++] = graphcomponents[graphcompbegins[graphcompidx]];
3124 }
3125
3126 /* add strong SBCs (lex-max order) for chosen graph component */
3127 for (k = graphcompbegins[graphcompidx]+1; k < graphcompbegins[graphcompidx+1]; ++k)
3128 {
3129 char name[SCIP_MAXSTRLEN];
3130 SCIP_CONS* cons;
3131 SCIP_VAR* vars[2];
3132 SCIP_Real vals[2] = {1, -1};
3133
3134 vars[0] = propdata->permvars[graphcomponents[k-1]];
3135 vars[1] = propdata->permvars[graphcomponents[k]];
3136
3137 if ( storelexorder )
3138 (*lexorder)[*nvarsorder++] = graphcomponents[k];
3139
3140 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "strong_sbcs_%s_%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3141
3142 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3143 SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3144 TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3145
3146 SCIP_CALL( SCIPaddCons(scip, cons) );
3147
3148#ifdef SCIP_MORE_DEBUG
3149 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3150 SCIPinfoMessage(scip, NULL, "\n");
3151#endif
3152
3153 /* check whether we need to resize */
3155 &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
3156 propdata->genlinconss[propdata->ngenlinconss] = cons;
3157 ++propdata->ngenlinconss;
3158 }
3159
3160 return SCIP_OKAY;
3161}
3162
3163/** adds weak SBCs for a suitable color of the subgroup graph */
3164static
3166 SCIP* scip, /**< SCIP instance */
3167 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3168 int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3169 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3170 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3171 int ncompcolors, /**< number of colors in the graph */
3172 int* chosencomppercolor, /**< array indicating which comp was handled per color */
3173 int* firstvaridxpercolor,/**< array indicating the largest variable per color */
3174 int symgrpcompidx, /**< index of the component of the symmetry group */
3175 int* naddedconss, /**< buffer to store the number of added constraints */
3176 SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3177 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3178 int* nvarsorder, /**< number of variables in lexicographic order */
3179 int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3180 )
3181{ /*lint --e{571}*/
3182 SCIP_HASHMAP* varsinlexorder;
3183 SCIP_Shortbool* usedvars;
3184 SCIP_VAR* vars[2];
3185 SCIP_Real vals[2] = {1, -1};
3186 SCIP_Shortbool* varfound;
3187 int* orbit[2];
3188 int orbitsize[2] = {1, 1};
3189 int activeorb = 0;
3190 int chosencolor = -1;
3191 int j;
3192 int k;
3193
3194 assert( scip != NULL );
3195 assert( propdata != NULL );
3196 assert( compcolorbegins != NULL );
3197 assert( graphcompbegins != NULL );
3198 assert( graphcomponents != NULL );
3199 assert( firstvaridxpercolor != NULL );
3200 assert( chosencomppercolor != NULL );
3201 assert( naddedconss != NULL );
3202 assert( symgrpcompidx >= 0 );
3203 assert( symgrpcompidx < propdata->ncomponents );
3204 assert( ! storelexorder || lexorder != NULL );
3205 assert( ! storelexorder || nvarsorder != NULL );
3206 assert( ! storelexorder || maxnvarsorder != NULL );
3207
3208 *naddedconss = 0;
3209
3210 SCIP_CALL( SCIPallocCleanBufferArray(scip, &usedvars, propdata->npermvars) );
3211 SCIP_CALL( SCIPallocClearBufferArray(scip, &varfound, propdata->npermvars) );
3212 SCIP_CALL( SCIPallocBufferArray(scip, &orbit[0], propdata->npermvars) );
3213 SCIP_CALL( SCIPallocBufferArray(scip, &orbit[1], propdata->npermvars) );
3214
3215 /* Store the entries in lexorder in a hashmap, for fast lookups. */
3216 if ( lexorder == NULL || *lexorder == NULL )
3217 {
3218 /* Lexorder does not exist, so do not create hashmap. */
3219 varsinlexorder = NULL;
3220 }
3221 else
3222 {
3223 assert( *maxnvarsorder >= 0 );
3224 assert( *nvarsorder >= 0 );
3225
3226 SCIP_CALL( SCIPhashmapCreate(&varsinlexorder, SCIPblkmem(scip), *maxnvarsorder) );
3227
3228 for (k = 0; k < *nvarsorder; ++k)
3229 {
3230 /* add element from lexorder to hashmap.
3231 * Use insert, as duplicate entries in lexorder is not permitted. */
3232 assert((*lexorder)[k] >= 0);
3233 assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) (*lexorder)[k]) ); /* Use int as pointer */
3234 SCIP_CALL( SCIPhashmapInsertInt(varsinlexorder, (void*) (size_t) (*lexorder)[k], k) );
3235 }
3236 }
3237
3238 /* We will store the newest and the largest orbit and activeorb will be used to mark at which entry of the array
3239 * orbit the newly computed one will be stored. */
3240 if ( ncompcolors > 0 )
3241 {
3243 }
3244 for (j = 0; j < ncompcolors; ++j)
3245 {
3246 int graphcomp;
3247 int graphcompsize;
3248 int varidx;
3249
3250 /* skip color for which we did not add anything */
3251 if ( chosencomppercolor[j] < 0 )
3252 continue;
3253
3254 assert( firstvaridxpercolor[j] >= 0 );
3255
3256 graphcomp = chosencomppercolor[j];
3257 graphcompsize = graphcompbegins[graphcomp+1] - graphcompbegins[graphcomp];
3258 varidx = firstvaridxpercolor[j];
3259 assert(varidx >= 0);
3260
3261 /* if the first variable was already contained in another orbit or if there are no variables left anyway, skip the
3262 * component */
3263 if ( varfound[varidx] || graphcompsize == propdata->npermvars )
3264 continue;
3265
3266 /* If varidx is in lexorder, then it must be the first entry of lexorder. */
3267 if ( varsinlexorder != NULL
3268 && SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx)
3269 && lexorder != NULL && *lexorder != NULL && *maxnvarsorder > 0 && *nvarsorder > 0
3270 && (*lexorder)[0] != varidx )
3271 continue;
3272
3273 /* mark all variables that have been used in strong SBCs */
3274 for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
3275 {
3276 assert( 0 <= graphcomponents[k] && graphcomponents[k] < propdata->npermvars );
3277
3278 usedvars[graphcomponents[k]] = TRUE;
3279 }
3280
3281 SCIP_CALL( SCIPcomputeOrbitVar(scip, propdata->npermvars, propdata->perms,
3282 propdata->permstrans, propdata->components, propdata->componentbegins,
3283 usedvars, varfound, varidx, symgrpcompidx,
3284 orbit[activeorb], &orbitsize[activeorb]) );
3285
3286 assert( orbit[activeorb][0] == varidx );
3287
3288 if ( orbitsize[activeorb] > orbitsize[1 - activeorb] ) /*lint !e514*/
3289 {
3290 /* if the new orbit is larger then the old largest one, flip activeorb */
3291 activeorb = 1 - activeorb;
3292 chosencolor = j;
3293 }
3294
3295 /* reset array */
3296 for (k = graphcompbegins[graphcomp]; k < graphcompbegins[graphcomp+1]; ++k)
3297 usedvars[graphcomponents[k]] = FALSE;
3298 }
3299
3300 /* check if we have found at least one non-empty orbit */
3301 if ( chosencolor > -1 )
3302 {
3303 /* flip activeorb again to avoid confusion, it is then at the largest orbit */
3304 activeorb = 1 - activeorb;
3305
3306 assert( orbit[activeorb][0] == firstvaridxpercolor[chosencolor] );
3307 vars[0] = propdata->permvars[orbit[activeorb][0]];
3308
3309 assert( chosencolor > -1 );
3310 SCIPdebugMsg(scip, " adding %d weak sbcs for enclosing orbit of color %d.\n", orbitsize[activeorb]-1, chosencolor);
3311
3312 *naddedconss = orbitsize[activeorb] - 1;
3313
3314 /* add weak SBCs for rest of enclosing orbit */
3315 for (j = 1; j < orbitsize[activeorb]; ++j)
3316 {
3317 SCIP_CONS* cons;
3318 char name[SCIP_MAXSTRLEN];
3319
3320 vars[1] = propdata->permvars[orbit[activeorb][j]];
3321
3322 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "weak_sbcs_%d_%s_%s", symgrpcompidx, SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3323
3324 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3325 SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3326 TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
3327
3328 SCIP_CALL( SCIPaddCons(scip, cons) );
3329
3330#ifdef SCIP_MORE_DEBUG
3331 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3332 SCIPinfoMessage(scip, NULL, "\n");
3333#endif
3334
3335 /* check whether we need to resize */
3337 &propdata->genlinconsssize, propdata->ngenlinconss + 1) );
3338 propdata->genlinconss[propdata->ngenlinconss] = cons;
3339 ++propdata->ngenlinconss;
3340 }
3341
3342 /* possibly store lexicographic order defined by weak SBCs */
3343 if ( storelexorder )
3344 {
3345 int varidx;
3346
3347 varidx = orbit[activeorb][0];
3348 assert(varidx >= 0);
3349
3350 if ( *maxnvarsorder == 0 )
3351 {
3352 *maxnvarsorder = 1;
3353 *nvarsorder = 0;
3354
3355 SCIP_CALL( SCIPallocBlockMemoryArray(scip, lexorder, *maxnvarsorder) );
3356 (*lexorder)[(*nvarsorder)++] = varidx;
3357 }
3358 else
3359 {
3360 assert( *nvarsorder == *maxnvarsorder );
3361 assert( varsinlexorder != NULL );
3362 assert( lexorder != NULL );
3363 assert( *lexorder != NULL );
3364
3365 /* the leader of the weak inequalities has to be the first element in the lexicographic order */
3366 if ( varidx == (*lexorder)[0] )
3367 {
3368 /* lexorder is already ok!! */
3369 assert( SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
3370 }
3371 else
3372 {
3373 /* Then varidx must not be in the lexorder,
3374 * We must add it at the front of the array, and maintain the current order. */
3375 assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
3376
3377 ++(*maxnvarsorder);
3378 ++(*nvarsorder);
3379
3380 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, lexorder, *nvarsorder, *maxnvarsorder) );
3381
3382 /* Shift array by one position to the right */
3383 for (k = *maxnvarsorder - 1; k >= 1; --k)
3384 (*lexorder)[k] = (*lexorder)[k - 1];
3385
3386 (*lexorder)[0] = varidx;
3387 }
3388 }
3389 }
3390 }
3391 else
3392 SCIPdebugMsg(scip, " no further weak sbcs are valid\n");
3393
3394 SCIPfreeBufferArray(scip, &orbit[1]);
3395 SCIPfreeBufferArray(scip, &orbit[0]);
3396 if ( varsinlexorder != NULL )
3397 SCIPhashmapFree(&varsinlexorder);
3398 SCIPfreeBufferArray(scip, &varfound);
3399 SCIPfreeCleanBufferArray(scip, &usedvars);
3400
3401 return SCIP_OKAY;
3402}
3403
3404
3405/** temporarily adapt symmetry data to new variable order given by Schreier Sims */
3406static
3408 SCIP* scip, /**< SCIP instance */
3409 int** origperms, /**< permutation matrix w.r.t. original variable ordering */
3410 int** modifiedperms, /**< memory for permutation matrix w.r.t. new variable ordering */
3411 int nperms, /**< number of permutations */
3412 SCIP_VAR** origpermvars, /**< array of permutation vars w.r.t. original variable ordering */
3413 SCIP_VAR** modifiedpermvars, /**< memory for array of permutation vars w.r.t. new variable ordering */
3414 int npermvars, /**< length or modifiedpermvars array */
3415 int* leaders, /**< leaders of Schreier Sims constraints */
3416 int nleaders /**< number of leaders */
3417 )
3418{
3419 int* permvaridx;
3420 int* posinpermvar;
3421 int leader;
3422 int curposleader;
3423 int varidx;
3424 int lidx;
3425 int i;
3426 int l;
3427 int p;
3428
3429 assert( scip != NULL );
3430 assert( origperms != NULL );
3431 assert( modifiedperms != NULL );
3432 assert( nperms > 0 );
3433 assert( origpermvars != NULL );
3434 assert( modifiedpermvars != NULL );
3435 assert( npermvars > 0 );
3436 assert( leaders != NULL );
3437 assert( nleaders > 0 );
3438
3439 /* initialize map from position in lexicographic order to index of original permvar */
3440 SCIP_CALL( SCIPallocBufferArray(scip, &permvaridx, npermvars) );
3441 for (i = 0; i < npermvars; ++i)
3442 permvaridx[i] = i;
3443
3444 /* initialize map from permvaridx to its current position in the reordered permvars array */
3445 SCIP_CALL( SCIPallocBufferArray(scip, &posinpermvar, npermvars) );
3446 for (i = 0; i < npermvars; ++i)
3447 posinpermvar[i] = i;
3448
3449 /* Iterate over leaders and put the l-th leader to the l-th position of the lexicographic order.
3450 * We do this by swapping the l-th leader with the element at position l of the current permvars array. */
3451 for (l = 0; l < nleaders; ++l)
3452 {
3453 leader = leaders[l];
3454 curposleader = posinpermvar[leader];
3455 varidx = permvaridx[curposleader];
3456 lidx = permvaridx[l];
3457
3458 /* swap the permvar at position l with the l-th leader */
3459 permvaridx[curposleader] = lidx;
3460 permvaridx[l] = varidx;
3461
3462 /* update the position map */
3463 posinpermvar[lidx] = curposleader;
3464 posinpermvar[leader] = l;
3465 }
3466
3467 /* update the permvars array to new variable order */
3468 for (i = 0; i < npermvars; ++i)
3469 modifiedpermvars[i] = origpermvars[permvaridx[i]];
3470
3471 /* update the permutation to the new variable order */
3472 for (p = 0; p < nperms; ++p)
3473 {
3474 for (i = 0; i < npermvars; ++i)
3475 modifiedperms[p][i] = posinpermvar[origperms[p][permvaridx[i]]];
3476 }
3477
3478 SCIPfreeBufferArray(scip, &permvaridx);
3479 SCIPfreeBufferArray(scip, &posinpermvar);
3480
3481 return SCIP_OKAY;
3482}
3483
3484
3485/* returns the number of found orbitopes with at least three columns per graph component or 0
3486 * if the found orbitopes do not satisfy certain criteria for being used
3487 */
3488static
3490 SCIP_VAR** permvars, /**< array of variables affected by symmetry */
3491 int* graphcomponents, /**< array of graph components */
3492 int* graphcompbegins, /**< array indicating starting position of graph components */
3493 int* compcolorbegins, /**< array indicating starting positions of potential orbitopes */
3494 int ncompcolors, /**< number of components encoded in compcolorbegins */
3495 int symcompsize /**< size of symmetry component for that we detect suborbitopes */
3496 )
3497{
3498 SCIP_Bool oneorbitopecriterion = FALSE;
3499 SCIP_Bool multorbitopecriterion = FALSE;
3500 int norbitopes = 0;
3501 int j;
3502
3503 assert( graphcompbegins != NULL );
3504 assert( compcolorbegins != NULL );
3505 assert( ncompcolors >= 0 );
3506 assert( symcompsize > 0 );
3507
3508 for (j = 0; j < ncompcolors; ++j)
3509 {
3510 SCIP_VAR* firstvar;
3511 int largestcompsize = 0;
3512 int nbinrows= 0;
3513 int k;
3514
3515 /* skip trivial components */
3516 if ( graphcompbegins[compcolorbegins[j+1]] - graphcompbegins[compcolorbegins[j]] < 2 )
3517 continue;
3518
3519 /* check whether components of this color build an orbitope (with > 2 columns) */
3520 for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
3521 {
3522 int compsize;
3523
3524 compsize = graphcompbegins[k+1] - graphcompbegins[k];
3525
3526 /* the first component that we are looking at for this color */
3527 if ( largestcompsize < 1 )
3528 {
3529 if ( compsize < 3 )
3530 break;
3531
3532 largestcompsize = compsize;
3533 }
3534 else if ( compsize != largestcompsize )
3535 break;
3536
3537 firstvar = permvars[graphcomponents[graphcompbegins[k]]];
3538
3539 /* count number of binary orbits (comps) */
3540 if ( SCIPvarIsBinary(firstvar) )
3541 ++nbinrows;
3542 }
3543
3544 /* we have found an orbitope */
3545 if ( k == compcolorbegins[j+1] )
3546 {
3547 SCIP_Real threshold;
3548 int ncols;
3549
3550 ++norbitopes;
3551 ncols = graphcompbegins[compcolorbegins[j] + 1] - graphcompbegins[compcolorbegins[j]];
3552
3553 threshold = 0.7 * (SCIP_Real) symcompsize;
3554
3555 /* check whether criteria for adding orbitopes are satisfied */
3556 if ( nbinrows <= 2 * ncols || (nbinrows <= 8 * ncols && nbinrows < 100) )
3557 multorbitopecriterion = TRUE;
3558 else if ( nbinrows <= 3 * ncols || (SCIP_Real) nbinrows * ncols >= threshold )
3559 oneorbitopecriterion = TRUE;
3560 }
3561 }
3562
3563 if ( (norbitopes == 1 && oneorbitopecriterion) || (norbitopes >= 2 && multorbitopecriterion) )
3564 return norbitopes;
3565
3566 return 0;
3567}
3568
3569
3570/** checks whether subgroups of the components are symmetric groups and adds SBCs for them */
3571static
3573 SCIP* scip, /**< SCIP instance */
3574 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3575 int cidx /**< index of component which shall be handled */
3576 )
3577{
3578 int* genorder;
3579 int p;
3580#ifdef SCIP_DEBUG
3581 int norbitopes = 0;
3582 int nstrongsbcs = 0;
3583 int nweaksbcs = 0;
3584#endif
3585 int** modifiedperms;
3586 SCIP_VAR** modifiedpermvars;
3587 int* nvarsincomponent;
3588
3589 int* graphcomponents;
3590 int* graphcompbegins;
3591 int* compcolorbegins;
3592 int* chosencomppercolor = NULL;
3593 int* firstvaridxpercolor = NULL;
3594 int* usedperms;
3595 int usedpermssize;
3596 int ngraphcomponents;
3597 int ncompcolors;
3598 int ntwocycleperms;
3599 int npermsincomp;
3600 int nusedperms;
3601 int ntrivialcolors = 0;
3602 int j;
3603 int* lexorder = NULL;
3604 int nvarslexorder = 0;
3605 int maxnvarslexorder = 0;
3606 SCIP_Shortbool* permused;
3607 SCIP_Bool allpermsused = FALSE;
3608 SCIP_Bool handlednonbinarysymmetry = FALSE;
3609 int norbitopesincomp;
3610
3611 assert( scip != NULL );
3612 assert( propdata != NULL );
3613 assert( propdata->computedsymmetry );
3614 assert( propdata->nperms >= 0 );
3615 assert( 0 <= cidx && cidx < propdata->ncomponents );
3616 assert( propdata->components != NULL );
3617 assert( propdata->componentbegins != NULL );
3618
3619 /* exit if no symmetry is present or component is blocked */
3620 if ( propdata->nperms == 0 || propdata->componentblocked[cidx] )
3621 return SCIP_OKAY;
3622
3623 /* exit if instance is too large */
3624 if ( SCIPgetNConss(scip) > propdata->maxnconsssubgroup )
3625 return SCIP_OKAY;
3626
3627 assert( propdata->nperms > 0 );
3628 assert( propdata->perms != NULL );
3629 assert( propdata->npermvars > 0 );
3630 assert( propdata->permvars != NULL );
3631
3632 /* create array for permutation order */
3633 SCIP_CALL( SCIPallocBufferArray(scip, &genorder, propdata->nperms) );
3634
3635 /* create arrays for modified permutations in case we adapt the lexicographic order because of suborbitopes */
3636 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, propdata->nperms) );
3637 for (p = 0; p < propdata->nperms; ++p)
3638 {
3639 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[p], propdata->npermvars) );
3640 }
3641 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, propdata->npermvars) );
3642
3643 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarsincomponent, propdata->npermvars) );
3644 for (p = 0; p < propdata->npermvars; ++p)
3645 {
3646 if ( propdata->vartocomponent[p] >= 0 )
3647 ++nvarsincomponent[propdata->vartocomponent[p]];
3648 }
3649
3650 SCIPdebugMsg(scip, "starting subgroup detection routine for component %d\n", cidx);
3651
3652 npermsincomp = propdata->componentbegins[cidx + 1] - propdata->componentbegins[cidx];
3653
3654 /* set the first npermsincomp entries of genorder; the others are not used for this component */
3655 for (j = 0; j < npermsincomp; ++j)
3656 genorder[j] = j;
3657
3658 SCIP_CALL( chooseOrderOfGenerators(scip, propdata, cidx, &genorder, &ntwocycleperms) );
3659
3660 assert( ntwocycleperms >= 0 );
3661 assert( ntwocycleperms <= npermsincomp );
3662
3663 SCIPdebugMsg(scip, "component %d has %d permutations consisting of 2-cycles\n", cidx, ntwocycleperms);
3664
3665#ifdef SCIP_MORE_DEBUG
3666 SCIP_Bool* used;
3667 int perm;
3668 int p;
3669
3670 SCIP_CALL( SCIPallocBufferArray(scip, &used, propdata->npermvars) );
3671 for (p = propdata->componentbegins[cidx]; p < propdata->componentbegins[cidx+1]; ++p)
3672 {
3673 int k;
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 * If not all variables in the matrix are binary, no constraints are added.
5897 * @todo Extend method to general variable types and dynamic variable orders.
5898 */
5899static
5901 SCIP* scip, /**< SCIP instance */
5902 SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5903 int id, /**< ID of double lex matrix (used for constraint names) */
5904 int** varidxmatrix, /**< matrix containing variable indices of double lex matrix */
5905 int nrows, /**< number of rows of matrix */
5906 int ncols, /**< number of columns of matrix */
5907 int* rowsbegin, /**< array indicating where a new row block begins */
5908 int* colsbegin, /**< array indicating where a new column block begins */
5909 int nrowblocks, /**< number of row blocks */
5910 int ncolblocks, /**< number of column blocks */
5911 SCIP_Bool* success /**< pointer to store whether orbitopes could be added successfully */
5912 )
5913{
5914 char name[SCIP_MAXSTRLEN];
5915 SCIP_VAR*** orbitopematrix;
5916 SCIP_CONS* cons;
5917 int maxdim;
5918 int i;
5919 int p;
5920 int j;
5921 int col;
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 *success = TRUE;
5935
5936 /* if not all variables in the matrix are binary, no constraints are added */
5937 for (i = 0; i < nrows; ++i)
5938 {
5939 for (j = 0; j < ncols; ++j)
5940 {
5941 if ( ! SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][j]]) )
5942 {
5943 *success = FALSE;
5944 return SCIP_OKAY;
5945 }
5946 }
5947 }
5948
5949 /* ensure that we can store orbitope constraints in propdata */
5951 &propdata->genorbconsssize, propdata->ngenorbconss + nrowblocks + ncolblocks) );
5952
5953 maxdim = MAX(nrows, ncols);
5954 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix, maxdim) );
5955 for (i = 0; i < maxdim; ++i)
5956 {
5957 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopematrix[i], maxdim) );
5958 }
5959
5960 /* add orbitopes corresponding to column blocks of doublelexmatrix */
5961 for (p = 0; p < ncolblocks; ++p)
5962 {
5963 for (i = 0; i < nrows; ++i)
5964 {
5965 for (col = 0, j = colsbegin[p]; j < colsbegin[p + 1]; ++j, ++col)
5966 {
5967 assert( SCIPvarIsBinary(propdata->permvars[varidxmatrix[i][j]]) );
5968 orbitopematrix[i][col] = propdata->permvars[varidxmatrix[i][j]];
5969 }
5970 }
5971
5972 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "doublelex_cols_%d_%d", id, p);
5973 SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopematrix, SCIP_ORBITOPETYPE_FULL,
5974 nrows, colsbegin[p + 1] - colsbegin[p], FALSE, TRUE, TRUE, FALSE,
5975 propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5976 SCIP_CALL( SCIPaddCons(scip, cons) );
5977 propdata->genorbconss[(propdata->ngenorbconss)++] = cons;
5978 /* do not release constraint here - will be done later */
5979 }
5980
5981 /* add orbitopes corresponding to row blocks of doublelexmatrix */
5982 for (p = 0; p < nrowblocks; ++p)
5983 {
5984 for (i = 0; i < ncols; ++i)
5985 {
5986 for (col = 0, j = rowsbegin[p]; j < rowsbegin[p + 1]; ++j, ++col)
5987 {
5988 assert( SCIPvarIsBinary(propdata->permvars[varidxmatrix[j][i]]) );
5989 orbitopematrix[i][col] = propdata->permvars[varidxmatrix[j][i]];
5990 }
5991 }
5992
5993 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "doublelex_rows_%d_%d", id, p);
5994 SCIP_CALL( SCIPcreateConsOrbitope(scip, &cons, name, orbitopematrix, SCIP_ORBITOPETYPE_FULL,
5995 ncols, rowsbegin[p + 1] - rowsbegin[p], FALSE, TRUE, TRUE, FALSE,
5996 propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5997 SCIP_CALL( SCIPaddCons(scip, cons) );
5998 propdata->genorbconss[(propdata->ngenorbconss)++] = cons;
5999 /* do not release constraint here - will be done later */
6000 }
6001
6002 for (i = maxdim - 1; i >= 0; --i)
6003 {
6004 SCIPfreeBufferArray(scip, &orbitopematrix[i]);
6005 }
6006 SCIPfreeBufferArray(scip, &orbitopematrix);
6007
6008 return SCIP_OKAY;
6009}
6010
6011/** tries to handle symmetries of single lex matrices (orbitopes) or double lex matrices */
6012static
6014 SCIP* scip, /**< SCIP instance */
6015 SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6016 SCIP_Bool detectsinglelex, /**< whether single lex matrices shall be detected */
6017 int cidx /**< index of component */
6018 )
6019{
6020 int** lexmatrix = NULL;
6021 int* lexrowsbegin = NULL;
6022 int* lexcolsbegin = NULL;
6023 int nrows;
6024 int ncols;
6025 int nrowmatrices;
6026 int ncolmatrices;
6027 int** perms;
6028 int compsize;
6029 int i;
6030 int p;
6031 SCIP_Bool isorbitope;
6032 SCIP_Bool success = FALSE;
6033
6034 assert( scip != NULL );
6035 assert( propdata != NULL );
6036 assert( 0 <= cidx && cidx < propdata->ncomponents );
6037
6038 /* exit if component is already blocked */
6039 if ( propdata->componentblocked[cidx] )
6040 return SCIP_OKAY;
6041
6042 /* exit if component has non-standard permutations */
6043 if ( propdata->componenthassignedperm[cidx] )
6044 return SCIP_OKAY;
6045
6046 /* exit if polyhedral methods are disabled when looking for double lex matrices */
6047 if ( !ISSYMRETOPESACTIVE(propdata->usesymmetry) && !detectsinglelex )
6048 return SCIP_OKAY;
6049
6050 /* get permutations of component */
6051 compsize = propdata->componentbegins[cidx + 1] - propdata->componentbegins[cidx];
6052 SCIP_CALL( SCIPallocBufferArray(scip, &perms, compsize) );
6053 for (p = 0, i = propdata->componentbegins[cidx]; i < propdata->componentbegins[cidx + 1]; ++i)
6054 perms[p++] = propdata->perms[propdata->components[i]];
6055
6056 SCIP_CALL( SCIPdetectSingleOrDoubleLexMatrices(scip, detectsinglelex, perms, compsize, propdata->npermvars,
6057 &success, &isorbitope, &lexmatrix, &nrows, &ncols,
6058 &lexrowsbegin, &lexcolsbegin, &nrowmatrices, &ncolmatrices) );
6059
6060 SCIPfreeBufferArray(scip, &perms);
6061
6062 /* possibly handle double lex matrix or orbitope */
6063 if ( success )
6064 {
6065 assert( lexmatrix != NULL );
6066 assert( nrows > 0 );
6067 assert( ncols > 0 );
6068
6069 if ( isorbitope )
6070 {
6071 SCIP_CALL( handleOrbitope(scip, propdata, cidx, lexmatrix, nrows, ncols, &success) );
6072 }
6073 else
6074 {
6075 SCIP_CALL( handleDoublelLexMatrix(scip, propdata, cidx, lexmatrix, nrows, ncols,
6076 lexrowsbegin, lexcolsbegin, nrowmatrices, ncolmatrices, &success) );
6077 }
6078
6079 /* free memory not needed anymore */
6080 for (i = nrows - 1; i >= 0; --i)
6081 {
6082 SCIPfreeBlockMemoryArray(scip, &lexmatrix[i], ncols);
6083 }
6084 SCIPfreeBlockMemoryArray(scip, &lexmatrix, nrows);
6085 if ( ncolmatrices > 0 )
6086 {
6087 SCIPfreeBlockMemoryArray(scip, &lexcolsbegin, ncolmatrices + 1);
6088 }
6089 if ( nrowmatrices > 0 )
6090 {
6091 SCIPfreeBlockMemoryArray(scip, &lexrowsbegin, nrowmatrices + 1);
6092 }
6093 }
6094
6095 if ( success )
6096 {
6097 propdata->componentblocked[cidx] |= SYM_HANDLETYPE_SYMBREAK;
6098 ++(propdata->ncompblocked);
6099 }
6100
6101 return SCIP_OKAY;
6102}
6103
6104/** tries to handle subgroups of component */
6105static
6107 SCIP* scip, /**< SCIP instance */
6108 SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6109 int cidx /**< index of component */
6110 )
6111{
6112 assert( scip != NULL );
6113 assert( propdata != NULL );
6114 assert( 0 <= cidx && cidx < propdata->ncomponents );
6115
6116 /* exit if component is already blocked */
6117 if ( propdata->componentblocked[cidx] )
6118 return SCIP_OKAY;
6119
6120 /* skip component if it has signed permutations */
6121 if ( propdata->componenthassignedperm[cidx] )
6122 return SCIP_OKAY;
6123
6124 /* only run if subgroups shall be detected and we can handle them */
6125 if ( !propdata->usedynamicprop && ISSYMRETOPESACTIVE(propdata->usesymmetry) && propdata->detectsubgroups
6126 && propdata->binvaraffected && propdata->ncompblocked < propdata->ncomponents )
6127 {
6128 /* @todo also implement a dynamic variant */
6129 SCIP_CALL( detectAndHandleSubgroups(scip, propdata, cidx) );
6130 }
6131
6132 return SCIP_OKAY;
6133}
6134
6135
6136/** tries to add symmetry handling methods to component of symmetry group
6137 *
6138 * For a component, we handle the symmetries as follows:
6139 * 1. If orbitope detection is enabled and the component is an orbitope: Apply one of the following:
6140 * 1.1. If dynamic symmetry handling methods are used:
6141 * 1.1.1. If the orbitope has a single row, add linear constraints x_1 >= x_2 ... >= x_n.
6142 * 1.1.2. If it has only two columns only, use lexicographic reduction; cf. symmetry_lexred.c
6143 * 1.1.3. If there are at least 3 binary rows with packing-partitioning constraints,
6144 * use a static packing-partitioning orbitopal fixing; cf. cons_orbitope.c
6145 * @todo make a dynamic adaptation for packing-partitioning orbitopes.
6146 * 1.1.4. If none of these standard cases apply, use dynamic orbitopal reduction; cf. symmetry_orbitopal.c
6147 * 1.2. If static symmetry handling methods are used: Use static orbitopal fixing (binary variables only);
6148 * cf. cons_orbitope.c
6149 * 2. If no dynamic symmetry handling methods are used, and if (orbitopal) subgroup detection is enabled,
6150 * detect those and add static orbitopes if necessary.
6151 * 3. Otherwise, if orbital reduction is enabled, or if dynamic methods are enabled and lexicographic reduction
6152 * propagations can be applied:
6153 * 3.1. If orbital reduction is enabled: Use orbital reduction.
6154 * 3.2. And, if dynamic methods and lexicographic for single permutations reduction are enabled, use that.
6155 * 4. Otherwise, if possible, use SST cuts.
6156 * 5. Otherwise, if possible, add symresacks (lexicographic reduction on binary variables using a static ordering).
6157 */
6158static
6160 SCIP* scip, /**< SCIP instance */
6161 SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
6162 int cidx, /**< index of component */
6163 int* nchgbds /**< pointer to store number of bound changes (or NULL)*/
6164 )
6165{
6166 SCIP_Bool useorbitalredorlexred;
6167
6168 assert( scip != NULL );
6169 assert( propdata != NULL );
6170 assert( propdata->ncomponents >= 0 );
6171 assert( 0 <= cidx && cidx < propdata->ncomponents );
6172
6173 /* ignore blocked components */
6174 if ( propdata->componentblocked[cidx] )
6175 return SCIP_OKAY;
6176
6177 /* detect if orbital reduction or lexicographic reduction shall be applied */
6178 useorbitalredorlexred = ISORBITALREDUCTIONACTIVE(propdata->usesymmetry)
6179 || ( ISSYMRETOPESACTIVE(propdata->usesymmetry) && propdata->usedynamicprop && propdata->addsymresacks );
6180
6181 /* try to apply symmetry handling methods */
6182 if ( propdata->detectdoublelex || propdata->detectorbitopes )
6183 {
6184 SCIP_Bool detectsinglelex;
6185
6186 detectsinglelex = propdata->detectdoublelex ? FALSE : TRUE;
6187
6188 SCIP_CALL( tryHandleSingleOrDoubleLexMatricesComponent(scip, propdata, detectsinglelex, cidx) );
6189 }
6190 SCIP_CALL( tryHandleSubgroups(scip, propdata, cidx) );
6191 if ( ISSSTACTIVE(propdata->usesymmetry) )
6192 {
6193 SCIP_CALL( addSSTConss(scip, propdata, useorbitalredorlexred, nchgbds, cidx) );
6194 }
6195 if ( useorbitalredorlexred )
6196 {
6197 SCIP_CALL( tryAddOrbitalRedLexRed(scip, propdata, cidx) );
6198 }
6199 SCIP_CALL( addSymresackConss(scip, propdata, cidx) );
6200
6201 return SCIP_OKAY;
6202}
6203
6204
6205/** determines problem symmetries and activates symmetry handling methods */
6206static
6208 SCIP* scip, /**< SCIP instance */
6209 SCIP_PROP* prop, /**< symmetry breaking propagator */
6210 int* nchgbds, /**< pointer to store number of bound changes (or NULL)*/
6211 SCIP_Bool* earlyterm /**< pointer to store whether we terminated early (or NULL) */
6212 )
6213{
6214 SCIP_PROPDATA* propdata;
6215 int c;
6216
6217 assert( prop != NULL );
6218 assert( scip != NULL );
6219
6220 if ( nchgbds != NULL )
6221 *nchgbds = 0;
6222 if ( earlyterm != NULL )
6223 *earlyterm = FALSE;
6224
6225 /* only allow symmetry handling methods if strong and weak dual reductions are permitted */
6227 {
6228 if ( earlyterm != NULL )
6229 *earlyterm = TRUE;
6230 return SCIP_OKAY;
6231 }
6232
6233 propdata = SCIPpropGetData(prop);
6234 assert( propdata != NULL );
6235 assert( propdata->usesymmetry >= 0 );
6236
6237 /* if no symmetries may be handled, stop here */
6238 if ( propdata->usesymmetry == 0 )
6239 {
6240 if ( earlyterm != NULL )
6241 *earlyterm = TRUE;
6242 return SCIP_OKAY;
6243 }
6244
6245 /* if symmetry handling methods have already been added */
6246 if ( propdata->triedaddsymmethods )
6247 {
6248 assert( propdata->nperms >= 0 );
6249
6250 if ( earlyterm != NULL )
6251 *earlyterm = TRUE;
6252
6253 return SCIP_OKAY;
6254 }
6255 assert( !propdata->triedaddsymmethods );
6256
6257 /* compute symmetries, if it is not computed before */
6258 if ( !propdata->computedsymmetry )
6259 {
6260 /* verify that no symmetry information is present */
6261 assert( checkSymmetryDataFree(propdata) );
6263 }
6264
6265 /* stop if symmetry computation failed, the reason should be given inside determineSymmetry */
6266 if ( !propdata->computedsymmetry )
6267 return SCIP_OKAY;
6268
6269 /* mark that symmetry handling methods are now tried to be added */
6270 propdata->triedaddsymmethods = TRUE;
6271 assert( propdata->nperms >= 0 );
6272
6273 /* no symmetries present, so nothing to be handled */
6274 if ( propdata->nperms == 0 )
6275 return SCIP_OKAY;
6276
6277 /* compute components of symmetry group */
6279 assert( propdata->ncomponents > 0 );
6280
6281 /* iterate over components and handle each by suitable symmetry handling methods */
6282 for (c = 0; c < propdata->ncomponents; ++c)
6283 {
6284 SCIP_CALL( tryAddSymmetryHandlingMethodsComponent(scip, propdata, c, nchgbds) );
6285
6286 if ( SCIPisStopped(scip) || propdata->ncompblocked >= propdata->ncomponents )
6287 break;
6288 }
6289
6290#ifdef SYMMETRY_STATISTICS
6292#endif
6293
6294 return SCIP_OKAY;
6295}
6296
6297
6298/** apply propagation methods for various symmetry handling constraints */
6299static
6301 SCIP* scip, /**< SCIP pointer */
6302 SCIP_PROPDATA* propdata, /**< propagator data */
6303 SCIP_Bool* infeasible, /**< pointer for storing feasibility state */
6304 int* nred, /**< pointer for number of reductions */
6305 SCIP_Bool* didrun /**< pointer for storing whether a propagator actually ran */
6306 )
6307{
6308 int nredlocal;
6309
6310 assert( scip != NULL );
6311 assert( propdata != NULL );
6312 assert( infeasible != NULL );
6313 assert( nred != NULL );
6314 assert( didrun != NULL );
6315
6316 *nred = 0;
6317 *infeasible = FALSE;
6318 *didrun = FALSE;
6319
6320 /* apply orbitopal reduction */
6321 SCIP_CALL( SCIPorbitopalReductionPropagate(scip, propdata->orbitopalreddata, infeasible, &nredlocal, didrun) );
6322 *nred += nredlocal;
6323 if ( *infeasible )
6324 return SCIP_OKAY;
6325
6326 /* apply orbital reduction */
6327 SCIP_CALL( SCIPorbitalReductionPropagate(scip, propdata->orbitalreddata, infeasible, &nredlocal, didrun) );
6328 *nred += nredlocal;
6329 if ( *infeasible )
6330 return SCIP_OKAY;
6331
6332 /* apply dynamic lexicographic reduction */
6333 SCIP_CALL( SCIPlexicographicReductionPropagate(scip, propdata->lexreddata, infeasible, &nredlocal, didrun) );
6334 *nred += nredlocal;
6335 if ( *infeasible )
6336 return SCIP_OKAY;
6337
6338 return SCIP_OKAY;
6339}
6340
6341
6342/*
6343 * Callback methods of propagator
6344 */
6345
6346/** presolving initialization method of propagator (called when presolving is about to begin) */
6347static
6348SCIP_DECL_PROPINITPRE(propInitpreSymmetry)
6349{ /*lint --e{715}*/
6350 SCIP_PROPDATA* propdata;
6351
6352 assert( scip != NULL );
6353 assert( prop != NULL );
6354
6355 propdata = SCIPpropGetData(prop);
6356 assert( propdata != NULL );
6357
6358 /* get nonlinear conshdlr for future checks on whether there are nonlinear constraints */
6359 propdata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
6360
6361 /* check whether we should run */
6362 if ( propdata->usesymmetry < 0 )
6363 {
6364 SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
6365 }
6366 assert( propdata->usesymmetry >= 0 );
6367
6368 /* terminate early if no symmetries will be handled */
6369 if ( propdata->usesymmetry == 0 )
6370 return SCIP_OKAY;
6371
6372 /* compute and handle symmetries if required */
6373 if ( propdata->symtiming == SYM_TIMING_BEFOREPRESOL )
6374 {
6375 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Symmetry computation before presolving:\n");
6376
6378 }
6379
6380 return SCIP_OKAY;
6381}
6382
6383
6384/** presolving deinitialization method of propagator (called after presolving has been finished) */
6385static
6386SCIP_DECL_PROPEXITPRE(propExitpreSymmetry)
6387{ /*lint --e{715}*/
6388 SCIP_PROPDATA* propdata;
6389
6390 assert( scip != NULL );
6391 assert( prop != NULL );
6392 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6393
6394 SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
6395
6396 propdata = SCIPpropGetData(prop);
6397 assert( propdata != NULL );
6398 assert( propdata->usesymmetry >= 0 );
6399
6400 /* terminate early if no symmetries will be handled */
6401 if ( propdata->usesymmetry == 0 )
6402 return SCIP_OKAY;
6403
6404 /* guarantee that symmetries are computed (and handled) if the solving process has not been interrupted
6405 * and even if presolving has been disabled */
6407 {
6409 }
6410
6411 return SCIP_OKAY;
6412}
6413
6414
6415/** solving process deinitialization method of propagator (called before branch and bound process data is freed) */
6416static
6417SCIP_DECL_PROPEXITSOL(propExitsolSymmetry)
6418{
6419 SCIP_PROPDATA* propdata;
6420
6421 assert( scip != NULL );
6422 assert( prop != NULL );
6423 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6424
6425 SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
6426
6427 propdata = SCIPpropGetData(prop);
6428 assert( propdata != NULL );
6429
6430 /* reset symmetry handling propagators that depend on the branch-and-bound tree structure */
6432
6433 return SCIP_OKAY;
6434} /*lint !e715*/
6435
6436
6437/** presolving method of propagator */
6438static
6439SCIP_DECL_PROPPRESOL(propPresolSymmetry)
6440{ /*lint --e{715}*/
6441 SCIP_PROPDATA* propdata;
6442 int i;
6443 int noldngenconns;
6444 int nchanges;
6445 SCIP_Bool earlyterm;
6446
6447 assert( scip != NULL );
6448 assert( prop != NULL );
6449 assert( result != NULL );
6451
6452 *result = SCIP_DIDNOTRUN;
6453
6454 propdata = SCIPpropGetData(prop);
6455 assert( propdata != NULL );
6456 assert( propdata->usesymmetry >= 0 );
6457
6458 /* terminate early if no symmetries will be handled */
6459 if ( propdata->usesymmetry == 0 )
6460 return SCIP_OKAY;
6461
6462 /* possibly create symmetry handling constraints */
6463
6464 /* skip presolving if we are not at the end and if symtiming == SYM_TIMING_DURINGPRESOL */
6465 assert( 0 <= propdata->symtiming && propdata->symtiming <= SYM_TIMING_AFTERPRESOL );
6466 if ( propdata->symtiming > SYM_TIMING_DURINGPRESOL && ! SCIPisPresolveFinished(scip) )
6467 return SCIP_OKAY;
6468
6469 /* possibly stop */
6470 if ( SCIPisStopped(scip) )
6471 return SCIP_OKAY;
6472
6473 noldngenconns = propdata->ngenorbconss + propdata->nsstconss + propdata->ngenlinconss;
6474
6475 SCIP_CALL( tryAddSymmetryHandlingMethods(scip, prop, &nchanges, &earlyterm) );
6476
6477 /* if we actually tried to add symmetry handling constraints */
6478 if ( ! earlyterm ) /*lint !e774*/
6479 {
6480 *result = SCIP_DIDNOTFIND;
6481
6482 if ( nchanges > 0 )
6483 {
6484 *result = SCIP_SUCCESS;
6485 *nchgbds += nchanges;
6486 }
6487
6488 /* if symmetry handling constraints have been added, presolve each */
6489 if ( propdata->ngenorbconss > 0 || propdata->ngenlinconss > 0 || propdata->nsstconss > 0 )
6490 {
6491 /* at this point, the symmetry group should be computed and nontrivial */
6492 assert( propdata->nperms > 0 );
6493 assert( propdata->triedaddsymmethods );
6494
6495 /* we have added at least one symmetry handling constraints, i.e., we were successful */
6496 *result = SCIP_SUCCESS;
6497
6498 *naddconss += propdata->ngenorbconss + propdata->ngenlinconss + propdata->nsstconss - noldngenconns;
6499 SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", *naddconss);
6500
6501 /* if constraints have been added, loop through generated constraints and presolve each */
6502 for (i = 0; i < propdata->ngenorbconss; ++i)
6503 {
6504 SCIP_CALL( SCIPpresolCons(scip, propdata->genorbconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6505 nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6506 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6507
6508 /* exit if cutoff or unboundedness has been detected */
6509 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6510 {
6511 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genorbconss[i]));
6512 return SCIP_OKAY;
6513 }
6514 }
6515
6516 for (i = 0; i < propdata->ngenlinconss; ++i)
6517 {
6518 SCIP_CALL( SCIPpresolCons(scip, propdata->genlinconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6519 nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6520 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6521
6522 /* exit if cutoff or unboundedness has been detected */
6523 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6524 {
6525 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genlinconss[i]));
6526 return SCIP_OKAY;
6527 }
6528 }
6529 SCIPdebugMsg(scip, "Presolved %d generated constraints.\n",
6530 propdata->ngenorbconss + propdata->ngenlinconss);
6531
6532 for (i = 0; i < propdata->nsstconss; ++i)
6533 {
6534 SCIP_CALL( SCIPpresolCons(scip, propdata->sstconss[i], nrounds, SCIP_PROPTIMING_ALWAYS, nnewfixedvars, nnewaggrvars, nnewchgvartypes,
6535 nnewchgbds, nnewholes, nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs, nnewchgsides, nfixedvars, naggrvars,
6536 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
6537
6538 /* exit if cutoff or unboundedness has been detected */
6539 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
6540 {
6541 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->sstconss[i]));
6542 return SCIP_OKAY;
6543 }
6544 }
6545 SCIPdebugMsg(scip, "Presolved %d generated Schreier Sims constraints.\n", propdata->nsstconss);
6546 }
6547 }
6548
6549 return SCIP_OKAY;
6550}
6551
6552
6553/** execution method of propagator */
6554static
6555SCIP_DECL_PROPEXEC(propExecSymmetry)
6556{ /*lint --e{715}*/
6557 SCIP_PROPDATA* propdata;
6558 SCIP_Bool infeasible;
6559 SCIP_Bool didrun;
6560 int nred;
6561
6562 assert( scip != NULL );
6563 assert( result != NULL );
6564
6565 *result = SCIP_DIDNOTRUN;
6566
6567 /* do not run if we are in the root or not yet solving */
6569 return SCIP_OKAY;
6570
6571 /* get data */
6572 propdata = SCIPpropGetData(prop);
6573 assert( propdata != NULL );
6574
6575 /* usesymmetry must be read in order for propdata to have initialized symmetry handling propagators */
6576 if ( propdata->usesymmetry < 0 )
6577 return SCIP_OKAY;
6578
6579 SCIP_CALL( propagateSymmetry(scip, propdata, &infeasible, &nred, &didrun) );
6580
6581 if ( infeasible )
6582 {
6583 *result = SCIP_CUTOFF;
6584 propdata->symfoundreduction = TRUE;
6585 return SCIP_OKAY;
6586 }
6587 if ( nred > 0 )
6588 {
6589 assert( didrun );
6590 *result = SCIP_REDUCEDDOM;
6591 propdata->symfoundreduction = TRUE;
6592 }
6593 else if ( didrun )
6594 *result = SCIP_DIDNOTFIND;
6595
6596 return SCIP_OKAY;
6597}
6598
6599
6600/** deinitialization method of propagator (called before transformed problem is freed) */
6601static
6602SCIP_DECL_PROPEXIT(propExitSymmetry)
6603{
6604 SCIP_PROPDATA* propdata;
6605
6606 assert( scip != NULL );
6607 assert( prop != NULL );
6608 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6609
6610 SCIPdebugMsg(scip, "Exiting propagator <%s>.\n", PROP_NAME);
6611
6612 propdata = SCIPpropGetData(prop);
6613 assert( propdata != NULL );
6614
6615 SCIP_CALL( freeSymmetryData(scip, propdata) );
6616
6617 /* reset basic data */
6618 propdata->usesymmetry = -1;
6619 propdata->triedaddsymmethods = FALSE;
6620 propdata->nsymresacks = 0;
6621 propdata->norbitopes = 0;
6622 propdata->lastrestart = 0;
6623 propdata->symfoundreduction = FALSE;
6624
6625 return SCIP_OKAY;
6626}
6627
6628
6629/** propagation conflict resolving method of propagator
6630 *
6631 * @todo Implement reverse propagation.
6632 *
6633 * Note that this is relatively difficult to obtain: One needs to include all bounds of variables that are responsible
6634 * for creating the orbit in which the variables that was propagated lies. This includes all variables that are moved
6635 * by the permutations which are involved in creating the orbit.
6636 */
6637static
6638SCIP_DECL_PROPRESPROP(propRespropSymmetry)
6639{ /*lint --e{715,818}*/
6640 assert( result != NULL );
6641
6642 *result = SCIP_DIDNOTFIND;
6643
6644 return SCIP_OKAY;
6645}
6646
6647
6648/** destructor of propagator to free user data (called when SCIP is exiting) */
6649static
6650SCIP_DECL_PROPFREE(propFreeSymmetry)
6651{ /*lint --e{715}*/
6652 SCIP_PROPDATA* propdata;
6653
6654 assert( scip != NULL );
6655 assert( prop != NULL );
6656 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
6657
6658 SCIPdebugMsg(scip, "Freeing symmetry propagator.\n");
6659
6660 propdata = SCIPpropGetData(prop);
6661 assert( propdata != NULL );
6662 assert( propdata->customsymopnodetypes != NULL );
6663
6664 SCIPhashmapFree(&propdata->customsymopnodetypes);
6665
6666 assert( propdata->lexreddata != NULL );
6667 SCIP_CALL( SCIPlexicographicReductionFree(scip, &propdata->lexreddata) );
6668
6669 assert( propdata->orbitalreddata != NULL );
6670 SCIP_CALL( SCIPorbitalReductionFree(scip, &propdata->orbitalreddata) );
6671
6672 assert( propdata->orbitopalreddata != NULL );
6673 SCIP_CALL( SCIPorbitopalReductionFree(scip, &propdata->orbitopalreddata) );
6674
6675 SCIPfreeBlockMemory(scip, &propdata);
6676
6677 return SCIP_OKAY;
6678}
6679
6680
6681/*
6682 * External methods
6683 */
6684
6685/** include symmetry propagator */
6687 SCIP* scip /**< SCIP data structure */
6688 )
6689{
6690 SCIP_TABLEDATA* tabledata;
6691 SCIP_PROPDATA* propdata = NULL;
6692 SCIP_PROP* prop = NULL;
6693 SCIP_DIALOG* rootdialog;
6694 SCIP_DIALOG* displaymenu;
6695 SCIP_DIALOG* dialog;
6696
6697 SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
6698 assert( propdata != NULL );
6699
6700 propdata->npermvars = 0;
6701 propdata->nbinpermvars = 0;
6702 propdata->permvars = NULL;
6703 propdata->nperms = -1;
6704 propdata->nmaxperms = 0;
6705 propdata->perms = NULL;
6706 propdata->permstrans = NULL;
6707 propdata->permvarmap = NULL;
6708 propdata->permvardomaincenter = NULL;
6709
6710 propdata->ncomponents = -1;
6711 propdata->ncompblocked = 0;
6712 propdata->components = NULL;
6713 propdata->componentbegins = NULL;
6714 propdata->vartocomponent = NULL;
6715 propdata->componentblocked = NULL;
6716 propdata->componenthassignedperm = NULL;
6717
6718 propdata->log10groupsize = -1.0;
6719 propdata->nmovedvars = -1;
6720 propdata->binvaraffected = FALSE;
6721 propdata->computedsymmetry = FALSE;
6722 propdata->conshdlr_nonlinear = NULL;
6723
6724 propdata->usesymmetry = -1;
6725 propdata->triedaddsymmethods = FALSE;
6726 propdata->genorbconss = NULL;
6727 propdata->genlinconss = NULL;
6728 propdata->ngenorbconss = 0;
6729 propdata->ngenlinconss = 0;
6730 propdata->genorbconsssize = 0;
6731 propdata->genlinconsssize = 0;
6732 propdata->nsymresacks = 0;
6733 propdata->norbitopes = 0;
6734 propdata->isnonlinvar = NULL;
6735
6736 propdata->nmovedpermvars = -1;
6737 propdata->nmovedbinpermvars = 0;
6738 propdata->nmovedintpermvars = 0;
6739 propdata->nmovedimplintpermvars = 0;
6740 propdata->nmovedcontpermvars = 0;
6741 propdata->lastrestart = 0;
6742 propdata->symfoundreduction = FALSE;
6743
6744 propdata->sstconss = NULL;
6745 propdata->nsstconss = 0;
6746 propdata->maxnsstconss = 0;
6747 propdata->leaders = NULL;
6748 propdata->nleaders = 0;
6749 propdata->maxnleaders = 0;
6750
6751 SCIP_CALL( SCIPhashmapCreate(&propdata->customsymopnodetypes, SCIPblkmem(scip), 10) );
6752 propdata->nopnodetypes = (int) SYM_CONSOPTYPE_LAST;
6753
6754 /* include constraint handler */
6756 PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING, propExecSymmetry, propdata) );
6757 assert( prop != NULL );
6758
6759 SCIP_CALL( SCIPsetPropFree(scip, prop, propFreeSymmetry) );
6760 SCIP_CALL( SCIPsetPropExit(scip, prop, propExitSymmetry) );
6761 SCIP_CALL( SCIPsetPropInitpre(scip, prop, propInitpreSymmetry) );
6762 SCIP_CALL( SCIPsetPropExitpre(scip, prop, propExitpreSymmetry) );
6763 SCIP_CALL( SCIPsetPropExitsol(scip, prop, propExitsolSymmetry) );
6764 SCIP_CALL( SCIPsetPropResprop(scip, prop, propRespropSymmetry) );
6766
6767 /* include table */
6768 SCIP_CALL( SCIPallocBlockMemory(scip, &tabledata) );
6769 tabledata->propdata = propdata;
6771 NULL, tableFreeSymmetry, NULL, NULL, NULL, NULL, tableOutputSymmetry,
6773
6774 /* include display dialog */
6775 rootdialog = SCIPgetRootDialog(scip);
6776 if( rootdialog != NULL )
6777 {
6778 if( SCIPdialogFindEntry(rootdialog, "display", &displaymenu) != 1 )
6779 {
6780 SCIPerrorMessage("display sub menu not found\n");
6781 return SCIP_PLUGINNOTFOUND;
6782 }
6783 assert( ! SCIPdialogHasEntry(displaymenu, "symmetries") );
6785 NULL, dialogExecDisplaySymmetry, NULL, NULL,
6786 "symmetry", "display generators of symmetry group in cycle notation, if available",
6787 FALSE, (SCIP_DIALOGDATA*)propdata) );
6788 SCIP_CALL( SCIPaddDialogEntry(scip, displaymenu, dialog) );
6789 SCIP_CALL( SCIPreleaseDialog(scip, &dialog) );
6790 }
6791
6792 /* add parameters for computing symmetry */
6794 "propagating/" PROP_NAME "/maxgenerators",
6795 "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
6796 &propdata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
6797
6799 "propagating/" PROP_NAME "/checksymmetries",
6800 "Should all symmetries be checked after computation?",
6801 &propdata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
6802
6804 "propagating/" PROP_NAME "/displaynorbitvars",
6805 "Should the number of variables affected by some symmetry be displayed?",
6806 &propdata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
6807
6809 "propagating/" PROP_NAME "/doubleequations",
6810 "Double equations to positive/negative version?",
6811 &propdata->doubleequations, TRUE, DEFAULT_DOUBLEEQUATIONS, NULL, NULL) );
6812
6813 /* add parameters for adding symmetry handling constraints */
6815 "propagating/" PROP_NAME "/conssaddlp",
6816 "Should the symmetry breaking constraints be added to the LP?",
6817 &propdata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
6818
6820 "propagating/" PROP_NAME "/addsymresacks",
6821 "Add inequalities for symresacks for each generator?",
6822 &propdata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
6823
6825 "propagating/" PROP_NAME "/detectdoublelex",
6826 "Should we check whether the components of the symmetry group can be handled by double lex matrices?",
6827 &propdata->detectdoublelex, TRUE, DEFAULT_DETECTDOUBLELEX, NULL, NULL) );
6828
6830 "propagating/" PROP_NAME "/detectorbitopes",
6831 "Should we check whether the components of the symmetry group can be handled by orbitopes?",
6832 &propdata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
6833
6835 "propagating/" PROP_NAME "/detectsubgroups",
6836 "Should we try to detect symmetric subgroups of the symmetry group on binary variables?",
6837 &propdata->detectsubgroups, TRUE, DEFAULT_DETECTSUBGROUPS, NULL, NULL) );
6838
6840 "propagating/" PROP_NAME "/addweaksbcs",
6841 "Should we add weak SBCs for enclosing orbit of symmetric subgroups?",
6842 &propdata->addweaksbcs, TRUE, DEFAULT_ADDWEAKSBCS, NULL, NULL) );
6843
6845 "propagating/" PROP_NAME "/addconsstiming",
6846 "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) [disabled parameter]",
6848
6849 /* add parameters for orbital reduction */
6851 "propagating/" PROP_NAME "/ofsymcomptiming",
6852 "timing of symmetry computation (0 = before presolving, 1 = during presolving, 2 = at first call) [disabled parameter]",
6854
6856 "propagating/" PROP_NAME "/performpresolving",
6857 "run orbital fixing during presolving? (disabled)",
6859
6861 "propagating/" PROP_NAME "/recomputerestart",
6862 "recompute symmetries after a restart has occurred? (0 = never)",
6863 &propdata->recomputerestart, TRUE, DEFAULT_RECOMPUTERESTART, 0, 0, NULL, NULL) );
6864
6866 "propagating/" PROP_NAME "/compresssymmetries",
6867 "Should non-affected variables be removed from permutation to save memory?",
6868 &propdata->compresssymmetries, TRUE, DEFAULT_COMPRESSSYMMETRIES, NULL, NULL) );
6869
6871 "propagating/" PROP_NAME "/compressthreshold",
6872 "Compression is used if percentage of moved vars is at most the threshold.",
6873 &propdata->compressthreshold, TRUE, DEFAULT_COMPRESSTHRESHOLD, 0.0, 1.0, NULL, NULL) );
6874
6876 "propagating/" PROP_NAME "/usecolumnsparsity",
6877 "Should the number of conss a variable is contained in be exploited in symmetry detection?",
6878 &propdata->usecolumnsparsity, TRUE, DEFAULT_USECOLUMNSPARSITY, NULL, NULL) );
6879
6881 "propagating/" PROP_NAME "/maxnconsssubgroup",
6882 "maximum number of constraints up to which subgroup structures are detected",
6883 &propdata->maxnconsssubgroup, TRUE, DEFAULT_MAXNCONSSSUBGROUP, 0, INT_MAX, NULL, NULL) );
6884
6886 "propagating/" PROP_NAME "/usedynamicprop",
6887 "whether dynamified symmetry handling constraint methods should be used",
6888 &propdata->usedynamicprop, TRUE, DEFAULT_USEDYNAMICPROP, NULL, NULL) );
6889
6891 "propagating/" PROP_NAME "/addstrongsbcs",
6892 "Should strong SBCs for enclosing orbit of symmetric subgroups be added if orbitopes are not used?",
6893 &propdata->addstrongsbcs, TRUE, DEFAULT_ADDSTRONGSBCS, NULL, NULL) );
6894
6896 "propagating/" PROP_NAME "/ssttiebreakrule",
6897 "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)",
6898 &propdata->ssttiebreakrule, TRUE, DEFAULT_SSTTIEBREAKRULE, 0, 2, NULL, NULL) );
6899
6901 "propagating/" PROP_NAME "/sstleaderrule",
6902 "rule to select the leader in an orbit (0: first var; 1: last var; 2: var having most conflicting vars in orbit)",
6903 &propdata->sstleaderrule, TRUE, DEFAULT_SSTLEADERRULE, 0, 2, NULL, NULL) );
6904
6906 "propagating/" PROP_NAME "/sstleadervartype",
6907 "bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);" \
6908 "if multiple types are allowed, take the one with most affected vars",
6909 &propdata->sstleadervartype, TRUE, DEFAULT_SSTLEADERVARTYPE, 1, 15, NULL, NULL) );
6910
6912 "propagating/" PROP_NAME "/addconflictcuts",
6913 "Should Schreier Sims constraints be added if we use a conflict based rule?",
6914 &propdata->addconflictcuts, TRUE, DEFAULT_ADDCONFLICTCUTS, NULL, NULL) );
6915
6917 "propagating/" PROP_NAME "/sstaddcuts",
6918 "Should Schreier Sims constraints be added?",
6919 &propdata->sstaddcuts, TRUE, DEFAULT_SSTADDCUTS, NULL, NULL) );
6920
6922 "propagating/" PROP_NAME "/sstmixedcomponents",
6923 "Should Schreier Sims constraints be added if a symmetry component contains variables of different types?",
6924 &propdata->sstmixedcomponents, TRUE, DEFAULT_SSTMIXEDCOMPONENTS, NULL, NULL) );
6925
6927 "propagating/" PROP_NAME "/symfixnonbinaryvars",
6928 "Whether all non-binary variables shall be not affected by symmetries if OF is active? (disabled)",
6930
6932 "propagating/" PROP_NAME "/enforcecomputesymmetry",
6933 "Is only symmetry on binary variables used?",
6934 &propdata->enforcecomputesymmetry, TRUE, DEFAULT_ENFORCECOMPUTESYMMETRY, NULL, NULL) );
6935
6937 "propagating/" PROP_NAME "/preferlessrows",
6938 "Shall orbitopes with less rows be preferred in detection?",
6939 &propdata->preferlessrows, TRUE, DEFAULT_PREFERLESSROWS, NULL, NULL) );
6940
6942 "propagating/" PROP_NAME "/symtype",
6943 "Type of symmetries that shall be computed?",
6944 &propdata->symtype, TRUE, DEFAULT_SYMTYPE, 0, 1, NULL, NULL) );
6945
6947 "propagating/" PROP_NAME "/symtiming",
6948 "timing of symmetry computation and handling (0 = before presolving, 1 = during presolving, 2 = after presolving)",
6949 &propdata->symtiming, TRUE, DEFAULT_SYMCOMPTIMING, 0, 2, NULL, NULL) );
6950
6951 /* for symmetry detection tool Nauty, we add further parameters to terminate it early */
6952 if ( strncmp(SYMsymmetryGetName(), "Nauty", 5) == 0 )
6953 {
6955 "propagating/" PROP_NAME "/nautymaxncells",
6956 "terminate symmetry detection using Nauty when number of cells in color refinment is at least this number",
6957 NULL, TRUE, DEFAULT_NAUTYMAXNCELLS, 0, INT_MAX, NULL, NULL) );
6958
6960 "propagating/" PROP_NAME "/nautymaxnnodes",
6961 "terminate symmetry detection using Nauty when its search tree has at least this number of nodes",
6962 NULL, TRUE, DEFAULT_NAUTYMAXNNODES, 0, INT_MAX, NULL, NULL) );
6963 }
6964
6965 /* possibly add description */
6966 if ( SYMcanComputeSymmetry() )
6967 {
6969 if ( SYMsymmetryGetAddName() != NULL )
6970 {
6972 }
6973 }
6974
6975 /* depending functionality */
6976 SCIP_CALL( SCIPincludeEventHdlrShadowTree(scip, &propdata->shadowtreeeventhdlr) );
6977 assert( propdata->shadowtreeeventhdlr != NULL );
6978
6979 SCIP_CALL( SCIPincludeOrbitopalReduction(scip, &propdata->orbitopalreddata) );
6980 assert( propdata->orbitopalreddata != NULL );
6981
6982 SCIP_CALL( SCIPincludeOrbitalReduction(scip, &propdata->orbitalreddata, propdata->shadowtreeeventhdlr) );
6983 assert( propdata->orbitalreddata != NULL );
6984
6985 SCIP_CALL( SCIPincludeLexicographicReduction(scip, &propdata->lexreddata, propdata->shadowtreeeventhdlr) );
6986 assert( propdata->lexreddata != NULL );
6987
6988 return SCIP_OKAY;
6989}
6990
6991
6992/** return currently available symmetry group information */
6994 SCIP* scip, /**< SCIP data structure */
6995 int* npermvars, /**< pointer to store number of variables for permutations */
6996 SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
6997 SCIP_HASHMAP** permvarmap, /**< pointer to store hash map of permvars (or NULL) */
6998 int* nperms, /**< pointer to store number of permutations */
6999 int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix (or NULL)*/
7000 int*** permstrans, /**< pointer to store permutation generators as (npermvars x nperms) matrix (or NULL)*/
7001 SCIP_Real* log10groupsize, /**< pointer to store log10 of group size (or NULL) */
7002 SCIP_Bool* binvaraffected, /**< pointer to store whether binary variables are affected (or NULL) */
7003 int** components, /**< pointer to store components of symmetry group (or NULL) */
7004 int** componentbegins, /**< pointer to store begin positions of components in components array (or NULL) */
7005 int** vartocomponent, /**< pointer to store assignment from variable to its component (or NULL) */
7006 int* ncomponents /**< pointer to store number of components (or NULL) */
7007 )
7008{
7009 SCIP_PROPDATA* propdata;
7010 SCIP_PROP* prop;
7011
7012 assert( scip != NULL );
7013 assert( npermvars != NULL );
7014 assert( permvars != NULL );
7015 assert( nperms != NULL );
7016 assert( perms != NULL || permstrans != NULL );
7017 assert( ncomponents != NULL || (components == NULL && componentbegins == NULL && vartocomponent == NULL) );
7018
7019 /* find symmetry propagator */
7020 prop = SCIPfindProp(scip, "symmetry");
7021 if ( prop == NULL )
7022 {
7023 SCIPerrorMessage("Could not find symmetry propagator.\n");
7024 return SCIP_PLUGINNOTFOUND;
7025 }
7026 assert( prop != NULL );
7027 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7028
7029 propdata = SCIPpropGetData(prop);
7030 assert( propdata != NULL );
7031
7032 *npermvars = propdata->npermvars;
7033 *permvars = propdata->permvars;
7034
7035 if ( permvarmap != NULL )
7036 {
7037 if ( propdata->nperms > 0 )
7038 {
7040 }
7041 *permvarmap = propdata->permvarmap;
7042 }
7043
7044 *nperms = propdata->nperms;
7045 if ( perms != NULL )
7046 {
7047 *perms = propdata->perms;
7048 assert( *perms != NULL || *nperms <= 0 );
7049 }
7050
7051 if ( permstrans != NULL )
7052 {
7053 if ( propdata->nperms > 0 )
7054 {
7056 }
7057 *permstrans = propdata->permstrans;
7058 assert( *permstrans != NULL || *nperms <= 0 );
7059 }
7060
7061 if ( log10groupsize != NULL )
7062 *log10groupsize = propdata->log10groupsize;
7063
7064 if ( binvaraffected != NULL )
7065 *binvaraffected = propdata->binvaraffected;
7066
7067 if ( components != NULL || componentbegins != NULL || vartocomponent != NULL || ncomponents != NULL )
7068 {
7069 if ( propdata->nperms > 0 )
7070 {
7072 }
7073 }
7074
7075 if ( components != NULL )
7076 *components = propdata->components;
7077
7078 if ( componentbegins != NULL )
7079 *componentbegins = propdata->componentbegins;
7080
7081 if ( vartocomponent )
7082 *vartocomponent = propdata->vartocomponent;
7083
7084 if ( ncomponents )
7085 *ncomponents = propdata->ncomponents;
7086
7087 return SCIP_OKAY;
7088}
7089
7090
7091/** return number of the symmetry group's generators */
7093 SCIP* scip /**< SCIP data structure */
7094 )
7095{
7096 SCIP_PROP* prop;
7097 SCIP_PROPDATA* propdata;
7098
7099 assert( scip != NULL );
7100
7101 prop = SCIPfindProp(scip, PROP_NAME);
7102 if ( prop == NULL )
7103 return 0;
7104
7105 propdata = SCIPpropGetData(prop);
7106 assert( propdata != NULL );
7107
7108 if ( propdata->nperms < 0 )
7109 return 0;
7110 else
7111 return propdata->nperms;
7112}
7113
7114/** creates new operator node type (used for symmetry detection) and returns its representation
7115 *
7116 * If the operator node already exists, the function terminates with SCIP_INVALIDDATA.
7117 */
7119 SCIP* scip, /**< SCIP pointer */
7120 const char* opnodename, /**< name of new operator node type */
7121 int* nodetype /**< pointer to store the node type */
7122 )
7123{
7124 SCIP_PROP* prop;
7125 SCIP_PROPDATA* propdata;
7126
7127 assert( scip != NULL );
7128 assert( nodetype != NULL );
7129
7130 prop = SCIPfindProp(scip, PROP_NAME);
7131 if ( prop == NULL )
7132 {
7133 SCIPerrorMessage("Cannot create operator node type, symmetry propagator has not been included.\n");
7134 return SCIP_PLUGINNOTFOUND;
7135 }
7136
7137 propdata = SCIPpropGetData(prop);
7138 assert( propdata != NULL );
7139 assert( propdata->customsymopnodetypes != NULL );
7140
7141 if ( SCIPhashmapExists(propdata->customsymopnodetypes, (void*) opnodename) )
7142 {
7143 SCIPerrorMessage("Cannot create operator node type %s, it already exists.\n", opnodename);
7144 return SCIP_INVALIDDATA;
7145 }
7146
7147 SCIP_CALL( SCIPhashmapInsertInt(propdata->customsymopnodetypes, (void*) opnodename, propdata->nopnodetypes) );
7148 *nodetype = propdata->nopnodetypes++;
7149
7150 return SCIP_OKAY;
7151}
7152
7153/** returns representation of an operator node type.
7154 *
7155 * If the node type does not already exist, a new node type will be created.
7156 */
7158 SCIP* scip, /**< SCIP pointer */
7159 const char* opnodename, /**< name of new operator node type */
7160 int* nodetype /**< pointer to store the node type */
7161 )
7162{
7163 SCIP_PROP* prop;
7164 SCIP_PROPDATA* propdata;
7165
7166 assert( scip != NULL );
7167
7168 prop = SCIPfindProp(scip, PROP_NAME);
7169 if ( prop == NULL )
7170 {
7171 SCIPerrorMessage("Cannot return operator node type, symmetry propagator has not been included.\n");
7172 return SCIP_PLUGINNOTFOUND;
7173 }
7174
7175 propdata = SCIPpropGetData(prop);
7176 assert( propdata != NULL );
7177 assert( propdata->customsymopnodetypes != NULL );
7178
7179 if ( ! SCIPhashmapExists(propdata->customsymopnodetypes, (void*) opnodename) )
7180 {
7181 SCIP_CALL( SCIPcreateSymOpNodeType(scip, opnodename, nodetype) );
7182 }
7183 else
7184 *nodetype = SCIPhashmapGetImageInt(propdata->customsymopnodetypes, (void*) opnodename);
7185
7186 return SCIP_OKAY;
7187}
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:262
#define SCIP_MAXSTRLEN
Definition: def.h:283
#define SCIP_Shortbool
Definition: def.h:99
#define SCIP_Bool
Definition: def.h:91
#define MIN(x, y)
Definition: def.h:238
#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:234
#define SCIP_CALL(x)
Definition: def.h:369
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:9583
SCIP_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9606
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9629
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:11395
void SCIPfreeDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset)
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition: misc.c:11298
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition: misc.c:11325
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:3110
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3283
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:3076
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3425
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition: misc.c:3194
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:5322
int SCIPconshdlrGetNConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4655
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4216
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:940
SCIP_Bool SCIPconshdlrSupportsPermsymDetection(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:5312
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4689
SCIP_CONS ** SCIPconshdlrGetConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4612
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:8253
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:8233
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:994
SCIP_RETCODE SCIPdialoghdlrAddHistory(SCIP_DIALOGHDLR *dialoghdlr, SCIP_DIALOG *dialog, const char *command, SCIP_Bool escapecommand)
Definition: dialog.c:725
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:1252
SCIP_DIALOG * SCIPgetRootDialog(SCIP *scip)
Definition: scip_dialog.c:157
int SCIPdialogFindEntry(SCIP_DIALOG *dialog, const char *entryname, SCIP_DIALOG **subdialog)
Definition: dialog.c:1027
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:3493
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:2103
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:17617
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:18162
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:17602
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:18106
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition: var.c:17786
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:17437
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1248
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition: var.c:17628
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:18152
int SCIPgetNCliques(SCIP *scip)
Definition: scip_var.c:7680
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:18096
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:5540
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:10878
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:59
@ SCIP_VERBLEVEL_HIGH
Definition: type_message.h:61
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