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