SCIP Doxygen Documentation
 
Loading...
Searching...
No Matches
prop_symmetry.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-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 *
33 * This propagator combines the following symmetry handling functionalities:
34 * - It allows to compute symmetries of the problem and to store this information in adequate form. The symmetry
35 * information can be accessed through external functions.
36 * - It allows to add the following symmetry breaking constraints:
37 * - symresack constraints, which separate minimal cover inequalities
38 * - orbitope constraints, if special symmetry group structures are detected
39 * - It allows to apply orbital fixing.
40 *
41 *
42 * @section SYMCOMP Symmetry Computation
43 *
44 * The following comments apply to symmetry computation.
45 *
46 * - The generic functionality of the compute_symmetry.h interface is used.
47 * - We treat implicit integer variables as if they were continuous/real variables. The reason is that there is currently
48 * no distinction between implicit integer and implicit binary. Moreover, currently implicit integer variables hurt
49 * our code more than continuous/real variables (we basically do not handle integral variables at all).
50 * - We do not copy symmetry information, since it is not clear how this information transfers. Moreover, copying
51 * symmetry might inhibit heuristics. But note that solving a sub-SCIP might then happen without symmetry information!
52 *
53 *
54 * @section SYMBREAK Symmetry Handling Constraints
55 *
56 * The following comments apply to adding symmetry handling constraints.
57 *
58 * - The code automatically detects whether symmetry substructures like symresacks or orbitopes are present and possibly
59 * adds the corresponding constraints.
60 * - If orbital fixing is active, only orbitopes are added (if present) and no symresacks.
61 * - We try to compute symmetry as late as possible and then add constraints based on this information.
62 * - Currently, we only allocate memory for pointers to symresack constraints for group generators. If further
63 * constraints are considered, we have to reallocate memory.
64 *
65 *
66 * @section OF Orbital Fixing
67 *
68 * Orbital fixing is implemented as introduced by@n
69 * F. Margot: Exploiting orbits in symmetric ILP. Math. Program., 98(1-3):3–21, 2003.
70 *
71 * The method computes orbits of variables with respect to the subgroup of the symmetry group that stabilizes the
72 * variables globally fixed or branched to 1. Then one can fix all variables in an orbit to 0 or 1 if one of the other
73 * variables in the orbit is fixed to 0 or 1, respectively. Different from Margot, the subgroup is obtained by filtering
74 * out generators that do not individually stabilize the variables branched to 1.
75 *
76 * @pre All variable fixings applied by other components are required to be strict, i.e., if one variable is fixed to
77 * a certain value v, all other variables in the same variable orbit can be fixed to v as well, c.f.@n
78 * F. Margot: Symmetry in integer linear programming. 50 Years of Integer Programming, 647-686, Springer 2010.
79 *
80 * To illustrate this, consider the example \f$\max\{x_1 + x_2 : x_1 + x_2 \leq 1, Ay \leq b,
81 * (x,y) \in \{0,1\}^{2 + n}\} \f$. Since \f$x_1\f$ and \f$x_2\f$ are independent from the remaining problem, the
82 * setppc constraint handler may fix \f$(x_1,x_2) = (1,0)\f$. However, since both variables are symmetric, this setting
83 * is not strict (if it was strict, both variables would have been set to the same value) and orbital fixing would
84 * declare this subsolution as infeasible (there exists an orbit of non-branching variables that are fixed to different
85 * values). To avoid this situation, we have to assume that all non-strict settings fix variables globally, i.e., we
86 * can take care of it by taking variables into account that have been globally fixed to 1. In fact, it suffices to
87 * consider one kind of global fixings since stabilizing one kind prevents an orbit to contain variables that have
88 * been fixed globally to different values.
89 *
90 * @pre All non-strict settings are global settings, since otherwise, we cannot (efficiently) take care of them.
91 *
92 * @pre No non-strict setting algorithm is interrupted early (e.g., by a time or iteration limit), since this may lead to
93 * wrong decisions by orbital fixing as well. For example, if cons_setppc in the above toy example starts by fixing
94 * \f$x_2 = 0\f$ and is interrupted afterwards, orbital fixing detects that the orbit \f$\{x_1, x_2\}\f$ contains
95 * one variable that is fixed to 0, and thus, it fixes \f$x_1\f$ to 0 as well. Thus, after these reductions, every
96 * feasible solution has objective 0 which is not optimal. This situation would not occur if the non-strict setting is
97 * complete, because then \f$x_1\f$ is globally fixed to 1, and thus, is stabilized in orbital fixing.
98 *
99 * Note that orbital fixing might lead to wrong results if it is called in repropagation of a node, because the path
100 * from the node to the root might have been changed. Thus, the stabilizers of global 1-fixing and 1-branchings of the
101 * initial propagation and repropagation might differ, which may cause conflicts. For this reason, orbital fixing cannot
102 * be called in repropagation.
103 *
104 * @note If, besides orbital fixing, also symmetry handling constraints shall be added, orbital fixing is only applied
105 * to symmetry components that are not handled by orbitope constraints.
106 *
107 *
108 * @section SST Cuts derived from the Schreier Sims table
109 *
110 * SST cuts have been introduced by@n
111 * D. Salvagnin: Symmetry Breaking Inequalities from the Schreier-Sims table. CPAIOR 2018 Proceedings, 521-529, 2018.
112 *
113 * These inequalities are computed as follows. Throughout these procedure a set of so-called leaders is maintained.
114 * 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.
115 * the symmetry group of the mixed-integer program. For each variable \f$x_j\f$ in the orbit of \f$x_i\f$, the
116 * inequality \f$x_i \geq x_j\f$ is a valid symmetry handling inequality, which can be added to the mixed-integer
117 * 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
118 * compute the pointwise stabilizer of the leader set. In the next step, select a new variable, compute its orbit
119 * w.r.t. the stabilizer group of the leaders, add the inequalities based on this orbit, and add the new leader
120 * to the set of leaders. This procedure is iterated until the pointwise stabilizer group of the leaders has become
121 * trivial.
122 *
123 * @todo Possibly turn off propagator in subtrees.
124 * @todo Check application of conflict resolution.
125 * @todo Check whether one should switch the role of 0 and 1
126 * @todo Implement stablizer computation?
127 * @todo Implement isomorphism pruning?
128 * @todo Implement particular preprocessing rules
129 * @todo Separate permuted cuts (first experiments not successful)
130 * @todo Allow the computation of local symmetries
131 * @todo Order rows of orbitopes (in particular packing/partitioning) w.r.t. cliques in conflict graph.
132 */
133/* #define SCIP_OUTPUT */
134/* #define SCIP_OUTPUT_COMPONENT */
135
136/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
137
138#include <scip/cons_linear.h>
139#include <scip/cons_knapsack.h>
140#include <scip/cons_varbound.h>
141#include <scip/cons_setppc.h>
142#include <scip/cons_and.h>
143#include <scip/cons_logicor.h>
144#include <scip/cons_or.h>
145#include <scip/cons_orbitope.h>
146#include <scip/cons_symresack.h>
147#include <scip/cons_xor.h>
148#include <scip/cons_linking.h>
150#include <scip/cons_nonlinear.h>
151#include <scip/pub_expr.h>
152#include <scip/misc.h>
154
155#include <scip/prop_symmetry.h>
157#include <scip/symmetry.h>
158
159#include <string.h>
160
161/* propagator properties */
162#define PROP_NAME "symmetry"
163#define PROP_DESC "propagator for handling symmetry"
164#define PROP_TIMING SCIP_PROPTIMING_BEFORELP /**< propagation timing mask */
165#define PROP_PRIORITY -1000000 /**< propagator priority */
166#define PROP_FREQ 1 /**< propagator frequency */
167#define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */
168
169#define PROP_PRESOL_PRIORITY -10000000 /**< priority of the presolving method (>= 0: before, < 0: after constraint handlers) */
170#define PROP_PRESOLTIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolving method (fast, medium, or exhaustive) */
171#define PROP_PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
172
173
174/* default parameter values for symmetry computation */
175#define DEFAULT_MAXGENERATORS 1500 /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
176#define DEFAULT_CHECKSYMMETRIES FALSE /**< Should all symmetries be checked after computation? */
177#define DEFAULT_DISPLAYNORBITVARS FALSE /**< Should the number of variables affected by some symmetry be displayed? */
178#define DEFAULT_USECOLUMNSPARSITY FALSE /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
179#define DEFAULT_DOUBLEEQUATIONS FALSE /**< Double equations to positive/negative version? */
180#define DEFAULT_COMPRESSSYMMETRIES TRUE /**< Should non-affected variables be removed from permutation to save memory? */
181#define DEFAULT_COMPRESSTHRESHOLD 0.5 /**< Compression is used if percentage of moved vars is at most the threshold. */
182#define DEFAULT_SYMFIXNONBINARYVARS FALSE /**< Whether all non-binary variables shall be not affected by symmetries if OF is active? */
183#define DEFAULT_ONLYBINARYSYMMETRY TRUE /**< Is only symmetry on binary variables used? */
184
185/* default parameters for linear symmetry constraints */
186#define DEFAULT_CONSSADDLP TRUE /**< Should the symmetry breaking constraints be added to the LP? */
187#define DEFAULT_ADDSYMRESACKS FALSE /**< Add inequalities for symresacks for each generator? */
188#define DEFAULT_DETECTORBITOPES TRUE /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
189#define DEFAULT_DETECTSUBGROUPS TRUE /**< Should we try to detect orbitopes in subgroups of the symmetry group? */
190#define DEFAULT_ADDWEAKSBCS TRUE /**< Should we add weak SBCs for enclosing orbit of symmetric subgroups? */
191#define DEFAULT_ADDSTRONGSBCS FALSE /**< Should we add strong SBCs for enclosing orbit of symmetric subgroups if orbitopes are not used? */
192#define DEFAULT_ADDCONSSTIMING 2 /**< timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) */
193#define DEFAULT_MAXNCONSSSUBGROUP 500000 /**< Maximum number of constraints up to which subgroup structures are detected */
194#define DEFAULT_USEDYNAMICPROP TRUE /**< whether dynamic propagation should be used for full orbitopes */
195#define DEFAULT_PREFERLESSROWS TRUE /**< Shall orbitopes with less rows be preferred in detection? */
196
197/* default parameters for orbital fixing */
198#define DEFAULT_OFSYMCOMPTIMING 2 /**< timing of symmetry computation for orbital fixing (0 = before presolving, 1 = during presolving, 2 = at first call) */
199#define DEFAULT_PERFORMPRESOLVING FALSE /**< Run orbital fixing during presolving? */
200#define DEFAULT_RECOMPUTERESTART 0 /**< Recompute symmetries after a restart has occurred? (0 = never) */
201
202/* default parameters for Schreier Sims constraints */
203#define DEFAULT_SSTTIEBREAKRULE 1 /**< index of tie break rule for selecting orbit for Schreier Sims constraints? */
204#define DEFAULT_SSTLEADERRULE 0 /**< index of rule for selecting leader variables for Schreier Sims constraints? */
205#define DEFAULT_SSTLEADERVARTYPE 14 /**< bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);
206 * if multiple types are allowed, take the one with most affected vars */
207#define DEFAULT_ADDCONFLICTCUTS TRUE /**< Should Schreier Sims constraints be added if we use a conflict based rule? */
208#define DEFAULT_SSTADDCUTS TRUE /**< Should Schreier Sims constraints be added? */
209#define DEFAULT_SSTMIXEDCOMPONENTS TRUE /**< Should Schreier Sims constraints be added if a symmetry component contains variables of different types? */
210
211/* event handler properties */
212#define EVENTHDLR_SYMMETRY_NAME "symmetry"
213#define EVENTHDLR_SYMMETRY_DESC "filter global variable fixing event handler for orbital fixing"
214
215/* output table properties */
216#define TABLE_NAME_ORBITALFIXING "orbitalfixing"
217#define TABLE_DESC_ORBITALFIXING "orbital fixing statistics"
218#define TABLE_POSITION_ORBITALFIXING 7001 /**< the position of the statistics table */
219#define TABLE_EARLIEST_ORBITALFIXING SCIP_STAGE_SOLVING /**< output of the statistics table is only printed from this stage onwards */
220
221
222/* other defines */
223#define MAXGENNUMERATOR 64000000 /**< determine maximal number of generators by dividing this number by the number of variables */
224#define SCIP_SPECIALVAL 1.12345678912345e+19 /**< special floating point value for handling zeros in bound disjunctions */
225#define COMPRESSNVARSLB 25000 /**< lower bound on the number of variables above which compression could be performed */
226
227/* macros for getting activeness of symmetry handling methods */
228#define ISSYMRETOPESACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_SYMBREAK) != 0)
229#define ISORBITALFIXINGACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_ORBITALFIXING) != 0)
230#define ISSSTACTIVE(x) (((unsigned) x & SYM_HANDLETYPE_SST) != 0)
231
232#define ISSSTBINACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_BINARY) != 0)
233#define ISSSTINTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_INTEGER) != 0)
234#define ISSSTIMPLINTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_IMPLINT) != 0)
235#define ISSSTCONTACTIVE(x) (((unsigned) x & SCIP_SSTTYPE_CONTINUOUS) != 0)
236
237
238/** propagator data */
239struct SCIP_PropData
240{
241 /* symmetry group information */
242 int npermvars; /**< number of variables for permutations */
243 int nbinpermvars; /**< number of binary variables for permuations */
244 SCIP_VAR** permvars; /**< variables on which permutations act */
245#ifndef NDEBUG
246 SCIP_Real* permvarsobj; /**< objective values of permuted variables (for debugging) */
247#endif
248 int nperms; /**< number of permutations */
249 int nmaxperms; /**< maximal number of permutations (needed for freeing storage) */
250 int** perms; /**< pointer to store permutation generators as (nperms x npermvars) matrix */
251 int** permstrans; /**< pointer to store transposed permutation generators as (npermvars x nperms) matrix */
252 SCIP_HASHMAP* permvarmap; /**< map of variables to indices in permvars array */
253 int nmovedpermvars; /**< number of variables moved by any permutation */
254 int nmovedbinpermvars; /**< number of binary variables moved by any permutation */
255 int nmovedintpermvars; /**< number of integer variables moved by any permutation */
256 int nmovedimplintpermvars; /**< number of implicitly integer variables moved by any permutation */
257 int nmovedcontpermvars; /**< number of continuous variables moved by any permutation */
258 SCIP_Shortbool* nonbinpermvarcaptured; /**< array to store which non-binary variables have been captured
259 * (only necessary for SST cuts) */
260
261 /* components of symmetry group */
262 int ncomponents; /**< number of components of symmetry group */
263 int ncompblocked; /**< number of components that have been blocked */
264 int* components; /**< array containing the indices of permutations sorted by components */
265 int* componentbegins; /**< array containing in i-th position the first position of
266 * component i in components array */
267 int* vartocomponent; /**< array containing for each permvar the index of the component it is
268 * contained in (-1 if not affected) */
269 unsigned* componentblocked; /**< array to store which symmetry methods have been applied to a component using
270 * the same bitset as for misc/usesymmetry */
271
272 /* further symmetry information */
273 int nmovedvars; /**< number of variables moved by some permutation */
274 SCIP_Real log10groupsize; /**< log10 of size of symmetry group */
275 SCIP_Bool binvaraffected; /**< whether binary variables are affected by some symmetry */
276
277 /* for symmetry computation */
278 int maxgenerators; /**< limit on the number of generators that should be produced within symmetry detection (0 = no limit) */
279 SCIP_Bool checksymmetries; /**< Should all symmetries be checked after computation? */
280 SCIP_Bool displaynorbitvars; /**< Whether the number of variables in non-trivial orbits shall be computed */
281 SCIP_Bool compresssymmetries; /**< Should non-affected variables be removed from permutation to save memory? */
282 SCIP_Real compressthreshold; /**< Compression is used if percentage of moved vars is at most the threshold. */
283 SCIP_Bool compressed; /**< Whether symmetry data has been compressed */
284 SCIP_Bool computedsymmetry; /**< Have we already tried to compute symmetries? */
285 int usesymmetry; /**< encoding of active symmetry handling methods (for debugging) */
286 SCIP_Bool usecolumnsparsity; /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
287 SCIP_Bool doubleequations; /**< Double equations to positive/negative version? */
288 SCIP_Bool symfixnonbinaryvars; /**< Whether all non-binary variables shall be not affected by symmetries if OF is active? */
289 SCIP_Bool onlybinarysymmetry; /**< Whether only symmetry on binary variables is used */
290
291 /* for symmetry constraints */
292 SCIP_Bool symconsenabled; /**< Should symmetry constraints be added? */
293 SCIP_Bool triedaddconss; /**< whether we already tried to add symmetry breaking constraints */
294 SCIP_Bool conssaddlp; /**< Should the symmetry breaking constraints be added to the LP? */
295 SCIP_Bool addsymresacks; /**< Add symresack constraints for each generator? */
296 int addconsstiming; /**< timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving) */
297 SCIP_CONS** genorbconss; /**< list of generated orbitope/orbisack/symresack constraints */
298 SCIP_CONS** genlinconss; /**< list of generated linear constraints */
299 int ngenorbconss; /**< number of generated orbitope/orbisack/symresack constraints */
300 int ngenlinconss; /**< number of generated linear constraints */
301 int genlinconsssize; /**< size of linear constraints array */
302 int nsymresacks; /**< number of symresack constraints */
303 SCIP_Bool detectorbitopes; /**< Should we check whether the components of the symmetry group can be handled by orbitopes? */
304 SCIP_Bool detectsubgroups; /**< Should we try to detect orbitopes in subgroups of the symmetry group? */
305 SCIP_Bool addweaksbcs; /**< Should we add weak SBCs for enclosing orbit of symmetric subgroups? */
306 SCIP_Bool addstrongsbcs; /**< Should we add strong SBCs for enclosing orbit of symmetric subgroups if orbitopes are not used? */
307 int norbitopes; /**< number of orbitope constraints */
308 SCIP_Bool* isnonlinvar; /**< array indicating whether variables apper non-linearly */
309 SCIP_CONSHDLR* conshdlr_nonlinear; /**< nonlinear constraint handler */
310 int maxnconsssubgroup; /**< maximum number of constraints up to which subgroup structures are detected */
311 SCIP_Bool usedynamicprop; /**< whether dynamic propagation should be used for full orbitopes */
312 SCIP_Bool preferlessrows; /**< Shall orbitopes with less rows be preferred in detection? */
313
314 /* data necessary for orbital fixing */
315 SCIP_Bool ofenabled; /**< Run orbital fixing? */
316 SCIP_EVENTHDLR* eventhdlr; /**< event handler for handling global variable fixings */
317 SCIP_Shortbool* bg0; /**< bitset to store variables globally fixed to 0 */
318 int* bg0list; /**< list of variables globally fixed to 0 */
319 int nbg0; /**< number of variables in bg0 and bg0list */
320 SCIP_Shortbool* bg1; /**< bitset to store variables globally fixed or branched to 1 */
321 int* bg1list; /**< list of variables globally fixed or branched to 1 */
322 int nbg1; /**< number of variables in bg1 and bg1list */
323 int* permvarsevents; /**< stores events caught for permvars */
324 SCIP_Shortbool* inactiveperms; /**< array to store whether permutations are inactive */
325 SCIP_Bool performpresolving; /**< Run orbital fixing during presolving? */
326 int recomputerestart; /**< Recompute symmetries after a restart has occured? (0 = never, 1 = always, 2 = if OF found reduction) */
327 int ofsymcomptiming; /**< timing of orbital fixing (0 = before presolving, 1 = during presolving, 2 = at first call) */
328 int lastrestart; /**< last restart for which symmetries have been computed */
329 int nfixedzero; /**< number of variables fixed to 0 */
330 int nfixedone; /**< number of variables fixed to 1 */
331 SCIP_Longint nodenumber; /**< number of node where propagation has been last applied */
332 SCIP_Bool offoundreduction; /**< whether orbital fixing has found a reduction since the last time computing symmetries */
333
334 /* data necessary for Schreier Sims constraints */
335 SCIP_Bool sstenabled; /**< Use Schreier Sims constraints? */
336 SCIP_CONS** sstconss; /**< list of generated schreier sims conss */
337 int nsstconss; /**< number of generated schreier sims conss */
338 int maxnsstconss; /**< maximum number of conss in sstconss */
339 int sstleaderrule; /**< rule to select leader */
340 int ssttiebreakrule; /**< tie break rule for leader selection */
341 int sstleadervartype; /**< bitset encoding which variable types can be leaders;
342 * if multiple types are allowed, take the one with most affected vars */
343 int* leaders; /**< index of orbit leaders in permvars */
344 int nleaders; /**< number of orbit leaders in leaders array */
345 int maxnleaders; /**< maximum number of leaders in leaders array */
346 SCIP_Bool addconflictcuts; /**< Should Schreier Sims constraints be added if we use a conflict based rule? */
347 SCIP_Bool sstaddcuts; /**< Should Schreier Sims constraints be added? */
348 SCIP_Bool sstmixedcomponents; /**< Should Schreier Sims constraints be added if a symmetry component contains variables of different types? */
349};
350
351/** node data of a given node in the conflict graph */
352struct SCIP_NodeData
353{
354 SCIP_VAR* var; /**< variable belonging to node */
355 int orbitidx; /**< orbit of variable w.r.t. current stabilizer subgroup
356 * or -1 if not affected by symmetry */
357 int nconflictinorbit; /**< number of variables the node's var is in conflict with */
358 int orbitsize; /**< size of the variable's orbit */
359 int posinorbit; /**< position of variable in its orbit */
360 SCIP_Bool active; /**< whether variable has not been fixed by Schreier Sims code */
361};
362typedef struct SCIP_NodeData SCIP_NODEDATA;
363
364
365/*
366 * Event handler callback methods
367 */
368
369/** exec the event handler for handling global variable bound changes (necessary for orbital fixing)
370 *
371 * Global variable fixings during the solving process might arise because parts of the tree are pruned or if certain
372 * preprocessing steps are performed that do not correspond to strict setting algorithms. Since these fixings might be
373 * caused by or be in conflict with orbital fixing, they can be in conflict with the symmetry handling decisions of
374 * orbital fixing in the part of the tree that is not pruned. Thus, we have to take global fixings into account when
375 * filtering out symmetries.
376 */
377static
379{
380 SCIP_PROPDATA* propdata;
381 SCIP_VAR* var;
382 int varidx;
383
384 assert( eventhdlr != NULL );
385 assert( eventdata != NULL );
387 assert( event != NULL );
388
389 propdata = (SCIP_PROPDATA*) eventdata;
390 assert( propdata != NULL );
391 assert( propdata->permvarmap != NULL );
392 assert( propdata->permstrans != NULL );
393 assert( propdata->nperms > 0 );
394 assert( propdata->permvars != NULL );
395 assert( propdata->npermvars > 0 );
396
397 /* get fixed variable */
399 assert( var != NULL );
401
402 if ( ! SCIPhashmapExists(propdata->permvarmap, (void*) var) )
403 {
404 SCIPerrorMessage("Invalid variable.\n");
405 SCIPABORT();
406 return SCIP_INVALIDDATA; /*lint !e527*/
407 }
408 varidx = SCIPhashmapGetImageInt(propdata->permvarmap, (void*) var);
409 assert( 0 <= varidx && varidx < propdata->npermvars );
410
412 {
415
416 SCIPdebugMsg(scip, "Mark variable <%s> as globally fixed to 0.\n", SCIPvarGetName(var));
417 assert( ! propdata->bg0[varidx] );
418 propdata->bg0[varidx] = TRUE;
419 propdata->bg0list[propdata->nbg0++] = varidx;
420 assert( propdata->nbg0 <= propdata->npermvars );
421 }
422
424 {
427
428 SCIPdebugMsg(scip, "Mark variable <%s> as globally fixed to 1.\n", SCIPvarGetName(var));
429 assert( ! propdata->bg1[varidx] );
430 propdata->bg1[varidx] = TRUE;
431 propdata->bg1list[propdata->nbg1++] = varidx;
432 assert( propdata->nbg1 <= propdata->npermvars );
433 }
434
435 return SCIP_OKAY;
436}
437
438
439
440
441/*
442 * Table callback methods
443 */
444
445/** table data */
446struct SCIP_TableData
447{
448 SCIP_PROPDATA* propdata; /** pass data of propagator for table output function */
449};
450
451
452/** output method of orbital fixing propagator statistics table to output file stream 'file' */
453static
455{
456 SCIP_TABLEDATA* tabledata;
457
458 assert( scip != NULL );
459 assert( table != NULL );
460
461 tabledata = SCIPtableGetData(table);
462 assert( tabledata != NULL );
463 assert( tabledata->propdata != NULL );
464
465 if ( tabledata->propdata->nperms > 0 )
466 {
467 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, "Orbital fixing :\n");
468 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " vars fixed to 0 :%11d\n", tabledata->propdata->nfixedzero);
469 SCIPverbMessage(scip, SCIP_VERBLEVEL_MINIMAL, file, " vars fixed to 1 :%11d\n", tabledata->propdata->nfixedone);
470 }
471
472 return SCIP_OKAY;
473}
474
475
476/** destructor of statistics table to free user data (called when SCIP is exiting) */
477static
479{
480 SCIP_TABLEDATA* tabledata;
481 tabledata = SCIPtableGetData(table);
482 assert( tabledata != NULL );
483
484 SCIPfreeBlockMemory(scip, &tabledata);
485
486 return SCIP_OKAY;
487}
488
489
490
491/*
492 * local data structures
493 */
494
495/** gets the key of the given element */
496static
498{ /*lint --e{715}*/
499 return elem;
500}
501
502/** returns TRUE iff both keys are equal
503 *
504 * Compare the types of two variables according to objective, lower and upper bound, variable type, and column sparsity.
505 */
506static
508{
509 SCIP* scip;
512
513 scip = (SCIP*) userptr;
514 k1 = (SYM_VARTYPE*) key1;
515 k2 = (SYM_VARTYPE*) key2;
516
517 /* first check objective coefficients */
518 if ( ! SCIPisEQ(scip, k1->obj, k2->obj) )
519 return FALSE;
520
521 /* if still undecided, take lower bound */
522 if ( ! SCIPisEQ(scip, k1->lb, k2->lb) )
523 return FALSE;
524
525 /* if still undecided, take upper bound */
526 if ( ! SCIPisEQ(scip, k1->ub, k2->ub) )
527 return FALSE;
528
529 /* if still undecided, take variable type */
530 if ( k1->type != k2->type )
531 return FALSE;
532
533 /* if still undecided, take number of conss var is contained in */
534 if ( k1->nconss != k2->nconss )
535 return FALSE;
536
537 return TRUE;
538}
539
540/** returns the hash value of the key */
541static
543{ /*lint --e{715}*/
544 SYM_VARTYPE* k;
545
546 k = (SYM_VARTYPE*) key;
547
548 return SCIPhashFour(SCIPrealHashCode(k->obj), SCIPrealHashCode(k->lb), SCIPrealHashCode((double) k->nconss), SCIPrealHashCode(k->ub));
549}
550
551/** data structure to store arrays used for sorting rhs types */
553{
554 SCIP_Real* vals; /**< array of values */
555 SYM_RHSSENSE* senses; /**< array of senses of rhs */
556 int nrhscoef; /**< size of arrays (for debugging) */
557};
559
560/** data structure to store arrays used for sorting colored component types */
562{
563 int* components; /**< array of components */
564 int* colors; /**< array of colors */
565};
567
568/** sorts rhs types - first by sense, then by value
569 *
570 * Due to numerical issues, we first sort by sense, then by value.
571 *
572 * result:
573 * < 0: ind1 comes before (is better than) ind2
574 * = 0: both indices have the same value
575 * > 0: ind2 comes after (is worse than) ind2
576 */
577static
579{
580 SYM_SORTRHSTYPE* data;
581 SCIP_Real diffvals;
582
583 data = (SYM_SORTRHSTYPE*) dataptr;
584 assert( 0 <= ind1 && ind1 < data->nrhscoef );
585 assert( 0 <= ind2 && ind2 < data->nrhscoef );
586
587 /* first sort by senses */
588 if ( data->senses[ind1] < data->senses[ind2] )
589 return -1;
590 else if ( data->senses[ind1] > data->senses[ind2] )
591 return 1;
592
593 /* senses are equal, use values */
594 diffvals = data->vals[ind1] - data->vals[ind2];
595
596 if ( diffvals < 0.0 )
597 return -1;
598 else if ( diffvals > 0.0 )
599 return 1;
600
601 return 0;
602}
603
604/** sorts matrix coefficients
605 *
606 * result:
607 * < 0: ind1 comes before (is better than) ind2
608 * = 0: both indices have the same value
609 * > 0: ind2 comes after (is worse than) ind2
610 */
611static
613{
614 SCIP_Real diffvals;
615 SCIP_Real* vals;
616
617 vals = (SCIP_Real*) dataptr;
618 diffvals = vals[ind1] - vals[ind2];
619
620 if ( diffvals < 0.0 )
621 return -1;
622 else if ( diffvals > 0.0 )
623 return 1;
624
625 return 0;
626}
627
628
629/** sorts variable indices according to their corresponding component in the graph
630 *
631 * Variables are sorted first by the color of their component and then by the component index.
632 *
633 * result:
634 * < 0: ind1 comes before (is better than) ind2
635 * = 0: both indices have the same value
636 * > 0: ind2 comes after (is worse than) ind2
637 */
638static
640{
642
643 data = (SYM_SORTGRAPHCOMPVARS*) dataptr;
644
645 if ( data->colors[ind1] < data->colors[ind2] )
646 return -1;
647 else if ( data->colors[ind1] > data->colors[ind2] )
648 return 1;
649
650 if ( data->components[ind1] < data->components[ind2] )
651 return -1;
652 if ( data->components[ind1] > data->components[ind2] )
653 return 1;
654
655 return 0;
656}
657
658
659
660/*
661 * Local methods
662 */
663
664#ifndef NDEBUG
665/** checks that symmetry data is all freed */
666static
668 SCIP_PROPDATA* propdata /**< propagator data */
669 )
670{
671 assert( propdata->permvarmap == NULL );
672 assert( propdata->permvarsevents == NULL );
673 assert( propdata->bg0list == NULL );
674 assert( propdata->bg0 == NULL );
675 assert( propdata->bg1list == NULL );
676 assert( propdata->bg1 == NULL );
677 assert( propdata->nbg0 == 0 );
678 assert( propdata->nbg1 == 0 );
679 assert( propdata->genorbconss == NULL );
680 assert( propdata->genlinconss == NULL );
681 assert( propdata->sstconss == NULL );
682 assert( propdata->leaders == NULL );
683
684 assert( propdata->permvars == NULL );
685 assert( propdata->permvarsobj == NULL );
686 assert( propdata->inactiveperms == NULL );
687 assert( propdata->perms == NULL );
688 assert( propdata->permstrans == NULL );
689 assert( propdata->nonbinpermvarcaptured == NULL );
690 assert( propdata->npermvars == 0 );
691 assert( propdata->nbinpermvars == 0 );
692 assert( propdata->nperms == -1 || propdata->nperms == 0 );
693 assert( propdata->nmaxperms == 0 );
694 assert( propdata->nmovedpermvars == -1 );
695 assert( propdata->nmovedbinpermvars == 0 );
696 assert( propdata->nmovedintpermvars == 0 );
697 assert( propdata->nmovedimplintpermvars == 0 );
698 assert( propdata->nmovedcontpermvars == 0 );
699 assert( propdata->nmovedvars == -1 );
700 assert( propdata->binvaraffected == FALSE );
701 assert( propdata->isnonlinvar == NULL );
702
703 assert( propdata->componentblocked == NULL );
704 assert( propdata->componentbegins == NULL );
705 assert( propdata->components == NULL );
706 assert( propdata->ncomponents == -1 );
707 assert( propdata->ncompblocked == 0 );
708
709 return TRUE;
710}
711#endif
712
713
714/** checks whether a variable has a type compatible with the leader vartype */
715static
717 SCIP_VAR* var, /**< variable to check */
718 int leadervartype /**< bit set encoding possible leader variable types */
719 )
720{
721 SCIP_VARTYPE vartype;
722 unsigned int vartypeencoding;
723
724 assert( var != NULL );
725 assert( leadervartype >= 0 );
726 assert( leadervartype <= 15 );
727
728 vartype = SCIPvarGetType(var);
729
730 if ( vartype == SCIP_VARTYPE_BINARY )
731 vartypeencoding = 1;
732 else if ( vartype == SCIP_VARTYPE_INTEGER )
733 vartypeencoding = 2;
734 else if ( vartype == SCIP_VARTYPE_IMPLINT )
735 vartypeencoding = 4;
736 else
737 vartypeencoding = 8;
738
739 return (SCIP_Bool) (vartypeencoding & (unsigned) leadervartype);
740}
741
742
743/** sets in propdata which symmetry handling methods are active */
744static
746 SCIP_PROPDATA* propdata /**< propagator data */
747 )
748{
749 assert( propdata != NULL );
750
751 if ( ISSYMRETOPESACTIVE(propdata->usesymmetry) )
752 propdata->symconsenabled = TRUE;
753 else
754 propdata->symconsenabled = FALSE;
755
756 if ( ISORBITALFIXINGACTIVE(propdata->usesymmetry) )
757 propdata->ofenabled = TRUE;
758 else
759 propdata->ofenabled = FALSE;
760
761 if ( ISSSTACTIVE(propdata->usesymmetry) )
762 propdata->sstenabled = TRUE;
763 else
764 propdata->sstenabled = FALSE;
765
766 return SCIP_OKAY;
767}
768
769
770/** frees symmetry data */
771static
773 SCIP* scip, /**< SCIP pointer */
774 SCIP_PROPDATA* propdata /**< propagator data */
775 )
776{
777 int i;
778
779 assert( scip != NULL );
780 assert( propdata != NULL );
781
782 if ( propdata->permvarmap != NULL )
783 {
784 SCIPhashmapFree(&propdata->permvarmap);
785 }
786
787 /* drop events */
788 if ( propdata->permvarsevents != NULL )
789 {
790 assert( propdata->permvars != NULL );
791 assert( propdata->npermvars > 0 );
792
793 for (i = 0; i < propdata->npermvars; ++i)
794 {
795 if ( SCIPvarGetType(propdata->permvars[i]) == SCIP_VARTYPE_BINARY )
796 {
797 /* If symmetry is computed before presolving, it might happen that some variables are turned into binary
798 * variables, for which no event has been catched. Since there currently is no way of checking whether a var
799 * event has been caught for a particular variable, we use the stored eventfilter positions. */
800 if ( propdata->permvarsevents[i] >= 0 )
801 {
803 propdata->eventhdlr, (SCIP_EVENTDATA*) propdata, propdata->permvarsevents[i]) );
804 }
805 }
806 }
807 SCIPfreeBlockMemoryArray(scip, &propdata->permvarsevents, propdata->npermvars);
808 }
809
810 /* release variables */
811 if ( propdata->nonbinpermvarcaptured != NULL )
812 {
813 int cnt;
814
815 /* memory should have been allocated only if the leader type is not binary */
816 assert( propdata->sstenabled && propdata->sstleadervartype != (int) SCIP_SSTTYPE_BINARY );
817
818 for (i = propdata->nbinpermvars, cnt = 0; i < propdata->npermvars; ++i, ++cnt)
819 {
820 /* release captured non-binary variables
821 * (cannot use isLeadervartypeCompatible(), because vartype may have changed in between)
822 */
823 if ( propdata->nonbinpermvarcaptured[cnt] )
824 {
825 SCIP_CALL( SCIPreleaseVar(scip, &propdata->permvars[i]) );
826 }
827 }
828 SCIPfreeBlockMemoryArray(scip, &propdata->nonbinpermvarcaptured, propdata->npermvars - propdata->nbinpermvars);
829 propdata->nonbinpermvarcaptured = NULL;
830 }
831
832 if ( propdata->binvaraffected )
833 {
834 for (i = 0; i < propdata->nbinpermvars; ++i)
835 {
836 SCIP_CALL( SCIPreleaseVar(scip, &propdata->permvars[i]) );
837 }
838 }
839
840 /* free lists for orbitopal fixing */
841 if ( propdata->bg0list != NULL )
842 {
843 assert( propdata->bg0 != NULL );
844 assert( propdata->bg1list != NULL );
845 assert( propdata->bg1 != NULL );
846
847 SCIPfreeBlockMemoryArray(scip, &propdata->bg0list, propdata->npermvars);
848 SCIPfreeBlockMemoryArray(scip, &propdata->bg0, propdata->npermvars);
849 SCIPfreeBlockMemoryArray(scip, &propdata->bg1list, propdata->npermvars);
850 SCIPfreeBlockMemoryArray(scip, &propdata->bg1, propdata->npermvars);
851
852 propdata->nbg0 = 0;
853 propdata->nbg1 = 0;
854 }
855
856 /* other data */
857 SCIPfreeBlockMemoryArrayNull(scip, &propdata->inactiveperms, propdata->nperms);
858
859 /* free permstrans matrix*/
860 if ( propdata->permstrans != NULL )
861 {
862 assert( propdata->nperms > 0 );
863 assert( propdata->permvars != NULL );
864 assert( propdata->npermvars > 0 );
865 assert( propdata->nmaxperms > 0 );
866
867 for (i = 0; i < propdata->npermvars; ++i)
868 {
869 SCIPfreeBlockMemoryArray(scip, &propdata->permstrans[i], propdata->nmaxperms);
870 }
871 SCIPfreeBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars);
872 }
873
874 /* free data of added orbitope/orbisack/symresack constraints */
875 if ( propdata->genorbconss != NULL )
876 {
877 assert( propdata->ngenorbconss + propdata->ngenlinconss > 0
878 || (ISORBITALFIXINGACTIVE(propdata->usesymmetry) && propdata->norbitopes == 0) );
879
880 /* release constraints */
881 for (i = 0; i < propdata->ngenorbconss; ++i)
882 {
883 assert( propdata->genorbconss[i] != NULL );
884 SCIP_CALL( SCIPreleaseCons(scip, &propdata->genorbconss[i]) );
885 }
886
887 /* free pointers to symmetry group and binary variables */
888 SCIPfreeBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms);
889 propdata->ngenorbconss = 0;
890 }
891
892 /* free data of added constraints */
893 if ( propdata->genlinconss != NULL )
894 {
895 /* release constraints */
896 for (i = 0; i < propdata->ngenlinconss; ++i)
897 {
898 assert( propdata->genlinconss[i] != NULL );
899 SCIP_CALL( SCIPreleaseCons(scip, &propdata->genlinconss[i]) );
900 }
901
902 /* free pointers to symmetry group and binary variables */
903 SCIPfreeBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize);
904 propdata->ngenlinconss = 0;
905 propdata->genlinconsssize = 0;
906 }
907
908 if ( propdata->sstconss != NULL )
909 {
910 assert( propdata->nsstconss > 0 );
911
912 /* release constraints */
913 for (i = 0; i < propdata->nsstconss; ++i)
914 {
915 assert( propdata->sstconss[i] != NULL );
916 SCIP_CALL( SCIPreleaseCons(scip, &propdata->sstconss[i]) );
917 }
918
919 /* free pointers to symmetry group and binary variables */
920 SCIPfreeBlockMemoryArray(scip, &propdata->sstconss, propdata->maxnsstconss);
921 propdata->sstconss = NULL;
922 propdata->nsstconss = 0;
923 propdata->maxnsstconss = 0;
924 }
925
926 if ( propdata->leaders != NULL )
927 {
928 assert( propdata->maxnleaders > 0 );
929
930 SCIPfreeBlockMemoryArray(scip, &propdata->leaders, propdata->maxnleaders);
931 propdata->maxnleaders = 0;
932 propdata->leaders = NULL;
933 propdata->nleaders = 0;
934 }
935
936 /* free components */
937 if ( propdata->ncomponents > 0 )
938 {
939 assert( propdata->componentblocked != NULL );
940 assert( propdata->vartocomponent != NULL );
941 assert( propdata->componentbegins != NULL );
942 assert( propdata->components != NULL );
943
944 SCIPfreeBlockMemoryArray(scip, &propdata->componentblocked, propdata->ncomponents);
945 SCIPfreeBlockMemoryArray(scip, &propdata->vartocomponent, propdata->npermvars);
946 SCIPfreeBlockMemoryArray(scip, &propdata->componentbegins, propdata->ncomponents + 1);
947 SCIPfreeBlockMemoryArray(scip, &propdata->components, propdata->nperms);
948
949 propdata->ncomponents = -1;
950 propdata->ncompblocked = 0;
951 }
952
953 /* free main symmetry data */
954 if ( propdata->nperms > 0 )
955 {
956 assert( propdata->permvars != NULL );
957
958 SCIPfreeBlockMemoryArray(scip, &propdata->permvars, propdata->npermvars);
959
960 /* if orbital fixing runs exclusively, propdata->perms was already freed in determineSymmetry() */
961 if ( propdata->perms != NULL )
962 {
963 for (i = 0; i < propdata->nperms; ++i)
964 {
965 SCIPfreeBlockMemoryArray(scip, &propdata->perms[i], propdata->npermvars);
966 }
967 SCIPfreeBlockMemoryArray(scip, &propdata->perms, propdata->nmaxperms);
968 }
969
970#ifndef NDEBUG
971 SCIPfreeBlockMemoryArrayNull(scip, &propdata->permvarsobj, propdata->npermvars);
972#endif
973
974 SCIPfreeBlockMemoryArrayNull(scip, &propdata->isnonlinvar, propdata->npermvars);
975
976 propdata->npermvars = 0;
977 propdata->nbinpermvars = 0;
978 propdata->nperms = -1;
979 propdata->nmaxperms = 0;
980 propdata->nmovedpermvars = -1;
981 propdata->nmovedbinpermvars = 0;
982 propdata->nmovedintpermvars = 0;
983 propdata->nmovedimplintpermvars = 0;
984 propdata->nmovedcontpermvars = 0;
985 propdata->nmovedvars = -1;
986 propdata->log10groupsize = -1.0;
987 propdata->binvaraffected = FALSE;
988 propdata->isnonlinvar = NULL;
989 }
990 propdata->nperms = -1;
991
992 assert( checkSymmetryDataFree(propdata) );
993
994 propdata->computedsymmetry = FALSE;
995 propdata->compressed = FALSE;
996
997 return SCIP_OKAY;
998}
999
1000
1001/** determines whether variable should be fixed by permutations */
1002static
1004 SYM_SPEC fixedtype, /**< bitset of variable types that should be fixed */
1005 SCIP_VAR* var /**< variable to be considered */
1006 )
1007{
1009 return TRUE;
1011 return TRUE;
1012 if ( (fixedtype & SYM_SPEC_REAL) &&
1014 return TRUE;
1015 return FALSE;
1016}
1017
1018
1019/** Transforms given variables, scalars, and constant to the corresponding active variables, scalars, and constant.
1020 *
1021 * @note @p constant needs to be initialized!
1022 */
1023static
1025 SCIP* scip, /**< SCIP data structure */
1026 SCIP_VAR*** vars, /**< pointer to vars array to get active variables for */
1027 SCIP_Real** scalars, /**< pointer to scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
1028 int* nvars, /**< pointer to number of variables and values in vars and vals array */
1029 SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
1030 SCIP_Bool transformed /**< transformed constraint? */
1031 )
1032{
1033 int requiredsize;
1034 int v;
1035
1036 assert( scip != NULL );
1037 assert( vars != NULL );
1038 assert( scalars != NULL );
1039 assert( *vars != NULL );
1040 assert( *scalars != NULL );
1041 assert( nvars != NULL );
1042 assert( constant != NULL );
1043
1044 if ( transformed )
1045 {
1047
1048 if ( requiredsize > *nvars )
1049 {
1052
1054 assert( requiredsize <= *nvars );
1055 }
1056 }
1057 else
1058 {
1059 for (v = 0; v < *nvars; ++v)
1060 {
1061 SCIP_CALL( SCIPvarGetOrigvarSum(&(*vars)[v], &(*scalars)[v], constant) );
1062 }
1063 }
1064 return SCIP_OKAY;
1065}
1066
1067
1068/** fills in matrix elements into coefficient arrays */
1069static
1071 SCIP* scip, /**< SCIP data structure */
1072 SCIP_Bool doubleequations, /**< Double equations to positive/negative version? */
1073 SCIP_VAR** linvars, /**< array of linear variables */
1074 SCIP_Real* linvals, /**< array of linear coefficients values (or NULL if all linear coefficient values are 1) */
1075 int nlinvars, /**< number of linear variables */
1076 SCIP_Real lhs, /**< left hand side */
1077 SCIP_Real rhs, /**< right hand side */
1078 SCIP_Bool istransformed, /**< whether the constraint is transformed */
1079 SYM_RHSSENSE rhssense, /**< identifier of constraint type */
1080 SYM_MATRIXDATA* matrixdata, /**< matrix data to be filled in */
1081 int* nconssforvar /**< pointer to array to store for each var the number of conss */
1082 )
1083{
1084 SCIP_VAR** vars;
1085 SCIP_Real* vals;
1086 SCIP_Real constant = 0.0;
1087 int nrhscoef;
1088 int nmatcoef;
1089 int nvars;
1090 int j;
1091
1092 assert( scip != NULL );
1093 assert( nlinvars == 0 || linvars != NULL );
1094 assert( lhs <= rhs );
1095
1096 /* do nothing if constraint is empty */
1097 if ( nlinvars == 0 )
1098 return SCIP_OKAY;
1099
1100 /* ignore redundant constraints */
1101 if ( SCIPisInfinity(scip, -lhs) && SCIPisInfinity(scip, rhs) )
1102 return SCIP_OKAY;
1103
1104 /* duplicate variable and value array */
1105 nvars = nlinvars;
1107 if ( linvals != NULL )
1108 {
1110 }
1111 else
1112 {
1114 for (j = 0; j < nvars; ++j)
1115 vals[j] = 1.0;
1116 }
1117 assert( vars != NULL );
1118 assert( vals != NULL );
1119
1120 /* get active variables */
1121 SCIP_CALL( getActiveVariables(scip, &vars, &vals, &nvars, &constant, istransformed) );
1122
1123 /* check whether constraint is empty after transformation to active variables */
1124 if ( nvars <= 0 )
1125 {
1126 SCIPfreeBufferArray(scip, &vals);
1128 return SCIP_OKAY;
1129 }
1130
1131 /* handle constant */
1132 if ( ! SCIPisInfinity(scip, -lhs) )
1133 lhs -= constant;
1134 if ( ! SCIPisInfinity(scip, rhs) )
1135 rhs -= constant;
1136
1137 /* check whether we have to resize; note that we have to add 2 * nvars since two inequalities may be added */
1138 if ( matrixdata->nmatcoef + 2 * nvars > matrixdata->nmaxmatcoef )
1139 {
1140 int newsize;
1141
1142 newsize = SCIPcalcMemGrowSize(scip, matrixdata->nmatcoef + 2 * nvars);
1143 assert( newsize >= 0 );
1145 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matrhsidx), matrixdata->nmaxmatcoef, newsize) );
1146 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(matrixdata->matvaridx), matrixdata->nmaxmatcoef, newsize) );
1148 SCIPdebugMsg(scip, "Resized matrix coefficients from %d to %d.\n", matrixdata->nmaxmatcoef, newsize);
1149 matrixdata->nmaxmatcoef = newsize;
1150 }
1151
1152 nrhscoef = matrixdata->nrhscoef;
1153 nmatcoef = matrixdata->nmatcoef;
1154
1155 /* check lhs/rhs */
1156 if ( SCIPisEQ(scip, lhs, rhs) )
1157 {
1158 SCIP_Bool poscoef = FALSE;
1159 SCIP_Bool negcoef = FALSE;
1160
1161 assert( ! SCIPisInfinity(scip, rhs) );
1162
1163 /* equality constraint */
1164 matrixdata->rhscoef[nrhscoef] = rhs;
1165
1166 /* if we deal with special constraints */
1167 if ( rhssense >= SYM_SENSE_XOR )
1168 matrixdata->rhssense[nrhscoef] = rhssense;
1169 else
1170 matrixdata->rhssense[nrhscoef] = SYM_SENSE_EQUATION;
1171 matrixdata->rhsidx[nrhscoef] = nrhscoef;
1172
1173 for (j = 0; j < nvars; ++j)
1174 {
1175 assert( nmatcoef < matrixdata->nmaxmatcoef );
1176
1177 matrixdata->matidx[nmatcoef] = nmatcoef;
1178 matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1179
1181
1182 if ( nconssforvar != NULL )
1184 matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1185 matrixdata->matcoef[nmatcoef++] = vals[j];
1186 if ( SCIPisPositive(scip, vals[j]) )
1187 poscoef = TRUE;
1188 else
1189 negcoef = TRUE;
1190 }
1191 nrhscoef++;
1192
1193 /* add negative of equation; increases chance to detect symmetry, but might increase time to compute symmetry. */
1194 if ( doubleequations && poscoef && negcoef )
1195 {
1196 for (j = 0; j < nvars; ++j)
1197 {
1198 assert( nmatcoef < matrixdata->nmaxmatcoef );
1200
1201 matrixdata->matidx[nmatcoef] = nmatcoef;
1202 matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1203 matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1204 matrixdata->matcoef[nmatcoef++] = -vals[j];
1205 }
1206 matrixdata->rhssense[nrhscoef] = SYM_SENSE_EQUATION;
1207 matrixdata->rhsidx[nrhscoef] = nrhscoef;
1208 matrixdata->rhscoef[nrhscoef++] = -rhs;
1209 }
1210 }
1211 else
1212 {
1213#ifndef NDEBUG
1214 if ( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 )
1215 {
1216 assert( ! SCIPisInfinity(scip, -lhs) );
1217 assert( ! SCIPisInfinity(scip, rhs) );
1218 }
1219#endif
1220
1221 if ( ! SCIPisInfinity(scip, -lhs) )
1222 {
1223 matrixdata->rhscoef[nrhscoef] = -lhs;
1224 if ( rhssense >= SYM_SENSE_XOR )
1225 {
1226 assert( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 );
1227 matrixdata->rhssense[nrhscoef] = rhssense;
1228 }
1229 else
1230 matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
1231
1232 matrixdata->rhsidx[nrhscoef] = nrhscoef;
1233
1234 for (j = 0; j < nvars; ++j)
1235 {
1236 assert( nmatcoef < matrixdata->nmaxmatcoef );
1237 matrixdata->matidx[nmatcoef] = nmatcoef;
1238 matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1239 matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1240
1242
1243 if ( nconssforvar != NULL )
1245
1246 matrixdata->matcoef[nmatcoef++] = -vals[j];
1247 }
1248 nrhscoef++;
1249 }
1250
1251 if ( ! SCIPisInfinity(scip, rhs) )
1252 {
1253 matrixdata->rhscoef[nrhscoef] = rhs;
1254 if ( rhssense >= SYM_SENSE_XOR )
1255 {
1256 assert( rhssense == SYM_SENSE_BOUNDIS_TYPE_2 );
1257 matrixdata->rhssense[nrhscoef] = rhssense;
1258 }
1259 else
1260 matrixdata->rhssense[nrhscoef] = SYM_SENSE_INEQUALITY;
1261
1262 matrixdata->rhsidx[nrhscoef] = nrhscoef;
1263
1264 for (j = 0; j < nvars; ++j)
1265 {
1266 assert( nmatcoef < matrixdata->nmaxmatcoef );
1267 matrixdata->matidx[nmatcoef] = nmatcoef;
1268 matrixdata->matrhsidx[nmatcoef] = nrhscoef;
1269
1271
1272 if ( nconssforvar != NULL )
1274
1275 matrixdata->matvaridx[nmatcoef] = SCIPvarGetProbindex(vars[j]);
1276 matrixdata->matcoef[nmatcoef++] = vals[j];
1277 }
1278 nrhscoef++;
1279 }
1280 }
1281 matrixdata->nrhscoef = nrhscoef;
1282 matrixdata->nmatcoef = nmatcoef;
1283
1284 SCIPfreeBufferArray(scip, &vals);
1286
1287 return SCIP_OKAY;
1288}
1289
1290
1291/** checks whether given permutations form a symmetry of a MIP
1292 *
1293 * We need the matrix and rhs in the original order in order to speed up the comparison process. The matrix is needed
1294 * in the right order to easily check rows. The rhs is used because of cache effects.
1295 */
1296static
1298 SCIP* scip, /**< SCIP data structure */
1299 SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1300 SYM_MATRIXDATA* matrixdata, /**< matrix data */
1301 int nperms, /**< number of permutations */
1302 int** perms /**< permutations */
1303 )
1304{
1305 SCIP_CONSHDLR* conshdlr;
1306 SCIP_HASHMAP* varmap;
1308 SCIP_Real* permrow = 0;
1309 SCIP_Bool success;
1310 int* rhsmatbeg = 0;
1311 int nconss;
1312 int noccuringvars;
1313 int oldrhs;
1314 int i;
1315 int j;
1316 int p;
1317
1318 SCIPdebugMsg(scip, "Checking whether symmetries are symmetries (generators: %d).\n", nperms);
1319
1320 /* set up dense row for permuted row */
1322
1323 /* set up map between rows and first entry in matcoef array */
1325 for (j = 0; j < matrixdata->nrhscoef; ++j)
1326 rhsmatbeg[j] = -1;
1327
1328 /* get info for non-linear part */
1329 conshdlr = SCIPfindConshdlr(scip, "nonlinear");
1330 nconss = conshdlr != NULL ? SCIPconshdlrGetNConss(conshdlr) : 0;
1331
1332 /* create hashmaps for variable permutation and constraints in non-linear part array for occuring variables */
1333 SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), matrixdata->npermvars) );
1335
1336 /* build map from rhs into matrix */
1337 oldrhs = -1;
1338 for (j = 0; j < matrixdata->nmatcoef; ++j)
1339 {
1340 int rhs;
1341
1342 rhs = matrixdata->matrhsidx[j];
1343 if ( rhs != oldrhs )
1344 {
1345 assert( 0 <= rhs && rhs < matrixdata->nrhscoef );
1346 rhsmatbeg[rhs] = j;
1347 oldrhs = rhs;
1348 }
1349 }
1350
1351 /* create row */
1352 for (j = 0; j < matrixdata->npermvars; ++j)
1353 permrow[j] = 0.0;
1354
1355 /* check all generators */
1356 for (p = 0; p < nperms; ++p)
1357 {
1358 int* P;
1359 int r1;
1360 int r2;
1361
1362 SCIPdebugMsg(scip, "Verifying automorphism group generator #%d for linear part ...\n", p);
1363 P = perms[p];
1364 assert( P != NULL );
1365
1366 for (j = 0; j < matrixdata->npermvars; ++j)
1367 {
1368 if ( SymmetryFixVar(fixedtype, matrixdata->permvars[j]) && P[j] != j )
1369 {
1370 SCIPdebugMsg(scip, "Permutation does not fix types %u, moving variable %d.\n", fixedtype, j);
1371 return SCIP_ERROR;
1372 }
1373 }
1374
1375 /*
1376 * linear part
1377 */
1378
1379 /* check all linear constraints == rhs */
1380 for (r1 = 0; r1 < matrixdata->nrhscoef; ++r1)
1381 {
1382 int npermuted = 0;
1383
1384 /* fill row into permrow (dense) */
1385 j = rhsmatbeg[r1];
1386 assert( 0 <= j && j < matrixdata->nmatcoef );
1387 assert( matrixdata->matrhsidx[j] == r1 ); /* note: row cannot be empty by construction */
1388
1389 /* loop through row */
1390 while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
1391 {
1392 int varidx;
1393
1394 assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
1395 varidx = P[matrixdata->matvaridx[j]];
1396 assert( 0 <= varidx && varidx < matrixdata->npermvars );
1397 if ( varidx != matrixdata->matvaridx[j] )
1398 ++npermuted;
1400 permrow[varidx] = matrixdata->matcoef[j];
1401 ++j;
1402 }
1403
1404 /* if row is not affected by permutation, we do not have to check it */
1405 if ( npermuted > 0 )
1406 {
1407 /* check other rows (sparse) */
1408 SCIP_Bool found = FALSE;
1409 for (r2 = 0; r2 < matrixdata->nrhscoef; ++r2)
1410 {
1411 /* a permutation must map constraints of the same type and respect rhs coefficients */
1412 if ( matrixdata->rhssense[r1] == matrixdata->rhssense[r2] && SCIPisEQ(scip, matrixdata->rhscoef[r1], matrixdata->rhscoef[r2]) )
1413 {
1414 j = rhsmatbeg[r2];
1415 assert( 0 <= j && j < matrixdata->nmatcoef );
1416 assert( matrixdata->matrhsidx[j] == r2 );
1417 assert( matrixdata->matvaridx[j] < matrixdata->npermvars );
1418
1419 /* loop through row r2 and check whether it is equal to permuted row r */
1420 while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r2 && SCIPisEQ(scip, permrow[matrixdata->matvaridx[j]], matrixdata->matcoef[j] ) )
1421 ++j;
1422
1423 /* check whether rows are completely equal */
1424 if ( j >= matrixdata->nmatcoef || matrixdata->matrhsidx[j] != r2 )
1425 {
1426 /* perm[p] is indeed a symmetry */
1427 found = TRUE;
1428 break;
1429 }
1430 }
1431 }
1432
1433 assert( found );
1434 if ( ! found ) /*lint !e774*/
1435 {
1436 SCIPerrorMessage("Found permutation that is not a symmetry.\n");
1437 return SCIP_ERROR;
1438 }
1439 }
1440
1441 /* reset permrow */
1442 j = rhsmatbeg[r1];
1443 while ( j < matrixdata->nmatcoef && matrixdata->matrhsidx[j] == r1 )
1444 {
1445 int varidx;
1446 varidx = P[matrixdata->matvaridx[j]];
1447 permrow[varidx] = 0.0;
1448 ++j;
1449 }
1450 }
1451
1452 /*
1453 * non-linear part
1454 */
1455
1456 SCIPdebugMsg(scip, "Verifying automorphism group generator #%d for non-linear part ...\n", p);
1457
1458 /* fill hashmap according to permutation */
1459 for (j = 0; j < matrixdata->npermvars; ++j)
1460 {
1461 SCIP_CALL( SCIPhashmapInsert(varmap, matrixdata->permvars[j], matrixdata->permvars[P[j]]) );
1462 }
1463
1464 /* check all non-linear constraints */
1465 for (i = 0; i < nconss; ++i)
1466 {
1468 SCIP_Bool permuted = FALSE;
1469
1470 cons1 = SCIPconshdlrGetConss(conshdlr)[i];
1471
1473 assert(success);
1475 assert(success);
1476
1477 /* count number of affected variables in this constraint */
1478 for (j = 0; j < noccuringvars && ! permuted; ++j)
1479 {
1480 int varidx;
1481
1483 assert( varidx >= 0 && varidx < matrixdata->npermvars );
1484
1485 if ( P[varidx] != varidx )
1486 permuted = TRUE;
1487 }
1488
1489 /* if constraint is not affected by permutation, we do not have to check it */
1490 if ( permuted )
1491 {
1494 SCIP_Bool found = FALSE;
1495 SCIP_Bool infeasible;
1496
1497 /* copy contraints but exchange variables according to hashmap */
1498 SCIP_CALL( SCIPgetConsCopy(scip, scip, cons1, &permutedcons, conshdlr, varmap, NULL, NULL,
1503 assert(success);
1505
1506 /* simplify permuted expr in order to guarantee sorted variables */
1509 assert( !infeasible );
1510
1511 /* look for a constraint with same lhs, rhs and expression */
1512 for (j = 0; j < nconss; ++j)
1513 {
1515
1516 cons2 = SCIPconshdlrGetConss(conshdlr)[j];
1517
1521 {
1522 found = TRUE;
1523 break;
1524 }
1525 }
1526
1527 /* release copied constraint and expression because simplify captures it */
1530
1531 assert( found );
1532 if( !found ) /*lint !e774*/
1533 {
1534 SCIPerrorMessage("Found permutation that is not a symmetry.\n");
1535 return SCIP_ERROR;
1536 }
1537 }
1538 }
1539
1540 /* reset varmap */
1542 }
1543
1544 SCIPhashmapFree(&varmap);
1548
1549 return SCIP_OKAY;
1550}
1551
1552
1553/** returns the number of active constraints that can be handled by symmetry */
1554static
1556 SCIP* scip, /**< SCIP instance */
1557 SCIP_CONSHDLR* conshdlr_nonlinear /**< nonlinear constraint handler, if included */
1558 )
1559{
1560 SCIP_CONSHDLR* conshdlr;
1561 int nhandleconss = 0;
1562
1563 assert( scip != NULL );
1564
1565 conshdlr = SCIPfindConshdlr(scip, "linear");
1567 conshdlr = SCIPfindConshdlr(scip, "linking");
1569 conshdlr = SCIPfindConshdlr(scip, "setppc");
1571 conshdlr = SCIPfindConshdlr(scip, "xor");
1573 conshdlr = SCIPfindConshdlr(scip, "and");
1575 conshdlr = SCIPfindConshdlr(scip, "or");
1577 conshdlr = SCIPfindConshdlr(scip, "logicor");
1579 conshdlr = SCIPfindConshdlr(scip, "knapsack");
1581 conshdlr = SCIPfindConshdlr(scip, "varbound");
1583 conshdlr = SCIPfindConshdlr(scip, "bounddisjunction");
1585 if( conshdlr_nonlinear != NULL )
1586 nhandleconss += SCIPconshdlrGetNActiveConss(conshdlr_nonlinear);
1587
1588 return nhandleconss;
1589}
1590
1591/** returns whether there are any active nonlinear constraints */
1592static
1594 SCIP_PROPDATA* propdata /**< propagator data */
1595 )
1596{
1597 assert(propdata != NULL);
1598
1599 return propdata->conshdlr_nonlinear != NULL && SCIPconshdlrGetNActiveConss(propdata->conshdlr_nonlinear) > 0;
1600}
1601
1602/** set symmetry data */
1603static
1605 SCIP* scip, /**< SCIP pointer */
1606 SCIP_VAR** vars, /**< vars present at time of symmetry computation */
1607 int nvars, /**< number of vars present at time of symmetry computation */
1608 int nbinvars, /**< number of binary vars present at time of symmetry computation */
1609 SCIP_VAR*** permvars, /**< pointer to permvars array */
1610 int* npermvars, /**< pointer to store number of permvars */
1611 int* nbinpermvars, /**< pointer to store number of binary permvars */
1612 int** perms, /**< permutations matrix (nperms x nvars) */
1613 int nperms, /**< number of permutations */
1614 int* nmovedvars, /**< pointer to store number of vars affected by symmetry (if usecompression) or NULL */
1615 SCIP_Bool* binvaraffected, /**< pointer to store whether a binary variable is affected by symmetry */
1616 SCIP_Bool usecompression, /**< whether symmetry data shall be compressed */
1617 SCIP_Real compressthreshold, /**< if percentage of moved vars is at most threshold, compression is done */
1618 SCIP_Bool* compressed /**< pointer to store whether compression has been performed */
1619 )
1620{
1621 int i;
1622 int p;
1623
1624 assert( scip != NULL );
1625 assert( vars != NULL );
1626 assert( nvars > 0 );
1627 assert( permvars != NULL );
1628 assert( npermvars != NULL );
1629 assert( nbinpermvars != NULL );
1630 assert( perms != NULL );
1631 assert( nperms > 0 );
1632 assert( binvaraffected != NULL );
1633 assert( SCIPisGE(scip, compressthreshold, 0.0) );
1634 assert( SCIPisLE(scip, compressthreshold, 1.0) );
1635 assert( compressed != NULL );
1636
1637 /* set default return values */
1638 *permvars = vars;
1639 *npermvars = nvars;
1640 *nbinpermvars = nbinvars;
1641 *binvaraffected = FALSE;
1642 *compressed = FALSE;
1643
1644 /* if we possibly perform compression */
1646 {
1647 SCIP_Real percentagemovedvars;
1648 int* labelmovedvars;
1649 int* labeltopermvaridx;
1650 int nbinvarsaffected = 0;
1651
1652 assert( nmovedvars != NULL );
1653
1654 *nmovedvars = 0;
1655
1656 /* detect number of moved vars and label moved vars */
1659 for (i = 0; i < nvars; ++i)
1660 {
1661 labelmovedvars[i] = -1;
1662
1663 for (p = 0; p < nperms; ++p)
1664 {
1665 if ( perms[p][i] != i )
1666 {
1667 labeltopermvaridx[*nmovedvars] = i;
1668 labelmovedvars[i] = (*nmovedvars)++;
1669
1672 break;
1673 }
1674 }
1675 }
1676
1677 if ( nbinvarsaffected > 0 )
1678 *binvaraffected = TRUE;
1679
1680 /* check whether compression should be performed */
1681 percentagemovedvars = (SCIP_Real) *nmovedvars / (SCIP_Real) nvars;
1682 if ( *nmovedvars > 0 && SCIPisLE(scip, percentagemovedvars, compressthreshold) )
1683 {
1684 /* remove variables from permutations that are not affected by any permutation */
1685 for (p = 0; p < nperms; ++p)
1686 {
1687 /* iterate over labels and adapt permutation */
1688 for (i = 0; i < *nmovedvars; ++i)
1689 {
1690 assert( i <= labeltopermvaridx[i] );
1691 perms[p][i] = labelmovedvars[perms[p][labeltopermvaridx[i]]];
1692 }
1693
1694 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &perms[p], nvars, *nmovedvars) );
1695 }
1696
1697 /* remove variables from permvars array that are not affected by any symmetry */
1698 SCIP_CALL( SCIPallocBlockMemoryArray(scip, permvars, *nmovedvars) );
1699 for (i = 0; i < *nmovedvars; ++i)
1700 {
1701 (*permvars)[i] = vars[labeltopermvaridx[i]];
1702 }
1703 *npermvars = *nmovedvars;
1704 *nbinpermvars = nbinvarsaffected;
1705 *compressed = TRUE;
1706
1708 }
1711 }
1712 else
1713 {
1714 /* detect whether binary variable is affected by symmetry and count number of binary permvars */
1715 for (i = 0; i < nbinvars; ++i)
1716 {
1717 for (p = 0; p < nperms && ! *binvaraffected; ++p)
1718 {
1719 if ( perms[p][i] != i )
1720 {
1722 *binvaraffected = TRUE;
1723 break;
1724 }
1725 }
1726 }
1727 }
1728
1729 return SCIP_OKAY;
1730}
1731
1732
1733/** computes symmetry group of a MIP */
1734static
1736 SCIP* scip, /**< SCIP pointer */
1737 SCIP_Bool doubleequations, /**< Double equations to positive/negative version? */
1738 SCIP_Bool compresssymmetries, /**< Should non-affected variables be removed from permutation to save memory? */
1739 SCIP_Real compressthreshold, /**< Compression is used if percentage of moved vars is at most the threshold. */
1740 int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
1741 SYM_SPEC fixedtype, /**< variable types that must be fixed by symmetries */
1742 SCIP_Bool local, /**< Use local variable bounds? */
1743 SCIP_Bool checksymmetries, /**< Should all symmetries be checked after computation? */
1744 SCIP_Bool usecolumnsparsity, /**< Should the number of conss a variable is contained in be exploited in symmetry detection? */
1745 SCIP_CONSHDLR* conshdlr_nonlinear, /**< Nonlinear constraint handler, if included */
1746 int* npermvars, /**< pointer to store number of variables for permutations */
1747 int* nbinpermvars, /**< pointer to store number of binary variables for permutations */
1748 SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
1749 int* nperms, /**< pointer to store number of permutations */
1750 int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
1751 int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
1752 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
1753 int* nmovedvars, /**< pointer to store number of moved vars */
1754 SCIP_Bool** isnonlinvar, /**< pointer to store which variables appear nonlinearly */
1755 SCIP_Bool* binvaraffected, /**< pointer to store wether a binary variable is affected by symmetry */
1756 SCIP_Bool* compressed, /**< pointer to store whether compression has been performed */
1757 SCIP_Real* symcodetime, /**< pointer to store the time for symmetry code */
1758 SCIP_Bool* success /**< pointer to store whether symmetry computation was successful */
1759 )
1760{
1761 SCIP_CONSHDLR* conshdlr;
1763 SYM_EXPRDATA exprdata;
1765 SCIP_VAR** consvars;
1766 SCIP_Real* consvals;
1767 SCIP_CONS** conss;
1768 SCIP_VAR** vars;
1770 SCIP_HASHSET* auxvars = NULL;
1774 SCIP_Real oldcoef = SCIP_INVALID;
1775 SCIP_Real val;
1776 int* nconssforvar = NULL;
1777 int nuniquevararray = 0;
1778 int nhandleconss;
1779 int nactiveconss;
1780 int nnlconss;
1781 int nconss;
1782 int nvars;
1783 int nbinvars;
1784 int nvarsorig;
1785 int nallvars;
1786 int c;
1787 int j;
1788
1789 assert( scip != NULL );
1790 assert( npermvars != NULL );
1791 assert( nbinpermvars != NULL );
1792 assert( permvars != NULL );
1793 assert( nperms != NULL );
1794 assert( nmaxperms != NULL );
1795 assert( perms != NULL );
1796 assert( log10groupsize != NULL );
1797 assert( binvaraffected != NULL );
1798 assert( compressed != NULL );
1799 assert( symcodetime != NULL );
1800 assert( success != NULL );
1801 assert( isnonlinvar != NULL );
1803
1804 /* init */
1805 *npermvars = 0;
1806 *nbinpermvars = 0;
1807 *permvars = NULL;
1808 *nperms = 0;
1809 *nmaxperms = 0;
1810 *perms = NULL;
1811 *log10groupsize = 0;
1812 *nmovedvars = -1;
1813 *binvaraffected = FALSE;
1814 *compressed = FALSE;
1815 *success = FALSE;
1816 *symcodetime = 0.0;
1817
1818 nconss = SCIPgetNConss(scip);
1821 nvarsorig = nvars;
1822
1823 /* exit if no constraints or no variables are available */
1824 if ( nconss == 0 || nvars == 0 )
1825 {
1826 *success = TRUE;
1827 return SCIP_OKAY;
1828 }
1829
1830 conss = SCIPgetConss(scip);
1831 assert( conss != NULL );
1832
1833 /* compute the number of active constraints */
1834 nactiveconss = SCIPgetNActiveConss(scip);
1835 nnlconss = conshdlr_nonlinear != NULL ? SCIPconshdlrGetNActiveConss(conshdlr_nonlinear) : 0;
1836
1837 /* exit if no active constraints are available */
1838 if ( nactiveconss == 0 )
1839 {
1840 *success = TRUE;
1841 return SCIP_OKAY;
1842 }
1843
1844 /* before we set up the matrix, check whether we can handle all constraints */
1845 nhandleconss = getNSymhandableConss(scip, conshdlr_nonlinear);
1846 assert( nhandleconss <= nactiveconss );
1847 if ( nhandleconss < nactiveconss )
1848 {
1849 /* In this case we found unkown constraints and we exit, since we cannot handle them. */
1850 *success = FALSE;
1851 *nperms = -1;
1852 return SCIP_OKAY;
1853 }
1854
1855 SCIPdebugMsg(scip, "Detecting %ssymmetry on %d variables and %d constraints.\n", local ? "local " : "", nvars, nactiveconss);
1856
1857 /* copy variables */
1859 assert( vars != NULL );
1860
1861 /* fill matrixdata */
1862
1863 /* use a staggered scheme for allocating space for non-zeros of constraint matrix since it can become large */
1864 if ( nvars <= 100000 )
1865 matrixdata.nmaxmatcoef = 100 * nvars;
1866 else if ( nvars <= 1000000 )
1867 matrixdata.nmaxmatcoef = 32 * nvars;
1868 else if ( nvars <= 16700000 )
1869 matrixdata.nmaxmatcoef = 16 * nvars;
1870 else
1871 matrixdata.nmaxmatcoef = INT_MAX / 10;
1872
1873 matrixdata.nmatcoef = 0;
1874 matrixdata.nrhscoef = 0;
1875 matrixdata.nuniquemat = 0;
1876 matrixdata.nuniquevars = 0;
1877 matrixdata.nuniquerhs = 0;
1878 matrixdata.npermvars = nvars;
1879 matrixdata.permvars = vars;
1880 matrixdata.permvarcolors = NULL;
1881 matrixdata.matcoefcolors = NULL;
1882 matrixdata.rhscoefcolors = NULL;
1883
1884 /* fill exprdata */
1885 exprdata.nuniqueoperators = 0;
1886 exprdata.nuniquecoefs = 0;
1887 exprdata.nuniqueconstants = 0;
1888
1889 /* prepare matrix data (use block memory, since this can become large) */
1892 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef) );
1893 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef) );
1894 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoef, 2 * nactiveconss) );
1895 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhssense, 2 * nactiveconss) );
1896 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhsidx, 2 * nactiveconss) );
1897
1898 /* prepare temporary constraint data (use block memory, since this can become large);
1899 * also allocate memory for fixed vars since some vars might have been deactivated meanwhile */
1900 nallvars = nvars + SCIPgetNFixedVars(scip);
1901 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consvars, nallvars) );
1903
1904 /* create hashset for auxvars and iterator for nonlinear constraints */
1905 if( nnlconss > 0 )
1906 {
1910 }
1911 else
1912 *isnonlinvar = NULL;
1913
1914 /* allocate memory for getting the number of constraints that contain a variable */
1915 if ( usecolumnsparsity )
1916 {
1918 }
1919
1920 /* loop through all constraints */
1921 for (c = 0; c < nconss; ++c)
1922 {
1923 const char* conshdlrname;
1924 SCIP_CONS* cons;
1925 SCIP_VAR** linvars;
1926 int nconsvars;
1927
1928 /* get constraint */
1929 cons = conss[c];
1930 assert( cons != NULL );
1931
1932 /* skip non-active constraints */
1933 if ( ! SCIPconsIsActive(cons) )
1934 continue;
1935
1936 /* Skip conflict constraints if we are late in the solving process */
1938 continue;
1939
1940 /* get constraint handler */
1941 conshdlr = SCIPconsGetHdlr(cons);
1942 assert( conshdlr != NULL );
1943
1945 assert( conshdlrname != NULL );
1946
1947 /* check type of constraint */
1948 if ( strcmp(conshdlrname, "linear") == 0 )
1949 {
1953 }
1954 else if ( strcmp(conshdlrname, "linking") == 0 )
1955 {
1957 SCIP_Real* curconsvals;
1958 int i;
1959
1960 /* get constraint variables and their coefficients */
1962 SCIP_CALL( SCIPgetBinvarsLinking(scip, cons, &curconsvars, &nconsvars) );
1963 /* SCIPgetBinVarsLinking returns the number of binary variables, but we also need the integer variable */
1964 nconsvars++;
1965
1966 /* copy vars and vals for binary variables */
1967 for (i = 0; i < nconsvars - 1; i++)
1968 {
1969 consvars[i] = curconsvars[i];
1971 }
1972
1973 /* set final entry of vars and vals to the linking variable and its coefficient, respectively */
1974 consvars[nconsvars - 1] = SCIPgetLinkvarLinking(scip, cons);
1975 consvals[nconsvars - 1] = -1.0;
1976
1977 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
1979 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, NULL, nconsvars - 1, 1.0, 1.0,
1981 }
1982 else if ( strcmp(conshdlrname, "setppc") == 0 )
1983 {
1984 linvars = SCIPgetVarsSetppc(scip, cons);
1985 nconsvars = SCIPgetNVarsSetppc(scip, cons);
1986
1987 switch ( SCIPgetTypeSetppc(scip, cons) )
1988 {
1990 SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, 1.0, 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_EQUATION, &matrixdata, nconssforvar) );
1991 break;
1993 SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, -SCIPinfinity(scip), 1.0, SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
1994 break;
1996 SCIP_CALL( collectCoefficients(scip, doubleequations, linvars, 0, nconsvars, 1.0, SCIPinfinity(scip), SCIPconsIsTransformed(cons), SYM_SENSE_INEQUALITY, &matrixdata, nconssforvar) );
1997 break;
1998 default:
1999 SCIPerrorMessage("Unknown setppc type %d.\n", SCIPgetTypeSetppc(scip, cons));
2000 return SCIP_ERROR;
2001 }
2002 }
2003 else if ( strcmp(conshdlrname, "xor") == 0 )
2004 {
2006 SCIP_VAR* var;
2007
2008 /* get number of variables of XOR constraint (without integer variable) */
2009 nconsvars = SCIPgetNVarsXor(scip, cons);
2010
2011 /* get variables of XOR constraint */
2013 for (j = 0; j < nconsvars; ++j)
2014 {
2015 assert( curconsvars[j] != NULL );
2016 consvars[j] = curconsvars[j];
2017 consvals[j] = 1.0;
2018 }
2019
2020 /* intVar of xor constraint might have been removed */
2021 var = SCIPgetIntVarXor(scip, cons);
2022 if ( var != NULL )
2023 {
2024 consvars[nconsvars] = var;
2025 consvals[nconsvars++] = 2.0;
2026 }
2027 assert( nconsvars <= nallvars );
2028
2029 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, (SCIP_Real) SCIPgetRhsXor(scip, cons),
2031 }
2032 else if ( strcmp(conshdlrname, "and") == 0 )
2033 {
2035
2036 /* get number of variables of AND constraint (without resultant) */
2037 nconsvars = SCIPgetNVarsAnd(scip, cons);
2038
2039 /* get variables of AND constraint */
2041
2042 for (j = 0; j < nconsvars; ++j)
2043 {
2044 assert( curconsvars[j] != NULL );
2045 consvars[j] = curconsvars[j];
2046 consvals[j] = 1.0;
2047 }
2048
2049 assert( SCIPgetResultantAnd(scip, cons) != NULL );
2050 consvars[nconsvars] = SCIPgetResultantAnd(scip, cons);
2051 consvals[nconsvars++] = 2.0;
2052 assert( nconsvars <= nallvars );
2053
2054 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
2056 }
2057 else if ( strcmp(conshdlrname, "or") == 0 )
2058 {
2060
2061 /* get number of variables of OR constraint (without resultant) */
2062 nconsvars = SCIPgetNVarsOr(scip, cons);
2063
2064 /* get variables of OR constraint */
2066
2067 for (j = 0; j < nconsvars; ++j)
2068 {
2069 assert( curconsvars[j] != NULL );
2070 consvars[j] = curconsvars[j];
2071 consvals[j] = 1.0;
2072 }
2073
2074 assert( SCIPgetResultantOr(scip, cons) != NULL );
2075 consvars[nconsvars] = SCIPgetResultantOr(scip, cons);
2076 consvals[nconsvars++] = 2.0;
2077 assert( nconsvars <= nallvars );
2078
2079 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nconsvars, 0.0, 0.0,
2081 }
2082 else if ( strcmp(conshdlrname, "logicor") == 0 )
2083 {
2086 }
2087 else if ( strcmp(conshdlrname, "knapsack") == 0 )
2088 {
2089 SCIP_Longint* weights;
2090
2091 nconsvars = SCIPgetNVarsKnapsack(scip, cons);
2092
2093 /* copy Longint array to SCIP_Real array and get active variables of constraint */
2094 weights = SCIPgetWeightsKnapsack(scip, cons);
2095 for (j = 0; j < nconsvars; ++j)
2096 consvals[j] = (SCIP_Real) weights[j];
2097 assert( nconsvars <= nallvars );
2098
2099 SCIP_CALL( collectCoefficients(scip, doubleequations, SCIPgetVarsKnapsack(scip, cons), consvals, nconsvars, -SCIPinfinity(scip),
2101 }
2102 else if ( strcmp(conshdlrname, "varbound") == 0 )
2103 {
2104 consvars[0] = SCIPgetVarVarbound(scip, cons);
2105 consvals[0] = 1.0;
2106
2107 consvars[1] = SCIPgetVbdvarVarbound(scip, cons);
2109
2110 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, 2, SCIPgetLhsVarbound(scip, cons),
2112 }
2113 else if ( strcmp(conshdlrname, "bounddisjunction") == 0 )
2114 {
2115 /* To model bound disjunctions, we normalize each constraint
2116 * \f[
2117 * (x_1 \{\leq,\geq\} b_1) \vee \ldots \vee (x_n \{\leq,\geq\} b_n)
2118 * \f]
2119 * to a constraint of type
2120 * \f[
2121 * (x_1 \leq b'_1 \vee \ldots \vee (x_n \leq b'_n).
2122 * \f]
2123 *
2124 * If no variable appears twice in such a normalized constraint, we say this bound disjunction
2125 * is of type 1. If the bound disjunction has length two and both disjunctions contain the same variable,
2126 * we say the bound disjunction is of type 2. Further bound disjunctions are possible, but can currently
2127 * not be handled.
2128 *
2129 * Bound disjunctions of type 1 are modeled as the linear constraint
2130 * \f[
2131 * b'_1 \cdot x_1 + \ldots + b'_n \cdot x_n = 0
2132 * \f]
2133 * and bound disjunctions of type 2 are modeled as the linear constraint
2134 * \f[
2135 * \min\{b'_1, b'_2\} \leq x_1 \leq \max\{b'_1, b'_2\}.
2136 * \f]
2137 * Note that problems arise if \fb'_i = 0\f for some variable \fx_i\f, because its coefficient in the
2138 * linear constraint is 0. To avoid this, we replace 0 by a special number.
2139 */
2141 SCIP_BOUNDTYPE* boundtypes;
2142 SCIP_Real* bounds;
2143 SCIP_Bool repetition = FALSE;
2144 int nbounddisjvars;
2145 int k;
2146
2147 /* collect coefficients for normalized constraint */
2150 boundtypes = SCIPgetBoundtypesBounddisjunction(scip, cons);
2151 bounds = SCIPgetBoundsBounddisjunction(scip, cons);
2152
2153 /* copy data */
2154 for (j = 0; j < nbounddisjvars; ++j)
2155 {
2156 consvars[j] = bounddisjvars[j];
2157
2158 /* normalize bounddisjunctions to SCIP_BOUNDTYPE_LOWER */
2159 if ( boundtypes[j] == SCIP_BOUNDTYPE_LOWER )
2160 consvals[j] = - bounds[j];
2161 else
2162 consvals[j] = bounds[j];
2163
2164 /* special treatment of 0 values */
2165 if ( SCIPisZero(scip, consvals[j]) )
2167
2168 /* detect whether a variable appears in two literals */
2169 for (k = 0; k < j && ! repetition; ++k)
2170 {
2171 if ( consvars[j] == consvars[k] )
2172 repetition = TRUE;
2173 }
2174
2175 /* stop, we cannot handle bounddisjunctions with more than two variables that contain a variable twice */
2176 if ( repetition && nbounddisjvars > 2 )
2177 {
2178 *success = FALSE;
2179
2181 " Deactivated symmetry handling methods, there exist constraints that cannot be handled by symmetry methods.\n");
2182
2183 if ( usecolumnsparsity )
2185
2187 SCIPfreeBlockMemoryArrayNull(scip, &consvars, nallvars);
2188 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2189 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2190 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2191 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2192 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2196
2197 return SCIP_OKAY;
2198 }
2199 }
2200 assert( ! repetition || nbounddisjvars == 2 );
2201
2202 /* if no variable appears twice */
2203 if ( ! repetition )
2204 {
2205 /* add information for bounddisjunction of type 1 */
2206 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, nbounddisjvars, 0.0, 0.0,
2208 }
2209 else
2210 {
2211 /* add information for bounddisjunction of type 2 */
2212 SCIP_Real lhs;
2213 SCIP_Real rhs;
2214
2215 lhs = MIN(consvals[0], consvals[1]);
2216 rhs = MAX(consvals[0], consvals[1]);
2217
2218 consvals[0] = 1.0;
2219
2220 SCIP_CALL( collectCoefficients(scip, doubleequations, consvars, consvals, 1, lhs, rhs,
2222 }
2223 }
2224 else if ( strcmp(conshdlrname, "nonlinear") == 0 )
2225 {
2226 SCIP_EXPR* expr;
2228
2230 assert(rootexpr != NULL);
2231
2232 /* for nonlinear constraints, only collect auxiliary variables for now */
2235
2236 for (expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it)) /*lint !e441*/ /*lint !e440*/
2237 {
2239
2240 /* for variables, we check whether they appear nonlinearly and store the result in the resp. array */
2241 if ( SCIPisExprVar(scip, expr) )
2242 {
2243 assert(*isnonlinvar != NULL);
2245 }
2246 else
2247 {
2248 SCIP_VAR* auxvar = SCIPgetExprAuxVarNonlinear(expr);
2249
2250 if ( auxvar != NULL && !SCIPhashsetExists(auxvars, (void*) auxvar) )
2251 {
2252 SCIP_CALL( SCIPhashsetInsert(auxvars, SCIPblkmem(scip), (void*) auxvar) );
2253 }
2254
2255 if ( SCIPisExprValue(scip, expr) )
2256 ++exprdata.nuniqueconstants;
2257 else if ( SCIPisExprSum(scip, expr) )
2258 {
2259 ++exprdata.nuniqueoperators;
2260 ++exprdata.nuniqueconstants;
2261 exprdata.nuniquecoefs += SCIPexprGetNChildren(expr);
2262 }
2263 else
2264 ++exprdata.nuniqueoperators;
2265 }
2266 }
2267 }
2268 else
2269 {
2270 /* if constraint is not one of the previous types, it cannot be handled */
2271 SCIPerrorMessage("Cannot determine symmetries for constraint <%s> of constraint handler <%s>.\n",
2272 SCIPconsGetName(cons), SCIPconshdlrGetName(conshdlr) );
2273 return SCIP_ERROR;
2274 }
2275 }
2276 assert( matrixdata.nrhscoef <= 2 * (nactiveconss - nnlconss) );
2277 assert( matrixdata.nrhscoef >= 0 );
2278
2280 SCIPfreeBlockMemoryArray(scip, &consvars, nallvars);
2281
2282 /* if no active constraint contains active variables */
2283 if ( nnlconss == 0 && matrixdata.nrhscoef == 0 )
2284 {
2285 *success = TRUE;
2286
2287 if ( usecolumnsparsity )
2289
2290 /* free matrix data */
2291 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2292 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2293 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2294 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2295 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2298
2300
2301 return SCIP_OKAY;
2302 }
2303
2304 /* sort matrix coefficients (leave matrix array intact) */
2305 SCIPsort(matrixdata.matidx, SYMsortMatCoef, (void*) matrixdata.matcoef, matrixdata.nmatcoef);
2306
2307 /* sort rhs types (first by sense, then by value, leave rhscoef intact) */
2308 sortrhstype.vals = matrixdata.rhscoef;
2309 sortrhstype.senses = matrixdata.rhssense;
2310 sortrhstype.nrhscoef = matrixdata.nrhscoef;
2311 SCIPsort(matrixdata.rhsidx, SYMsortRhsTypes, (void*) &sortrhstype, matrixdata.nrhscoef);
2312
2313 /* create map for variables to indices */
2315 assert( vartypemap != NULL );
2316
2317 /* allocate space for mappings to colors */
2319 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef) );
2320 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef) );
2322
2323 /* determine number of different coefficients */
2324
2325 /* find non-equivalent variables: same objective, lower and upper bounds, and variable type */
2326 for (j = 0; j < nvars; ++j)
2327 {
2328 SCIP_VAR* var;
2329
2330 var = vars[j];
2331 assert( var != NULL );
2332
2333 /* if the variable type should be fixed, just increase the color */
2334 if ( SymmetryFixVar(fixedtype, var) || (nnlconss > 0 && SCIPhashsetExists(auxvars, (void*) var)) )
2335 {
2336 matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
2337#ifdef SCIP_OUTPUT
2338 SCIPdebugMsg(scip, "Detected variable <%s> of fixed type %d - color %d.\n", SCIPvarGetName(var), SCIPvarGetType(var), matrixdata.nuniquevars - 1);
2339#endif
2340 }
2341 else
2342 {
2343 SYM_VARTYPE* vt;
2344
2346 assert( nuniquevararray <= matrixdata.nuniquevars );
2347
2348 vt->obj = SCIPvarGetObj(var);
2349 if ( local )
2350 {
2351 vt->lb = SCIPvarGetLbLocal(var);
2352 vt->ub = SCIPvarGetUbLocal(var);
2353 }
2354 else
2355 {
2356 vt->lb = SCIPvarGetLbGlobal(var);
2357 vt->ub = SCIPvarGetUbGlobal(var);
2358 }
2359 vt->type = SCIPvarGetType(var);
2360 vt->nconss = usecolumnsparsity ? nconssforvar[j] : 0; /*lint !e613*/
2361
2362 if ( ! SCIPhashtableExists(vartypemap, (void*) vt) )
2363 {
2365 vt->color = matrixdata.nuniquevars;
2366 matrixdata.permvarcolors[j] = matrixdata.nuniquevars++;
2368#ifdef SCIP_OUTPUT
2369 SCIPdebugMsg(scip, "Detected variable <%s> of new type (probindex: %d, obj: %g, lb: %g, ub: %g, type: %d) - color %d.\n",
2370 SCIPvarGetName(var), SCIPvarGetProbindex(var), vt->obj, vt->lb, vt->ub, vt->type, matrixdata.nuniquevars - 1);
2371#endif
2372 }
2373 else
2374 {
2376
2378 matrixdata.permvarcolors[j] = vtr->color;
2379 }
2380 }
2381 }
2382
2383 /* If every variable is unique, terminate. -> no symmetries can be present */
2384 if ( matrixdata.nuniquevars == nvars )
2385 {
2386 *success = TRUE;
2387
2388 /* free matrix data */
2390
2391 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef);
2392 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef);
2395
2396 if ( usecolumnsparsity )
2398
2399 if ( nnlconss > 0 )
2400 {
2402 SCIPhashsetFree(&auxvars, SCIPblkmem(scip));
2404 }
2405
2406 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2407 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2408 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2409 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2410 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2413
2415
2416 return SCIP_OKAY;
2417 }
2418
2419 /* find non-equivalent matrix entries (use sorting to avoid too many map calls) */
2420 for (j = 0; j < matrixdata.nmatcoef; ++j)
2421 {
2422 int idx;
2423
2424 idx = matrixdata.matidx[j];
2425 assert( 0 <= idx && idx < matrixdata.nmatcoef );
2426
2427 val = matrixdata.matcoef[idx];
2428 assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
2429
2430 if ( ! SCIPisEQ(scip, val, oldcoef) )
2431 {
2432#ifdef SCIP_OUTPUT
2433 SCIPdebugMsg(scip, "Detected new matrix entry type %f - color: %d\n.", val, matrixdata.nuniquemat);
2434#endif
2435 matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat++;
2436 oldcoef = val;
2437 }
2438 else
2439 {
2440 assert( matrixdata.nuniquemat > 0 );
2441 matrixdata.matcoefcolors[idx] = matrixdata.nuniquemat - 1;
2442 }
2443 }
2444
2445 /* find non-equivalent rhs */
2447 for (j = 0; j < matrixdata.nrhscoef; ++j)
2448 {
2450 int idx;
2451
2452 idx = matrixdata.rhsidx[j];
2453 assert( 0 <= idx && idx < matrixdata.nrhscoef );
2454 sense = matrixdata.rhssense[idx];
2455 val = matrixdata.rhscoef[idx];
2456
2457 /* make sure that new senses are treated with new color */
2458 if ( sense != oldsense )
2460 oldsense = sense;
2461 assert( oldcoef == SCIP_INVALID || oldcoef <= val ); /*lint !e777*/
2462
2463 /* assign new color to new type */
2464 if ( ! SCIPisEQ(scip, val, oldcoef) )
2465 {
2466#ifdef SCIP_OUTPUT
2467 SCIPdebugMsg(scip, "Detected new rhs type %f, type: %u - color: %d\n", val, sense, matrixdata.nuniquerhs);
2468#endif
2469 matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs++;
2470 oldcoef = val;
2471 }
2472 else
2473 {
2474 assert( matrixdata.nuniquerhs > 0 );
2475 matrixdata.rhscoefcolors[idx] = matrixdata.nuniquerhs - 1;
2476 }
2477 }
2478 assert( 0 < matrixdata.nuniquevars && matrixdata.nuniquevars <= nvars );
2479 assert( 0 <= matrixdata.nuniquerhs && matrixdata.nuniquerhs <= matrixdata.nrhscoef );
2480 assert( 0 <= matrixdata.nuniquemat && matrixdata.nuniquemat <= matrixdata.nmatcoef );
2481
2482 SCIPdebugMsg(scip, "Number of detected different variables: %d (total: %d).\n", matrixdata.nuniquevars, nvars);
2483 SCIPdebugMsg(scip, "Number of detected different rhs types: %d (total: %d).\n", matrixdata.nuniquerhs, matrixdata.nrhscoef);
2484 SCIPdebugMsg(scip, "Number of detected different matrix coefficients: %d (total: %d).\n", matrixdata.nuniquemat, matrixdata.nmatcoef);
2485
2486 /* do not compute symmetry if all variables are non-equivalent (unique) or if all matrix coefficients are different */
2487 if ( matrixdata.nuniquevars < nvars && (matrixdata.nuniquemat == 0 || matrixdata.nuniquemat < matrixdata.nmatcoef) )
2488 {
2489 /* determine generators */
2490 SCIP_CALL( SYMcomputeSymmetryGenerators(scip, maxgenerators, &matrixdata, &exprdata, nperms, nmaxperms,
2491 perms, log10groupsize, symcodetime) );
2492 assert( *nperms <= *nmaxperms );
2493
2494 /* SCIPisStopped() might call SCIPgetGap() which is only available after initpresolve */
2495 if ( checksymmetries && SCIPgetStage(scip) > SCIP_STAGE_INITPRESOLVE && ! SCIPisStopped(scip) )
2496 {
2498 }
2499
2500 if ( *nperms > 0 )
2501 {
2502 SCIP_CALL( setSymmetryData(scip, vars, nvars, nbinvars, permvars, npermvars, nbinpermvars, *perms, *nperms,
2503 nmovedvars, binvaraffected, compresssymmetries, compressthreshold, compressed) );
2504 }
2505 else
2506 {
2509 }
2510 }
2511 else
2512 {
2515 }
2516 *success = TRUE;
2517
2518 /* free matrix data */
2520
2521 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoefcolors, matrixdata.nrhscoef);
2522 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matcoefcolors, matrixdata.nmatcoef);
2525
2526 if ( usecolumnsparsity )
2528
2529 /* free cons expr specific data */
2530 if ( nnlconss > 0 )
2531 {
2532 assert( it != NULL );
2533 assert( auxvars != NULL );
2534
2536 SCIPhashsetFree(&auxvars, SCIPblkmem(scip));
2537 }
2538
2539 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhsidx, 2 * nactiveconss);
2540 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhssense, 2 * nactiveconss);
2541 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.rhscoef, 2 * nactiveconss);
2542 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matvaridx, matrixdata.nmaxmatcoef);
2543 SCIPfreeBlockMemoryArrayNull(scip, &matrixdata.matrhsidx, matrixdata.nmaxmatcoef);
2546
2547 return SCIP_OKAY;
2548}
2549
2550
2551/** determines symmetry */
2552static
2554 SCIP* scip, /**< SCIP instance */
2555 SCIP_PROPDATA* propdata, /**< propagator data */
2556 SYM_SPEC symspecrequire, /**< symmetry specification for which we need to compute symmetries */
2557 SYM_SPEC symspecrequirefixed /**< symmetry specification of variables which must be fixed by symmetries */
2558 )
2559{ /*lint --e{641}*/
2560 SCIP_Bool successful;
2561 SCIP_Real symcodetime = 0.0;
2562 int maxgenerators;
2563 int nhandleconss;
2564 int nconss;
2565 unsigned int type = 0;
2566 int nvars;
2567 int j;
2568 int p;
2569
2570 assert( scip != NULL );
2571 assert( propdata != NULL );
2572 assert( propdata->usesymmetry >= 0 );
2573 assert( propdata->ofenabled || propdata->symconsenabled || propdata->sstenabled );
2574
2575 /* do not compute symmetry if reoptimization is enabled */
2576 if ( SCIPisReoptEnabled(scip) )
2577 {
2578 propdata->ofenabled = FALSE;
2579 propdata->symconsenabled = FALSE;
2580 propdata->sstenabled = FALSE;
2581 return SCIP_OKAY;
2582 }
2583
2584 /* do not compute symmetry if Benders decomposition enabled */
2585 if ( SCIPgetNActiveBenders(scip) > 0 )
2586 {
2587 propdata->ofenabled = FALSE;
2588 propdata->symconsenabled = FALSE;
2589 propdata->sstenabled = FALSE;
2590 return SCIP_OKAY;
2591 }
2592
2593 /* skip symmetry computation if no graph automorphism code was linked */
2594 if ( ! SYMcanComputeSymmetry() )
2595 {
2596 propdata->ofenabled = FALSE;
2597 propdata->symconsenabled = FALSE;
2598 propdata->sstenabled = FALSE;
2599
2600 nconss = SCIPgetNActiveConss(scip);
2601 nhandleconss = getNSymhandableConss(scip, propdata->conshdlr_nonlinear);
2602
2603 /* print verbMessage only if problem consists of symmetry handable constraints */
2604 assert( nhandleconss <= nconss );
2605 if ( nhandleconss < nconss )
2606 return SCIP_OKAY;
2607
2609 " Deactivated symmetry handling methods, since SCIP was built without symmetry detector (SYM=none).\n");
2610
2611 return SCIP_OKAY;
2612 }
2613
2614 /* do not compute symmetry if there are active pricers */
2615 if ( SCIPgetNActivePricers(scip) > 0 )
2616 {
2617 propdata->ofenabled = FALSE;
2618 propdata->symconsenabled = FALSE;
2619 propdata->sstenabled = FALSE;
2620
2621 return SCIP_OKAY;
2622 }
2623
2624 /* avoid trivial cases */
2626 if ( nvars <= 0 )
2627 {
2628 propdata->ofenabled = FALSE;
2629 propdata->symconsenabled = FALSE;
2630 propdata->sstenabled = FALSE;
2631
2632 return SCIP_OKAY;
2633 }
2634
2635 /* do not compute symmetry if there are no binary variables and non-binary variables cannot be handled, but only binary variables would be used */
2636 if ( propdata->onlybinarysymmetry && SCIPgetNBinVars(scip) == 0 )
2637 {
2638 propdata->ofenabled = FALSE;
2639 propdata->symconsenabled = FALSE;
2640
2641 /* terminate if only Schreier Sims for binary variables is selected */
2642 if ( propdata->sstenabled )
2643 {
2644 if ( ! ((ISSSTINTACTIVE(propdata->sstleadervartype) && SCIPgetNIntVars(scip) > 0)
2645 || (ISSSTIMPLINTACTIVE(propdata->sstleadervartype) && SCIPgetNImplVars(scip) > 0)
2646 || (ISSSTCONTACTIVE(propdata->sstleadervartype) && SCIPgetNContVars(scip) > 0)) )
2647 return SCIP_OKAY;
2648 }
2649 else
2650 return SCIP_OKAY;
2651 }
2652
2653 /* determine symmetry specification */
2654 if ( SCIPgetNBinVars(scip) > 0 )
2655 type |= (int) SYM_SPEC_BINARY;
2656 if ( SCIPgetNIntVars(scip) > 0 )
2657 type |= (int) SYM_SPEC_INTEGER;
2658 /* count implicit integer variables as real variables, since we cannot currently handle integral variables well */
2659 if ( SCIPgetNContVars(scip) > 0 || SCIPgetNImplVars(scip) > 0 )
2660 type |= (int) SYM_SPEC_REAL;
2661
2662 /* skip symmetry computation if required variables are not present */
2663 if ( ! (type & symspecrequire) )
2664 {
2666 " (%.1fs) symmetry computation skipped: type (bin %c, int %c, cont %c) does not match requirements (bin %c, int %c, cont %c).\n",
2668 SCIPgetNBinVars(scip) > 0 ? '+' : '-',
2669 SCIPgetNIntVars(scip) > 0 ? '+' : '-',
2670 SCIPgetNContVars(scip) + SCIPgetNImplVars(scip) > 0 ? '+' : '-',
2671 (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2672 (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2673 (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2674
2675 propdata->ofenabled = FALSE;
2676 propdata->symconsenabled = FALSE;
2677 propdata->sstenabled = FALSE;
2678
2679 return SCIP_OKAY;
2680 }
2681
2682 /* skip computation if symmetry has already been computed */
2683 if ( propdata->computedsymmetry )
2684 return SCIP_OKAY;
2685
2686 /* skip symmetry computation if there are constraints that cannot be handled by symmetry */
2687 nconss = SCIPgetNActiveConss(scip);
2688 nhandleconss = getNSymhandableConss(scip, propdata->conshdlr_nonlinear);
2689 if ( nhandleconss < nconss )
2690 {
2692 " (%.1fs) symmetry computation skipped: there exist constraints that cannot be handled by symmetry methods.\n",
2694
2695 propdata->ofenabled = FALSE;
2696 propdata->symconsenabled = FALSE;
2697 propdata->sstenabled = FALSE;
2698
2699 return SCIP_OKAY;
2700 }
2701
2702 assert( propdata->npermvars == 0 );
2703 assert( propdata->permvars == NULL );
2704#ifndef NDEBUG
2705 assert( propdata->permvarsobj == NULL );
2706#endif
2707 assert( propdata->nperms < 0 );
2708 assert( propdata->nmaxperms == 0 );
2709 assert( propdata->perms == NULL );
2710
2711 /* output message */
2713 " (%.1fs) symmetry computation started: requiring (bin %c, int %c, cont %c), (fixed: bin %c, int %c, cont %c)\n",
2715 (symspecrequire & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2716 (symspecrequire & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2717 (symspecrequire & (int) SYM_SPEC_REAL) != 0 ? '+' : '-',
2718 (symspecrequirefixed & (int) SYM_SPEC_BINARY) != 0 ? '+' : '-',
2719 (symspecrequirefixed & (int) SYM_SPEC_INTEGER) != 0 ? '+' : '-',
2720 (symspecrequirefixed & (int) SYM_SPEC_REAL) != 0 ? '+' : '-');
2721
2722 /* output warning if we want to fix certain symmetry parts that we also want to compute */
2724 SCIPwarningMessage(scip, "Warning: some required symmetries must be fixed.\n");
2725
2726 /* determine maximal number of generators depending on the number of variables */
2727 maxgenerators = propdata->maxgenerators;
2728 maxgenerators = MIN(maxgenerators, MAXGENNUMERATOR / nvars);
2729
2730 /* actually compute (global) symmetry */
2731 SCIP_CALL( computeSymmetryGroup(scip, propdata->doubleequations, propdata->compresssymmetries, propdata->compressthreshold,
2732 maxgenerators, symspecrequirefixed, FALSE, propdata->checksymmetries, propdata->usecolumnsparsity, propdata->conshdlr_nonlinear,
2733 &propdata->npermvars, &propdata->nbinpermvars, &propdata->permvars, &propdata->nperms, &propdata->nmaxperms,
2734 &propdata->perms, &propdata->log10groupsize, &propdata->nmovedvars, &propdata->isnonlinvar,
2735 &propdata->binvaraffected, &propdata->compressed, &symcodetime, &successful) );
2736
2737 /* mark that we have computed the symmetry group */
2738 propdata->computedsymmetry = TRUE;
2739
2740 /* store restart level */
2741 propdata->lastrestart = SCIPgetNRuns(scip);
2742
2743 /* return if not successful */
2744 if ( ! successful )
2745 {
2746 assert( checkSymmetryDataFree(propdata) );
2747 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) could not compute symmetry\n", SCIPgetSolvingTime(scip));
2748
2749 propdata->ofenabled = FALSE;
2750 propdata->symconsenabled = FALSE;
2751 propdata->sstenabled = FALSE;
2752
2753 return SCIP_OKAY;
2754 }
2755
2756 /* return if no symmetries found */
2757 if ( propdata->nperms == 0 )
2758 {
2759 assert( checkSymmetryDataFree(propdata) );
2760 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry present (symcode time: %.2f)\n", SCIPgetSolvingTime(scip), symcodetime);
2761
2762 propdata->ofenabled = FALSE;
2763 propdata->symconsenabled = FALSE;
2764 propdata->sstenabled = FALSE;
2765
2766 return SCIP_OKAY;
2767 }
2768
2769 /* display statistics */
2770 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) symmetry computation finished: %d generators found (max: ",
2771 SCIPgetSolvingTime(scip), propdata->nperms);
2772
2773 /* display statistics: maximum number of generators */
2774 if ( maxgenerators == 0 )
2776 else
2777 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%d", maxgenerators);
2778
2779 /* display statistics: log10 group size, number of affected vars*/
2780 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", log10 of symmetry group size: %.1f", propdata->log10groupsize);
2781
2782 if ( propdata->displaynorbitvars )
2783 {
2784 if ( propdata->nmovedvars == -1 )
2785 {
2786 SCIP_CALL( SCIPdetermineNVarsAffectedSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2787 propdata->npermvars, &(propdata->nmovedvars)) );
2788 }
2789 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ", number of affected variables: %d)\n", propdata->nmovedvars);
2790 }
2791 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, ") (symcode time: %.2f)\n", symcodetime);
2792
2793 /* exit if no binary variables are affected by symmetry and we cannot handle non-binary symmetries */
2794 if ( ! propdata->binvaraffected )
2795 {
2796 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) no symmetry on binary variables present.\n", SCIPgetSolvingTime(scip));
2797
2798 /* disable OF and symmetry handling constraints based on symretopes */
2799 propdata->ofenabled = FALSE;
2800 propdata->symconsenabled = FALSE;
2801 if ( ! propdata->sstenabled ||
2802 ! ( ISSSTINTACTIVE(propdata->sstleadervartype) || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
2803 || ISSSTCONTACTIVE(propdata->sstleadervartype) ) )
2804 {
2805 propdata->sstenabled = FALSE;
2806
2807 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) -> no handable symmetry found, free symmetry data.\n",
2809
2810 /* free data and exit */
2811 SCIP_CALL( freeSymmetryData(scip, propdata) );
2812
2813 return SCIP_OKAY;
2814 }
2815 }
2816
2817 assert( propdata->nperms > 0 );
2818 assert( propdata->npermvars > 0 );
2819
2820 /* compute components */
2821 assert( propdata->components == NULL );
2822 assert( propdata->componentbegins == NULL );
2823 assert( propdata->vartocomponent == NULL );
2824
2825#ifdef SCIP_OUTPUT_COMPONENT
2826 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation started\n", SCIPgetSolvingTime(scip));
2827#endif
2828
2829 /* we only need the components for orbital fixing, orbitope and subgroup detection, and Schreier Sims constraints */
2830 if ( propdata->ofenabled || ( propdata->symconsenabled && propdata->detectorbitopes )
2831 || propdata->detectsubgroups || propdata->sstenabled )
2832 {
2833 SCIP_CALL( SCIPcomputeComponentsSym(scip, propdata->perms, propdata->nperms, propdata->permvars,
2834 propdata->npermvars, FALSE, &propdata->components, &propdata->componentbegins,
2835 &propdata->vartocomponent, &propdata->componentblocked, &propdata->ncomponents) );
2836 }
2837
2838#ifdef SCIP_OUTPUT_COMPONENT
2839 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " (%.1fs) component computation finished\n", SCIPgetSolvingTime(scip));
2840#endif
2841
2842 /* set up data for OF */
2843 if ( propdata->ofenabled )
2844 {
2845 int componentidx;
2846 int v;
2847
2848 /* transpose symmetries matrix here if necessary */
2849 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
2850 for (v = 0; v < propdata->npermvars; ++v)
2851 {
2852 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
2853 for (p = 0; p < propdata->nperms; ++p)
2854 {
2855 if ( SCIPvarIsBinary(propdata->permvars[v]) || propdata->sstenabled )
2856 propdata->permstrans[v][p] = propdata->perms[p][v];
2857 else
2858 propdata->permstrans[v][p] = v; /* ignore symmetry information on non-binary variables */
2859 }
2860 }
2861
2862 /* prepare array for active permutations */
2863 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->inactiveperms, propdata->nperms) );
2864 for (v = 0; v < propdata->nperms; ++v)
2865 propdata->inactiveperms[v] = FALSE;
2866
2867 /* create hashmap for storing the indices of variables */
2868 assert( propdata->permvarmap == NULL );
2869 SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
2870
2871 /* prepare data structures */
2872 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permvarsevents, propdata->npermvars) );
2873 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg0, propdata->npermvars) );
2874 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg0list, propdata->npermvars) );
2875 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg1, propdata->npermvars) );
2876 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->bg1list, propdata->npermvars) );
2877
2878 /* insert variables into hashmap */
2879 assert( propdata->nmovedpermvars == -1 );
2880 propdata->nmovedpermvars = 0;
2881 for (v = 0; v < propdata->npermvars; ++v)
2882 {
2883 SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
2884
2885 propdata->bg0[v] = FALSE;
2886 propdata->bg1[v] = FALSE;
2887 propdata->permvarsevents[v] = -1;
2888
2889 /* collect number of moved permvars */
2890 componentidx = propdata->vartocomponent[v];
2891 if ( componentidx > -1 && ! propdata->componentblocked[componentidx] )
2892 {
2893 propdata->nmovedpermvars += 1;
2894
2895 if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_BINARY )
2896 ++propdata->nmovedbinpermvars;
2897 else if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_INTEGER )
2898 ++propdata->nmovedintpermvars;
2899 else if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_IMPLINT )
2900 ++propdata->nmovedimplintpermvars;
2901 else
2902 ++propdata->nmovedcontpermvars;
2903 }
2904
2905 /* Only catch binary variables, since integer variables should be fixed pointwise; implicit integer variables
2906 * are not branched on. */
2907 if ( SCIPvarGetType(propdata->permvars[v]) == SCIP_VARTYPE_BINARY )
2908 {
2909 /* catch whether binary variables are globally fixed; also store filter position */
2911 propdata->eventhdlr, (SCIP_EVENTDATA*) propdata, &propdata->permvarsevents[v]) );
2912 }
2913 }
2914 assert( propdata->nbg1 == 0 );
2915 }
2916
2917 /* set up data for Schreier Sims constraints or subgroup detection */
2918 if ( (propdata->sstenabled || propdata->detectsubgroups) && ! propdata->ofenabled )
2919 {
2920 int v;
2921
2922 /* transpose symmetries matrix here if necessary */
2923 assert( propdata->permstrans == NULL );
2924 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permstrans, propdata->npermvars) );
2925 for (v = 0; v < propdata->npermvars; ++v)
2926 {
2927 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->permstrans[v]), propdata->nmaxperms) );
2928 for (p = 0; p < propdata->nperms; ++p)
2929 propdata->permstrans[v][p] = propdata->perms[p][v];
2930 }
2931
2932 /* create hashmap for storing the indices of variables */
2933 assert( propdata->permvarmap == NULL );
2934 SCIP_CALL( SCIPhashmapCreate(&propdata->permvarmap, SCIPblkmem(scip), propdata->npermvars) );
2935
2936 /* insert variables into hashmap */
2937 for (v = 0; v < propdata->npermvars; ++v)
2938 {
2939 SCIP_CALL( SCIPhashmapInsertInt(propdata->permvarmap, propdata->permvars[v], v) );
2940 }
2941 }
2942
2943 /* handle several general aspects */
2944#ifndef NDEBUG
2945 /* store objective coefficients for debug purposes */
2946 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->permvarsobj, propdata->npermvars) );
2947 for (j = 0; j < propdata->npermvars; ++j)
2948 propdata->permvarsobj[j] = SCIPvarGetObj(propdata->permvars[j]);
2949#endif
2950
2951 /* capture symmetric variables and forbid multi aggregation */
2952
2953 /* binary symmetries are always handled
2954 *
2955 * note: binary variables are in the beginning of permvars
2956 */
2957 if ( propdata->binvaraffected )
2958 {
2959 for (j = 0; j < propdata->nbinpermvars; ++j)
2960 {
2961 SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[j]) );
2962
2963 if ( propdata->compressed )
2964 {
2965 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
2966 }
2967 else
2968 {
2969 for (p = 0; p < propdata->nperms; ++p)
2970 {
2971 if ( propdata->perms[p][j] != j )
2972 {
2973 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
2974 break;
2975 }
2976 }
2977 }
2978 }
2979 }
2980
2981 /* if Schreier-Sims constraints are enabled, also capture symmetric variables and forbid multi aggregation of handable vars */
2982 if ( propdata->sstenabled && propdata->sstleadervartype != (int) SCIP_SSTTYPE_BINARY )
2983 {
2984 int cnt;
2985
2986 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->nonbinpermvarcaptured,
2987 propdata->npermvars - propdata->nbinpermvars) );
2988 for (j = propdata->nbinpermvars, cnt = 0; j < propdata->npermvars; ++j, ++cnt)
2989 {
2990 if ( ! isLeadervartypeCompatible(propdata->permvars[j], propdata->sstleadervartype) )
2991 {
2992 propdata->nonbinpermvarcaptured[cnt] = FALSE;
2993 continue;
2994 }
2995
2996 SCIP_CALL( SCIPcaptureVar(scip, propdata->permvars[j]) );
2997 propdata->nonbinpermvarcaptured[cnt] = TRUE;
2998
2999 if ( propdata->compressed )
3000 {
3001 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3002 }
3003 else
3004 {
3005 for (p = 0; p < propdata->nperms; ++p)
3006 {
3007 if ( propdata->perms[p][j] != j )
3008 {
3009 SCIP_CALL( SCIPmarkDoNotMultaggrVar(scip, propdata->permvars[j]) );
3010 break;
3011 }
3012 }
3013 }
3014 }
3015 }
3016
3017 /* free original perms matrix if no symmetry constraints are added */
3018 if ( ! propdata->symconsenabled && ! propdata->sstenabled )
3019 {
3020 for (p = 0; p < propdata->nperms; ++p)
3021 {
3022 SCIPfreeBlockMemoryArray(scip, &(propdata->perms)[p], propdata->npermvars);
3023 }
3024 SCIPfreeBlockMemoryArrayNull(scip, &propdata->perms, propdata->nmaxperms);
3025 }
3026
3027 return SCIP_OKAY;
3028}
3029
3030
3031/*
3032 * Functions for symmetry constraints
3033 */
3034
3035
3036/** Checks whether given set of 2-cycle permutations forms an orbitope and if so, builds the variable index matrix.
3037 *
3038 * If @p activevars == NULL, then the function assumes all permutations of the component are active and therefore all
3039 * moved vars are considered.
3040 *
3041 * We need to keep track of the number of generatored columns, because we might not be able to detect all orbitopes.
3042 * 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
3043 * in our construction need shape (1,2), (2,3), (3,4), (4,5).
3044 *
3045 * @pre @p orbitopevaridx has to be an initialized 2D array of size @p ntwocycles x @p nperms
3046 * @pre @p columnorder has to be an initialized array of size nperms
3047 * @pre @p nusedelems has to be an initialized array of size npermvars
3048 */
3049static
3051 SCIP* scip, /**< SCIP instance */
3052 SCIP_VAR** permvars, /**< array of all permutation variables */
3053 int npermvars, /**< number of permutation variables */
3054 int** perms, /**< array of all permutations of the symmety group */
3055 int* activeperms, /**< indices of the relevant permutations in perms */
3056 int ntwocycles, /**< number of 2-cycles in the permutations */
3057 int nactiveperms, /**< number of active permutations */
3058 int** orbitopevaridx, /**< pointer to store variable index matrix */
3059 int* columnorder, /**< pointer to store column order */
3060 int* nusedelems, /**< pointer to store how often each element was used */
3061 int* nusedcols, /**< pointer to store numer of columns used in orbitope (or NULL) */
3062 SCIP_Shortbool* rowisbinary, /**< pointer to store which rows are binary (or NULL) */
3063 SCIP_Bool* isorbitope, /**< buffer to store result */
3064 SCIP_Shortbool* activevars /**< bitset to store whether a variable is active (or NULL) */
3065 )
3066{ /*lint --e{571}*/
3067 SCIP_Bool* usedperm;
3068 SCIP_Bool foundperm = FALSE;
3069 int nusedperms = 0;
3070 int nfilledcols;
3071 int coltoextend;
3072 int ntestedperms = 0;
3073 int row = 0;
3074 int j;
3075
3076 assert( scip != NULL );
3077 assert( permvars != NULL );
3078 assert( perms != NULL );
3079 assert( activeperms != NULL );
3081 assert( columnorder != NULL );
3082 assert( nusedelems != NULL );
3083 assert( isorbitope != NULL );
3084 assert( nactiveperms > 0 );
3085 assert( ntwocycles > 0 );
3086 assert( npermvars > 0 );
3087 assert( activevars == NULL || (0 <= nactiveperms && nactiveperms < npermvars) );
3088
3089 *isorbitope = TRUE;
3090 if ( nusedcols != NULL )
3091 *nusedcols = 0;
3092
3093 /* whether a permutation was considered to contribute to orbitope */
3095
3096 /* fill first two columns of orbitopevaridx matrix */
3097
3098 /* look for the first active permutation which moves an active variable */
3099 while ( ! foundperm )
3100 {
3101 int permidx;
3102
3104
3106
3107 for (j = 0; j < npermvars; ++j)
3108 {
3109 if ( activevars != NULL && ! activevars[j] )
3110 continue;
3111
3112 assert( activevars == NULL || activevars[perms[permidx][j]] );
3113
3114 /* avoid adding the same 2-cycle twice */
3115 if ( perms[permidx][j] > j )
3116 {
3117 assert( SCIPvarIsBinary(permvars[j]) == SCIPvarIsBinary(permvars[perms[permidx][j]]) );
3118
3119 if ( rowisbinary != NULL && SCIPvarIsBinary(permvars[j]) )
3120 rowisbinary[row] = TRUE;
3121
3122 orbitopevaridx[row][0] = j;
3123 orbitopevaridx[row++][1] = perms[permidx][j];
3124 ++(nusedelems[j]);
3125 ++(nusedelems[perms[permidx][j]]);
3126
3127 foundperm = TRUE;
3128 }
3129
3130 if ( row == ntwocycles )
3131 break;
3132 }
3133
3134 ++ntestedperms;
3135 }
3136
3137 /* in the subgroup case it might happen that a generator has less than ntwocycles many 2-cyles */
3138 if ( row != ntwocycles )
3139 {
3140 *isorbitope = FALSE;
3142 return SCIP_OKAY;
3143 }
3144
3145 usedperm[ntestedperms - 1] = TRUE;
3146 ++nusedperms;
3147 columnorder[0] = 0;
3148 columnorder[1] = 1;
3149 nfilledcols = 2;
3150
3151 /* extend orbitopevaridx matrix to the left, i.e., iteratively find new permutations that
3152 * intersect the last added left column in each row in exactly one entry, starting with
3153 * column 0 */
3154 coltoextend = 0;
3155 for (j = ntestedperms; j < nactiveperms; ++j)
3156 { /* lint --e{850} */
3157 SCIP_Bool success = FALSE;
3158 SCIP_Bool infeasible = FALSE;
3159
3160 if ( nusedperms == nactiveperms )
3161 break;
3162
3163 if ( usedperm[j] )
3164 continue;
3165
3167 perms[activeperms[j]], TRUE, &nusedelems, permvars, NULL, &success, &infeasible) );
3168
3169 if ( infeasible )
3170 {
3171 *isorbitope = FALSE;
3172 break;
3173 }
3174 else if ( success )
3175 {
3176 usedperm[j] = TRUE;
3177 ++nusedperms;
3179 columnorder[nfilledcols++] = -1; /* mark column to be filled from the left */
3180 j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
3181 }
3182 }
3183
3184 if ( ! *isorbitope ) /*lint !e850*/
3185 {
3187 return SCIP_OKAY;
3188 }
3189
3190 coltoextend = 1;
3191 for (j = ntestedperms; j < nactiveperms; ++j)
3192 { /*lint --e(850)*/
3193 SCIP_Bool success = FALSE;
3194 SCIP_Bool infeasible = FALSE;
3195
3196 if ( nusedperms == nactiveperms )
3197 break;
3198
3199 if ( usedperm[j] )
3200 continue;
3201
3203 perms[activeperms[j]], FALSE, &nusedelems, permvars, NULL, &success, &infeasible) );
3204
3205 if ( infeasible )
3206 {
3207 *isorbitope = FALSE;
3208 break;
3209 }
3210 else if ( success )
3211 {
3212 usedperm[j] = TRUE;
3213 ++nusedperms;
3215 columnorder[nfilledcols] = 1; /* mark column to be filled from the right */
3216 ++nfilledcols;
3217 j = 0; /*lint !e850*/ /* reset j since previous permutations can now intersect with the latest added column */
3218 }
3219 }
3220
3221 if ( activevars == NULL && nusedperms < nactiveperms ) /*lint !e850*/
3222 *isorbitope = FALSE;
3223
3224 if ( nusedcols != NULL )
3227
3229
3230 return SCIP_OKAY;
3231}
3232
3233/** choose an order in which the generators should be added for subgroup detection */
3234static
3236 SCIP* scip, /**< SCIP instance */
3237 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3238 int compidx, /**< index of component */
3239 int** genorder, /**< (initialized) buffer to store the resulting order of generator */
3240 int* ntwocycleperms /**< pointer to store the number of 2-cycle permutations in component compidx */
3241 )
3242{
3243 int** perms;
3244 int* components;
3245 int* componentbegins;
3246 int* ntwocycles;
3247 int npermvars;
3248 int npermsincomp;
3249 int i;
3250
3251 assert( scip != NULL );
3252 assert( propdata != NULL );
3253 assert( compidx >= 0 );
3254 assert( compidx < propdata->ncomponents );
3255 assert( genorder != NULL );
3256 assert( *genorder != NULL );
3258 assert( propdata->computedsymmetry );
3259 assert( propdata->nperms > 0 );
3260 assert( propdata->perms != NULL );
3261 assert( propdata->npermvars > 0 );
3262 assert( propdata->ncomponents > 0 );
3263 assert( propdata->components != NULL );
3264 assert( propdata->componentbegins != NULL );
3265
3266 perms = propdata->perms;
3267 npermvars = propdata->npermvars;
3268 components = propdata->components;
3269 componentbegins = propdata->componentbegins;
3270 npermsincomp = componentbegins[compidx + 1] - componentbegins[compidx];
3272
3274
3275 for (i = 0; i < npermsincomp; ++i)
3276 {
3277 int* perm;
3278 int nbincycles;
3279
3280 perm = perms[components[componentbegins[compidx] + i]];
3281
3282 SCIP_CALL( SCIPisInvolutionPerm(perm, propdata->permvars, npermvars, &(ntwocycles[i]), &nbincycles, FALSE) );
3283
3284 /* we skip permutations which do not purely consist of 2-cycles */
3285 if ( ntwocycles[i] == 0 )
3286 {
3287 /* we change the number of two cycles for this perm so that it will be sorted to the end */
3288 if ( propdata->preferlessrows )
3289 ntwocycles[i] = npermvars;
3290 else
3291 ntwocycles[i] = 0;
3292 --(*ntwocycleperms);
3293 }
3294 else if ( ! propdata->preferlessrows )
3295 ntwocycles[i] = - ntwocycles[i];
3296 }
3297
3299
3301
3302 return SCIP_OKAY;
3303}
3304
3305
3306/** builds the graph for symmetric subgroup detection from the given permutation of generators
3307 *
3308 * After execution, @p graphcomponents contains all permvars sorted by their color and component,
3309 * @p graphcompbegins points to the indices where new components in @p graphcomponents start and
3310 * @p compcolorbegins points to the indices where new colors in @p graphcompbegins start.
3311*/
3312static
3314 SCIP* scip, /**< SCIP instance */
3315 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3316 int* genorder, /**< order in which the generators should be considered */
3317 int ntwocycleperms, /**< number of 2-cycle permutations in this component */
3318 int compidx, /**< index of the component */
3319 int** graphcomponents, /**< buffer to store the components of the graph (ordered var indices) */
3320 int** graphcompbegins, /**< buffer to store the indices of each new graph component */
3321 int** compcolorbegins, /**< buffer to store at which indices a new color begins */
3322 int* ngraphcomponents, /**< pointer to store the number of graph components */
3323 int* ncompcolors, /**< pointer to store the number of different colors */
3324 int** usedperms, /**< buffer to store the indices of permutations that were used */
3325 int* nusedperms, /**< pointer to store the number of used permutations in the graph */
3326 int usedpermssize, /**< initial size of usedperms */
3327 SCIP_Shortbool* permused /**< initialized buffer to store which permutations have been used
3328 * (identified by index in component) */
3329 )
3330{
3331 SCIP_DISJOINTSET* vartocomponent;
3333 int** perms;
3334 int* components;
3335 int* componentbegins;
3336 int* componentslastperm;
3338 int npermvars;
3339 int nextcolor;
3340 int nextcomp;
3341 int j;
3342 int k;
3343
3344 assert( scip != NULL );
3345 assert( propdata != NULL );
3350 assert( ncompcolors != NULL );
3351 assert( genorder != NULL );
3352 assert( usedperms != NULL );
3353 assert( nusedperms != NULL );
3354 assert( usedpermssize > 0 );
3355 assert( permused != NULL );
3356 assert( ntwocycleperms >= 0 );
3357 assert( compidx >= 0 );
3358 assert( compidx < propdata->ncomponents );
3359 assert( propdata->computedsymmetry );
3360 assert( propdata->nperms > 0 );
3361 assert( propdata->perms != NULL );
3362 assert( propdata->npermvars > 0 );
3363 assert( propdata->ncomponents > 0 );
3364 assert( propdata->components != NULL );
3365 assert( propdata->componentbegins != NULL );
3366 assert( !propdata->componentblocked[compidx] );
3367
3368 perms = propdata->perms;
3369 npermvars = propdata->npermvars;
3370 components = propdata->components;
3371 componentbegins = propdata->componentbegins;
3372 *nusedperms = 0;
3373
3374 assert( ntwocycleperms <= componentbegins[compidx + 1] - componentbegins[compidx] );
3375
3376 SCIP_CALL( SCIPcreateDisjointset(scip, &vartocomponent, npermvars) );
3379
3380 for (k = 0; k < npermvars; ++k)
3381 componentslastperm[k] = -1;
3382
3383 for (j = 0; j < ntwocycleperms; ++j)
3384 {
3385 int* perm;
3386 int firstcolor = -1;
3387
3388 /* use given order of generators */
3389 perm = perms[components[componentbegins[compidx] + genorder[j]]];
3390 assert( perm != NULL );
3391
3392 /* iteratively handle each swap of perm until an invalid one is found or all edges have been added */
3393 for (k = 0; k < npermvars; ++k)
3394 {
3395 int comp1;
3396 int comp2;
3397 int color1;
3398 int color2;
3399 int img;
3400
3401 img = perm[k];
3402 assert( perm[img] == k );
3403
3404 if ( img <= k )
3405 continue;
3406
3407 comp1 = SCIPdisjointsetFind(vartocomponent, k);
3408 comp2 = SCIPdisjointsetFind(vartocomponent, img);
3409
3410 if ( comp1 == comp2 )
3411 {
3412 /* another permutation has already merged these variables into one component; store its color */
3413 if ( firstcolor < 0 )
3414 {
3417 }
3419 continue;
3420 }
3421
3422 /* if it is the second time that the component is used for this generator,
3423 * it is not guaranteed that the group acts like the symmetric group, so skip it
3424 */
3426 break;
3427
3430
3431 /* a generator is not allowed to connect two components of the same color, since they depend on each other */
3432 if ( color1 == color2 )
3433 break;
3434
3437
3438 if ( firstcolor < 0 )
3440 }
3441
3442 /* if the generator is invalid, delete the newly added edges, go to next generator */
3443 if ( k < npermvars )
3444 continue;
3445
3446 /* if the generator only acts on already existing components, we don't have to store it */
3447 if ( firstcolor == -1 )
3448 continue;
3449
3450 /* check whether we need to resize */
3451 if ( *nusedperms >= usedpermssize )
3452 {
3455
3457
3459 }
3460
3461 (*usedperms)[*nusedperms] = components[componentbegins[compidx] + genorder[j]];
3462 ++(*nusedperms);
3463 permused[genorder[j]] = TRUE;
3464
3465 /* if the generator can be added, update the datastructures for graph components and colors */
3466 for (k = 0; k < npermvars; ++k)
3467 {
3468 int comp1;
3469 int comp2;
3470 int color1;
3471 int color2;
3472 int img;
3473
3474 img = perm[k];
3475 assert( perm[img] == k );
3476
3477 if ( img <= k )
3478 continue;
3479
3480 comp1 = SCIPdisjointsetFind(vartocomponent, k);
3481 comp2 = SCIPdisjointsetFind(vartocomponent, img);
3482
3483 /* components and colors don't have to be updated if the components are the same */
3484 if ( comp1 == comp2 )
3485 continue;
3486
3489
3490 if ( color1 != color2 )
3491 {
3494 }
3495
3496 SCIPdisjointsetUnion(vartocomponent, comp1, comp2, FALSE);
3497
3498 assert( SCIPdisjointsetFind(vartocomponent, k) == SCIPdisjointsetFind(vartocomponent, img) );
3501 }
3502 }
3503
3505 SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.components), npermvars) );
3506 SCIP_CALL( SCIPallocBufferArray(scip, &(graphcompvartype.colors), npermvars) );
3507
3508 /*
3509 * At this point, we have built the colored graph. Now we transform the information in the
3510 * disjoint sets to the arrays graphcomponents, graphcompbegins, and compcolorbegins (see above).
3511 */
3512
3513 /* build the struct graphcompvartype which is used to sort the graphcomponents array */
3514 for (j = 0; j < npermvars; ++j)
3515 {
3516 int comp;
3517
3518 comp = SCIPdisjointsetFind(vartocomponent, j);
3519
3520 graphcompvartype.components[j] = comp;
3522
3523 (*graphcomponents)[j] = j;
3524 }
3525
3526 /* sort graphcomponents first by color, then by component */
3528
3533
3534 nextcolor = 1;
3535 nextcomp = 1;
3536 (*graphcompbegins)[0] = 0;
3537 (*compcolorbegins)[0] = 0;
3538
3539 /* find the starting indices of new components and new colors */
3540 for (j = 1; j < npermvars; ++j)
3541 {
3542 int idx1;
3543 int idx2;
3544
3545 idx1 = (*graphcomponents)[j];
3546 idx2 = (*graphcomponents)[j-1];
3547
3548 assert( graphcompvartype.colors[idx1] >= graphcompvartype.colors[idx2] );
3549
3550 if ( graphcompvartype.components[idx1] != graphcompvartype.components[idx2] )
3551 {
3552 (*graphcompbegins)[nextcomp] = j;
3553
3554 if ( graphcompvartype.colors[idx1] > graphcompvartype.colors[idx2] )
3555 {
3556 (*compcolorbegins)[nextcolor] = nextcomp;
3557 ++nextcolor;
3558 }
3559
3560 ++nextcomp;
3561 }
3562 }
3565
3566 (*compcolorbegins)[nextcolor] = *ngraphcomponents;
3567 (*graphcompbegins)[nextcomp] = npermvars;
3568
3573 SCIPfreeDisjointset(scip, &vartocomponent);
3574
3575 return SCIP_OKAY;
3576}
3577
3578/** adds an orbitope constraint for a suitable color of the subgroup graph */
3579static
3581 SCIP* scip, /**< SCIP instance */
3582 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3583 int* usedperms, /**< array of the permutations that build the orbitope */
3584 int nusedperms, /**< number of permutations in usedperms */
3585 int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3586 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3587 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3588 int graphcoloridx, /**< index of the graph color */
3589 int nrows, /**< number of rows in the orbitope */
3590 int ncols, /**< number of columns in the orbitope */
3591 int* firstvaridx, /**< buffer to store the index of the largest variable (or NULL) */
3592 int* compidxfirstrow, /**< buffer to store the comp index for the first row (or NULL) */
3593 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3594 int* nvarslexorder, /**< number of variables in lexicographic order */
3595 int* maxnvarslexorder, /**< maximum number of variables in lexicographic order */
3596 SCIP_Bool mayinteract, /**< whether orbitope's symmetries might interact with other symmetries */
3597 SCIP_Bool* success /**< whether the orbitpe could be added */
3598 )
3599{ /*lint --e{571}*/
3600 char name[SCIP_MAXSTRLEN];
3603 int** orbitopevaridx;
3604 int* columnorder;
3605 int* nusedelems;
3606 SCIP_CONS* cons;
3607 SCIP_Bool isorbitope;
3608 SCIP_Bool infeasible = FALSE;
3609#ifndef NDEBUG
3610 int nactivevars = 0;
3611#endif
3612 int ngencols = 0;
3613 int k;
3614
3615 assert( scip != NULL );
3616 assert( propdata != NULL );
3617 assert( usedperms != NULL );
3621 assert( nusedperms > 0 );
3622 assert( nrows > 0 );
3623 assert( ncols > 0 );
3624 assert( lexorder != NULL );
3627
3628 *success = FALSE;
3629
3630 /* create hashset to mark variables */
3631 SCIP_CALL( SCIPallocClearBufferArray(scip, &activevars, propdata->npermvars) );
3632
3633 /* orbitope matrix for indices of variables in permvars array */
3635 for (k = 0; k < nrows; ++k)
3636 {
3637 SCIP_CALL( SCIPallocBufferArray(scip, &orbitopevaridx[k], ncols) ); /*lint !e866*/
3638 }
3639
3640 /* order of columns of orbitopevaridx */
3642 for (k = 0; k < ncols; ++k)
3643 columnorder[k] = ncols + 1;
3644
3645 /* count how often an element was used in the potential orbitope */
3646 SCIP_CALL( SCIPallocClearBufferArray(scip, &nusedelems, propdata->npermvars) );
3647
3648 /* mark variables in this subgroup orbitope */
3650 {
3652 int compstart;
3653 int l;
3654
3656 firstvar = propdata->permvars[graphcomponents[compstart]];
3657
3658 if ( ! SCIPvarIsBinary(firstvar) )
3659 continue;
3660
3661 for (l = 0; l < ncols; ++l)
3662 {
3663 int varidx;
3664
3666 assert( ! activevars[varidx] );
3667
3669#ifndef NDEBUG
3670 ++nactivevars;
3671#endif
3672 }
3673 }
3674 assert( nactivevars == nrows * ncols );
3675
3676 /* build the variable index matrix for the orbitope
3677 *
3678 * It is possible that we find an orbitope, but not using all possible columns. For example
3679 * (1,2), (2,3), (3,4), (3,5) defines the symmetric group on {1,2,3,4,5}, but the generators
3680 * we expect in our construction need shape (1,2), (2,3), (3,4), (4,5). For this reason,
3681 * we need to store how many columns have been generated.
3682 *
3683 * @todo ensure compatibility with more general generators
3684 */
3685 SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, propdata->permvars, propdata->npermvars,
3686 propdata->perms, usedperms, nrows, nusedperms, orbitopevaridx, columnorder,
3688
3689 /* it might happen that we cannot detect the orbitope if it is generated by permutations with different
3690 * number of 2-cycles.
3691 */
3692 if ( ! isorbitope )
3693 {
3696 for (k = nrows - 1; k >= 0; --k)
3697 {
3699 }
3702
3703 return SCIP_OKAY;
3704 }
3705
3706 /* There are three possibilities for the structure of columnorder:
3707 * 1) [0, 1, -1, -1, ..., -1]
3708 * 2) [0, 1, 1, 1, ..., 1]
3709 * 3) [0, 1, -1, -1, ...., -1, 1, 1, ..., 1]
3710 *
3711 * The '1'-columns will be added to the matrix first and in the last 2
3712 * cases the method starts from the right. So to store the variable index
3713 * that will be in the upper-left corner, we need either the entryin the
3714 * second column (case 1) or the entry in the last column (cases 2 and 3).
3715 */
3716 if ( firstvaridx != NULL )
3717 {
3718 if ( columnorder[ngencols-1] > -1 )
3720 else
3721 *firstvaridx = orbitopevaridx[0][1];
3722 }
3723
3724 /* find corresponding graphcomponent of first variable (needed for weak sbcs) */
3725 if ( compidxfirstrow != NULL && firstvaridx != NULL )
3726 {
3727 *compidxfirstrow = -1;
3728
3729 for (k = compcolorbegins[graphcoloridx]; k < compcolorbegins[graphcoloridx+1] && (*compidxfirstrow) < 0; ++k)
3730 {
3732 int compstart;
3733 int l;
3734
3736 firstvar = propdata->permvars[graphcomponents[compstart]];
3737
3738 if ( ! SCIPvarIsBinary(firstvar) )
3739 continue;
3740
3741 /* iterate over all columns (elements in orbit), because we cannot see from ngencols which columns
3742 * have been left out
3743 */
3744 for (l = 0; l < ncols; ++l)
3745 {
3747 {
3748 *compidxfirstrow = k;
3749 break;
3750 }
3751 }
3752 }
3753 assert( *compidxfirstrow > -1 );
3754 }
3755
3756 /* prepare orbitope variable matrix */
3758 for (k = 0; k < nrows; ++k)
3759 {
3761 }
3762
3763 /* build the matrix containing the actual variables of the orbitope */
3765 propdata->permvars, propdata->npermvars, orbitopevaridx, columnorder,
3767
3768 assert( ! infeasible );
3769 assert( firstvaridx == NULL || propdata->permvars[*firstvaridx] == orbitopevarmatrix[0][0] );
3770
3771 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "suborbitope_%d_%d", graphcoloridx, propdata->norbitopes);
3772
3774 SCIP_ORBITOPETYPE_FULL, nrows, ngencols, FALSE, mayinteract, FALSE, FALSE, propdata->conssaddlp,
3776
3777 SCIP_CALL( SCIPaddCons(scip, cons) );
3778 *success = TRUE;
3779
3780 /* do not release constraint here - will be done later */
3781 propdata->genorbconss[propdata->ngenorbconss++] = cons;
3782 ++propdata->norbitopes;
3783
3784 for (k = nrows - 1; k >= 0; --k)
3789 for (k = nrows - 1; k >= 0; --k)
3793
3794 return SCIP_OKAY;
3795}
3796
3797/** adds strong SBCs for a suitable color of the subgroup graph */
3798static
3800 SCIP* scip, /**< SCIP instance */
3801 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3802 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3803 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3804 int graphcompidx, /**< index of the graph component */
3805 SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3806 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3807 int* nvarsorder, /**< number of variables in lexicographic order */
3808 int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3809 )
3810{
3811 int k;
3812
3813 assert( scip != NULL );
3814 assert( propdata != NULL );
3817 assert( graphcompidx >= 0 );
3818 assert( ! storelexorder || lexorder != NULL );
3821
3822 /* possibly store lexicographic order defined by strong SBCs */
3823 if ( storelexorder )
3824 {
3825 if ( *maxnvarsorder == 0 )
3826 {
3828 *nvarsorder = 0;
3829
3831 }
3832 else
3833 {
3835
3837
3839 }
3840
3842 }
3843
3844 /* add strong SBCs (lex-max order) for chosen graph component */
3846 {
3847 char name[SCIP_MAXSTRLEN];
3848 SCIP_CONS* cons;
3849 SCIP_VAR* vars[2];
3850 SCIP_Real vals[2] = {1, -1};
3851
3852 vars[0] = propdata->permvars[graphcomponents[k-1]];
3853 vars[1] = propdata->permvars[graphcomponents[k]];
3854
3855 if ( storelexorder )
3856 (*lexorder)[*nvarsorder++] = graphcomponents[k];
3857
3858 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "strong_sbcs_%s_%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3859
3860 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
3861 SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
3863
3864 SCIP_CALL( SCIPaddCons(scip, cons) );
3865
3866#ifdef SCIP_MORE_DEBUG
3867 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3868 SCIPinfoMessage(scip, NULL, "\n");
3869#endif
3870
3871 /* check whether we need to resize */
3872 if ( propdata->ngenlinconss >= propdata->genlinconsssize )
3873 {
3874 int newsize;
3875
3876 newsize = SCIPcalcMemGrowSize(scip, propdata->ngenlinconss + 1);
3877 assert( newsize > propdata->ngenlinconss );
3878
3879 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize, newsize) );
3880
3881 propdata->genlinconsssize = newsize;
3882 }
3883
3884 propdata->genlinconss[propdata->ngenlinconss] = cons;
3885 ++propdata->ngenlinconss;
3886 }
3887
3888 return SCIP_OKAY;
3889}
3890
3891/** adds weak SBCs for a suitable color of the subgroup graph */
3892static
3894 SCIP* scip, /**< SCIP instance */
3895 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
3896 int* compcolorbegins, /**< array indicating where a new graphcolor begins */
3897 int* graphcompbegins, /**< array indicating where a new graphcomponent begins */
3898 int* graphcomponents, /**< array of all variable indices sorted by color and comp */
3899 int ncompcolors, /**< number of colors in the graph */
3900 int* chosencomppercolor, /**< array indicating which comp was handled per color */
3901 int* firstvaridxpercolor,/**< array indicating the largest variable per color */
3902 int symgrpcompidx, /**< index of the component of the symmetry group */
3903 int* naddedconss, /**< buffer to store the number of added constraints */
3904 SCIP_Bool storelexorder, /**< whether the lexicographic order induced by the orbitope shall be stored */
3905 int** lexorder, /**< pointer to array storing lexicographic order defined by sub orbitopes */
3906 int* nvarsorder, /**< number of variables in lexicographic order */
3907 int* maxnvarsorder /**< maximum number of variables in lexicographic order */
3908 )
3909{ /*lint --e{571}*/
3912 SCIP_VAR* vars[2];
3913 SCIP_Real vals[2] = {1, -1};
3915 int* orbit[2];
3916 int orbitsize[2] = {1, 1};
3917 int activeorb = 0;
3918 int chosencolor = -1;
3919 int j;
3920 int k;
3921
3922 assert( scip != NULL );
3923 assert( propdata != NULL );
3929 assert( naddedconss != NULL );
3930 assert( symgrpcompidx >= 0 );
3931 assert( symgrpcompidx < propdata->ncomponents );
3932 assert( ! storelexorder || lexorder != NULL );
3935
3936 *naddedconss = 0;
3937
3938 SCIP_CALL( SCIPallocCleanBufferArray(scip, &usedvars, propdata->npermvars) );
3939 SCIP_CALL( SCIPallocClearBufferArray(scip, &varfound, propdata->npermvars) );
3940 SCIP_CALL( SCIPallocBufferArray(scip, &orbit[0], propdata->npermvars) );
3941 SCIP_CALL( SCIPallocBufferArray(scip, &orbit[1], propdata->npermvars) );
3942
3943 /* Store the entries in lexorder in a hashmap, for fast lookups. */
3944 if ( lexorder == NULL || *lexorder == NULL )
3945 {
3946 /* Lexorder does not exist, so do not create hashmap. */
3948 }
3949 else
3950 {
3951 assert( *maxnvarsorder >= 0 );
3952 assert( *nvarsorder >= 0 );
3953
3955
3956 for (k = 0; k < *nvarsorder; ++k)
3957 {
3958 /* add element from lexorder to hashmap.
3959 * Use insert, as duplicate entries in lexorder is not permitted. */
3960 assert((*lexorder)[k] >= 0);
3961 assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) (*lexorder)[k]) ); /* Use int as pointer */
3962 SCIP_CALL( SCIPhashmapInsertInt(varsinlexorder, (void*) (size_t) (*lexorder)[k], k) );
3963 }
3964 }
3965
3966 /* We will store the newest and the largest orbit and activeorb will be used to mark at which entry of the array
3967 * orbit the newly computed one will be stored. */
3968 for (j = 0; j < ncompcolors; ++j)
3969 {
3970 int graphcomp;
3971 int graphcompsize;
3972 int varidx;
3973
3974 /* skip color for which we did not add anything */
3975 if ( chosencomppercolor[j] < 0 )
3976 continue;
3977
3978 assert( firstvaridxpercolor[j] >= 0 );
3979
3983 assert(varidx >= 0);
3984
3985 /* if the first variable was already contained in another orbit or if there are no variables left anyway, skip the
3986 * component */
3987 if ( varfound[varidx] || graphcompsize == propdata->npermvars )
3988 continue;
3989
3990 /* If varidx is in lexorder, then it must be the first entry of lexorder. */
3991 if ( varsinlexorder != NULL
3992 && SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx)
3993 && lexorder != NULL && *lexorder != NULL && *maxnvarsorder > 0 && *nvarsorder > 0
3994 && (*lexorder)[0] != varidx )
3995 continue;
3996
3997 /* mark all variables that have been used in strong SBCs */
3999 {
4000 assert( 0 <= graphcomponents[k] && graphcomponents[k] < propdata->npermvars );
4001
4003 }
4004
4005 SCIP_CALL( SCIPcomputeOrbitVar(scip, propdata->npermvars, propdata->perms,
4006 propdata->permstrans, propdata->components, propdata->componentbegins,
4008 orbit[activeorb], &orbitsize[activeorb]) );
4009
4010 assert( orbit[activeorb][0] == varidx );
4011
4012 if ( orbitsize[activeorb] > orbitsize[1 - activeorb] ) /*lint !e514*/
4013 {
4014 /* if the new orbit is larger then the old largest one, flip activeorb */
4015 activeorb = 1 - activeorb;
4016 chosencolor = j;
4017 }
4018
4019 /* reset array */
4022 }
4023
4024 /* check if we have found at least one non-empty orbit */
4025 if ( chosencolor > -1 )
4026 {
4027 /* flip activeorb again to avoid confusion, it is then at the largest orbit */
4028 activeorb = 1 - activeorb;
4029
4031 vars[0] = propdata->permvars[orbit[activeorb][0]];
4032
4033 assert( chosencolor > -1 );
4034 SCIPdebugMsg(scip, " adding %d weak sbcs for enclosing orbit of color %d.\n", orbitsize[activeorb]-1, chosencolor);
4035
4036 *naddedconss = orbitsize[activeorb] - 1;
4037
4038 /* add weak SBCs for rest of enclosing orbit */
4039 for (j = 1; j < orbitsize[activeorb]; ++j)
4040 {
4041 SCIP_CONS* cons;
4042 char name[SCIP_MAXSTRLEN];
4043
4044 vars[1] = propdata->permvars[orbit[activeorb][j]];
4045
4046 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "weak_sbcs_%d_%s_%s", symgrpcompidx, SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
4047
4048 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, 0.0,
4049 SCIPinfinity(scip), propdata->conssaddlp, propdata->conssaddlp, TRUE,
4051
4052 SCIP_CALL( SCIPaddCons(scip, cons) );
4053
4054#ifdef SCIP_MORE_DEBUG
4055 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
4056 SCIPinfoMessage(scip, NULL, "\n");
4057#endif
4058
4059 /* check whether we need to resize */
4060 if ( propdata->ngenlinconss >= propdata->genlinconsssize )
4061 {
4062 int newsize;
4063
4064 newsize = SCIPcalcMemGrowSize(scip, propdata->ngenlinconss + 1);
4065 assert( newsize > propdata->ngenlinconss );
4066
4067 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize, newsize) );
4068
4069 propdata->genlinconsssize = newsize;
4070 }
4071
4072 propdata->genlinconss[propdata->ngenlinconss] = cons;
4073 ++propdata->ngenlinconss;
4074 }
4075
4076 /* possibly store lexicographic order defined by weak SBCs */
4077 if ( storelexorder )
4078 {
4079 int varidx;
4080
4081 varidx = orbit[activeorb][0];
4082 assert(varidx >= 0);
4083
4084 if ( *maxnvarsorder == 0 )
4085 {
4086 *maxnvarsorder = 1;
4087 *nvarsorder = 0;
4088
4090 (*lexorder)[(*nvarsorder)++] = varidx;
4091 }
4092 else
4093 {
4096 assert( lexorder != NULL );
4097 assert( *lexorder != NULL );
4098
4099 /* the leader of the weak inequalities has to be the first element in the lexicographic order */
4100 if ( varidx == (*lexorder)[0] )
4101 {
4102 /* lexorder is already ok!! */
4103 assert( SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
4104 }
4105 else
4106 {
4107 /* Then varidx must not be in the lexorder,
4108 * We must add it at the front of the array, and maintain the current order. */
4109 assert( ! SCIPhashmapExists(varsinlexorder, (void*) (size_t) varidx) );
4110
4111 ++(*maxnvarsorder);
4112 ++(*nvarsorder);
4113
4115
4116 /* Shift array by one position to the right */
4117 for (k = *maxnvarsorder - 1; k >= 1; --k)
4118 (*lexorder)[k] = (*lexorder)[k - 1];
4119
4120 (*lexorder)[0] = varidx;
4121 }
4122 }
4123 }
4124 }
4125 else
4126 SCIPdebugMsg(scip, " no further weak sbcs are valid\n");
4127
4130 if ( varsinlexorder != NULL )
4134
4135 return SCIP_OKAY;
4136}
4137
4138
4139/** temporarily adapt symmetry data to new variable order given by Schreier Sims */
4140static
4142 SCIP* scip, /**< SCIP instance */
4143 int** origperms, /**< permutation matrix w.r.t. original variable ordering */
4144 int** modifiedperms, /**< memory for permutation matrix w.r.t. new variable ordering */
4145 int nperms, /**< number of permutations */
4146 SCIP_VAR** origpermvars, /**< array of permutation vars w.r.t. original variable ordering */
4147 SCIP_VAR** modifiedpermvars, /**< memory for array of permutation vars w.r.t. new variable ordering */
4148 int npermvars, /**< length or modifiedpermvars array */
4149 int* leaders, /**< leaders of Schreier Sims constraints */
4150 int nleaders /**< number of leaders */
4151 )
4152{
4153 int* permvaridx;
4154 int* posinpermvar;
4155 int leader;
4156 int curposleader;
4157 int varidx;
4158 int lidx;
4159 int i;
4160 int l;
4161 int p;
4162
4163 assert( scip != NULL );
4164 assert( origperms != NULL );
4166 assert( nperms > 0 );
4167 assert( origpermvars != NULL );
4169 assert( npermvars > 0 );
4170 assert( leaders != NULL );
4171 assert( nleaders > 0 );
4172
4173 /* initialize map from position in lexicographic order to index of original permvar */
4175 for (i = 0; i < npermvars; ++i)
4176 permvaridx[i] = i;
4177
4178 /* initialize map from permvaridx to its current position in the reordered permvars array */
4180 for (i = 0; i < npermvars; ++i)
4181 posinpermvar[i] = i;
4182
4183 /* Iterate over leaders and put the l-th leader to the l-th position of the lexicographic order.
4184 * We do this by swapping the l-th leader with the element at position l of the current permvars array. */
4185 for (l = 0; l < nleaders; ++l)
4186 {
4187 leader = leaders[l];
4190 lidx = permvaridx[l];
4191
4192 /* swap the permvar at position l with the l-th leader */
4194 permvaridx[l] = varidx;
4195
4196 /* update the position map */
4199 }
4200
4201 /* update the permvars array to new variable order */
4202 for (i = 0; i < npermvars; ++i)
4204
4205 /* update the permutation to the new variable order */
4206 for (p = 0; p < nperms; ++p)
4207 {
4208 for (i = 0; i < npermvars; ++i)
4210 }
4211
4214
4215 return SCIP_OKAY;
4216}
4217
4218
4219/* returns the number of found orbitopes with at least three columns per graph component or 0
4220 * if the found orbitopes do not satisfy certain criteria for being used
4221 */
4222static
4224 SCIP_VAR** permvars, /**< array of variables affected by symmetry */
4225 int* graphcomponents, /**< array of graph components */
4226 int* graphcompbegins, /**< array indicating starting position of graph components */
4227 int* compcolorbegins, /**< array indicating starting positions of potential orbitopes */
4228 int ncompcolors, /**< number of components encoded in compcolorbegins */
4229 int symcompsize /**< size of symmetry component for that we detect suborbitopes */
4230 )
4231{
4232 SCIP_Bool oneorbitopecriterion = FALSE;
4233 SCIP_Bool multorbitopecriterion = FALSE;
4234 int norbitopes = 0;
4235 int j;
4236
4239 assert( ncompcolors >= 0 );
4240 assert( symcompsize > 0 );
4241
4242 for (j = 0; j < ncompcolors; ++j)
4243 {
4245 int largestcompsize = 0;
4246 int nbinrows= 0;
4247 int k;
4248
4249 /* skip trivial components */
4251 continue;
4252
4253 /* check whether components of this color build an orbitope (with > 2 columns) */
4254 for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4255 {
4256 int compsize;
4257
4259
4260 /* the first component that we are looking at for this color */
4261 if ( largestcompsize < 1 )
4262 {
4263 if ( compsize < 3 )
4264 break;
4265
4267 }
4268 else if ( compsize != largestcompsize )
4269 break;
4270
4272
4273 /* count number of binary orbits (comps) */
4275 ++nbinrows;
4276 }
4277
4278 /* we have found an orbitope */
4279 if ( k == compcolorbegins[j+1] )
4280 {
4281 SCIP_Real threshold;
4282 int ncols;
4283
4284 ++norbitopes;
4286
4288
4289 /* check whether criteria for adding orbitopes are satisfied */
4290 if ( nbinrows <= 2 * ncols || (nbinrows <= 8 * ncols && nbinrows < 100) )
4292 else if ( nbinrows <= 3 * ncols || (SCIP_Real) nbinrows * ncols >= threshold )
4294 }
4295 }
4296
4297 if ( (norbitopes == 1 && oneorbitopecriterion) || (norbitopes >= 2 && multorbitopecriterion) )
4298 return norbitopes;
4299
4300 return 0;
4301}
4302
4303
4304/** checks whether subgroups of the components are symmetric groups and adds SBCs for them */
4305static
4307 SCIP* scip, /**< SCIP instance */
4308 SCIP_PROPDATA* propdata /**< pointer to data of symmetry propagator */
4309 )
4310{
4311 int* genorder;
4312 int i;
4313#ifdef SCIP_DEBUG
4314 int norbitopes = 0;
4315 int nstrongsbcs = 0;
4316 int nweaksbcs = 0;
4317#endif
4318 int** modifiedperms;
4320 int* nvarsincomponent;
4321
4322 assert( scip != NULL );
4323 assert( propdata != NULL );
4324 assert( propdata->computedsymmetry );
4325 assert( propdata->components != NULL );
4326 assert( propdata->componentbegins != NULL );
4327 assert( propdata->ncomponents > 0 );
4328 assert( propdata->nperms >= 0 );
4329
4330 /* exit if no symmetry is present */
4331 if ( propdata->nperms == 0 )
4332 return SCIP_OKAY;
4333
4334 /* exit if instance is too large */
4335 if ( SCIPgetNConss(scip) > propdata->maxnconsssubgroup )
4336 return SCIP_OKAY;
4337
4338 assert( propdata->nperms > 0 );
4339 assert( propdata->perms != NULL );
4340 assert( propdata->npermvars > 0 );
4341 assert( propdata->permvars != NULL );
4342
4343 /* create array for permutation order */
4344 SCIP_CALL( SCIPallocBufferArray(scip, &genorder, propdata->nperms) );
4345
4346 /* create arrays for modified permutations in case we adapt the lexicographic order because of suborbitopes */
4347 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms, propdata->nperms) );
4348 for (i = 0; i < propdata->nperms; ++i)
4349 {
4350 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedperms[i], propdata->npermvars) );
4351 }
4352 SCIP_CALL( SCIPallocBufferArray(scip, &modifiedpermvars, propdata->npermvars) );
4353
4354 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarsincomponent, propdata->npermvars) );
4355 for (i = 0; i < propdata->npermvars; ++i)
4356 {
4357 if ( propdata->vartocomponent[i] >= 0 )
4358 ++nvarsincomponent[propdata->vartocomponent[i]];
4359 }
4360
4361 SCIPdebugMsg(scip, "starting subgroup detection routine for %d components\n", propdata->ncomponents);
4362
4363 /* iterate over components */
4364 for (i = 0; i < propdata->ncomponents; ++i)
4365 {
4366 int* graphcomponents;
4367 int* graphcompbegins;
4368 int* compcolorbegins;
4369 int* chosencomppercolor = NULL;
4371 int* usedperms;
4372 int usedpermssize;
4373 int ngraphcomponents;
4374 int ncompcolors;
4375 int ntwocycleperms;
4376 int npermsincomp;
4377 int nusedperms;
4378 int ntrivialcolors = 0;
4379 int j;
4380 int* lexorder = NULL;
4381 int nvarslexorder = 0;
4382 int maxnvarslexorder = 0;
4384 SCIP_Bool allpermsused = FALSE;
4385 SCIP_Bool handlednonbinarysymmetry = FALSE;
4386 int norbitopesincomp;
4387
4388 /* if component is blocked, skip it */
4389 if ( propdata->componentblocked[i] )
4390 {
4391 SCIPdebugMsg(scip, "component %d has already been handled and will be skipped\n", i);
4392 continue;
4393 }
4394
4395 npermsincomp = propdata->componentbegins[i + 1] - propdata->componentbegins[i];
4396
4397 /* set the first npermsincomp entries of genorder; the others are not used for this component */
4398 for (j = 0; j < npermsincomp; ++j)
4399 genorder[j] = j;
4400
4402
4403 assert( ntwocycleperms >= 0 );
4405
4406 SCIPdebugMsg(scip, "component %d has %d permutations consisting of 2-cycles\n", i, ntwocycleperms);
4407
4408#ifdef SCIP_MORE_DEBUG
4409 SCIP_Bool* used;
4410 int perm;
4411 int p;
4412 int k;
4413
4414 SCIP_CALL( SCIPallocBufferArray(scip, &used, propdata->npermvars) );
4415 for (p = propdata->componentbegins[i]; p < propdata->componentbegins[i+1]; ++p)
4416 {
4417 perm = propdata->components[p];
4418
4419 for (k = 0; k < propdata->npermvars; ++k)
4420 used[k] = FALSE;
4421
4422 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "permutation %d\n", perm);
4423
4424 for (k = 0; k < propdata->npermvars; ++k)
4425 {
4426 if ( used[k] )
4427 continue;
4428
4429 j = propdata->perms[perm][k];
4430
4431 if ( k == j )
4432 continue;
4433
4434 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "(%s,", SCIPvarGetName(propdata->permvars[k]));
4435 used[k] = TRUE;
4436 while (j != k)
4437 {
4438 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%s,", SCIPvarGetName(propdata->permvars[j]));
4439 used[j] = TRUE;
4440
4441 j = propdata->perms[perm][j];
4442 }
4444 }
4446 }
4447
4448 SCIPfreeBufferArray(scip, &used);
4449#endif
4450
4451 if ( ntwocycleperms < 2 )
4452 {
4453 SCIPdebugMsg(scip, " --> skip\n");
4454 continue;
4455 }
4456
4460
4464
4465 SCIPdebugMsg(scip, " created subgroup detection graph using %d of the permutations\n", nusedperms);
4466
4467 if ( nusedperms == npermsincomp )
4469
4473 assert( ngraphcomponents > 0 );
4474 assert( ncompcolors > 0 );
4476 assert( ncompcolors < propdata->npermvars );
4477
4478 if ( nusedperms == 0 )
4479 {
4480 SCIPdebugMsg(scip, " -> skipping component, since less no permutation was used\n");
4481
4484
4485 continue;
4486 }
4487
4488 SCIPdebugMsg(scip, " number of different colors in the graph: %d\n", ncompcolors);
4489
4490 if ( propdata->addstrongsbcs || propdata->addweaksbcs )
4491 {
4494
4495 /* Initialize the arrays with -1 to encode that we have not added orbitopes/strong SBCs
4496 * yet. In case we do not modify this entry, no weak inequalities are added based on
4497 * this component.
4498 */
4499 for (j = 0; j < ncompcolors; ++j)
4500 {
4501 chosencomppercolor[j] = -1;
4502 firstvaridxpercolor[j] = -1;
4503 }
4504 }
4505
4508
4509 /* if there is just one orbitope satisfying the requirements, handle the full component by symresacks */
4510 if ( norbitopesincomp == 1 )
4511 {
4512 int k;
4513
4514 for (k = 0; k < npermsincomp; ++k)
4515 {
4516 SCIP_CONS* cons;
4517 char name[SCIP_MAXSTRLEN];
4518
4519 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4520
4521 SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, propdata->perms[propdata->components[propdata->componentbegins[i] + k]],
4522 propdata->permvars, propdata->npermvars, FALSE,
4523 propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4524 SCIP_CALL( SCIPaddCons(scip, cons));
4525
4526 /* do not release constraint here - will be done later */
4527 propdata->genorbconss[propdata->ngenorbconss++] = cons;
4528 ++propdata->nsymresacks;
4529
4530 if ( ! propdata->componentblocked[i] )
4531 {
4532 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4533 ++propdata->ncompblocked;
4534 }
4535
4536 SCIPdebugMsg(scip, " add symresack for permutation %d of component %d\n", k, i);
4537 }
4538
4539 goto FREELOOPMEMORY;
4540 }
4541
4542 for (j = 0; j < ncompcolors; ++j)
4543 {
4544 int nbinarycomps = 0;
4545 int largestcolorcomp = -1;
4546 int largestcompsize = 0;
4547 int k;
4548 SCIP_Bool isorbitope = TRUE;
4549 SCIP_Bool orbitopeadded = FALSE;
4550 SCIP_Bool useorbitope;
4551#ifdef SCIP_DEBUG
4552 SCIP_Bool binaffected = FALSE;
4553 SCIP_Bool intaffected = FALSE;
4554 SCIP_Bool contaffected = FALSE;
4555#endif
4556
4557 /* skip trivial components */
4559 {
4560 if( chosencomppercolor != NULL )
4561 chosencomppercolor[j] = -1;
4562
4564 continue;
4565 }
4566
4567 SCIPdebugMsg(scip, " color %d has %d components with overall %d variables\n", j, compcolorbegins[j+1] - compcolorbegins[j],
4569
4570 /* check whether components of this color might build an orbitope (with > 2 columns) */
4571 for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4572 {
4574 int compsize;
4575
4577
4578 /* the first component that we are looking at for this color */
4579 if ( largestcompsize < 1 )
4580 {
4581 if ( compsize < 3 )
4582 {
4583 isorbitope = FALSE;
4584 break;
4585 }
4586
4589 }
4590 else if ( compsize != largestcompsize )
4591 {
4592 /* variable orbits (compsize) have not the same size, cannot define orbitope */
4593 isorbitope = FALSE;
4594 break;
4595 }
4596
4597 firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4598
4599 /* count number of binary orbits (comps) */
4601 ++nbinarycomps;
4602 }
4603
4604#ifdef SCIP_DEBUG
4605 for (k = compcolorbegins[j]; k < compcolorbegins[j+1]; ++k)
4606 {
4608
4609 firstvar = propdata->permvars[graphcomponents[graphcompbegins[k]]];
4610
4612 binaffected = TRUE;
4613 else if (SCIPvarIsIntegral(firstvar) )
4614 intaffected = TRUE;
4615 else
4617 }
4618
4619 SCIPdebugMsg(scip, " affected types (bin,int,cont): (%d,%d,%d)\n", binaffected, intaffected, contaffected);
4620#endif
4621
4622 /* only use the orbitope if there are binary rows */
4624 if ( norbitopesincomp > 0 && nbinarycomps > 0 )
4625 useorbitope = TRUE;
4626
4627 if ( isorbitope && useorbitope )
4628 {
4629 int firstvaridx;
4630 int chosencomp;
4631
4632 SCIPdebugMsg(scip, " detected an orbitope with %d rows and %d columns\n", nbinarycomps, largestcompsize);
4633
4634 assert( nbinarycomps > 0 );
4635 assert( largestcompsize > 2 );
4636
4637 /* add the orbitope constraint for this color
4638 *
4639 * It might happen that we cannot generate the orbitope matrix if the orbitope is not generated by permutations
4640 * all having the same number of 2-cycles, e.g., the orbitope generated by (1,2)(4,5), (2,3), (5,6).
4641 */
4645
4646 if ( orbitopeadded )
4647 {
4648 if ( propdata->addstrongsbcs || propdata->addweaksbcs )
4649 {
4652
4653 /* adapt the first variable per color to be compatible with the created orbiope (upper left variable) */
4655 assert( 0 <= firstvaridx && firstvaridx < propdata->npermvars );
4656
4659 }
4660
4661 if ( ! propdata->componentblocked[i] )
4662 {
4663 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4664 ++propdata->ncompblocked;
4665 }
4666
4667#ifdef SCIP_DEBUG
4668 ++norbitopes;
4669#endif
4670 }
4671 }
4672
4673 /* if no (useable) orbitope was found, possibly add strong SBCs */
4674 if ( propdata->addstrongsbcs && ! orbitopeadded )
4675 {
4676 assert( largestcolorcomp >= 0 );
4678 assert( largestcompsize > 0 );
4679
4680 if( propdata->addweaksbcs )
4681 {
4684
4687 }
4688
4689 SCIPdebugMsg(scip, " choosing component %d with %d variables and adding strong SBCs\n",
4691
4692 /* add the strong SBCs for the corresponding component */
4694 propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4695
4696 /* store whether symmetries on non-binary symmetries have been handled */
4697 if ( ! SCIPvarIsBinary(propdata->permvars[graphcomponents[graphcompbegins[largestcolorcomp]]]) )
4699
4700 if ( ! propdata->componentblocked[i] )
4701 {
4702 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4703 ++propdata->ncompblocked;
4704 }
4705
4706#ifdef SCIP_DEBUG
4708#endif
4709 }
4710 else if ( ! orbitopeadded )
4711 {
4712 SCIPdebugMsg(scip, " no useable orbitope found and no SBCs added\n");
4713
4714 /* mark the color as not handled */
4715 if ( propdata->addweaksbcs )
4716 {
4718 chosencomppercolor[j] = -1; /*lint !e613*/
4719 }
4720 }
4721 }
4722
4723 SCIPdebugMsg(scip, " skipped %d trivial colors\n", ntrivialcolors);
4724
4725 /* possibly add weak SBCs for enclosing orbit of first component */
4726 if ( propdata->addweaksbcs && propdata->componentblocked[i] && nusedperms < npermsincomp )
4727 {
4728 int naddedconss;
4729
4732
4735 i, &naddedconss, propdata->addsymresacks, &lexorder, &nvarslexorder, &maxnvarslexorder) );
4736
4737 assert( naddedconss < propdata->npermvars );
4738
4739#ifdef SCIP_DEBUG
4740 nweaksbcs += naddedconss;
4741#endif
4742 }
4743 else
4744 SCIPdebugMsg(scip, " don't add weak sbcs because all generators were used or the settings forbid it\n");
4745
4746 /* if suborbitopes or strong group actions have been found, potentially add symresacks adapted to
4747 * variable order given by lexorder if no symmetries on non-binary variables have been handled
4748 */
4749 if ( nvarslexorder > 0 && propdata->addsymresacks && ! handlednonbinarysymmetry )
4750 {
4751 int k;
4752
4753 SCIP_CALL( adaptSymmetryDataSST(scip, propdata->perms, modifiedperms, propdata->nperms,
4754 propdata->permvars, modifiedpermvars, propdata->npermvars, lexorder, nvarslexorder) );
4755
4756 for (k = 0; k < npermsincomp; ++k)
4757 {
4758 SCIP_CONS* cons;
4759 char name[SCIP_MAXSTRLEN];
4760
4761 /* skip permutations that have been used to build an orbitope */
4762 if ( permused[k] )
4763 continue;
4764
4765 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symresack_comp%d_perm%d", i, k);
4766
4767 SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, modifiedperms[propdata->components[propdata->componentbegins[i] + k]],
4768 modifiedpermvars, propdata->npermvars, FALSE,
4769 propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
4770 SCIP_CALL( SCIPaddCons(scip, cons));
4771
4772 /* do not release constraint here - will be done later */
4773 propdata->genorbconss[propdata->ngenorbconss++] = cons;
4774 ++propdata->nsymresacks;
4775
4776 if ( ! propdata->componentblocked[i] )
4777 {
4778 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
4779 ++propdata->ncompblocked;
4780 }
4781
4782 SCIPdebugMsg(scip, " add symresack for permutation %d of component %d adapted to suborbitope lexorder\n", k, i);
4783 }
4784 }
4785
4788
4793 SCIPfreeBlockMemoryArrayNull(scip, &graphcomponents, propdata->npermvars);
4796 }
4797
4798#ifdef SCIP_DEBUG
4799 SCIPdebugMsg(scip, "total number of added (sub-)orbitopes: %d\n", norbitopes);
4800 SCIPdebugMsg(scip, "total number of added strong sbcs: %d\n", nstrongsbcs);
4801 SCIPdebugMsg(scip, "total number of added weak sbcs: %d\n", nweaksbcs);
4802#endif
4803
4805
4807 for (i = propdata->nperms - 1; i >= 0; --i)
4808 {
4810 }
4813
4814 return SCIP_OKAY;
4815}
4816
4817
4818/*
4819 * Functions for symmetry constraints
4820 */
4821
4822
4823/** sorts orbitope vars matrix such that rows are sorted increasingly w.r.t. minimum variable index in row;
4824 * columns are sorted such that first row is sorted increasingly w.r.t. variable indices
4825 */
4826static
4828 SCIP* scip, /**< SCIP instance */
4829 int** orbitopevaridx, /**< variable index matrix of orbitope */
4830 SCIP_VAR*** vars, /**< variable matrix of orbitope */
4831 int nrows, /**< number of binary rows of orbitope */
4832 int ncols /**< number of columns of orbitope */
4833 )
4834{
4836 int* colorder;
4837 int* idcs;
4838 int arrlen;
4839 int minrowidx = INT_MAX;
4840 int minrow = INT_MAX;
4841 int i;
4842 int j;
4843
4844 assert( scip != NULL );
4846 assert( vars != NULL );
4847 assert( nrows > 0 );
4848 assert( ncols > 0 );
4849
4850 arrlen = MAX(nrows, ncols);
4852
4853 /* detect minimum index per row */
4854 for (i = 0; i < nrows; ++i)
4855 {
4856 int idx;
4857
4858 idcs[i] = INT_MAX;
4859
4860 for (j = 0; j < ncols; ++j)
4861 {
4862 idx = orbitopevaridx[i][j];
4863
4864 if ( idx < idcs[i] )
4865 idcs[i] = idx;
4866
4867 if ( idx < minrowidx )
4868 {
4869 minrowidx = idx;
4870 minrow = i;
4871 }
4872 }
4873 }
4874
4875 /* sort rows increasingly w.r.t. minimum variable indices */
4876 SCIPsortIntPtr(idcs, (void**) vars, nrows);
4877
4878 /* sort columns increasingly w.r.t. variable indices of first row */
4880 for (j = 0; j < ncols; ++j)
4881 {
4883 colorder[j] = j;
4884 }
4885
4886 /* sort columns of first row and store new column order */
4887 SCIPsortIntIntPtr(idcs, colorder, (void**) vars[0], ncols);
4888
4889 /* adapt rows 1, ..., nrows - 1 to new column order*/
4891 for (i = 1; i < nrows; ++i)
4892 {
4893 for (j = 0; j < ncols; ++j)
4894 sortedrow[j] = vars[i][colorder[j]];
4895 for (j = 0; j < ncols; ++j)
4896 vars[i][j] = sortedrow[j];
4897 }
4898
4902
4903 return SCIP_OKAY;
4904}
4905
4906
4907/** checks whether components of the symmetry group can be completely handled by orbitopes */
4908static
4910 SCIP* scip, /**< SCIP instance */
4911 SCIP_PROPDATA* propdata, /**< pointer to data of symmetry propagator */
4912 int* components, /**< array containing components of symmetry group */
4913 int* componentbegins, /**< array containing begin positions of components in components array */
4914 int ncomponents /**< number of components */
4915 )
4916{
4917 SCIP_VAR** permvars;
4918 int** perms;
4919 int npermvars;
4920 int i;
4921
4922 assert( scip != NULL );
4923 assert( propdata != NULL );
4924 assert( components != NULL );
4925 assert( componentbegins != NULL );
4926 assert( ncomponents > 0 );
4927 assert( propdata->nperms >= 0 );
4928
4929 /* exit if no symmetry is present */
4930 if ( propdata->nperms == 0 )
4931 return SCIP_OKAY;
4932
4933 assert( propdata->nperms > 0 );
4934 assert( propdata->perms != NULL );
4935 assert( propdata->nbinpermvars >= 0 );
4936 assert( propdata->npermvars >= 0 );
4937 assert( propdata->permvars != NULL );
4938
4939 /* exit if no symmetry on binary variables is present */
4940 if ( propdata->nbinpermvars == 0 )
4941 {
4942 assert( ! propdata->binvaraffected );
4943 return SCIP_OKAY;
4944 }
4945
4946 perms = propdata->perms;
4947 npermvars = propdata->npermvars;
4948 permvars = propdata->permvars;
4949
4950 /* iterate over components */
4951 for (i = 0; i < ncomponents; ++i)
4952 {
4953 SCIP_VAR*** vars;
4955 SCIP_CONS* cons;
4957 SCIP_Bool isorbitope = TRUE;
4958 SCIP_Bool infeasibleorbitope;
4959 int** orbitopevaridx;
4960 int* columnorder;
4962 int ntwocyclescomp = INT_MAX;
4963 int nbincyclescomp = INT_MAX;
4964 int* nusedelems;
4965 int j;
4966 int cnt;
4967
4968 /* orbitopes are detected first, so no component should be blocked */
4969 assert( ! propdata->componentblocked[i] );
4970
4971 /* get properties of permutations */
4972 npermsincomponent = componentbegins[i + 1] - componentbegins[i];
4974 for (j = componentbegins[i]; j < componentbegins[i + 1]; ++j)
4975 {
4976 int ntwocyclesperm = 0;
4977 int nbincyclesperm = 0;
4978
4979 SCIP_CALL( SCIPisInvolutionPerm(perms[components[j]], permvars, npermvars,
4981
4982 if ( ntwocyclesperm == 0 )
4983 {
4984 isorbitope = FALSE;
4985 break;
4986 }
4987
4988 /* if we are checking the first permutation */
4989 if ( ntwocyclescomp == INT_MAX )
4990 {
4993
4994 /* if there are no binary rows */
4995 if ( nbincyclescomp == 0 )
4996 {
4997 isorbitope = FALSE;
4998 break;
4999 }
5000 }
5001
5002 /* no or different number of 2-cycles or not all vars binary: permutations cannot generate orbitope */
5004 {
5005 isorbitope = FALSE;
5006 break;
5007 }
5008 }
5009
5010 /* if no orbitope was detected */
5011 if ( ! isorbitope )
5012 continue;
5013 assert( ntwocyclescomp > 0 );
5015
5016 /* iterate over permutations and check whether for each permutation there exists
5017 * another permutation whose 2-cycles intersect pairwise in exactly one element */
5018
5019 /* orbitope matrix for indices of variables in permvars array */
5021 for (j = 0; j < ntwocyclescomp; ++j)
5022 {
5024 }
5025
5026 /* order of columns of orbitopevaridx */
5028 for (j = 0; j < npermsincomponent + 1; ++j)
5030
5031 /* count how often an element was used in the potential orbitope */
5033
5034 /* store whether a row of the potential orbitope contains only binary variables */
5036
5037 /* check if the permutations fulfill properties of an orbitope */
5038 SCIP_CALL( checkTwoCyclePermsAreOrbitope(scip, permvars, npermvars, perms,
5039 &(components[componentbegins[i]]), ntwocyclescomp, npermsincomponent,
5041
5042 if ( ! isorbitope )
5043 goto FREEDATASTRUCTURES;
5044
5045 /* we have found a potential orbitope, prepare data for orbitope conshdlr */
5048 cnt = 0;
5049 for (j = 0; j < ntwocyclescomp; ++j)
5050 {
5051 if ( ! rowisbinary[j] )
5052 continue;
5053
5054 SCIP_CALL( SCIPallocBufferArray(scip, &vars[cnt], npermsincomponent + 1) ); /*lint !e866*/
5055 varsallocorder[cnt] = vars[cnt]; /* to ensure that we can free the buffer in reverse order */
5056 ++cnt;
5057 }
5058 assert( cnt == nbincyclescomp );
5059
5060 /* prepare variable matrix (reorder columns of orbitopevaridx) */
5064
5065 if ( ! infeasibleorbitope )
5066 {
5067 char name[SCIP_MAXSTRLEN];
5068
5069 SCIPdebugMsg(scip, "found an orbitope of size %d x %d in component %d\n", ntwocyclescomp, npermsincomponent + 1, i);
5070
5071 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "orbitope_component%d", i);
5072
5073 /* to ensure same orbitope is added if different sets of generators are found */
5075
5077 nbincyclescomp, npermsincomponent + 1, propdata->usedynamicprop, FALSE, FALSE, FALSE,
5078 propdata->conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5079
5080 SCIP_CALL( SCIPaddCons(scip, cons) );
5081
5082 /* do not release constraint here - will be done later */
5083 propdata->genorbconss[propdata->ngenorbconss++] = cons;
5084 ++propdata->norbitopes;
5085
5086 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5087 }
5088
5089 /* free data structures */
5090 for (j = nbincyclescomp - 1; j >= 0; --j)
5091 {
5093 }
5096
5101 for (j = ntwocyclescomp - 1; j >= 0; --j)
5102 {
5104 }
5106 }
5107
5108 return SCIP_OKAY;
5109}
5110
5111
5112/** update symmetry information of conflict graph */
5113static
5115 SCIP* scip, /**< SCIP instance */
5116 SCIP_DIGRAPH* conflictgraph, /**< conflict graph */
5117 SCIP_VAR** graphvars, /**< variables encoded in conflict graph (either all vars or permvars) */
5118 int ngraphvars, /**< number of nodes/vars in conflict graph */
5119 SCIP_VAR** permvars, /**< variables considered in permutations */
5120 int npermvars, /**< number of permvars */
5121 SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5122 SCIP_HASHMAP* varmap, /**< map from graphvar to node label in conflict graph
5123 * (or NULL if onlypermvars == TRUE) */
5124 int* orbits, /**< array of non-trivial orbits */
5125 int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
5126 int norbits /**< number of non-trivial orbits */
5127 )
5128{
5129 int i;
5130 int j;
5131
5132 assert( scip != NULL );
5133 assert( conflictgraph != NULL );
5134 assert( graphvars != NULL );
5135 assert( ngraphvars > 0 );
5136 assert( permvars != NULL );
5137 assert( npermvars > 0 );
5138 assert( onlypermvars || varmap != NULL );
5139 assert( orbits != NULL );
5140 assert( orbitbegins != NULL );
5141 assert( norbits >= 0 );
5142
5143 /* initialize/reset variable information of nodes in conflict graph */
5144 for (i = 0; i < ngraphvars; ++i)
5145 {
5147
5148 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5149
5150 /* possibly create node data */
5151 if ( nodedata == NULL )
5152 {
5154 nodedata->var = graphvars[i];
5155 nodedata->active = TRUE;
5156 }
5157
5158 /* (re-)set node data */
5159 nodedata->orbitidx = -1;
5160 nodedata->nconflictinorbit = 0;
5161 nodedata->orbitsize = -1;
5162 nodedata->posinorbit = -1;
5163
5164 /* set node data */
5165 SCIPdigraphSetNodeData(conflictgraph, (void*) nodedata, i);
5166 }
5167
5168 /* add orbit information to nodes of conflict graph */
5169 for (j = 0; j < norbits; ++j)
5170 {
5171 int posinorbit = 0;
5172 int orbitsize;
5173
5174 orbitsize = orbitbegins[j + 1] - orbitbegins[j];
5175 assert( orbitsize >= 0 );
5176
5177 for (i = orbitbegins[j]; i < orbitbegins[j + 1]; ++i)
5178 {
5180 SCIP_VAR* var;
5181 int pos;
5182
5183 /* get variable and position in conflict graph */
5184 if ( onlypermvars )
5185 {
5186 pos = orbits[i];
5187 var = permvars[pos];
5188 }
5189 else
5190 {
5191 var = permvars[orbits[i]];
5192 assert( var != NULL );
5193
5194 assert( SCIPhashmapExists(varmap, var) );
5195 pos = SCIPhashmapGetImageInt(varmap, var);
5196 assert( pos != INT_MAX );
5197 }
5198
5199 /* get node data */
5200 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, pos);
5201 assert( nodedata != NULL );
5202 assert( nodedata->var == var );
5203
5204 nodedata->orbitidx = j;
5205 nodedata->orbitsize = orbitsize;
5206 nodedata->posinorbit = posinorbit++;
5207 }
5208 }
5209
5210 /* add information on number of conflicts within orbit to conflict graph */
5211 for (i = 0; i < ngraphvars; ++i)
5212 {
5215 int* conflictvaridx;
5216 int nconflictinorbit = 0;
5217 int curorbit;
5218
5219 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5220 conflictvaridx = SCIPdigraphGetSuccessors(conflictgraph, i);
5221
5222 assert( nodedata != NULL );
5223 assert( nodedata->nconflictinorbit == 0 );
5224 assert( conflictvaridx != NULL || SCIPdigraphGetNSuccessors(conflictgraph, i) == 0 );
5225
5226 curorbit = nodedata->orbitidx;
5227
5228 /* i-th variable is fixed by all permutations */
5229 if ( curorbit == -1 )
5230 {
5231 nodedata->nconflictinorbit = 0;
5232 continue;
5233 }
5234
5235 /* get conflicts in orbit by couting the active neighbors of i in the same orbit */
5236 for (j = 0; j < SCIPdigraphGetNSuccessors(conflictgraph, i); ++j)
5237 {
5241
5242 if ( nodedataconflict->active && nodedataconflict->orbitidx == curorbit )
5243 ++nconflictinorbit;
5244 }
5245
5246 nodedata->nconflictinorbit = nconflictinorbit;
5247 }
5248
5249 return SCIP_OKAY;
5250}
5251
5252
5253/** create conflict graph either for symmetric or for all variables
5254 *
5255 * This routine just creates the graph, but does not add (symmetry) information to its nodes.
5256 * This has to be done separately by the routine updateSymInfoConflictGraphSST().
5257 */
5258static
5260 SCIP* scip, /**< SCIP instance */
5261 SCIP_DIGRAPH** conflictgraph, /**< pointer to store conflict graph */
5262 SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5263 int ngraphvars, /**< number of vars encoded in conflict graph */
5264 SCIP_Bool onlypermvars, /**< whether conflict graph contains only permvars */
5265 SCIP_HASHMAP* permvarmap, /**< map of variables to indices in permvars array (or NULL) */
5266 SCIP_Bool* success /**< pointer to store whether conflict graph could be created successfully */
5267 )
5268{
5272 SCIP_CONS* cons;
5273 int nsetppcconss;
5274 int nsetppcvars;
5275 int nodei;
5276 int nodej;
5277 int c;
5278 int i;
5279 int j;
5280 int nedges = 0;
5281
5282 assert( scip != NULL );
5283 assert( conflictgraph != NULL );
5284 assert( graphvars != NULL );
5285 assert( ngraphvars > 0 );
5286 assert( success != NULL );
5287
5288 *success = FALSE;
5289
5290 /* get setppcconss for creating conflict graph */
5292 if ( setppcconshdlr == NULL )
5293 {
5294 SCIPdebugMsg(scip, "Could not find setppc conshdlr --> construction of conflict graph aborted.\n");
5295 return SCIP_OKAY;
5296 }
5298
5301 if ( nsetppcconss == 0 )
5302 {
5303 SCIPdebugMsg(scip, "No setppc constraints present --> construction of conflict graph aborted.\n");
5304 return SCIP_OKAY;
5305 }
5306
5307 /* construct conflict graph */
5308 SCIP_CALL( SCIPcreateDigraph(scip, conflictgraph, ngraphvars) );
5309 *success = TRUE;
5310
5311 SCIPdebugMsg(scip, "Construction of conflict graph:\n");
5312
5313 for (c = 0; c < nsetppcconss; ++c)
5314 {
5315 cons = setppcconss[c];
5316 assert( cons != NULL );
5317
5318 /* skip covering constraints */
5320 continue;
5321
5324 assert( setppcvars != NULL );
5325 assert( nsetppcvars > 0 );
5326
5327 SCIPdebugMsg(scip, "\tAdd edges for constraint %s.\n", SCIPconsGetName(cons));
5328
5329 /* iterate over pairs of variables in constraint and add bidirected arc
5330 * if both are affected by a symmetry or active */
5331 for (i = 0; i < nsetppcvars; ++i)
5332 {
5333 if ( onlypermvars )
5334 {
5335 nodei = SCIPhashmapGetImageInt(permvarmap, setppcvars[i]);
5336
5337 /* skip variables that are not affected by symmetry */
5338 if ( nodei == INT_MAX )
5339 continue;
5340 }
5341 else
5342 {
5344
5345 /* skip inactive variables */
5346 if ( nodei < 0 )
5347 continue;
5348 }
5349
5350 for (j = i + 1; j < nsetppcvars; ++j)
5351 {
5352 if ( onlypermvars )
5353 {
5354 nodej = SCIPhashmapGetImageInt(permvarmap, setppcvars[j]);
5355
5356 /* skip variables that are not affected by symmetyr */
5357 if ( nodej == INT_MAX )
5358 continue;
5359 }
5360 else
5361 {
5363
5364 /* skip inactive variables */
5365 if ( nodej < 0 )
5366 continue;
5367 }
5368
5369 SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodei, nodej, NULL) );
5370 SCIP_CALL( SCIPdigraphAddArcSafe(*conflictgraph, nodej, nodei, NULL) );
5371 ++nedges;
5372 }
5373 }
5374 }
5375 SCIPdebugMsg(scip, "Construction of conflict graph terminated; %d conflicts detected.\n", nedges);
5376
5377 return SCIP_OKAY;
5378}
5379
5380
5381/** frees conflict graph */
5382static
5384 SCIP* scip, /**< SCIP instance */
5385 SCIP_DIGRAPH** conflictgraph, /**< conflict graph */
5386 int nnodes /**< number of nodes in conflict graph */
5387 )
5388{
5389 int i;
5390
5391 assert( scip != NULL );
5392 assert( conflictgraph != NULL );
5393 assert( *conflictgraph != NULL );
5394 assert( nnodes > 0 );
5395
5396 /* free node data */
5397 for (i = 0; i < nnodes; ++i)
5398 {
5400
5401 /* get node data */
5402 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(*conflictgraph, i);
5403
5404 /* free node data (might not have been allocated if all components are already blocked) */
5405 if ( nodedata != NULL )
5406 {
5408 }
5409 }
5410
5411 /* free conflict graph */
5412 SCIPdigraphFree(conflictgraph);
5413
5414 return SCIP_OKAY;
5415}
5416
5417
5418/** adds symresack constraints */
5419static
5421 SCIP* scip, /**< SCIP instance */
5422 SCIP_PROP* prop, /**< symmetry breaking propagator */
5423 int* components, /**< array containing components of symmetry group */
5424 int* componentbegins, /**< array containing begin positions of components in components array */
5425 int ncomponents /**< number of components */
5426 )
5427{ /*lint --e{641}*/
5428 SCIP_PROPDATA* propdata;
5429 SCIP_VAR** permvars;
5430 SCIP_Bool conssaddlp;
5431 int** modifiedperms = NULL;
5433 int** perms;
5434 int nsymresackcons = 0;
5435 int npermvars;
5436 int nperms;
5437 int i;
5438 int p;
5439
5440 assert( scip != NULL );
5441 assert( prop != NULL );
5442
5443 propdata = SCIPpropGetData(prop);
5444 assert( propdata != NULL );
5445 assert( propdata->npermvars >= 0 );
5446 assert( propdata->nbinpermvars >= 0 );
5447
5448 /* if no symmetries on binary variables are present */
5449 if ( propdata->nbinpermvars == 0 )
5450 {
5451 assert( propdata->binvaraffected == 0 );
5452 return SCIP_OKAY;
5453 }
5454
5455 perms = propdata->perms;
5456 nperms = propdata->nperms;
5457 permvars = propdata->permvars;
5458 npermvars = propdata->npermvars;
5459 conssaddlp = propdata->conssaddlp;
5460
5461 assert( nperms <= 0 || perms != NULL );
5462 assert( permvars != NULL );
5463 assert( npermvars > 0 );
5464
5465 /* adapt natural variable order to a variable order that is compatible with Schreier Sims constraints */
5466 if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5467 {
5469 for (p = 0; p < nperms; ++p)
5470 {
5472 }
5474
5475 for (i = 0; i < npermvars; ++i)
5476 modifiedpermvars[i] = permvars[i];
5477
5478 SCIP_CALL( adaptSymmetryDataSST(scip, perms, modifiedperms, nperms, permvars, modifiedpermvars, npermvars,
5479 propdata->leaders, propdata->nleaders) );
5480 }
5481
5482 /* if components have not been computed */
5483 if ( ncomponents == -1 )
5484 {
5485 assert( ! propdata->ofenabled );
5486 assert( ! propdata->detectorbitopes );
5487 assert( ! propdata->sstenabled );
5488
5489 /* loop through perms and add symresack constraints */
5490 for (p = 0; p < propdata->nperms; ++p)
5491 {
5492 SCIP_CONS* cons;
5493 char name[SCIP_MAXSTRLEN];
5494
5495 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_perm%d", p);
5496
5497 SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[p], permvars, npermvars, FALSE,
5498 conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5499
5500 SCIP_CALL( SCIPaddCons(scip, cons) );
5501
5502 /* do not release constraint here - will be done later */
5503 propdata->genorbconss[propdata->ngenorbconss++] = cons;
5504 ++propdata->nsymresacks;
5506 }
5507 }
5508 else
5509 {
5510 /* loop through components */
5511 for (i = 0; i < ncomponents; ++i)
5512 {
5513 SCIP_Bool sstcompatible = TRUE;
5514
5515 if ( ISSSTINTACTIVE(propdata->sstleadervartype)
5516 || ISSSTIMPLINTACTIVE(propdata->sstleadervartype)
5517 || ISSSTCONTACTIVE(propdata->sstleadervartype) )
5519
5520 /* skip components that were treated by incompatible symmetry handling techniques */
5521 if ( (propdata->componentblocked[i] & SYM_HANDLETYPE_SYMBREAK) != 0
5522 || (propdata->componentblocked[i] & SYM_HANDLETYPE_ORBITALFIXING) != 0
5523 || ((propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 && ! sstcompatible) )
5524 continue;
5525
5526 /* loop through perms in component i and add symresack constraints */
5527 for (p = componentbegins[i]; p < componentbegins[i + 1]; ++p)
5528 {
5529 SCIP_CONS* cons;
5530 int permidx;
5531 char name[SCIP_MAXSTRLEN];
5532
5533 permidx = components[p];
5534
5535 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "symbreakcons_component%d_perm%d", i, permidx);
5536
5537 /* adapt permutation to leader */
5538 if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5539 {
5540 assert( (propdata->componentblocked[i] & SYM_HANDLETYPE_SST) != 0 );
5543
5545 conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5546 }
5547 else
5548 {
5549 SCIP_CALL( SCIPcreateSymbreakCons(scip, &cons, name, perms[permidx], permvars, npermvars, FALSE,
5550 conssaddlp, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
5551 }
5552 propdata->componentblocked[i] |= SYM_HANDLETYPE_SYMBREAK;
5553 SCIP_CALL( SCIPaddCons(scip, cons) );
5554
5555 /* do not release constraint here - will be done later */
5556 propdata->genorbconss[propdata->ngenorbconss++] = cons;
5557 ++propdata->nsymresacks;
5559 }
5560 }
5561 }
5562
5563 if ( propdata->nleaders > 0 && ISSSTBINACTIVE(propdata->sstleadervartype) )
5564 {
5567
5569 for (p = nperms - 1; p >= 0; --p)
5570 {
5572 }
5574 }
5575
5576 SCIPdebugMsg(scip, "Added %d symresack constraints.\n", nsymresackcons);
5577
5578 return SCIP_OKAY;
5579}
5580
5581
5582/** add Schreier Sims constraints for a specific orbit and update Schreier Sims table */
5583static
5585 SCIP* scip, /**< SCIP instance */
5586 SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5587 SCIP_PROPDATA* propdata, /**< data of symmetry propagator */
5588 SCIP_VAR** permvars, /**< permvars array */
5589 int* orbits, /**< symmetry orbits */
5590 int* orbitbegins, /**< array storing begin position for each orbit */
5591 int orbitidx, /**< index of orbit for Schreier Sims constraints */
5592 int orbitleaderidx, /**< index of leader variable for Schreier Sims constraints */
5593 SCIP_Shortbool* orbitvarinconflict, /**< indicator whether orbitvar is in conflict with orbit leader */
5594 int norbitvarinconflict, /**< number of variables in conflict with orbit leader */
5595 int* nchgbds, /**< pointer to store number of bound changes (or NULL) */
5596 SCIP_Bool useconflictgraph /**< whether conflict graph shall be used */
5597 )
5598{ /*lint --e{613,641}*/
5599 SCIP_CONS* cons;
5600 char name[SCIP_MAXSTRLEN];
5601 SCIP_VAR* vars[2];
5602 SCIP_Real vals[2];
5603 int orbitsize;
5604 int posleader;
5605 int poscur;
5606 int ncuts = 0;
5607 SCIP_Bool addcuts = FALSE;
5608 int i;
5609#ifndef NDEBUG
5610 int j;
5611#endif
5612
5613 assert( scip != NULL );
5614 assert( conflictgraph != NULL || ! useconflictgraph );
5615 assert( propdata != NULL );
5616 assert( permvars != NULL );
5617 assert( orbits != NULL );
5618 assert( orbitbegins != NULL );
5619 assert( orbitidx >= 0 );
5620 assert( orbitleaderidx >= 0 );
5623 assert( nchgbds != NULL );
5624
5625 orbitsize = orbitbegins[orbitidx + 1] - orbitbegins[orbitidx];
5626
5627 /* variables in conflict with leader are fixed and not treated by a cut; trailing -1 to not count the leader */
5628 if ( propdata->sstaddcuts )
5629 addcuts = TRUE;
5630 else if ( propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTSINORBIT
5631 || propdata->sstleaderrule == SCIP_LEADERRULE_MAXCONFLICTS
5632 || propdata->ssttiebreakrule == SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT )
5633 addcuts = propdata->addconflictcuts;
5634
5635 if ( addcuts )
5636 ncuts = orbitsize - norbitvarinconflict - 1;
5637
5638 /* (re-)allocate memory for Schreier Sims constraints and leaders */
5639 if ( ncuts > 0 )
5640 {
5641 if ( propdata->nsstconss == 0 )
5642 {
5643 assert( propdata->sstconss == NULL );
5644 assert( propdata->maxnsstconss == 0 );
5645 propdata->maxnsstconss = 2 * ncuts;
5646 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->sstconss), propdata->maxnsstconss) );
5647 }
5648 else if ( propdata->nsstconss + ncuts > propdata->maxnsstconss )
5649 {
5650 int newsize;
5651
5652 newsize = SCIPcalcMemGrowSize(scip, propdata->maxnsstconss + 2 * ncuts);
5653 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &(propdata->sstconss),
5654 propdata->maxnsstconss, newsize) );
5655 propdata->maxnsstconss = newsize;
5656 }
5657 }
5658
5659 if ( propdata->nleaders == 0 )
5660 {
5661 propdata->maxnleaders = MIN(propdata->nperms, propdata->npermvars);
5662 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(propdata->leaders), propdata->maxnleaders) );
5663 }
5664 assert( propdata->nleaders < propdata->maxnleaders );
5665
5666 /* add Schreier Sims constraints vars[0] >= vars[1], where vars[0] is always the leader */
5667 posleader = orbitbegins[orbitidx] + orbitleaderidx;
5668 vars[0] = permvars[orbits[posleader]];
5669 vals[0] = -1.0;
5670 vals[1] = 1.0;
5671 propdata->leaders[propdata->nleaders++] = orbits[posleader];
5672 *nchgbds = 0;
5673 for (i = 0, poscur = orbitbegins[orbitidx]; i < orbitsize; ++i, ++poscur)
5674 {
5675 if ( i == orbitleaderidx )
5676 {
5678 continue;
5679 }
5680
5681 vars[1] = permvars[orbits[poscur]];
5682#ifndef NDEBUG
5683 for (j = 0; j < propdata->nleaders - 1; ++j)
5684 {
5685 assert( propdata->leaders[j] != orbits[poscur] );
5686 }
5687#endif
5688
5689 /* if the i-th variable in the orbit is in a conflict with the leader, fix it to 0 */
5690 if ( useconflictgraph )
5691 {
5692 if ( orbitvarinconflict[i] )
5693 {
5695 assert( SCIPvarGetLbLocal(vars[1]) < 0.5 );
5697
5698 /* if variable is fixed */
5699 if ( SCIPvarGetUbLocal(vars[1]) > 0.5 )
5700 {
5702
5703 SCIP_CALL( SCIPchgVarUb(scip, vars[1], 0.0) );
5704 ++(*nchgbds);
5705
5706 /* deactivate the fixed variable (cannot contribute to a conflict anymore) */
5707 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, orbits[poscur]);
5708 assert( nodedata != NULL );
5709 assert( nodedata->active );
5710
5711 nodedata->active = FALSE;
5712 }
5713
5714 /* reset value */
5716 }
5717 else if ( addcuts )
5718 {
5719 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5720 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5722
5723 SCIP_CALL( SCIPaddCons(scip, cons) );
5724 propdata->sstconss[propdata->nsstconss++] = cons;
5725 }
5726 }
5727 else if ( addcuts )
5728 {
5729 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "SSTcut_%d_%d", orbits[posleader], orbits[poscur]);
5730 SCIP_CALL( SCIPcreateConsLinear(scip, &cons, name, 2, vars, vals, - SCIPinfinity(scip), 0.0,
5732
5733 SCIP_CALL( SCIPaddCons(scip, cons) );
5734 propdata->sstconss[propdata->nsstconss++] = cons;
5735 }
5736 }
5737
5738 return SCIP_OKAY;
5739}
5740
5741
5742/** selection rule of next orbit/leader in orbit for Schreier Sims constraints */
5743static
5745 SCIP* scip, /**< SCIP instance */
5746 SCIP_DIGRAPH* conflictgraph, /**< conflict graph or NULL if useconflictgraph == FALSE */
5747 SCIP_VAR** graphvars, /**< variables encoded in conflict graph */
5748 int ngraphvars, /**< number of variables encoded in conflict graph */
5749 SCIP_HASHMAP* varmap, /**< map from variable to node label in conflict graph or NULL if useconflictgraph == FALSE */
5750 SCIP_VAR** permvars, /**< vars encoded in a permutation */
5751 int npermvars, /**< number of vars in a permutation */
5752 int* orbits, /**< orbits of stabilizer subgroup */
5753 int* orbitbegins, /**< array storing the begin position of each orbit in orbits */
5754 int norbits, /**< number of orbits */
5755 int leaderrule, /**< rule to select leader */
5756 int tiebreakrule, /**< tie break rule to select leader */
5757 SCIP_VARTYPE leadervartype, /**< variable type of leader */
5758 int* orbitidx, /**< pointer to index of selected orbit */
5759 int* leaderidx, /**< pointer to leader in orbit */
5760 SCIP_Shortbool* orbitvarinconflict, /**< array to store whether a var in the orbit is conflicting with leader */
5761 int* norbitvarinconflict, /**< pointer to store number of vars in the orbit in conflict with leader */
5762 SCIP_Bool useconflictgraph, /**< whether conflict graph shall be used */
5763 SCIP_Bool* success /**< pointer to store whether orbit cut be selected successfully */
5764 )
5765{
5767 int* conflictvars;
5768 int nconflictvars = 0;
5769 int varidx;
5770 int orbitcriterion;
5771 int curcriterion = INT_MIN;
5772 int orbitsize;
5773 int i;
5775 int leader = -1;
5776 int j;
5777
5778 assert( scip != NULL );
5779 assert( conflictgraph != NULL || ! useconflictgraph );
5780 assert( graphvars != NULL );
5781 assert( ngraphvars > 0 );
5782 assert( varmap != NULL || ! useconflictgraph );
5783 assert( permvars != NULL );
5784 assert( npermvars > 0 );
5785 assert( orbits != NULL );
5786 assert( orbitbegins != NULL );
5787 assert( norbits > 0 );
5788 assert( orbitidx != NULL );
5789 assert( leaderidx != NULL );
5792 assert( success != NULL );
5793
5794 *orbitidx = 0;
5795 *leaderidx = 0;
5797 *success = FALSE;
5798
5799 /* terminate if leader or tiebreak rule cannot be checked */
5803 return SCIP_OKAY;
5804
5805 /* select the leader and its orbit */
5807 {
5809
5810 /* iterate over orbits and select the first one that meets the tiebreak rule */
5811 for (i = 0; i < norbits; ++i)
5812 {
5813 /* skip orbits containing vars different to the leader's vartype */
5814 if ( SCIPvarGetType(permvars[orbits[orbitbegins[i]]]) != leadervartype )
5815 continue;
5816
5818 curcriterion = orbitbegins[i] - orbitbegins[i + 1];
5820 curcriterion = orbitbegins[i + 1] - orbitbegins[i];
5821 else
5822 {
5823 /* get first or last active variable in orbit */
5825 {
5826 int cnt = orbitbegins[i];
5827
5828 do
5829 {
5830 varidx = SCIPvarGetProbindex(permvars[orbits[cnt++]]);
5831 }
5832 while ( varidx == -1 && cnt < orbitbegins[i + 1]);
5833 }
5834 else
5835 {
5836 int cnt = orbitbegins[i + 1] - 1;
5837
5838 do
5839 {
5840 varidx = SCIPvarGetProbindex(permvars[orbits[cnt--]]);
5841 }
5842 while ( varidx == -1 && cnt >= orbitbegins[i]);
5843 }
5844
5845 /* skip inactive variables */
5846 if ( varidx == -1 )
5847 continue;
5848
5850 assert( nodedata != NULL );
5851 assert( nodedata->orbitidx == i );
5852
5853 if ( nodedata->nconflictinorbit > 0 )
5854 curcriterion = nodedata->nconflictinorbit;
5855 }
5856
5857 /* update selected orbit */
5859 {
5861 *orbitidx = i;
5862 *success = TRUE;
5863
5865 *leaderidx = 0;
5866 else
5867 *leaderidx = orbitbegins[i + 1] - orbitbegins[i] - 1;
5868 }
5869 }
5870
5871 /* store variables in conflict with leader */
5872 if ( useconflictgraph )
5873 {
5874 leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
5875 assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
5876
5878 }
5879
5881 {
5882 SCIP_VAR* var;
5883 orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
5885 assert( leader >= 0 && leader < npermvars );
5886
5888 assert( conflictvars != NULL );
5890
5891 for (i = 0; i < orbitsize; ++i)
5892 {
5893 /* skip the leader */
5894 if ( i == *leaderidx )
5895 continue;
5896
5897 var = permvars[orbits[orbitbegins[*orbitidx] + i]];
5898
5899 for (j = 0; j < nconflictvars; ++j)
5900 {
5902
5903 assert( neighbordata != NULL );
5904
5905 if ( neighbordata->var == var && neighbordata->active )
5906 {
5908 *norbitvarinconflict += 1;
5909 break;
5910 }
5911 }
5912 }
5913 }
5914 }
5915 else if ( useconflictgraph )
5916 {
5917 orbitcriterion = 0;
5918
5919 /* iterate over variables and select the first one that meets the tiebreak rule */
5920 for (i = 0; i < ngraphvars; ++i)
5921 {
5922 /* skip vars different to the leader's vartype */
5924 continue;
5925
5926 nodedata = (SCIP_NODEDATA*) SCIPdigraphGetNodeData(conflictgraph, i);
5927 assert( nodedata != NULL );
5928
5929 /* skip variables not affected by symmetry */
5930 if ( nodedata->orbitidx == -1 )
5931 continue;
5932
5934 curcriterion = nodedata->nconflictinorbit;
5935 else
5936 curcriterion = SCIPdigraphGetNSuccessors(conflictgraph, i);
5937
5939 {
5941 *orbitidx = nodedata->orbitidx;
5942 *leaderidx = nodedata->posinorbit;
5943 *success = TRUE;
5944 }
5945 }
5946
5947 /* store variables in conflict with leader */
5948 leader = SCIPhashmapGetImageInt(varmap, permvars[orbits[orbitbegins[*orbitidx] + *leaderidx]]);
5949 assert( leader < SCIPdigraphGetNNodes(conflictgraph) );
5951
5953 if ( *success && nconflictvars > 0 )
5954 {
5955 SCIP_VAR* var;
5957
5958 orbitsize = orbitbegins[*orbitidx + 1] - orbitbegins[*orbitidx];
5959
5961 assert( conflictvars != NULL );
5962
5963 for (i = 0; i < orbitsize; ++i)
5964 {
5965 /* skip the leader */
5966 if ( i == *leaderidx )
5967 continue;
5968
5969 var = permvars[orbits[orbitbegins[*orbitidx] + i]];
5970
5971 for (j = 0; j < nconflictvars; ++j)
5972 {
5974 assert( neighbordata != NULL );
5975
5976 if ( neighbordata->var == var && neighbordata->active )
5977 {
5979 *norbitvarinconflict += 1;
5980 break;
5981 }
5982 }
5983 }
5984 }
5985 }
5986
5987 return SCIP_OKAY;
5988}
5989
5990
5991/** add Schreier Sims constraints to the problem */
5992static
5994 SCIP* scip, /**< SCIP instance */
5995 SCIP_PROPDATA* propdata, /**< datas of symmetry propagator */
5996 int* nchgbds /**< pointer to store number of bound changes (or NULL) */
5997 )
5998{ /*lint --e{641}*/
5999 SCIP_DIGRAPH* conflictgraph = NULL;
6000 SCIP_HASHMAP* varmap = NULL;
6001 SCIP_VAR** vars;
6002 int nvars;
6003
6004 SCIP_HASHMAP* permvarmap;
6005 SCIP_VAR** permvars;
6006 int** permstrans;
6007 int npermvars;
6008 int nmovedpermvars;
6009 int nmovedbinpermvars;
6010 int nmovedintpermvars;
6011 int nmovedimplintpermvars;
6012 int nmovedcontpermvars;
6013 int nperms;
6014
6015 int* orbits;
6016 int* orbitbegins;
6017 int norbits;
6018 int* components;
6019 int* componentbegins;
6020 int* vartocomponent;
6021 int ncomponents;
6022 unsigned* componentblocked;
6023
6024 int orbitidx;
6025 int orbitleaderidx;
6028 SCIP_Shortbool* inactiveperms;
6029 int ninactiveperms;
6030 int posleader;
6031 int leaderrule;
6032 int tiebreakrule;
6033 int leadervartype;
6036 SCIP_Bool conflictgraphcreated = FALSE;
6037 SCIP_Bool mixedcomponents;
6039
6040 int c;
6041 int v;
6042 int p;
6043
6044 assert( scip != NULL );
6045 assert( propdata != NULL );
6046
6047 permvars = propdata->permvars;
6048 npermvars = propdata->npermvars;
6049 permvarmap = propdata->permvarmap;
6050 permstrans = propdata->permstrans;
6051 nperms = propdata->nperms;
6052 components = propdata->components;
6053 componentbegins = propdata->componentbegins;
6054 componentblocked = propdata->componentblocked;
6055 vartocomponent = propdata->vartocomponent;
6056 ncomponents = propdata->ncomponents;
6057 nmovedpermvars = propdata->nmovedpermvars;
6058 nmovedbinpermvars = propdata->nmovedbinpermvars;
6059 nmovedintpermvars = propdata->nmovedintpermvars;
6060 nmovedimplintpermvars = propdata->nmovedimplintpermvars;
6061 nmovedcontpermvars = propdata->nmovedcontpermvars;
6062
6063 assert( permvars != NULL );
6064 assert( npermvars > 0 );
6065 assert( permvarmap != NULL );
6066 assert( permstrans != NULL );
6067 assert( nperms > 0 );
6068 assert( components != NULL );
6069 assert( componentbegins != NULL );
6070 assert( vartocomponent != NULL );
6071 assert( ncomponents > 0 );
6072 assert( nmovedpermvars > 0 || ! propdata->ofenabled );
6073 assert( nmovedbinpermvars > 0 || ! propdata->ofenabled );
6074
6075 leaderrule = propdata->sstleaderrule;
6076 tiebreakrule = propdata->ssttiebreakrule;
6077 leadervartype = propdata->sstleadervartype;
6078 mixedcomponents = propdata->sstmixedcomponents;
6079
6080 /* if not already computed, get number of affected vars */
6081 if ( nmovedpermvars == -1 )
6082 {
6083 nmovedpermvars = 0;
6084
6085 for (v = 0; v < npermvars; ++v)
6086 {
6087 for (p = 0; p < nperms; ++p)
6088 {
6089 if ( permstrans[v][p] != v )
6090 {
6091 ++nmovedpermvars;
6092
6093 switch ( SCIPvarGetType(permvars[v]) )
6094 {
6096 ++nmovedbinpermvars;
6097 break;
6099 ++nmovedintpermvars;
6100 break;
6102 ++nmovedimplintpermvars;
6103 break;
6105 default:
6106 ++nmovedcontpermvars;
6107 }
6108 }
6109 }
6110 }
6111 }
6112 propdata->nmovedbinpermvars = nmovedbinpermvars;
6113 propdata->nmovedintpermvars = nmovedintpermvars;
6114 propdata->nmovedimplintpermvars = nmovedimplintpermvars;
6115 propdata->nmovedcontpermvars = nmovedcontpermvars;
6116
6119
6120 /* determine the leader's vartype */
6122 if ( ISSSTBINACTIVE(leadervartype) && nmovedbinpermvars > nvarsselectedtype )
6123 {
6125 nvarsselectedtype = nmovedbinpermvars;
6126 }
6127
6128 if ( ISSSTINTACTIVE(leadervartype) && nmovedintpermvars > nvarsselectedtype )
6129 {
6131 nvarsselectedtype = nmovedintpermvars;
6132 }
6133
6134 if ( ISSSTIMPLINTACTIVE(leadervartype) && nmovedimplintpermvars > nvarsselectedtype )
6135 {
6137 nvarsselectedtype = nmovedimplintpermvars;
6138 }
6139
6140 if ( ISSSTCONTACTIVE(leadervartype) && nmovedcontpermvars > nvarsselectedtype )
6141 {
6143 nvarsselectedtype = nmovedcontpermvars;
6144 }
6145
6146 /* terminate if no variables of a possible leader type is affected */
6147 if ( nvarsselectedtype == 0 )
6148 return SCIP_OKAY;
6149
6150 /* possibly create conflict graph; graph is not created if no setppc conss are present */
6154 {
6156 permvarmap, &conflictgraphcreated) );
6157 }
6158
6159 /* allocate data structures necessary for orbit computations and conflict graph */
6160 SCIP_CALL( SCIPallocBufferArray(scip, &inactiveperms, nperms) );
6161 SCIP_CALL( SCIPallocBufferArray(scip, &orbits, npermvars) );
6162 SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, npermvars) );
6163
6165 {
6168 for (v = 0; v < nvars; ++v)
6169 {
6170 assert( ! SCIPhashmapExists(varmap, vars[v]) );
6171 SCIP_CALL( SCIPhashmapInsertInt(varmap, vars[v], v) );
6172 }
6173 }
6174
6175 SCIPdebugMsg(scip, "Start selection of orbits and leaders for Schreier Sims constraints.\n");
6176 SCIPdebugMsg(scip, "orbitidx\tleaderidx\torbitsize\n");
6177
6178 if ( nchgbds != NULL )
6179 *nchgbds = 0;
6180
6181 /* initialize array indicating whether permutations shall not be considered for orbit permutations */
6182 for (p = 0; p < nperms; ++p)
6183 inactiveperms[p] = TRUE;
6184
6186 for (c = 0; c < ncomponents; ++c)
6188
6189 /* iterate over components and compute orbits */
6190 for (c = 0; c < ncomponents; ++c)
6191 {
6192 SCIP_Bool success = TRUE;
6193
6194 if ( componentblocked[c] )
6195 continue;
6196
6197 for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6198 inactiveperms[components[p]] = FALSE;
6199 ninactiveperms = nperms - componentbegins[c + 1] + componentbegins[c];
6200
6201 /* as long as the stabilizer is non-trivial, add Schreier Sims constraints */
6202 while ( ninactiveperms < nperms )
6203 {
6204 int nchanges = 0;
6205
6206 /* compute orbits w.r.t. active perms */
6207 SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, npermvars, permstrans, nperms, inactiveperms,
6208 orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent,
6209 componentblocked, ncomponents, nmovedpermvars) );
6210
6211 /* stop if we require pure components and a component contains variables of different types */
6212 if ( ! mixedcomponents )
6213 {
6214 for (p = 0; p < norbits; ++p)
6215 {
6216 /* stop if the first element of an orbits has the wrong vartype */
6217 if ( SCIPvarGetType(permvars[orbits[orbitbegins[p]]]) != selectedtype )
6218 {
6219 success = FALSE;
6220 break;
6221 }
6222 }
6223 }
6224
6225 if ( ! success )
6226 break;
6227
6228 /* update symmetry information of conflict graph */
6230 {
6231 assert( conflictgraph != NULL );
6232 SCIP_CALL( updateSymInfoConflictGraphSST(scip, conflictgraph, vars, nvars, permvars, npermvars, FALSE,
6233 varmap, orbits, orbitbegins, norbits) );
6234 }
6235
6236 /* possibly adapt the leader and tie-break rule */
6247
6248 /* select orbit and leader */
6249 SCIP_CALL( selectOrbitLeaderSSTConss(scip, conflictgraph, vars, nvars, varmap,
6250 permvars, npermvars, orbits, orbitbegins, norbits, propdata->sstleaderrule, propdata->ssttiebreakrule, selectedtype,
6252
6253 if ( ! success )
6254 break;
6255
6256 assert( 0 <= orbitidx && orbitidx < norbits );
6257 assert( 0 <= orbitleaderidx && orbitleaderidx < orbitbegins[orbitidx + 1] - orbitbegins[orbitidx] );
6258 SCIPdebugMsg(scip, "%d\t\t%d\t\t%d\n", orbitidx, orbitleaderidx, orbitbegins[orbitidx + 1] - orbitbegins[orbitidx]);
6259
6260 /* add Schreier Sims constraints for the selected orbit and update Schreier Sims table */
6261 SCIP_CALL( addSSTConssOrbitAndUpdateSST(scip, conflictgraph, propdata, permvars,
6263
6264 ++norbitleadercomponent[propdata->vartocomponent[orbits[orbitbegins[orbitidx] + orbitleaderidx]]];
6265
6266 if ( nchgbds != NULL )
6267 *nchgbds += nchanges;
6268
6269 /* deactivate permutations that move the orbit leader */
6270 posleader = orbits[orbitbegins[orbitidx] + orbitleaderidx];
6271 for (p = 0; p < nperms; ++p)
6272 {
6273 if ( inactiveperms[p] )
6274 continue;
6275
6276 if ( permstrans[posleader][p] != posleader )
6277 {
6278 inactiveperms[p] = TRUE;
6280 }
6281 }
6282 }
6283
6284 for (p = componentbegins[c]; p < componentbegins[c + 1]; ++p)
6285 inactiveperms[components[p]] = TRUE;
6286 }
6287
6288 /* if Schreier Sims constraints have been added, store that Schreier Sims has been used for this component */
6289 for (c = 0; c < ncomponents; ++c)
6290 {
6291 if ( norbitleadercomponent[c] > 0 )
6292 componentblocked[c] |= SYM_HANDLETYPE_SST;
6293 }
6295
6297 {
6298 SCIPhashmapFree(&varmap);
6300 }
6301 SCIPfreeBufferArray(scip, &orbitbegins);
6302 SCIPfreeBufferArray(scip, &orbits);
6304 {
6305 assert( conflictgraph != NULL );
6306 SCIP_CALL( freeConflictGraphSST(scip, &conflictgraph, nvars) );
6307 }
6308 SCIPfreeBufferArray(scip, &inactiveperms);
6309
6310 return SCIP_OKAY;
6311}
6312
6313
6314/** finds problem symmetries */
6315static
6317 SCIP* scip, /**< SCIP instance */
6318 SCIP_PROP* prop, /**< symmetry breaking propagator */
6319 int* nchgbds, /**< pointer to store number of bound changes (or NULL)*/
6320 SCIP_Bool* earlyterm /**< pointer to store whether we terminated early (or NULL) */
6321 )
6322{
6323 SCIP_PROPDATA* propdata;
6324
6325 assert( prop != NULL );
6326 assert( scip != NULL );
6327
6328 propdata = SCIPpropGetData(prop);
6329 assert( propdata != NULL );
6330 assert( propdata->symconsenabled || propdata->sstenabled );
6331
6332 /* if constraints have already been added */
6333 if ( propdata->triedaddconss )
6334 {
6335 assert( propdata->nperms > 0 );
6336
6337 if ( earlyterm != NULL )
6338 *earlyterm = TRUE;
6339
6340 return SCIP_OKAY;
6341 }
6342
6343 /* possibly compute symmetry */
6344 if ( propdata->ofenabled && SCIPgetNBinVars(scip) > 1 )
6345 {
6346 SCIP_Bool oldsymconsenabled;
6347
6348 oldsymconsenabled = propdata->symconsenabled;
6349
6350 /* in the nonlinear case, all non-binary variables have to be fixed
6351 (fix non-binary potential branching variables)
6352 */
6353 if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6354 {
6356 }
6357 else
6358 {
6360 }
6361
6362 /* if there is no symmetry compatible with OF, check whether there are more general symmetries */
6363 if ( propdata->nperms == 0 && SCIPgetNIntVars(scip) + SCIPgetNImplVars(scip) > 1 )
6364 {
6365 SCIP_CALL( freeSymmetryData(scip, propdata) );
6366 propdata->symconsenabled = oldsymconsenabled;
6367 propdata->ofenabled = FALSE;
6368 propdata->sstenabled = FALSE;
6369
6371 }
6372 }
6373 else
6374 {
6376 }
6377 assert( propdata->binvaraffected || ! propdata->ofenabled || ! propdata->symconsenabled );
6378
6379 if ( propdata->nperms <= 0 || (! propdata->symconsenabled && ! propdata->sstenabled) )
6380 return SCIP_OKAY;
6381
6382 if ( ! propdata->binvaraffected )
6383 {
6384 SCIPdebugMsg(scip, "Symmetry propagator: problem is linear and no symmetry on binary variables has been found, turning symretope constraints off.\n");
6385 propdata->symconsenabled = FALSE;
6386 }
6387 assert( propdata->nperms > 0 );
6388 assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || propdata->sstenabled );
6389
6390 propdata->triedaddconss = TRUE;
6391
6392 if ( propdata->symconsenabled )
6393 {
6394 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genorbconss, propdata->nperms) );
6395
6396 if ( propdata->detectorbitopes )
6397 {
6398 SCIP_CALL( detectOrbitopes(scip, propdata, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6399 }
6400 }
6401
6402 /* disable orbital fixing if all components are handled by orbitopes */
6403 if ( propdata->ncomponents == propdata->norbitopes )
6404 propdata->ofenabled = FALSE;
6405
6406 /* possibly stop */
6407 if ( SCIPisStopped(scip) )
6408 {
6409 if ( propdata->ngenorbconss == 0 )
6410 {
6411 SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6412 }
6413 return SCIP_OKAY;
6414 }
6415
6416 if ( propdata->ncompblocked < propdata->ncomponents && propdata->detectsubgroups && propdata->symconsenabled )
6417 {
6418 /* @TODO: create array only when needed */
6419 propdata->genlinconsssize = propdata->nperms;
6420 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &propdata->genlinconss, propdata->genlinconsssize) );
6421
6423 }
6424
6425 if ( propdata->sstenabled )
6426 {
6427 SCIP_CALL( addSSTConss(scip, propdata, nchgbds) );
6428 }
6429
6430 /* possibly stop */
6431 if ( SCIPisStopped(scip) || ! propdata->symconsenabled )
6432 return SCIP_OKAY;
6433
6434 /* add symmetry breaking constraints if orbital fixing is not used outside orbitopes */
6435 if ( ! propdata->ofenabled )
6436 {
6437 /* exit if no or only trivial symmetry group is available */
6438 if ( propdata->nperms < 1 || ! propdata->binvaraffected )
6439 return SCIP_OKAY;
6440
6441 if ( propdata->addsymresacks )
6442 {
6443 SCIP_CALL( addSymresackConss(scip, prop, propdata->components, propdata->componentbegins, propdata->ncomponents) );
6444 }
6445
6446 /* free symmetry conss if no orbitope/symresack constraints have been found (may happen if Schreier-Sims constraints are active) */
6447 if ( propdata->ngenorbconss == 0 )
6448 SCIPfreeBlockMemoryArrayNull(scip, &propdata->genorbconss, propdata->nperms);
6449 }
6450
6451 return SCIP_OKAY;
6452}
6453
6454
6455
6456/*
6457 * Local methods for orbital fixing
6458 */
6459
6460
6461/** performs orbital fixing
6462 *
6463 * Note that we do not have to distinguish between variables that have been fixed or branched to 1, since the
6464 * stabilizer is with respect to the variables that have been branched to 1. Thus, if an orbit contains a variable that
6465 * has been branched to 1, the whole orbit only contains variables that have been branched to 1 - and nothing can be
6466 * fixed.
6467 */
6468static
6470 SCIP* scip, /**< SCIP pointer */
6471 SCIP_VAR** permvars, /**< variables */
6472 int npermvars, /**< number of variables */
6473 int* orbits, /**< array of non-trivial orbits */
6474 int* orbitbegins, /**< array containing begin positions of new orbits in orbits array */
6475 int norbits, /**< number of orbits */
6476 SCIP_Bool* infeasible, /**< pointer to store whether problem is infeasible */
6477 int* nfixedzero, /**< pointer to store number of variables fixed to 0 */
6478 int* nfixedone /**< pointer to store number of variables fixed to 1 */
6479 )
6480{
6481 SCIP_Bool tightened;
6482 int i;
6483
6484 assert( scip != NULL );
6485 assert( permvars != NULL );
6486 assert( orbits != NULL );
6487 assert( orbitbegins != NULL );
6488 assert( infeasible != NULL );
6489 assert( nfixedzero != NULL );
6490 assert( nfixedone != NULL );
6491 assert( norbits > 0 );
6492 assert( orbitbegins[0] == 0 );
6493
6494 *infeasible = FALSE;
6495 *nfixedzero = 0;
6496 *nfixedone = 0;
6497
6498 /* check all orbits */
6499 for (i = 0; i < norbits; ++i)
6500 {
6501 SCIP_Bool havefixedone = FALSE;
6502 SCIP_Bool havefixedzero = FALSE;
6503 SCIP_VAR* var;
6504 int j;
6505
6506 /* we only have nontrivial orbits */
6507 assert( orbitbegins[i+1] - orbitbegins[i] >= 2 );
6508
6509 /* check all variables in the orbit */
6510 for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6511 {
6512 assert( 0 <= orbits[j] && orbits[j] < npermvars );
6513 var = permvars[orbits[j]];
6514 assert( var != NULL );
6515
6516 /* check whether variable is not binary (and not implicit integer!) */
6518 {
6519 /* skip orbit if there are non-binary variables */
6522 break;
6523 }
6524
6525 /* if variable is fixed to 1 -> can fix all variables in orbit to 1 */
6526 if ( SCIPvarGetLbLocal(var) > 0.5 )
6528
6529 /* check for zero-fixed variables */
6530 if ( SCIPvarGetUbLocal(var) < 0.5 )
6532 }
6533
6534 /* check consistency */
6535 if ( havefixedone && havefixedzero )
6536 {
6537 *infeasible = TRUE;
6538 return SCIP_OKAY;
6539 }
6540
6541 /* fix all variables to 0 if there is one variable fixed to 0 */
6542 if ( havefixedzero )
6543 {
6544 assert( ! havefixedone );
6545
6546 for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6547 {
6548 assert( 0 <= orbits[j] && orbits[j] < npermvars );
6549 var = permvars[orbits[j]];
6550 assert( var != NULL );
6551
6552 /* only variables that are not yet fixed to 0 */
6553 if ( SCIPvarGetUbLocal(var) > 0.5 )
6554 {
6555 SCIPdebugMsg(scip, "can fix <%s> (index %d) to 0.\n", SCIPvarGetName(var), orbits[j]);
6557 /* due to aggregation, var might already be fixed to 1, so do not put assert here */
6558
6559 /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6560 SCIP_CALL( SCIPtightenVarUb(scip, var, 0.0, FALSE, infeasible, &tightened) );
6561 if ( *infeasible )
6562 return SCIP_OKAY;
6563 if ( tightened )
6564 ++(*nfixedzero);
6565 }
6566 }
6567 }
6568
6569 /* fix all variables to 1 if there is one variable fixed to 1 */
6570 if ( havefixedone )
6571 {
6572 assert( ! havefixedzero );
6573
6574 for (j = orbitbegins[i]; j < orbitbegins[i+1]; ++j)
6575 {
6576 assert( 0 <= orbits[j] && orbits[j] < npermvars );
6577 var = permvars[orbits[j]];
6578 assert( var != NULL );
6579
6580 /* only variables that are not yet fixed to 1 */
6581 if ( SCIPvarGetLbLocal(var) < 0.5)
6582 {
6583 SCIPdebugMsg(scip, "can fix <%s> (index %d) to 1.\n", SCIPvarGetName(var), orbits[j]);
6585 /* due to aggregation, var might already be fixed to 0, so do not put assert here */
6586
6587 /* do not use SCIPinferBinvarProp(), since conflict analysis is not valid */
6588 SCIP_CALL( SCIPtightenVarLb(scip, var, 1.0, FALSE, infeasible, &tightened) );
6589 if ( *infeasible )
6590 return SCIP_OKAY;
6591 if ( tightened )
6592 ++(*nfixedone);
6593 }
6594 }
6595 }
6596 }
6597
6598 return SCIP_OKAY;
6599}
6600
6601
6602/** Gets branching variables on the path to root
6603 *
6604 * The variables are added to bg1 and bg1list, which are prefilled with the variables globally fixed to 1.
6605 */
6606static
6608 SCIP* scip, /**< SCIP pointer */
6609 int nvars, /**< number of variables */
6610 SCIP_HASHMAP* varmap, /**< map of variables to indices in vars array */
6611 SCIP_Shortbool* bg1, /**< bitset marking the variables globally fixed or branched to 1 */
6612 int* bg1list, /**< array to store the variable indices globally fixed or branched to 1 */
6613 int* nbg1 /**< pointer to store the number of variables in bg1 and bg1list */
6614 )
6615{
6616 SCIP_NODE* node;
6617
6618 assert( scip != NULL );
6619 assert( varmap != NULL );
6620 assert( bg1 != NULL );
6621 assert( bg1list != NULL );
6622 assert( nbg1 != NULL );
6623 assert( *nbg1 >= 0 );
6624
6625 /* get current node */
6626 node = SCIPgetCurrentNode(scip);
6627
6628#ifdef SCIP_OUTPUT
6630#endif
6631
6632 /* follow path to the root (in the root no domains were changed due to branching) */
6633 while ( SCIPnodeGetDepth(node) != 0 )
6634 {
6636 SCIP_DOMCHG* domchg;
6637 SCIP_VAR* branchvar;
6638 int nboundchgs;
6639 int i;
6640
6641 /* get domain changes of current node */
6642 domchg = SCIPnodeGetDomchg(node);
6643
6644 /* If we stopped due to a solving limit, it might happen that a non-root node has no domain changes, in all other
6645 * cases domchg should not be NULL. */
6646 if ( domchg != NULL )
6647 {
6648 /* loop through all bound changes */
6649 nboundchgs = SCIPdomchgGetNBoundchgs(domchg);
6650 for (i = 0; i < nboundchgs; ++i)
6651 {
6652 /* get bound change info */
6653 boundchg = SCIPdomchgGetBoundchg(domchg, i);
6654 assert( boundchg != NULL );
6655
6656 /* branching decisions have to be in the beginning of the bound change array */
6658 break;
6659
6660 /* get corresponding branching variable */
6661 branchvar = SCIPboundchgGetVar(boundchg);
6662
6663 /* we only consider binary variables */
6664 if ( SCIPvarGetType(branchvar) == SCIP_VARTYPE_BINARY )
6665 {
6666 /* if branching variable is not known (may have been created meanwhile,
6667 * e.g., by prop_inttobinary; may have been removed from symmetry data
6668 * due to compression), continue with parent node */
6669 if ( ! SCIPhashmapExists(varmap, (void*) branchvar) )
6670 break;
6671
6672 if ( SCIPvarGetLbLocal(branchvar) > 0.5 )
6673 {
6674 int branchvaridx;
6675
6676 branchvaridx = SCIPhashmapGetImageInt(varmap, (void*) branchvar);
6678
6679 /* the variable might already be fixed to 1 */
6680 if ( ! bg1[branchvaridx] )
6681 {
6682 bg1[branchvaridx] = TRUE;
6683 bg1list[(*nbg1)++] = branchvaridx;
6684 }
6685 }
6686 }
6687 }
6688 }
6689
6690 node = SCIPnodeGetParent(node);
6691 }
6692
6693 return SCIP_OKAY;
6694}
6695
6696
6697/** propagates orbital fixing */
6698static
6700 SCIP* scip, /**< SCIP pointer */
6701 SCIP_PROPDATA* propdata, /**< data of symmetry breaking propagator */
6702 SCIP_Bool* infeasible, /**< pointer to store whether the node is detected to be infeasible */
6703 int* nprop /**< pointer to store the number of propagations */
6704 )
6705{
6706 SCIP_Shortbool* inactiveperms;
6707 SCIP_Shortbool* bg0;
6708 SCIP_Shortbool* bg1;
6709 SCIP_VAR** permvars;
6710 int* orbitbegins;
6711 int* orbits;
6712 int* components;
6713 int* componentbegins;
6714 int* vartocomponent;
6715 int ncomponents;
6716 int* bg0list;
6717 int nbg0;
6718 int* bg1list;
6719 int nbg1;
6720 int nactiveperms;
6721 int norbits;
6722 int npermvars;
6723 int nbinpermvars;
6724 int** permstrans;
6725 int nperms;
6726 int p;
6727 int v;
6728 int j;
6729 int componentidx;
6730
6731 assert( scip != NULL );
6732 assert( propdata != NULL );
6733 assert( propdata->ofenabled );
6734 assert( infeasible != NULL );
6735 assert( nprop != NULL );
6736
6737 *infeasible = FALSE;
6738 *nprop = 0;
6739
6740 /* possibly compute symmetry; fix non-binary potential branching variables */
6741 if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
6742 {
6744 }
6745 else
6746 {
6748 }
6749 assert( hasNonlinearConstraints(propdata) || propdata->binvaraffected || ! propdata->ofenabled );
6750
6751 /* return if there is no symmetry available */
6752 nperms = propdata->nperms;
6753 if ( nperms <= 0 || ! propdata->ofenabled )
6754 return SCIP_OKAY;
6755
6756 assert( propdata->permvars != NULL );
6757 assert( propdata->npermvars > 0 );
6758 assert( propdata->permvarmap != NULL );
6759 assert( propdata->permstrans != NULL );
6760 assert( propdata->inactiveperms != NULL );
6761 assert( propdata->components != NULL );
6762 assert( propdata->componentbegins != NULL );
6763 assert( propdata->vartocomponent != NULL );
6764 assert( propdata->ncomponents > 0 );
6765
6766 permvars = propdata->permvars;
6767 npermvars = propdata->npermvars;
6768 nbinpermvars = propdata->nbinpermvars;
6769 permstrans = propdata->permstrans;
6770 inactiveperms = propdata->inactiveperms;
6771 components = propdata->components;
6772 componentbegins = propdata->componentbegins;
6773 vartocomponent = propdata->vartocomponent;
6774 ncomponents = propdata->ncomponents;
6775
6776 /* init bitset for marking variables (globally fixed or) branched to 1 */
6777 assert( propdata->bg1 != NULL );
6778 assert( propdata->bg1list != NULL );
6779 assert( propdata->nbg1 >= 0 );
6780 assert( propdata->nbg1 <= npermvars );
6781
6782 bg1 = propdata->bg1;
6783 bg1list = propdata->bg1list;
6784 nbg1 = propdata->nbg1;
6785
6786 /* get branching variables */
6787 SCIP_CALL( computeBranchingVariables(scip, npermvars, propdata->permvarmap, bg1, bg1list, &nbg1) );
6788 assert( nbg1 >= propdata->nbg1 );
6789
6790 /* reset inactive permutations */
6791 nactiveperms = nperms;
6792 for (p = 0; p < nperms; ++p)
6793 propdata->inactiveperms[p] = FALSE;
6794
6795 /* get pointers for bg0 */
6796 assert( propdata->bg0 != NULL );
6797 assert( propdata->bg0list != NULL );
6798 assert( propdata->nbg0 >= 0 );
6799 assert( propdata->nbg0 <= npermvars );
6800
6801 bg0 = propdata->bg0;
6802 bg0list = propdata->bg0list;
6803 nbg0 = propdata->nbg0;
6804
6805 /* filter out permutations that move variables that are fixed to 0 */
6806 for (j = 0; j < nbg0 && nactiveperms > 0; ++j)
6807 {
6808 int* pt;
6809
6810 v = bg0list[j];
6811 assert( 0 <= v && v < npermvars );
6812 assert( bg0[v] );
6813
6814 componentidx = vartocomponent[v];
6815
6816 /* skip unaffected variables and blocked components */
6817 if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6818 continue;
6819
6820 pt = permstrans[v];
6821 assert( pt != NULL );
6822
6823 for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6824 {
6825 int img;
6826 int perm;
6827
6828 perm = components[p];
6829
6830 /* skip inactive permutations */
6831 if ( inactiveperms[perm] )
6832 continue;
6833
6834 img = pt[perm];
6835
6836 if ( img != v )
6837 {
6838#ifndef NDEBUG
6839 SCIP_VAR* varv = permvars[v];
6840 SCIP_VAR* varimg = permvars[img];
6841
6842 /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
6851 assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
6852#endif
6853
6854 /* we are moving a variable globally fixed to 0 to a variable not of this type */
6855 if ( ! bg0[img] )
6856 {
6857 inactiveperms[perm] = TRUE; /* mark as inactive */
6858 --nactiveperms;
6859 }
6860 }
6861 }
6862 }
6863
6864 /* filter out permutations that move variables that are fixed to different values */
6865 for (j = 0; j < nbg1 && nactiveperms > 0; ++j)
6866 {
6867 int* pt;
6868
6869 v = bg1list[j];
6870 assert( 0 <= v && v < npermvars );
6871 assert( bg1[v] );
6872
6873 componentidx = vartocomponent[v];
6874
6875 /* skip unaffected variables and blocked components */
6876 if ( componentidx < 0 || propdata->componentblocked[componentidx] )
6877 continue;
6878
6879 pt = permstrans[v];
6880 assert( pt != NULL );
6881
6882 for (p = componentbegins[componentidx]; p < componentbegins[componentidx + 1]; ++p)
6883 {
6884 int img;
6885 int perm;
6886
6887 perm = components[p];
6888
6889 /* skip inactive permutations */
6890 if ( inactiveperms[perm] )
6891 continue;
6892
6893 img = pt[perm];
6894
6895 if ( img != v )
6896 {
6897#ifndef NDEBUG
6898 SCIP_VAR* varv = permvars[v];
6899 SCIP_VAR* varimg = permvars[img];
6900
6901 /* check whether moved variables have the same type (might have been aggregated in the meanwhile) */
6910 assert( SCIPisEQ(scip, propdata->permvarsobj[v], propdata->permvarsobj[img]) );
6911#endif
6912
6913 /* we are moving a variable globally fixed or branched to 1 to a variable not of this type */
6914 if ( ! bg1[img] )
6915 {
6916 inactiveperms[perm] = TRUE; /* mark as inactive */
6917 --nactiveperms;
6918 }
6919 }
6920 }
6921 }
6922
6923 /* Clean bg1 list - need to do this after the main loop! (Not needed for bg0.)
6924 * Note that variables globally fixed to 1 are not resetted, since the loop starts at propdata->nbg1. */
6925 for (j = propdata->nbg1; j < nbg1; ++j)
6926 bg1[bg1list[j]] = FALSE;
6927
6928 /* exit if no active permuations left */
6929 if ( nactiveperms == 0 )
6930 return SCIP_OKAY;
6931
6932 /* compute orbits of binary variables */
6933 SCIP_CALL( SCIPallocBufferArray(scip, &orbits, nbinpermvars) );
6934 SCIP_CALL( SCIPallocBufferArray(scip, &orbitbegins, nbinpermvars) );
6935 SCIP_CALL( SCIPcomputeOrbitsFilterSym(scip, nbinpermvars, permstrans, nperms, inactiveperms,
6936 orbits, orbitbegins, &norbits, components, componentbegins, vartocomponent, propdata->componentblocked, ncomponents, propdata->nmovedpermvars) );
6937
6938 if ( norbits > 0 )
6939 {
6940 int nfixedzero = 0;
6941 int nfixedone = 0;
6942
6943 SCIPdebugMsg(scip, "Perform orbital fixing on %d orbits (%d active perms).\n", norbits, nactiveperms);
6944 SCIP_CALL( performOrbitalFixing(scip, permvars, nbinpermvars, orbits, orbitbegins, norbits, infeasible, &nfixedzero, &nfixedone) );
6945
6946 propdata->nfixedzero += nfixedzero;
6947 propdata->nfixedone += nfixedone;
6948 *nprop = nfixedzero + nfixedone;
6949
6950 SCIPdebugMsg(scip, "Orbital fixings: %d 0s, %d 1s.\n", nfixedzero, nfixedone);
6951 }
6952
6953 SCIPfreeBufferArray(scip, &orbitbegins);
6954 SCIPfreeBufferArray(scip, &orbits);
6955
6956 return SCIP_OKAY;
6957}
6958
6959
6960
6961/*
6962 * Callback methods of propagator
6963 */
6964
6965/** presolving initialization method of propagator (called when presolving is about to begin) */
6966static
6968{ /*lint --e{715}*/
6969 SCIP_PROPDATA* propdata;
6970
6971 assert( scip != NULL );
6972 assert( prop != NULL );
6973
6974 propdata = SCIPpropGetData(prop);
6975 assert( propdata != NULL );
6976
6977 /* get nonlinear conshdlr for future checks on whether there are nonlinear constraints */
6978 propdata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
6979
6980 /* check whether we should run */
6981 if ( propdata->usesymmetry < 0 )
6982 {
6983 SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
6985 }
6986
6987 /* add symmetry handling constraints if required */
6988 if ( (propdata->symconsenabled || propdata->sstenabled) && propdata->addconsstiming == 0 )
6989 {
6990 SCIPdebugMsg(scip, "Try to add symmetry handling constraints before presolving.");
6991
6993 }
6994 else if ( propdata->ofenabled && propdata->ofsymcomptiming == 0 )
6995 {
6996 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Symmetry computation before presolving:\n");
6997
6998 /* otherwise compute symmetry if timing requests it; fix non-binary potential branching variables */
6999 if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7000 {
7002 }
7003 else
7004 {
7006 }
7007 assert( propdata->binvaraffected || ! propdata->ofenabled );
7008 }
7009
7010 return SCIP_OKAY;
7011}
7012
7013
7014/** presolving deinitialization method of propagator (called after presolving has been finished) */
7015static
7017{ /*lint --e{715}*/
7018 SCIP_PROPDATA* propdata;
7019
7020 assert( scip != NULL );
7021 assert( prop != NULL );
7022 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7023
7024 SCIPdebugMsg(scip, "Exitpre method of propagator <%s> ...\n", PROP_NAME);
7025
7026 propdata = SCIPpropGetData(prop);
7027 assert( propdata != NULL );
7028 assert( propdata->usesymmetry >= 0 );
7029
7030 /* guarantee that symmetries are computed (and handled) if the solving process has not been interrupted
7031 * and even if presolving has been disabled */
7032 if ( (propdata->symconsenabled || propdata->sstenabled) && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7033 {
7035 }
7036
7037 /* if timing requests it, guarantee that symmetries are computed even if presolving is disabled */
7038 if ( propdata->ofenabled && propdata->ofsymcomptiming <= 1 && SCIPgetStatus(scip) == SCIP_STATUS_UNKNOWN )
7039 {
7040 /* fix non-binary potential branching variables */
7041 if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7042 {
7044 }
7045 else
7046 {
7048 }
7049 assert( propdata->binvaraffected || ! propdata->ofenabled );
7050 }
7051
7052 return SCIP_OKAY;
7053}
7054
7055
7056/** presolving method of propagator */
7057static
7059{ /*lint --e{715}*/
7060 SCIP_PROPDATA* propdata;
7061 int i;
7062
7063 assert( scip != NULL );
7064 assert( prop != NULL );
7065 assert( result != NULL );
7067
7069
7070 propdata = SCIPpropGetData(prop);
7071 assert( propdata != NULL );
7072 assert( propdata->usesymmetry >= 0 );
7073
7074 /* possibly create symmetry handling constraints */
7075 if ( propdata->symconsenabled || propdata->sstenabled )
7076 {
7077 int noldngenconns;
7078 int nchanges = 0;
7079 SCIP_Bool earlyterm = FALSE;
7080
7081 /* skip presolving if we are not at the end if addconsstiming == 2 */
7082 assert( 0 <= propdata->addconsstiming && propdata->addconsstiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7083 if ( propdata->addconsstiming > SYM_COMPUTETIMING_DURINGPRESOL && ! SCIPisPresolveFinished(scip) )
7084 return SCIP_OKAY;
7085
7086 /* possibly stop */
7087 if ( SCIPisStopped(scip) )
7088 return SCIP_OKAY;
7089
7090 noldngenconns = propdata->ngenorbconss + propdata->nsstconss + propdata->ngenlinconss;
7091
7092 SCIP_CALL( tryAddSymmetryHandlingConss(scip, prop, &nchanges, &earlyterm) );
7093
7094 /* if we actually tried to add symmetry handling constraints */
7095 if ( ! earlyterm ) /*lint !e774*/
7096 {
7098
7099 if ( nchanges > 0 )
7100 {
7102 *nchgbds += nchanges;
7103 }
7104
7105 /* if symmetry handling constraints have been added, presolve each */
7106 if ( propdata->ngenorbconss > 0 || propdata->ngenlinconss > 0 || propdata->nsstconss > 0 )
7107 {
7108 /* at this point, the symmetry group should be computed and nontrivial */
7109 assert( propdata->nperms > 0 );
7110 assert( propdata->triedaddconss );
7111
7112 /* we have added at least one symmetry handling constraints, i.e., we were successful */
7114
7115 *naddconss += propdata->ngenorbconss + propdata->ngenlinconss + propdata->nsstconss - noldngenconns;
7116 SCIPdebugMsg(scip, "Added symmetry breaking constraints: %d.\n", *naddconss);
7117
7118 /* if constraints have been added, loop through generated constraints and presolve each */
7119 for (i = 0; i < propdata->ngenorbconss; ++i)
7120 {
7123 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7124
7125 /* exit if cutoff or unboundedness has been detected */
7126 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7127 {
7128 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genorbconss[i]));
7129 return SCIP_OKAY;
7130 }
7131 }
7132
7133 for (i = 0; i < propdata->ngenlinconss; ++i)
7134 {
7137 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7138
7139 /* exit if cutoff or unboundedness has been detected */
7140 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7141 {
7142 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->genlinconss[i]));
7143 return SCIP_OKAY;
7144 }
7145 }
7146 SCIPdebugMsg(scip, "Presolved %d generated constraints.\n",
7147 propdata->ngenorbconss + propdata->ngenlinconss);
7148
7149 for (i = 0; i < propdata->nsstconss; ++i)
7150 {
7153 nchgvartypes, nchgbds, naddholes, ndelconss, naddconss, nupgdconss, nchgcoefs, nchgsides, result) );
7154
7155 /* exit if cutoff or unboundedness has been detected */
7156 if ( *result == SCIP_CUTOFF || *result == SCIP_UNBOUNDED )
7157 {
7158 SCIPdebugMsg(scip, "Presolving constraint <%s> detected cutoff or unboundedness.\n", SCIPconsGetName(propdata->sstconss[i]));
7159 return SCIP_OKAY;
7160 }
7161 }
7162 SCIPdebugMsg(scip, "Presolved %d generated Schreier Sims constraints.\n", propdata->nsstconss);
7163 }
7164 }
7165 }
7166
7167 /* run OF presolving */
7168 assert( 0 <= propdata->ofsymcomptiming && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_AFTERPRESOL );
7169 if ( propdata->ofenabled && propdata->performpresolving && propdata->ofsymcomptiming <= SYM_COMPUTETIMING_DURINGPRESOL )
7170 {
7171 SCIP_Bool infeasible;
7172 int nprop;
7173
7174 /* if we have not tried to add symmetry handling constraints */
7175 if ( *result == SCIP_DIDNOTRUN )
7177
7178 SCIPdebugMsg(scip, "Presolving <%s>.\n", PROP_NAME);
7179
7180 SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7181
7182 if ( infeasible )
7183 {
7185 propdata->offoundreduction = TRUE;
7186 }
7187 else if ( nprop > 0 )
7188 {
7190 *nfixedvars += nprop;
7191 propdata->offoundreduction = TRUE;
7192 }
7193 }
7194 else if ( propdata->ofenabled && propdata->ofsymcomptiming == SYM_COMPUTETIMING_DURINGPRESOL )
7195 {
7196 /* otherwise compute symmetry early if timing requests it; fix non-binary potential branching variables */
7197 if ( hasNonlinearConstraints(propdata) || propdata->symfixnonbinaryvars )
7198 {
7200 }
7201 else
7202 {
7204 }
7205 assert( propdata->binvaraffected || ! propdata->ofenabled );
7206 }
7207
7208 return SCIP_OKAY;
7209}
7210
7211
7212/** execution method of propagator */
7213static
7215{ /*lint --e{715}*/
7216 SCIP_PROPDATA* propdata;
7217 SCIP_Bool infeasible = FALSE;
7218 SCIP_Longint nodenumber;
7219 int nprop = 0;
7220
7221 assert( scip != NULL );
7222 assert( result != NULL );
7223
7225
7226 /* do not run if we are in the root or not yet solving */
7228 return SCIP_OKAY;
7229
7230 /* do nothing if we are in a probing node */
7231 if ( SCIPinProbing(scip) )
7232 return SCIP_OKAY;
7233
7234 /* do not run again in repropagation, since the path to the root might have changed */
7236 return SCIP_OKAY;
7237
7238 /* get data */
7239 propdata = SCIPpropGetData(prop);
7240 assert( propdata != NULL );
7241
7242 /* if usesymmetry has not been read so far */
7243 if ( propdata->usesymmetry < 0 )
7244 {
7245 SCIP_CALL( SCIPgetIntParam(scip, "misc/usesymmetry", &propdata->usesymmetry) );
7247 }
7248
7249 /* do not propagate if orbital fixing is not enabled */
7250 if ( ! propdata->ofenabled )
7251 return SCIP_OKAY;
7252
7253 /* return if there is no symmetry available */
7254 if ( propdata->nperms == 0 )
7255 return SCIP_OKAY;
7256
7257 /* return if we already ran in this node */
7259 if ( nodenumber == propdata->nodenumber )
7260 return SCIP_OKAY;
7261 propdata->nodenumber = nodenumber;
7262
7263 /* propagate */
7265
7266 SCIPdebugMsg(scip, "Propagating <%s>.\n", SCIPpropGetName(prop));
7267
7268 SCIP_CALL( propagateOrbitalFixing(scip, propdata, &infeasible, &nprop) );
7269
7270 if ( infeasible )
7271 {
7273 propdata->offoundreduction = TRUE;
7274 }
7275 else if ( nprop > 0 )
7276 {
7278 propdata->offoundreduction = TRUE;
7279 }
7280
7281 return SCIP_OKAY;
7282}
7283
7284
7285/** deinitialization method of propagator (called before transformed problem is freed) */
7286static
7288{
7289 SCIP_PROPDATA* propdata;
7290
7291 assert( scip != NULL );
7292 assert( prop != NULL );
7293 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7294
7295 SCIPdebugMsg(scip, "Exiting propagator <%s>.\n", PROP_NAME);
7296
7297 propdata = SCIPpropGetData(prop);
7298 assert( propdata != NULL );
7299
7300 SCIP_CALL( freeSymmetryData(scip, propdata) );
7301
7302 /* reset basic data */
7303 propdata->usesymmetry = -1;
7304 propdata->symconsenabled = FALSE;
7305 propdata->triedaddconss = FALSE;
7306 propdata->nsymresacks = 0;
7307 propdata->norbitopes = 0;
7308 propdata->ofenabled = FALSE;
7309 propdata->sstenabled = FALSE;
7310 propdata->lastrestart = 0;
7311 propdata->nfixedzero = 0;
7312 propdata->nfixedone = 0;
7313 propdata->nodenumber = -1;
7314 propdata->offoundreduction = FALSE;
7315
7316 return SCIP_OKAY;
7317}
7318
7319
7320/** propagation conflict resolving method of propagator
7321 *
7322 * @todo Implement reverse propagation.
7323 *
7324 * Note that this is relatively difficult to obtain: One needs to include all bounds of variables that are responsible
7325 * for creating the orbit in which the variables that was propagated lies. This includes all variables that are moved
7326 * by the permutations which are involved in creating the orbit.
7327 */
7328static
7330{ /*lint --e{715,818}*/
7331 assert( result != NULL );
7332
7334
7335 return SCIP_OKAY;
7336}
7337
7338
7339/** destructor of propagator to free user data (called when SCIP is exiting) */
7340static
7342{ /*lint --e{715}*/
7343 SCIP_PROPDATA* propdata;
7344
7345 assert( scip != NULL );
7346 assert( prop != NULL );
7347 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7348
7349 SCIPdebugMsg(scip, "Freeing symmetry propagator.\n");
7350
7351 propdata = SCIPpropGetData(prop);
7352 assert( propdata != NULL );
7353
7354 SCIPfreeBlockMemory(scip, &propdata);
7355
7356 return SCIP_OKAY;
7357}
7358
7359
7360/*
7361 * External methods
7362 */
7363
7364/** include symmetry propagator */
7366 SCIP* scip /**< SCIP data structure */
7367 )
7368{
7369 SCIP_TABLEDATA* tabledata;
7370 SCIP_PROPDATA* propdata = NULL;
7371 SCIP_PROP* prop = NULL;
7372
7373 SCIP_CALL( SCIPallocBlockMemory(scip, &propdata) );
7374 assert( propdata != NULL );
7375
7376 propdata->npermvars = 0;
7377 propdata->nbinpermvars = 0;
7378 propdata->permvars = NULL;
7379#ifndef NDEBUG
7380 propdata->permvarsobj = NULL;
7381#endif
7382 propdata->nperms = -1;
7383 propdata->nmaxperms = 0;
7384 propdata->perms = NULL;
7385 propdata->permstrans = NULL;
7386 propdata->permvarmap = NULL;
7387 propdata->nonbinpermvarcaptured = NULL;
7388
7389 propdata->ncomponents = -1;
7390 propdata->ncompblocked = 0;
7391 propdata->components = NULL;
7392 propdata->componentbegins = NULL;
7393 propdata->vartocomponent = NULL;
7394 propdata->componentblocked = NULL;
7395
7396 propdata->log10groupsize = -1.0;
7397 propdata->nmovedvars = -1;
7398 propdata->binvaraffected = FALSE;
7399 propdata->computedsymmetry = FALSE;
7400 propdata->conshdlr_nonlinear = NULL;
7401
7402 propdata->usesymmetry = -1;
7403 propdata->symconsenabled = FALSE;
7404 propdata->triedaddconss = FALSE;
7405 propdata->genorbconss = NULL;
7406 propdata->genlinconss = NULL;
7407 propdata->ngenorbconss = 0;
7408 propdata->ngenlinconss = 0;
7409 propdata->genlinconsssize = 0;
7410 propdata->nsymresacks = 0;
7411 propdata->norbitopes = 0;
7412 propdata->isnonlinvar = NULL;
7413
7414 propdata->ofenabled = FALSE;
7415 propdata->bg0 = NULL;
7416 propdata->bg0list = NULL;
7417 propdata->nbg0 = 0;
7418 propdata->bg1 = NULL;
7419 propdata->bg1list = NULL;
7420 propdata->nbg1 = 0;
7421 propdata->permvarsevents = NULL;
7422 propdata->inactiveperms = NULL;
7423 propdata->nmovedpermvars = -1;
7424 propdata->nmovedbinpermvars = 0;
7425 propdata->nmovedintpermvars = 0;
7426 propdata->nmovedimplintpermvars = 0;
7427 propdata->nmovedcontpermvars = 0;
7428 propdata->lastrestart = 0;
7429 propdata->nfixedzero = 0;
7430 propdata->nfixedone = 0;
7431 propdata->nodenumber = -1;
7432 propdata->offoundreduction = FALSE;
7433
7434 propdata->sstenabled = FALSE;
7435 propdata->sstconss = NULL;
7436 propdata->nsstconss = 0;
7437 propdata->maxnsstconss = 0;
7438 propdata->leaders = NULL;
7439 propdata->nleaders = 0;
7440 propdata->maxnleaders = 0;
7441
7442 /* create event handler */
7443 propdata->eventhdlr = NULL;
7446 assert( propdata->eventhdlr != NULL );
7447
7448 /* include constraint handler */
7451 assert( prop != NULL );
7452
7459
7460 /* include table */
7461 SCIP_CALL( SCIPallocBlockMemory(scip, &tabledata) );
7462 tabledata->propdata = propdata;
7466
7467 /* add parameters for computing symmetry */
7469 "propagating/" PROP_NAME "/maxgenerators",
7470 "limit on the number of generators that should be produced within symmetry detection (0 = no limit)",
7471 &propdata->maxgenerators, TRUE, DEFAULT_MAXGENERATORS, 0, INT_MAX, NULL, NULL) );
7472
7474 "propagating/" PROP_NAME "/checksymmetries",
7475 "Should all symmetries be checked after computation?",
7476 &propdata->checksymmetries, TRUE, DEFAULT_CHECKSYMMETRIES, NULL, NULL) );
7477
7479 "propagating/" PROP_NAME "/displaynorbitvars",
7480 "Should the number of variables affected by some symmetry be displayed?",
7481 &propdata->displaynorbitvars, TRUE, DEFAULT_DISPLAYNORBITVARS, NULL, NULL) );
7482
7484 "propagating/" PROP_NAME "/doubleequations",
7485 "Double equations to positive/negative version?",
7486 &propdata->doubleequations, TRUE, DEFAULT_DOUBLEEQUATIONS, NULL, NULL) );
7487
7488 /* add parameters for adding symmetry handling constraints */
7490 "propagating/" PROP_NAME "/conssaddlp",
7491 "Should the symmetry breaking constraints be added to the LP?",
7492 &propdata->conssaddlp, TRUE, DEFAULT_CONSSADDLP, NULL, NULL) );
7493
7495 "propagating/" PROP_NAME "/addsymresacks",
7496 "Add inequalities for symresacks for each generator?",
7497 &propdata->addsymresacks, TRUE, DEFAULT_ADDSYMRESACKS, NULL, NULL) );
7498
7500 "propagating/" PROP_NAME "/detectorbitopes",
7501 "Should we check whether the components of the symmetry group can be handled by orbitopes?",
7502 &propdata->detectorbitopes, TRUE, DEFAULT_DETECTORBITOPES, NULL, NULL) );
7503
7505 "propagating/" PROP_NAME "/detectsubgroups",
7506 "Should we try to detect symmetric subgroups of the symmetry group on binary variables?",
7507 &propdata->detectsubgroups, TRUE, DEFAULT_DETECTSUBGROUPS, NULL, NULL) );
7508
7510 "propagating/" PROP_NAME "/addweaksbcs",
7511 "Should we add weak SBCs for enclosing orbit of symmetric subgroups?",
7512 &propdata->addweaksbcs, TRUE, DEFAULT_ADDWEAKSBCS, NULL, NULL) );
7513
7515 "propagating/" PROP_NAME "/addconsstiming",
7516 "timing of adding constraints (0 = before presolving, 1 = during presolving, 2 = after presolving)",
7517 &propdata->addconsstiming, TRUE, DEFAULT_ADDCONSSTIMING, 0, 2, NULL, NULL) );
7518
7519 /* add parameters for orbital fixing */
7521 "propagating/" PROP_NAME "/ofsymcomptiming",
7522 "timing of symmetry computation for orbital fixing (0 = before presolving, 1 = during presolving, 2 = at first call)",
7523 &propdata->ofsymcomptiming, TRUE, DEFAULT_OFSYMCOMPTIMING, 0, 2, NULL, NULL) );
7524
7526 "propagating/" PROP_NAME "/performpresolving",
7527 "run orbital fixing during presolving?",
7528 &propdata->performpresolving, TRUE, DEFAULT_PERFORMPRESOLVING, NULL, NULL) );
7529
7531 "propagating/" PROP_NAME "/recomputerestart",
7532 "recompute symmetries after a restart has occured? (0 = never)",
7533 &propdata->recomputerestart, TRUE, DEFAULT_RECOMPUTERESTART, 0, 0, NULL, NULL) );
7534
7536 "propagating/" PROP_NAME "/compresssymmetries",
7537 "Should non-affected variables be removed from permutation to save memory?",
7538 &propdata->compresssymmetries, TRUE, DEFAULT_COMPRESSSYMMETRIES, NULL, NULL) );
7539
7541 "propagating/" PROP_NAME "/compressthreshold",
7542 "Compression is used if percentage of moved vars is at most the threshold.",
7543 &propdata->compressthreshold, TRUE, DEFAULT_COMPRESSTHRESHOLD, 0.0, 1.0, NULL, NULL) );
7544
7546 "propagating/" PROP_NAME "/usecolumnsparsity",
7547 "Should the number of conss a variable is contained in be exploited in symmetry detection?",
7548 &propdata->usecolumnsparsity, TRUE, DEFAULT_USECOLUMNSPARSITY, NULL, NULL) );
7549
7551 "propagating/" PROP_NAME "/maxnconsssubgroup",
7552 "maximum number of constraints up to which subgroup structures are detected",
7553 &propdata->maxnconsssubgroup, TRUE, DEFAULT_MAXNCONSSSUBGROUP, 0, INT_MAX, NULL, NULL) );
7554
7556 "propagating/" PROP_NAME "/usedynamicprop",
7557 "whether dynamic propagation should be used for full orbitopes",
7558 &propdata->usedynamicprop, TRUE, DEFAULT_USEDYNAMICPROP, NULL, NULL) );
7559
7561 "propagating/" PROP_NAME "/addstrongsbcs",
7562 "Should strong SBCs for enclosing orbit of symmetric subgroups be added if orbitopes are not used?",
7563 &propdata->addstrongsbcs, TRUE, DEFAULT_ADDSTRONGSBCS, NULL, NULL) );
7564
7566 "propagating/" PROP_NAME "/ssttiebreakrule",
7567 "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)",
7568 &propdata->ssttiebreakrule, TRUE, DEFAULT_SSTTIEBREAKRULE, 0, 2, NULL, NULL) );
7569
7571 "propagating/" PROP_NAME "/sstleaderrule",
7572 "rule to select the leader in an orbit (0: first var; 1: last var; 2: var having most conflicting vars in orbit; 3: var having most conflicting vars in problem)",
7573 &propdata->sstleaderrule, TRUE, DEFAULT_SSTLEADERRULE, 0, 3, NULL, NULL) );
7574
7576 "propagating/" PROP_NAME "/sstleadervartype",
7577 "bitset encoding which variable types can be leaders (1: binary; 2: integer; 4: impl. int; 8: continuous);" \
7578 "if multiple types are allowed, take the one with most affected vars",
7579 &propdata->sstleadervartype, TRUE, DEFAULT_SSTLEADERVARTYPE, 1, 15, NULL, NULL) );
7580
7582 "propagating/" PROP_NAME "/addconflictcuts",
7583 "Should Schreier Sims constraints be added if we use a conflict based rule?",
7584 &propdata->addconflictcuts, TRUE, DEFAULT_ADDCONFLICTCUTS, NULL, NULL) );
7585
7587 "propagating/" PROP_NAME "/sstaddcuts",
7588 "Should Schreier Sims constraints be added?",
7589 &propdata->sstaddcuts, TRUE, DEFAULT_SSTADDCUTS, NULL, NULL) );
7590
7592 "propagating/" PROP_NAME "/sstmixedcomponents",
7593 "Should Schreier Sims constraints be added if a symmetry component contains variables of different types?",
7594 &propdata->sstmixedcomponents, TRUE, DEFAULT_SSTMIXEDCOMPONENTS, NULL, NULL) );
7595
7597 "propagating/" PROP_NAME "/symfixnonbinaryvars",
7598 "Whether all non-binary variables shall be not affected by symmetries if OF is active?",
7599 &propdata->symfixnonbinaryvars, TRUE, DEFAULT_SYMFIXNONBINARYVARS, NULL, NULL) );
7600
7602 "propagating/" PROP_NAME "/onlybinarysymmetry",
7603 "Is only symmetry on binary variables used?",
7604 &propdata->onlybinarysymmetry, TRUE, DEFAULT_ONLYBINARYSYMMETRY, NULL, NULL) );
7605
7607 "propagating/" PROP_NAME "/preferlessrows",
7608 "Shall orbitopes with less rows be preferred in detection?",
7609 &propdata->preferlessrows, TRUE, DEFAULT_PREFERLESSROWS, NULL, NULL) );
7610
7611 /* possibly add description */
7612 if ( SYMcanComputeSymmetry() )
7613 {
7615 if ( SYMsymmetryGetAddName() != NULL )
7616 {
7618 }
7619 }
7620
7621 return SCIP_OKAY;
7622}
7623
7624
7625/** return currently available symmetry group information */
7627 SCIP* scip, /**< SCIP data structure */
7628 int* npermvars, /**< pointer to store number of variables for permutations */
7629 SCIP_VAR*** permvars, /**< pointer to store variables on which permutations act */
7630 SCIP_HASHMAP** permvarmap, /**< pointer to store hash map of permvars (or NULL) */
7631 int* nperms, /**< pointer to store number of permutations */
7632 int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix (or NULL)*/
7633 int*** permstrans, /**< pointer to store permutation generators as (npermvars x nperms) matrix (or NULL)*/
7634 SCIP_Real* log10groupsize, /**< pointer to store log10 of group size (or NULL) */
7635 SCIP_Bool* binvaraffected, /**< pointer to store whether binary variables are affected (or NULL) */
7636 int** components, /**< pointer to store components of symmetry group (or NULL) */
7637 int** componentbegins, /**< pointer to store begin positions of components in components array (or NULL) */
7638 int** vartocomponent, /**< pointer to store assignment from variable to its component (or NULL) */
7639 int* ncomponents /**< pointer to store number of components (or NULL) */
7640 )
7641{
7642 SCIP_PROPDATA* propdata;
7643 SCIP_PROP* prop;
7644
7645 assert( scip != NULL );
7646 assert( npermvars != NULL );
7647 assert( permvars != NULL );
7648 assert( nperms != NULL );
7649 assert( perms != NULL || permstrans != NULL );
7650 assert( ncomponents != NULL || (components == NULL && componentbegins == NULL && vartocomponent == NULL) );
7651
7652 /* find symmetry propagator */
7653 prop = SCIPfindProp(scip, "symmetry");
7654 if ( prop == NULL )
7655 {
7656 SCIPerrorMessage("Could not find symmetry propagator.\n");
7657 return SCIP_PLUGINNOTFOUND;
7658 }
7659 assert( prop != NULL );
7660 assert( strcmp(SCIPpropGetName(prop), PROP_NAME) == 0 );
7661
7662 propdata = SCIPpropGetData(prop);
7663 assert( propdata != NULL );
7664
7665 *npermvars = propdata->npermvars;
7666 *permvars = propdata->permvars;
7667
7668 if ( permvarmap != NULL )
7669 *permvarmap = propdata->permvarmap;
7670
7671 *nperms = propdata->nperms;
7672 if ( perms != NULL )
7673 {
7674 *perms = propdata->perms;
7675 assert( *perms != NULL || *nperms <= 0 );
7676 }
7677
7678 if ( permstrans != NULL )
7679 {
7680 *permstrans = propdata->permstrans;
7681 assert( *permstrans != NULL || *nperms <= 0 );
7682 }
7683
7684 if ( log10groupsize != NULL )
7685 *log10groupsize = propdata->log10groupsize;
7686
7687 if ( binvaraffected != NULL )
7688 *binvaraffected = propdata->binvaraffected;
7689
7690 if ( components != NULL )
7691 *components = propdata->components;
7692
7693 if ( componentbegins != NULL )
7694 *componentbegins = propdata->componentbegins;
7695
7696 if ( vartocomponent )
7697 *vartocomponent = propdata->vartocomponent;
7698
7699 if ( ncomponents )
7700 *ncomponents = propdata->ncomponents;
7701
7702 return SCIP_OKAY;
7703}
7704
7705/** return whether orbital fixing is enabled */
7707 SCIP* scip /**< SCIP data structure */
7708 )
7709{
7710 SCIP_PROP* prop;
7711 SCIP_PROPDATA* propdata;
7712
7713 assert( scip != NULL );
7714
7715 prop = SCIPfindProp(scip, PROP_NAME);
7716 if ( prop == NULL )
7717 return FALSE;
7718
7719 propdata = SCIPpropGetData(prop);
7720 assert( propdata != NULL );
7721
7722 return propdata->ofenabled;
7723}
7724
7725/** return number of the symmetry group's generators */
7727 SCIP* scip /**< SCIP data structure */
7728 )
7729{
7730 SCIP_PROP* prop;
7731 SCIP_PROPDATA* propdata;
7732
7733 assert( scip != NULL );
7734
7735 prop = SCIPfindProp(scip, PROP_NAME);
7736 if ( prop == NULL )
7737 return 0;
7738
7739 propdata = SCIPpropGetData(prop);
7740 assert( propdata != NULL );
7741
7742 if ( propdata->nperms < 0 )
7743 return 0;
7744 else
7745 return propdata->nperms;
7746}
interface for symmetry computations
const char * SYMsymmetryGetName(void)
const char * SYMsymmetryGetAddName(void)
SCIP_Bool SYMcanComputeSymmetry(void)
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_MATRIXDATA *matrixdata, SYM_EXPRDATA *exprdata, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Real *symcodetime)
const char * SYMsymmetryGetDesc(void)
const char * SYMsymmetryGetAddDesc(void)
Constraint handler for AND constraints, .
constraint handler for bound disjunction constraints
struct SCIP_NodeData SCIP_NODEDATA
Constraint handler for knapsack constraints of the form , x binary and .
Constraint handler for linear constraints in their most general form, .
constraint handler for linking binary variables to a linking (continuous or integer) variable
Constraint handler for logicor constraints (equivalent to set covering, but algorithms are suited fo...
constraint handler for nonlinear constraints specified by algebraic expressions
Constraint handler for "or" constraints, .
constraint handler for (partitioning/packing/full) orbitope constraints w.r.t. the full symmetric gro...
Constraint handler for the set partitioning / packing / covering constraints .
constraint handler for symresack constraints
Constraint handler for variable bound constraints .
Constraint handler for XOR constraints, .
#define SCIP_MAXSTRLEN
Definition def.h:302
#define SCIP_Shortbool
Definition def.h:101
#define SCIP_INVALID
Definition def.h:206
#define SCIP_Real
Definition def.h:186
#define TRUE
Definition def.h:95
#define FALSE
Definition def.h:96
#define SCIPABORT()
Definition def.h:360
#define SCIP_CALL(x)
Definition def.h:388
#define nnodes
Definition gastrans.c:74
static const NodeData nodedata[]
Definition gastrans.c:83
SCIP_RETCODE SCIPgetBinvarsLinking(SCIP *scip, SCIP_CONS *cons, SCIP_VAR ***binvars, int *nbinvars)
SCIP_VAR ** SCIPgetVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition cons_or.c:2231
SCIP_RETCODE SCIPcreateSymbreakCons(SCIP *scip, SCIP_CONS **cons, const char *name, int *perm, SCIP_VAR **vars, int nvars, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
int SCIPgetNVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetVbdcoefVarbound(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsLogicor(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetRhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR ** SCIPgetVarsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR * SCIPgetLinkvarLinking(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsOr(SCIP *scip, SCIP_CONS *cons)
Definition cons_or.c:2208
SCIP_Real * SCIPgetBoundsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition cons_xor.c:5955
SCIP_Real SCIPgetLhsLinear(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR * SCIPgetResultantAnd(SCIP *scip, SCIP_CONS *cons)
Definition cons_and.c:5175
int SCIPgetNVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition cons_and.c:5126
SCIP_VAR * SCIPgetIntVarXor(SCIP *scip, SCIP_CONS *cons)
Definition cons_xor.c:6001
SCIP_Real * SCIPgetValsLinear(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR * SCIPgetExprAuxVarNonlinear(SCIP_EXPR *expr)
SCIP_VAR * SCIPgetVbdvarVarbound(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsSetppc(SCIP *scip, SCIP_CONS *cons)
SCIP_BOUNDTYPE * SCIPgetBoundtypesBounddisjunction(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR * SCIPgetResultantOr(SCIP *scip, SCIP_CONS *cons)
Definition cons_or.c:2254
SCIP_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
SCIP_EXPR * SCIPgetExprNonlinear(SCIP_CONS *cons)
SCIP_Real SCIPgetRhsNonlinear(SCIP_CONS *cons)
SCIP_VAR * SCIPgetVarVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_Longint * SCIPgetWeightsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Longint SCIPgetCapacityKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetLhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
SCIP_VAR ** SCIPgetVarsLogicor(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetRhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR ** SCIPgetVarsAnd(SCIP *scip, SCIP_CONS *cons)
Definition cons_and.c:5150
SCIP_VAR ** SCIPgetVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Bool SCIPgetRhsXor(SCIP *scip, SCIP_CONS *cons)
Definition cons_xor.c:6024
SCIP_VAR ** SCIPgetVarsXor(SCIP *scip, SCIP_CONS *cons)
Definition cons_xor.c:5978
SCIP_RETCODE SCIPcreateConsOrbitope(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR ***vars, SCIP_ORBITOPETYPE orbitopetype, int nspcons, int nblocks, SCIP_Bool usedynamicprop, SCIP_Bool mayinteract, SCIP_Bool resolveprop, SCIP_Bool ismodelcons, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode)
SCIP_Real SCIPgetLhsNonlinear(SCIP_CONS *cons)
SCIP_Real * SCIPgetValsLinking(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR ** SCIPgetVarsBounddisjunction(SCIP *scip, SCIP_CONS *cons)
@ SCIP_SETPPCTYPE_PARTITIONING
Definition cons_setppc.h:87
@ SCIP_SETPPCTYPE_COVERING
Definition cons_setppc.h:89
@ SCIP_SETPPCTYPE_PACKING
Definition cons_setppc.h:88
SCIP_RETCODE SCIPgetConsCopy(SCIP *sourcescip, SCIP *targetscip, SCIP_CONS *sourcecons, SCIP_CONS **targetcons, SCIP_CONSHDLR *sourceconshdlr, SCIP_HASHMAP *varmap, SCIP_HASHMAP *consmap, const char *name, SCIP_Bool initial, SCIP_Bool separate, SCIP_Bool enforce, SCIP_Bool check, SCIP_Bool propagate, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode, SCIP_Bool global, SCIP_Bool *valid)
Definition scip_copy.c:1591
int SCIPdigraphGetNSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition misc.c:7716
int SCIPdigraphGetNNodes(SCIP_DIGRAPH *digraph)
Definition misc.c:7658
SCIP_RETCODE SCIPdigraphAddArcSafe(SCIP_DIGRAPH *digraph, int startnode, int endnode, void *data)
Definition misc.c:7605
void SCIPdigraphFree(SCIP_DIGRAPH **digraph)
Definition misc.c:7480
void * SCIPdigraphGetNodeData(SCIP_DIGRAPH *digraph, int node)
Definition misc.c:7668
void SCIPdigraphSetNodeData(SCIP_DIGRAPH *digraph, void *dataptr, int node)
Definition misc.c:7684
int * SCIPdigraphGetSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition misc.c:7731
SCIP_RETCODE SCIPcreateDigraph(SCIP *scip, SCIP_DIGRAPH **digraph, int nnodes)
int SCIPdisjointsetGetComponentCount(SCIP_DISJOINTSET *djset)
Definition misc.c:11278
void SCIPfreeDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset)
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition misc.c:11181
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition misc.c:11208
SCIP_RETCODE SCIPcreateDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset, int ncomponents)
SCIP_Bool SCIPisPresolveFinished(SCIP *scip)
SCIP_Bool SCIPisStopped(SCIP *scip)
SCIP_STATUS SCIPgetStatus(SCIP *scip)
SCIP_STAGE SCIPgetStage(SCIP *scip)
int SCIPgetNIntVars(SCIP *scip)
Definition scip_prob.c:2082
int SCIPgetNImplVars(SCIP *scip)
Definition scip_prob.c:2127
int SCIPgetNContVars(SCIP *scip)
Definition scip_prob.c:2172
SCIP_CONS ** SCIPgetConss(SCIP *scip)
Definition scip_prob.c:3088
int SCIPgetNVars(SCIP *scip)
Definition scip_prob.c:1992
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition scip_prob.c:2770
int SCIPgetNConss(SCIP *scip)
Definition scip_prob.c:3042
SCIP_VAR ** SCIPgetVars(SCIP *scip)
Definition scip_prob.c:1947
int SCIPgetNFixedVars(SCIP *scip)
Definition scip_prob.c:2309
int SCIPgetNBinVars(SCIP *scip)
Definition scip_prob.c:2037
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition misc.c:3058
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition misc.c:3231
SCIP_RETCODE SCIPhashmapInsert(SCIP_HASHMAP *hashmap, void *origin, void *image)
Definition misc.c:3106
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition misc.c:3024
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition misc.c:3373
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition misc.c:3142
SCIP_RETCODE SCIPhashmapRemoveAll(SCIP_HASHMAP *hashmap)
Definition misc.c:3583
void SCIPhashsetFree(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem)
Definition misc.c:3740
SCIP_Bool SCIPhashsetExists(SCIP_HASHSET *hashset, void *element)
Definition misc.c:3767
SCIP_RETCODE SCIPhashsetInsert(SCIP_HASHSET *hashset, BMS_BLKMEM *blkmem, void *element)
Definition misc.c:3750
SCIP_RETCODE SCIPhashsetCreate(SCIP_HASHSET **hashset, BMS_BLKMEM *blkmem, int size)
Definition misc.c:3709
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition misc.c:2296
SCIP_Bool SCIPhashtableExists(SCIP_HASHTABLE *hashtable, void *element)
Definition misc.c:2609
#define SCIPhashFour(a, b, c, d)
Definition pub_misc.h:524
SCIP_RETCODE SCIPhashtableCreate(SCIP_HASHTABLE **hashtable, BMS_BLKMEM *blkmem, int tablesize, SCIP_DECL_HASHGETKEY((*hashgetkey)), SCIP_DECL_HASHKEYEQ((*hashkeyeq)), SCIP_DECL_HASHKEYVAL((*hashkeyval)), void *userptr)
Definition misc.c:2246
void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
Definition misc.c:2558
static INLINE uint32_t SCIPrealHashCode(double x)
Definition pub_misc.h:544
SCIP_RETCODE SCIPhashtableInsert(SCIP_HASHTABLE *hashtable, void *element)
Definition misc.c:2497
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
#define SCIPdebugMsg
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:83
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:139
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:57
SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
Definition scip_param.c:269
int SCIPgetNActiveBenders(SCIP *scip)
int SCIPconshdlrGetNConss(SCIP_CONSHDLR *conshdlr)
Definition cons.c:4595
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition cons.c:4180
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition scip_cons.c:886
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition cons.c:4629
SCIP_CONS ** SCIPconshdlrGetConss(SCIP_CONSHDLR *conshdlr)
Definition cons.c:4552
SCIP_RETCODE SCIPgetConsNVars(SCIP *scip, SCIP_CONS *cons, int *nvars, SCIP_Bool *success)
Definition scip_cons.c:2567
SCIP_Bool SCIPconsIsConflict(SCIP_CONS *cons)
Definition cons.c:8237
SCIP_Bool SCIPconsIsDynamic(SCIP_CONS *cons)
Definition cons.c:8347
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition cons.c:8108
SCIP_RETCODE SCIPpresolCons(SCIP *scip, SCIP_CONS *cons, int nrounds, SCIP_PRESOLTIMING presoltiming, int nnewfixedvars, int nnewaggrvars, int nnewchgvartypes, int nnewchgbds, int nnewholes, int nnewdelconss, int nnewaddconss, int nnewupgdconss, int nnewchgcoefs, int nnewchgsides, int *nfixedvars, int *naggrvars, int *nchgvartypes, int *nchgbds, int *naddholes, int *ndelconss, int *naddconss, int *nupgdconss, int *nchgcoefs, int *nchgsides, SCIP_RESULT *result)
Definition scip_cons.c:2352
SCIP_Bool SCIPconsIsInitial(SCIP_CONS *cons)
Definition cons.c:8257
SCIP_RETCODE SCIPprintCons(SCIP *scip, SCIP_CONS *cons, FILE *file)
Definition scip_cons.c:2482
SCIP_Bool SCIPconsIsChecked(SCIP_CONS *cons)
Definition cons.c:8287
SCIP_Bool SCIPconsIsTransformed(SCIP_CONS *cons)
Definition cons.c:8397
SCIP_RETCODE SCIPgetConsVars(SCIP *scip, SCIP_CONS *cons, SCIP_VAR **vars, int varssize, SCIP_Bool *success)
Definition scip_cons.c:2523
SCIP_Bool SCIPconsIsEnforced(SCIP_CONS *cons)
Definition cons.c:8277
SCIP_Bool SCIPconsIsActive(SCIP_CONS *cons)
Definition cons.c:8149
SCIP_Bool SCIPconsIsPropagated(SCIP_CONS *cons)
Definition cons.c:8307
SCIP_Bool SCIPconsIsLocal(SCIP_CONS *cons)
Definition cons.c:8327
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition cons.c:8088
SCIP_Bool SCIPconsIsModifiable(SCIP_CONS *cons)
Definition cons.c:8337
SCIP_Bool SCIPconsIsStickingAtNode(SCIP_CONS *cons)
Definition cons.c:8367
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition scip_cons.c:1119
SCIP_Bool SCIPconsIsSeparated(SCIP_CONS *cons)
Definition cons.c:8267
SCIP_Bool SCIPconsIsRemovable(SCIP_CONS *cons)
Definition cons.c:8357
SCIP_RETCODE SCIPincludeEventhdlrBasic(SCIP *scip, SCIP_EVENTHDLR **eventhdlrptr, const char *name, const char *desc, SCIP_DECL_EVENTEXEC((*eventexec)), SCIP_EVENTHDLRDATA *eventhdlrdata)
Definition scip_event.c:104
const char * SCIPeventhdlrGetName(SCIP_EVENTHDLR *eventhdlr)
Definition event.c:324
SCIP_EVENTTYPE SCIPeventGetType(SCIP_EVENT *event)
Definition event.c:1030
SCIP_RETCODE SCIPcatchVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int *filterpos)
Definition scip_event.c:354
SCIP_RETCODE SCIPdropVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int filterpos)
Definition scip_event.c:400
SCIP_Real SCIPeventGetOldbound(SCIP_EVENT *event)
Definition event.c:1218
SCIP_VAR * SCIPeventGetVar(SCIP_EVENT *event)
Definition event.c:1053
SCIP_Real SCIPeventGetNewbound(SCIP_EVENT *event)
Definition event.c:1242
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition expr.c:3801
SCIP_Bool SCIPexpriterIsEnd(SCIP_EXPRITER *iterator)
Definition expriter.c:968
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1443
SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1432
int SCIPcompareExpr(SCIP *scip, SCIP_EXPR *expr1, SCIP_EXPR *expr2)
Definition scip_expr.c:1724
SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
Definition scip_expr.c:1407
SCIP_EXPR * SCIPexpriterGetCurrent(SCIP_EXPRITER *iterator)
Definition expriter.c:682
void SCIPexpriterSetStagesDFS(SCIP_EXPRITER *iterator, SCIP_EXPRITER_STAGE stopstages)
Definition expriter.c:663
SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1421
SCIP_RETCODE SCIPcreateExpriter(SCIP *scip, SCIP_EXPRITER **iterator)
Definition scip_expr.c:2296
SCIP_EXPR * SCIPexpriterGetParentDFS(SCIP_EXPRITER *iterator)
Definition expriter.c:739
SCIP_EXPR * SCIPexpriterGetNext(SCIP_EXPRITER *iterator)
Definition expriter.c:857
SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
Definition expr_var.c:416
void SCIPfreeExpriter(SCIP_EXPRITER **iterator)
Definition scip_expr.c:2310
SCIP_EXPRITER_STAGE SCIPexpriterGetStageDFS(SCIP_EXPRITER *iterator)
Definition expriter.c:695
SCIP_RETCODE SCIPexpriterInit(SCIP_EXPRITER *iterator, SCIP_EXPR *expr, SCIP_EXPRITER_TYPE type, SCIP_Bool allowrevisit)
Definition expriter.c:500
SCIP_RETCODE SCIPsimplifyExpr(SCIP *scip, SCIP_EXPR *rootexpr, SCIP_EXPR **simplified, SCIP_Bool *changed, SCIP_Bool *infeasible, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition scip_expr.c:1763
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
#define SCIPfreeCleanBufferArray(scip, ptr)
Definition scip_mem.h:146
#define SCIPallocCleanBufferArray(scip, ptr, num)
Definition scip_mem.h:142
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:110
#define SCIPallocClearBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:97
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition scip_mem.h:126
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition scip_mem.c:139
#define SCIPallocBufferArray(scip, ptr, num)
Definition scip_mem.h:124
#define SCIPreallocBufferArray(scip, ptr, num)
Definition scip_mem.h:128
#define SCIPfreeBufferArray(scip, ptr)
Definition scip_mem.h:136
#define SCIPduplicateBufferArray(scip, ptr, source, num)
Definition scip_mem.h:132
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:93
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition scip_mem.h:99
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition scip_mem.h:111
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition scip_mem.h:137
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
#define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
Definition scip_mem.h:105
SCIP_DOMCHG * SCIPnodeGetDomchg(SCIP_NODE *node)
Definition tree.c:7539
SCIP_Longint SCIPnodeGetNumber(SCIP_NODE *node)
Definition tree.c:7444
SCIP_NODE * SCIPnodeGetParent(SCIP_NODE *node)
Definition tree.c:7724
int SCIPnodeGetDepth(SCIP_NODE *node)
Definition tree.c:7454
int SCIPgetNActivePricers(SCIP *scip)
SCIP_Bool SCIPinProbing(SCIP *scip)
SCIP_PROP * SCIPfindProp(SCIP *scip, const char *name)
Definition scip_prop.c:329
SCIP_RETCODE SCIPsetPropInitpre(SCIP *scip, SCIP_PROP *prop,)
Definition scip_prop.c:247
SCIP_RETCODE SCIPsetPropExitpre(SCIP *scip, SCIP_PROP *prop,)
Definition scip_prop.c:263
SCIP_PROPDATA * SCIPpropGetData(SCIP_PROP *prop)
Definition prop.c:789
SCIP_RETCODE SCIPsetPropPresol(SCIP *scip, SCIP_PROP *prop, SCIP_DECL_PROPPRESOL((*proppresol)), int presolpriority, int presolmaxrounds, SCIP_PRESOLTIMING presoltiming)
Definition scip_prop.c:279
const char * SCIPpropGetName(SCIP_PROP *prop)
Definition prop.c:941
SCIP_RETCODE SCIPsetPropResprop(SCIP *scip, SCIP_PROP *prop,)
Definition scip_prop.c:312
SCIP_RETCODE SCIPsetPropFree(SCIP *scip, SCIP_PROP *prop,)
Definition scip_prop.c:167
SCIP_RETCODE SCIPsetPropExit(SCIP *scip, SCIP_PROP *prop,)
Definition scip_prop.c:199
SCIP_RETCODE SCIPincludePropBasic(SCIP *scip, SCIP_PROP **propptr, const char *name, const char *desc, int priority, int freq, SCIP_Bool delay, SCIP_PROPTIMING timingmask, SCIP_DECL_PROPEXEC((*propexec)), SCIP_PROPDATA *propdata)
Definition scip_prop.c:114
SCIP_Bool SCIPisReoptEnabled(SCIP *scip)
int SCIPgetNRuns(SCIP *scip)
int SCIPgetNActiveConss(SCIP *scip)
SCIP_RETCODE SCIPdetermineNVarsAffectedSym(SCIP *scip, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, int *nvarsaffected)
Definition symmetry.c:584
SCIP_RETCODE SCIPcomputeComponentsSym(SCIP *scip, int **perms, int nperms, SCIP_VAR **permvars, int npermvars, SCIP_Bool transposed, int **components, int **componentbegins, int **vartocomponent, unsigned **componentblocked, int *ncomponents)
Definition symmetry.c:766
SCIP_RETCODE SCIPcomputeOrbitVar(SCIP *scip, int npermvars, int **perms, int **permstrans, int *components, int *componentbegins, SCIP_Shortbool *ignoredvars, SCIP_Shortbool *varfound, int varidx, int component, int *orbit, int *orbitsize)
Definition symmetry.c:311
SCIP_RETCODE SCIPisInvolutionPerm(int *perm, SCIP_VAR **vars, int nvars, int *ntwocyclesperm, int *nbincyclesperm, SCIP_Bool earlytermination)
Definition symmetry.c:533
SCIP_RETCODE SCIPgenerateOrbitopeVarsMatrix(SCIP *scip, SCIP_VAR ****vars, int nrows, int ncols, SCIP_VAR **permvars, int npermvars, int **orbitopevaridx, int *columnorder, int *nusedelems, SCIP_Shortbool *rowisbinary, SCIP_Bool *infeasible, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
Definition symmetry.c:970
SCIP_RETCODE SCIPcomputeOrbitsFilterSym(SCIP *scip, int npermvars, int **permstrans, int nperms, SCIP_Shortbool *inactiveperms, int *orbits, int *orbitbegins, int *norbits, int *components, int *componentbegins, int *vartocomponent, unsigned *componentblocked, int ncomponents, int nmovedpermvars)
Definition symmetry.c:163
SCIP_RETCODE SCIPextendSubOrbitope(int **suborbitope, int nrows, int nfilledcols, int coltoextend, int *perm, SCIP_Bool leftextension, int **nusedelems, SCIP_VAR **permvars, SCIP_Shortbool *rowisbinary, SCIP_Bool *success, SCIP_Bool *infeasible)
Definition symmetry.c:636
SCIP_TABLEDATA * SCIPtableGetData(SCIP_TABLE *table)
Definition table.c:288
SCIP_RETCODE SCIPincludeTable(SCIP *scip, const char *name, const char *desc, SCIP_Bool active, SCIP_DECL_TABLECOPY((*tablecopy)), SCIP_DECL_TABLEFREE((*tablefree)), SCIP_DECL_TABLEINIT((*tableinit)), SCIP_DECL_TABLEEXIT((*tableexit)), SCIP_DECL_TABLEINITSOL((*tableinitsol)), SCIP_DECL_TABLEEXITSOL((*tableexitsol)), SCIP_DECL_TABLEOUTPUT((*tableoutput)), SCIP_TABLEDATA *tabledata, int position, SCIP_STAGE earlieststage)
Definition scip_table.c:56
SCIP_Real SCIPgetSolvingTime(SCIP *scip)
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPinRepropagation(SCIP *scip)
Definition scip_tree.c:146
SCIP_RETCODE SCIPprintNodeRootPath(SCIP *scip, SCIP_NODE *node, FILE *file)
Definition scip_tree.c:529
int SCIPgetDepth(SCIP *scip)
Definition scip_tree.c:670
SCIP_NODE * SCIPgetCurrentNode(SCIP *scip)
Definition scip_tree.c:91
SCIP_RETCODE SCIPgetProbvarLinearSum(SCIP *scip, SCIP_VAR **vars, SCIP_Real *scalars, int *nvars, int varssize, SCIP_Real *constant, int *requiredsize, SCIP_Bool mergemultiples)
Definition scip_var.c:1738
SCIP_RETCODE SCIPtightenVarLb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition scip_var.c:5203
SCIP_RETCODE SCIPvarGetOrigvarSum(SCIP_VAR **var, SCIP_Real *scalar, SCIP_Real *constant)
Definition var.c:12763
SCIP_Bool SCIPvarIsBinary(SCIP_VAR *var)
Definition var.c:17421
SCIP_VAR * SCIPboundchgGetVar(SCIP_BOUNDCHG *boundchg)
Definition var.c:17148
SCIP_BOUNDCHG * SCIPdomchgGetBoundchg(SCIP_DOMCHG *domchg, int pos)
Definition var.c:17196
SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(SCIP_BOUNDCHG *boundchg)
Definition var.c:17158
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition var.c:17966
SCIP_RETCODE SCIPchgVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition scip_var.c:4766
SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
Definition var.c:17748
SCIP_RETCODE SCIPtightenVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition scip_var.c:5320
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition var.c:17406
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition var.c:17910
int SCIPdomchgGetNBoundchgs(SCIP_DOMCHG *domchg)
Definition var.c:17188
int SCIPvarGetProbindex(SCIP_VAR *var)
Definition var.c:17590
const char * SCIPvarGetName(SCIP_VAR *var)
Definition var.c:17241
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition scip_var.c:1248
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition var.c:17432
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition var.c:17956
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition var.c:17900
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP *scip, SCIP_VAR *var)
Definition scip_var.c:8715
SCIP_RETCODE SCIPcaptureVar(SCIP *scip, SCIP_VAR *var)
Definition scip_var.c:1214
void SCIPsortIntIntPtr(int *intarray1, int *intarray2, void **ptrarray, int len)
void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
void SCIPsortIntPtr(int *intarray, void **ptrarray, int len)
void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
Definition misc.c:5450
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition misc.c:10788
return SCIP_OKAY
int c
assert(minobj< SCIPgetCutoffbound(scip))
int nvars
SCIP_VAR * var
static SCIP_VAR ** vars
int nbinvars
static const SCIP_Real scalars[]
Definition lp.c:5743
#define NULL
Definition lpi_spx1.cpp:161
internal miscellaneous methods
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition scip_mem.c:57
#define PROP_PRESOL_MAXROUNDS
#define PROP_PRESOLTIMING
#define PROP_DESC
#define DEFAULT_CONSSADDLP
SCIP_Bool SCIPisOrbitalfixingEnabled(SCIP *scip)
#define MAXGENNUMERATOR
#define DEFAULT_MAXGENERATORS
static SCIP_Bool isLeadervartypeCompatible(SCIP_VAR *var, int leadervartype)
static SCIP_RETCODE getActiveVariables(SCIP *scip, SCIP_VAR ***vars, SCIP_Real **scalars, int *nvars, SCIP_Real *constant, SCIP_Bool transformed)
#define DEFAULT_DETECTSUBGROUPS
#define DEFAULT_SSTLEADERRULE
#define DEFAULT_PREFERLESSROWS
#define ISSSTINTACTIVE(x)
#define COMPRESSNVARSLB
#define DEFAULT_ADDWEAKSBCS
static SCIP_RETCODE buildSubgroupGraph(SCIP *scip, SCIP_PROPDATA *propdata, int *genorder, int ntwocycleperms, int compidx, int **graphcomponents, int **graphcompbegins, int **compcolorbegins, int *ngraphcomponents, int *ncompcolors, int **usedperms, int *nusedperms, int usedpermssize, SCIP_Shortbool *permused)
#define DEFAULT_ADDSTRONGSBCS
#define PROP_NAME
SCIP_RETCODE SCIPgetSymmetry(SCIP *scip, int *npermvars, SCIP_VAR ***permvars, SCIP_HASHMAP **permvarmap, int *nperms, int ***perms, int ***permstrans, SCIP_Real *log10groupsize, SCIP_Bool *binvaraffected, int **components, int **componentbegins, int **vartocomponent, int *ncomponents)
#define DEFAULT_SSTLEADERVARTYPE
#define TABLE_EARLIEST_ORBITALFIXING
#define DEFAULT_COMPRESSSYMMETRIES
static SCIP_RETCODE adaptSymmetryDataSST(SCIP *scip, int **origperms, int **modifiedperms, int nperms, SCIP_VAR **origpermvars, SCIP_VAR **modifiedpermvars, int npermvars, int *leaders, int nleaders)
#define ISSYMRETOPESACTIVE(x)
static SCIP_RETCODE selectOrbitLeaderSSTConss(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_HASHMAP *varmap, SCIP_VAR **permvars, int npermvars, int *orbits, int *orbitbegins, int norbits, int leaderrule, int tiebreakrule, SCIP_VARTYPE leadervartype, int *orbitidx, int *leaderidx, SCIP_Shortbool *orbitvarinconflict, int *norbitvarinconflict, SCIP_Bool useconflictgraph, SCIP_Bool *success)
static SCIP_RETCODE determineSymmetry(SCIP *scip, SCIP_PROPDATA *propdata, SYM_SPEC symspecrequire, SYM_SPEC symspecrequirefixed)
static SCIP_RETCODE addWeakSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int ncompcolors, int *chosencomppercolor, int *firstvaridxpercolor, int symgrpcompidx, int *naddedconss, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
#define DEFAULT_ADDSYMRESACKS
#define DEFAULT_ONLYBINARYSYMMETRY
static SCIP_RETCODE createConflictGraphSST(SCIP *scip, SCIP_DIGRAPH **conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_Bool onlypermvars, SCIP_HASHMAP *permvarmap, SCIP_Bool *success)
static int getNOrbitopesInComp(SCIP_VAR **permvars, int *graphcomponents, int *graphcompbegins, int *compcolorbegins, int ncompcolors, int symcompsize)
#define DEFAULT_SSTTIEBREAKRULE
#define DEFAULT_DOUBLEEQUATIONS
#define DEFAULT_SSTMIXEDCOMPONENTS
static SCIP_RETCODE checkSymmetriesAreSymmetries(SCIP *scip, SYM_SPEC fixedtype, SYM_MATRIXDATA *matrixdata, int nperms, int **perms)
#define DEFAULT_ADDCONFLICTCUTS
#define ISSSTBINACTIVE(x)
static SCIP_RETCODE performOrbitalFixing(SCIP *scip, SCIP_VAR **permvars, int npermvars, int *orbits, int *orbitbegins, int norbits, SCIP_Bool *infeasible, int *nfixedzero, int *nfixedone)
static SCIP_RETCODE chooseOrderOfGenerators(SCIP *scip, SCIP_PROPDATA *propdata, int compidx, int **genorder, int *ntwocycleperms)
static SCIP_RETCODE SCIPsortOrbitope(SCIP *scip, int **orbitopevaridx, SCIP_VAR ***vars, int nrows, int ncols)
#define ISSSTIMPLINTACTIVE(x)
static SCIP_RETCODE computeSymmetryGroup(SCIP *scip, SCIP_Bool doubleequations, SCIP_Bool compresssymmetries, SCIP_Real compressthreshold, int maxgenerators, SYM_SPEC fixedtype, SCIP_Bool local, SCIP_Bool checksymmetries, SCIP_Bool usecolumnsparsity, SCIP_CONSHDLR *conshdlr_nonlinear, int *npermvars, int *nbinpermvars, SCIP_VAR ***permvars, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, int *nmovedvars, SCIP_Bool **isnonlinvar, SCIP_Bool *binvaraffected, SCIP_Bool *compressed, SCIP_Real *symcodetime, SCIP_Bool *success)
#define PROP_DELAY
#define DEFAULT_COMPRESSTHRESHOLD
static SCIP_RETCODE addSSTConssOrbitAndUpdateSST(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_PROPDATA *propdata, SCIP_VAR **permvars, int *orbits, int *orbitbegins, int orbitidx, int orbitleaderidx, SCIP_Shortbool *orbitvarinconflict, int norbitvarinconflict, int *nchgbds, SCIP_Bool useconflictgraph)
static SCIP_RETCODE updateSymInfoConflictGraphSST(SCIP *scip, SCIP_DIGRAPH *conflictgraph, SCIP_VAR **graphvars, int ngraphvars, SCIP_VAR **permvars, int npermvars, SCIP_Bool onlypermvars, SCIP_HASHMAP *varmap, int *orbits, int *orbitbegins, int norbits)
#define DEFAULT_DETECTORBITOPES
static SCIP_RETCODE freeSymmetryData(SCIP *scip, SCIP_PROPDATA *propdata)
static SCIP_RETCODE computeBranchingVariables(SCIP *scip, int nvars, SCIP_HASHMAP *varmap, SCIP_Shortbool *bg1, int *bg1list, int *nbg1)
#define DEFAULT_ADDCONSSTIMING
#define DEFAULT_SSTADDCUTS
static SCIP_Bool checkSymmetryDataFree(SCIP_PROPDATA *propdata)
#define DEFAULT_CHECKSYMMETRIES
static SCIP_RETCODE addOrbitopeSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *usedperms, int nusedperms, int *compcolorbegins, int *graphcompbegins, int *graphcomponents, int graphcoloridx, int nrows, int ncols, int *firstvaridx, int *compidxfirstrow, int **lexorder, int *nvarslexorder, int *maxnvarslexorder, SCIP_Bool mayinteract, SCIP_Bool *success)
#define PROP_TIMING
static int getNSymhandableConss(SCIP *scip, SCIP_CONSHDLR *conshdlr_nonlinear)
#define DEFAULT_USEDYNAMICPROP
#define DEFAULT_RECOMPUTERESTART
#define TABLE_DESC_ORBITALFIXING
static SCIP_Bool hasNonlinearConstraints(SCIP_PROPDATA *propdata)
#define EVENTHDLR_SYMMETRY_NAME
#define DEFAULT_MAXNCONSSSUBGROUP
#define TABLE_POSITION_ORBITALFIXING
static SCIP_RETCODE detectAndHandleSubgroups(SCIP *scip, SCIP_PROPDATA *propdata)
static SCIP_RETCODE addSymresackConss(SCIP *scip, SCIP_PROP *prop, int *components, int *componentbegins, int ncomponents)
static SCIP_RETCODE addStrongSBCsSubgroup(SCIP *scip, SCIP_PROPDATA *propdata, int *graphcompbegins, int *graphcomponents, int graphcompidx, SCIP_Bool storelexorder, int **lexorder, int *nvarsorder, int *maxnvarsorder)
static SCIP_RETCODE propagateOrbitalFixing(SCIP *scip, SCIP_PROPDATA *propdata, SCIP_Bool *infeasible, int *nprop)
#define SCIP_SPECIALVAL
static SCIP_RETCODE collectCoefficients(SCIP *scip, SCIP_Bool doubleequations, SCIP_VAR **linvars, SCIP_Real *linvals, int nlinvars, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool istransformed, SYM_RHSSENSE rhssense, SYM_MATRIXDATA *matrixdata, int *nconssforvar)
#define ISORBITALFIXINGACTIVE(x)
#define DEFAULT_PERFORMPRESOLVING
static SCIP_RETCODE checkTwoCyclePermsAreOrbitope(SCIP *scip, SCIP_VAR **permvars, int npermvars, int **perms, int *activeperms, int ntwocycles, int nactiveperms, int **orbitopevaridx, int *columnorder, int *nusedelems, int *nusedcols, SCIP_Shortbool *rowisbinary, SCIP_Bool *isorbitope, SCIP_Shortbool *activevars)
SCIP_RETCODE SCIPincludePropSymmetry(SCIP *scip)
static SCIP_RETCODE addSSTConss(SCIP *scip, SCIP_PROPDATA *propdata, int *nchgbds)
#define TABLE_NAME_ORBITALFIXING
int SCIPgetSymmetryNGenerators(SCIP *scip)
static SCIP_RETCODE detectOrbitopes(SCIP *scip, SCIP_PROPDATA *propdata, int *components, int *componentbegins, int ncomponents)
#define DEFAULT_SYMFIXNONBINARYVARS
#define DEFAULT_OFSYMCOMPTIMING
static SCIP_RETCODE tryAddSymmetryHandlingConss(SCIP *scip, SCIP_PROP *prop, int *nchgbds, SCIP_Bool *earlyterm)
static SCIP_RETCODE setSymmetryData(SCIP *scip, SCIP_VAR **vars, int nvars, int nbinvars, SCIP_VAR ***permvars, int *npermvars, int *nbinpermvars, int **perms, int nperms, int *nmovedvars, SCIP_Bool *binvaraffected, SCIP_Bool usecompression, SCIP_Real compressthreshold, SCIP_Bool *compressed)
static SCIP_RETCODE setSymmetryMethodEnabledValues(SCIP_PROPDATA *propdata)
#define ISSSTCONTACTIVE(x)
#define DEFAULT_DISPLAYNORBITVARS
#define EVENTHDLR_SYMMETRY_DESC
#define PROP_FREQ
#define PROP_PRIORITY
#define PROP_PRESOL_PRIORITY
static SCIP_Bool SymmetryFixVar(SYM_SPEC fixedtype, SCIP_VAR *var)
#define ISSSTACTIVE(x)
#define DEFAULT_USECOLUMNSPARSITY
static SCIP_RETCODE freeConflictGraphSST(SCIP *scip, SCIP_DIGRAPH **conflictgraph, int nnodes)
propagator for symmetry handling
public functions to work with algebraic expressions
#define SCIPerrorMessage
Definition pub_message.h:64
public methods for data structures
SCIP_Real * vals
SYM_RHSSENSE * senses
methods for handling symmetries
#define MAX(x, y)
Definition tclique_def.h:92
#define SCIP_EVENTTYPE_GUBCHANGED
Definition type_event.h:76
struct SCIP_EventData SCIP_EVENTDATA
Definition type_event.h:173
#define SCIP_DECL_EVENTEXEC(x)
Definition type_event.h:253
#define SCIP_EVENTTYPE_GLBCHANGED
Definition type_event.h:75
@ SCIP_EXPRITER_DFS
Definition type_expr.h:700
#define SCIP_EXPRITER_ENTEREXPR
Definition type_expr.h:676
@ SCIP_BOUNDTYPE_LOWER
Definition type_lp.h:56
enum SCIP_BoundType SCIP_BOUNDTYPE
Definition type_lp.h:59
@ SCIP_VERBLEVEL_MINIMAL
@ SCIP_VERBLEVEL_HIGH
#define SCIP_DECL_HASHKEYEQ(x)
Definition type_misc.h:194
#define SCIP_DECL_SORTINDCOMP(x)
Definition type_misc.h:180
#define SCIP_DECL_HASHGETKEY(x)
Definition type_misc.h:191
#define SCIP_DECL_HASHKEYVAL(x)
Definition type_misc.h:197
#define SCIP_DECL_PROPEXITPRE(x)
Definition type_prop.h:114
#define SCIP_DECL_PROPFREE(x)
Definition type_prop.h:69
#define SCIP_DECL_PROPEXIT(x)
Definition type_prop.h:85
#define SCIP_DECL_PROPPRESOL(x)
Definition type_prop.h:193
#define SCIP_DECL_PROPINITPRE(x)
Definition type_prop.h:99
#define SCIP_DECL_PROPRESPROP(x)
Definition type_prop.h:258
struct SCIP_PropData SCIP_PROPDATA
Definition type_prop.h:52
#define SCIP_DECL_PROPEXEC(x)
Definition type_prop.h:217
@ SCIP_DIDNOTRUN
Definition type_result.h:42
@ SCIP_CUTOFF
Definition type_result.h:48
@ SCIP_REDUCEDDOM
Definition type_result.h:51
@ SCIP_DIDNOTFIND
Definition type_result.h:44
@ SCIP_UNBOUNDED
Definition type_result.h:47
@ SCIP_SUCCESS
Definition type_result.h:58
@ SCIP_INVALIDDATA
@ SCIP_PLUGINNOTFOUND
@ SCIP_ERROR
enum SCIP_Retcode SCIP_RETCODE
@ SCIP_STAGE_INITPRESOLVE
Definition type_set.h:48
@ SCIP_STAGE_PRESOLVING
Definition type_set.h:49
@ SCIP_STAGE_SOLVING
Definition type_set.h:53
@ SCIP_STATUS_UNKNOWN
Definition type_stat.h:42
#define SYM_COMPUTETIMING_DURINGPRESOL
@ SCIP_ORBITOPETYPE_FULL
#define SYM_HANDLETYPE_ORBITALFIXING
@ SCIP_LEADERRULE_MAXCONFLICTS
@ SCIP_LEADERRULE_LASTINORBIT
@ SCIP_LEADERRULE_MAXCONFLICTSINORBIT
@ SCIP_LEADERRULE_FIRSTINORBIT
@ SCIP_LEADERTIEBREAKRULE_MAXORBIT
@ SCIP_LEADERTIEBREAKRULE_MINORBIT
@ SCIP_LEADERTIEBREAKRULE_MAXCONFLICTSINORBIT
#define SYM_SPEC_BINARY
#define SYM_SPEC_INTEGER
enum SYM_Rhssense SYM_RHSSENSE
#define SYM_HANDLETYPE_SYMBREAK
#define SYM_COMPUTETIMING_AFTERPRESOL
#define SYM_HANDLETYPE_SST
uint32_t SYM_SPEC
#define SYM_SPEC_REAL
@ SYM_SENSE_EQUATION
@ SYM_SENSE_OR
@ SYM_SENSE_BOUNDIS_TYPE_2
@ SYM_SENSE_BOUNDIS_TYPE_1
@ SYM_SENSE_UNKOWN
@ SYM_SENSE_XOR
@ SYM_SENSE_INEQUALITY
@ SYM_SENSE_AND
@ SCIP_SSTTYPE_BINARY
#define SCIP_DECL_TABLEFREE(x)
Definition type_table.h:75
struct SCIP_TableData SCIP_TABLEDATA
Definition type_table.h:58
#define SCIP_DECL_TABLEOUTPUT(x)
Definition type_table.h:122
#define SCIP_PROPTIMING_ALWAYS
Definition type_timing.h:72
@ SCIP_VARTYPE_INTEGER
Definition type_var.h:63
@ SCIP_VARTYPE_CONTINUOUS
Definition type_var.h:71
@ SCIP_VARTYPE_IMPLINT
Definition type_var.h:64
@ SCIP_VARTYPE_BINARY
Definition type_var.h:62
@ SCIP_BOUNDCHGTYPE_BRANCHING
Definition type_var.h:87
enum SCIP_Vartype SCIP_VARTYPE
Definition type_var.h:73