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