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