Scippy

SCIP

Solving Constraint Integer Programs

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