SCIP Doxygen Documentation
Loading...
Searching...
No Matches
symmetry_orbital.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-2026 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 symmetry_orbital.c
26 * @ingroup OTHER_CFILES
27 * @brief methods for handling symmetries by orbital reduction
28 * @author Jasper van Doornmalen
29 *
30 * Orbital fixing is introduced by@n
31 * F. Margot: Exploiting orbits in symmetric ILP. Math. Program., 98(1-3):3–21, 2003.
32 * The method computes orbits of variables with respect to the subgroup of the symmetry group that stabilizes the
33 * 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
34 * variables in the orbit is fixed to 0 or 1, respectively. This method only works for binary variables.
35 * Margot considers the subgroup that stabilizes the set of one-fixings setwise. We determine a subgroup of this group,
36 * namely the group generated by the given symmetry group component generators, where the generators satisfy the
37 * stabilization condition.
38 *
39 * A generalisation is given in the unified symmetry handling constraint paper, Section 4.3 and 5.1 in [vD,H]:@n
40 * J. van Doornmalen, C. Hojny, "A Unified Framework for Symmetry Handling", preprint, 2023,
41 * https://doi.org/10.48550/arXiv.2211.01295.
42 *
43 * We assume that the provided symmetries (given by a generating permutation set) are symmetries for the problem at the
44 * root node. It is possible that dual (presolving) reductions break this symmetry. As an example, in cons_components.c,
45 * if the problem contains an independent component (i.e., variables are not connected logically by constraints), then
46 * these individual 'components' can be solved. If an optimal solution is easily retrieved, the variables of this
47 * component are fixed, even if symmetrically equivalent solutions exist. Another example is 'stuffing' for linear
48 * constraints.
49 *
50 * To illustrate this, consider the example \f$\max\{x_1 + x_2 : x_1 + x_2 \leq 1, Ay \leq b,
51 * (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
52 * setppc constraint handler may fix \f$(x_1,x_2) = (1,0)\f$. However, since both variables are symmetric, this setting
53 * is not strict (if it was strict, both variables would have been set to the same value) and orbital fixing would
54 * declare this subsolution as infeasible (there exists an orbit of non-branching variables that are fixed to different
55 * values).
56 *
57 * We have observed, and assume, that such dual reductions only take place at presolving or in the root node.
58 * So, to avoid this situation, if we detect that a symmetry-breaking reduction is applied at the root node,
59 * we disable orbital fixing for certain generating permutations based on the bounds of the affected global variables,
60 * see identifyOrbitalSymmetriesBroken.
61 *
62 * With the assumption that the symmetries are actual symmetries at the root node, symmetries are broken by the
63 * branching decisions.
64 * For a branch-and-bound tree node \f$\beta\f$ and variable vector \f$x\f$,
65 * let \f$\sigma_\beta(x)\f$ be the permuted and restricted vector \f$x\f$ that enumerates the branching variables,
66 * following the path of the root node to \f$\beta\f$ (cf., Example 11 in [vD,H]).
67 * Consider a component of the symmetry group, given by a set of generating permutations.
68 * Of this set, we select these permutations (not disabled by identifyOrbitalSymmetriesBroken)
69 * for which te variable domains of the branched variables \f$\sigma_\beta(x)\f$
70 * are smaller or equal to the variable domains of the permuted variable. This defines a group (cf. [vD,H, Lemma 22]).
71 * This group is a subgroup of \f$\delta^\beta\f$ of [vD,H, Section 4.3], meaning that the reductions are valid.
72 *
73 * The reductions are:
74 *
75 * - For each orbit of the group, every variable domains can be shrunk to the intersection of all variable domains in
76 * the orbit.
77 * - The domains of the branching variables are upper bounds to the domains of the variables in its orbits.
78 *
79 * For orbital fixing, it is crucial that the vectors \f$\sigma_\beta(x)\f$ are the branching variables up to node
80 * \f$\beta\f$ in the given order. Since SCIP can change the tree structure during solving (re-writing history),
81 * we store the original branching decisions at the moment they are made. See event_shadowtree.c .
82 */
83
84/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
85
88#include "scip/pub_cons.h"
89#include "scip/pub_message.h"
90#include "scip/pub_var.h"
91#include "scip/struct_var.h"
92#include "scip/type_var.h"
93#include "scip/scip.h"
94#include "scip/scip_branch.h"
95#include "scip/scip_conflict.h"
96#include "scip/scip_cons.h"
97#include "scip/scip_copy.h"
98#include "scip/scip_cut.h"
99#include "scip/scip_general.h"
100#include "scip/scip_lp.h"
101#include "scip/scip_mem.h"
102#include "scip/scip_message.h"
103#include "scip/scip_numerics.h"
104#include "scip/scip_param.h"
105#include "scip/scip_prob.h"
106#include "scip/scip_probing.h"
107#include "scip/scip_sol.h"
108#include "scip/scip_var.h"
109#include "scip/debug.h"
110#include "scip/struct_scip.h"
111#include "scip/struct_mem.h"
112#include "scip/struct_tree.h"
113#include "scip/symmetry.h"
115#include <ctype.h>
116#include <memory.h>
117
118
119/* event handler properties */
120#define EVENTHDLR_SYMMETRY_NAME "symmetry_orbital"
121#define EVENTHDLR_SYMMETRY_DESC "filter global variable bound reduction event handler for orbital reduction"
122
123
124/*
125 * Data structures
126 */
127
128
129/** data for orbital reduction component propagator */
131{
132 SCIP_NODE* lastnode; /**< last node processed by orbital reduction component */
133 SCIP_Real* globalvarlbs; /**< global variable lower bounds until before branching starts */
134 SCIP_Real* globalvarubs; /**< global variable upper bounds until before branching starts */
135 int** perms; /**< the permutations for orbital reduction */
136 int nperms; /**< the number of permutations in perms */
137 SCIP_VAR** permvars; /**< array consisting of the variables of this component */
138 int npermvars; /**< number of vars in this component */
139 SCIP_HASHMAP* permvarmap; /**< map of variables to indices in permvars array */
140
141 SCIP_Bool symmetrybrokencomputed; /**< whether the symmetry broken information is computed already */
142 int* symbrokenvarids; /**< variables to be stabilized because the symmetry is globally broken */
143 int nsymbrokenvarids; /**< symbrokenvarids array length, is 0 iff symbrokenvarids is NULL */
144
145 SCIP_Bool treewarninggiven; /**< whether a warning is given for missing nodes in shadowtree */
146};
148
149
150/** data for orbital reduction propagator */
151struct SCIP_OrbitalReductionData
152{
153 SCIP_EVENTHDLR* shadowtreeeventhdlr;/**< eventhandler for the shadow tree data structure */
154 SCIP_EVENTHDLR* globalfixeventhdlr; /**< event handler for handling global variable bound reductions */
155
156 ORCDATA** componentdatas; /**< array of pointers to individual components for orbital reduction */
157 int ncomponents; /**< number of orbital reduction datas in array */
158 int maxncomponents; /**< allocated orbital reduction datas array size */
159 int nred; /**< total number of reductions */
160 int ncutoff; /**< total number of cutoffs */
161};
162
163
164/*
165 * Local methods
166 */
167
168
169/** identifies the orbits at which symmetry is broken according to the global bounds
170 *
171 * An example of a symmetry-breaking constraint is cons_components.
172 */
173static
175 SCIP* scip, /**< pointer to SCIP data structure */
176 ORCDATA* orcdata /**< pointer to data for orbital reduction data */
177)
178{
179 SCIP_DISJOINTSET* orbitset;
180 int i;
181 int j;
182 int p;
183 int* perm;
184 int* varorbitids;
185 int* varorbitidssort;
186 int orbitbegin;
187 int orbitend;
188 int orbitid;
189 int maxnsymbrokenvarids;
190 SCIP_Real orbitglb;
191 SCIP_Real orbitgub;
192 SCIP_Bool orbitsymbroken;
193
194 assert( scip != NULL );
195 assert( orcdata != NULL );
196 assert( !orcdata->symmetrybrokencomputed );
197 orcdata->symbrokenvarids = NULL;
198 orcdata->nsymbrokenvarids = 0;
199 maxnsymbrokenvarids = 0;
200
201 /* determine all orbits */
202 SCIP_CALL( SCIPcreateDisjointset(scip, &orbitset, orcdata->npermvars) );
203 for (p = 0; p < orcdata->nperms; ++p)
204 {
205 perm = orcdata->perms[p];
206 assert( perm != NULL );
207
208 for (i = 0; i < orcdata->npermvars; ++i)
209 {
210 j = perm[i];
211 if ( i != j )
212 SCIPdisjointsetUnion(orbitset, i, j, FALSE);
213 }
214 }
215
216#ifndef NDEBUG
217 /* no arithmetic is performed on these bounds, so we can compare floats by their value exactly */
218 for (i = 0; i < orcdata->npermvars; ++i)
219 {
220 assert( SCIPvarGetLbGlobal(orcdata->permvars[i]) == orcdata->globalvarlbs[i] ); /*lint !e777*/
221 assert( SCIPvarGetUbGlobal(orcdata->permvars[i]) == orcdata->globalvarubs[i] ); /*lint !e777*/
222 }
223#endif
224
225 /* sort all orbits */
226 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitids, orcdata->npermvars) );
227 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitidssort, orcdata->npermvars) );
228 for (i = 0; i < orcdata->npermvars; ++i)
229 varorbitids[i] = SCIPdisjointsetFind(orbitset, i);
230 SCIPsort(varorbitidssort, SCIPsortArgsortInt, varorbitids, orcdata->npermvars);
231
232 /* iterate over all orbits and get the maximal orbit lower bound and minimal orbit upper bound */
233 for (orbitbegin = 0; orbitbegin < orcdata->npermvars; orbitbegin = orbitend)
234 {
235 /* get id of the orbit */
236 orbitid = varorbitids[varorbitidssort[orbitbegin]];
237
238 /* the orbit must have the same bounds */
239 orbitsymbroken = FALSE;
240 j = varorbitidssort[orbitbegin];
241 orbitglb = orcdata->globalvarlbs[j];
242 orbitgub = orcdata->globalvarubs[j];
243 for (i = orbitbegin + 1; i < orcdata->npermvars; ++i)
244 {
245 j = varorbitidssort[i];
246
247 /* stop if j is not the element in the orbit, then it is part of the next orbit */
248 if ( varorbitids[j] != orbitid )
249 break;
250
251 if ( !orbitsymbroken )
252 {
253 if ( !SCIPsymEQ(scip, orbitglb, orcdata->globalvarlbs[j]) || !SCIPsymEQ(scip, orbitgub, orcdata->globalvarubs[j]) )
254 {
255 orbitsymbroken = TRUE;
256 break;
257 }
258 }
259 }
260 assert( orbitsymbroken || i == orcdata->npermvars || varorbitids[j] != orbitid );
261
262 /* in case we terminated the orbit due to broken symmetries, find the correct end of the orbit */
263 if ( orbitsymbroken )
264 {
265 while ( i < orcdata->npermvars && varorbitids[j] == orbitid )
266 j = varorbitidssort[++i];
267 }
268 orbitend = i;
269
270 /* symmetry is broken within this orbit if the intersection of the global variable domains are empty */
271 if ( orbitsymbroken )
272 {
273 /* add all variable ids in the orbit to the symbrokenvarids array: resize if needed */
274 if ( orcdata->nsymbrokenvarids + orbitend - orbitbegin > maxnsymbrokenvarids )
275 {
276 int newsize;
277
278 newsize = SCIPcalcMemGrowSize(scip, orcdata->nsymbrokenvarids + orbitend - orbitbegin);
279 assert( newsize >= 0 );
280
281 if ( orcdata->nsymbrokenvarids == 0 )
282 {
283 assert( orcdata->symbrokenvarids == NULL );
285 }
286 else
287 {
288 assert( orcdata->symbrokenvarids != NULL );
290 maxnsymbrokenvarids, newsize) );
291 }
292
293 maxnsymbrokenvarids = newsize;
294 }
295
296 /* add all variable ids in the orbit to the symbrokenvarids array: add */
297 for (i = orbitbegin; i < orbitend; ++i)
298 {
299 j = varorbitidssort[i];
300 assert( varorbitids[j] == orbitid );
301 assert( orcdata->nsymbrokenvarids < maxnsymbrokenvarids );
302 orcdata->symbrokenvarids[orcdata->nsymbrokenvarids++] = j;
303 }
304 }
305 }
306
307 /* shrink the allocated array size to the actually needed size */
308 assert( orcdata->nsymbrokenvarids <= maxnsymbrokenvarids );
309 if ( orcdata->nsymbrokenvarids > 0 && orcdata->nsymbrokenvarids < maxnsymbrokenvarids )
310 {
312 maxnsymbrokenvarids, orcdata->nsymbrokenvarids) );
313 }
314 assert( (orcdata->nsymbrokenvarids == 0) == (orcdata->symbrokenvarids == NULL) );
315
316 /* mark that this method is executed for the component */
317 orcdata->symmetrybrokencomputed = TRUE;
318
319 /* output information */
320 if ( orcdata->nsymbrokenvarids > 0 )
321 {
323 "Orbital fixing symmetry for %p broken before symmetry. Requires fixing %d/%d affected variables.\n",
324 (void*) orcdata, orcdata->nsymbrokenvarids, orcdata->npermvars);
325 }
326
327 SCIPfreeBufferArray(scip, &varorbitidssort);
328 SCIPfreeBufferArray(scip, &varorbitids);
329 SCIPfreeDisjointset(scip, &orbitset);
330
331 return SCIP_OKAY;
332}
333
334
335/** populates chosenperms with a generating set of the symmetry group stabilizing the branching decisions
336 *
337 * The symmetry subgroup considered is generated by all permutations where for all branching variables \f$x\f$
338 * with permuted variable \f$y\f$ for all possible variable assignments we have \f$x \leq y\f$.
339 * We restrict ourselves to testing this only for the group generators.
340 */
341static
343 SCIP* scip, /**< pointer to SCIP data structure */
344 ORCDATA* orcdata, /**< pointer to data for orbital reduction data */
345 int** chosenperms, /**< pointer to permutations that are chosen */
346 int* nchosenperms, /**< pointer to store the number of chosen permutations */
347 SCIP_Real* varlbs, /**< array of orcdata->permvars variable LBs. If NULL, use local bounds */
348 SCIP_Real* varubs, /**< array of orcdata->permvars variable UBs. If NULL, use local bounds */
349 int* branchedvarindices, /**< array of given branching decisions, in branching order */
350 SCIP_Bool* inbranchedvarindices, /**< array stating whether variable with index in orcdata->permvars is
351 * contained in the branching decisions. */
352 int nbranchedvarindices /**< number of branching decisions */
353 )
354{
355 int i;
356 int p;
357 int* perm;
358 int varid;
359 int varidimage;
360
361 assert( scip != NULL );
362 assert( orcdata != NULL );
363 assert( chosenperms != NULL );
364 assert( nchosenperms != NULL );
365 assert( (varlbs == NULL) == (varubs == NULL) );
366 assert( branchedvarindices != NULL );
367 assert( inbranchedvarindices != NULL );
368 assert( nbranchedvarindices >= 0 );
369 assert( orcdata->symmetrybrokencomputed );
370 assert( (orcdata->nsymbrokenvarids == 0) == (orcdata->symbrokenvarids == NULL) );
371
372 *nchosenperms = 0;
373
374 for (p = 0; p < orcdata->nperms; ++p)
375 {
376 perm = orcdata->perms[p];
377
378 /* make sure that the symmetry broken orbit variable indices are met with equality */
379 for (i = 0; i < orcdata->nsymbrokenvarids; ++i)
380 {
381 varid = orcdata->symbrokenvarids[i];
382 assert( varid >= 0 );
383 assert( varid < orcdata->npermvars );
384 assert( orcdata->permvars[varid] != NULL );
385 varidimage = perm[varid];
386 assert( varidimage >= 0 );
387 assert( varidimage < orcdata->npermvars );
388 assert( orcdata->permvars[varidimage] != NULL );
389
390 /* branching variable is not affected by this permutation */
391 if ( varidimage == varid )
392 continue;
393
394 /* the variables on which symmetry is broken must be permuted to entries with the same fixed value
395 *
396 * Because we check a whole orbit of the group and perm is part of it, it suffices to compare the upper bound
397 * of varid with the lower bound of varidimage. Namely, for all indices i, \f$lb_i \leq ub_i\f$, so we get
398 * a series of equalities yielding that all expressions must be the same:
399 * \f$ub_i = lb_j <= ub_j = lb_{\cdots} <= \cdots = lb_j < ub_j \f$
400 */
401 if ( ! SCIPsymEQ(scip,
402 varubs ? varubs[varid] : SCIPvarGetUbLocal(orcdata->permvars[varid]),
403 varlbs ? varlbs[varidimage] : SCIPvarGetLbLocal(orcdata->permvars[varidimage]) )
404 )
405 break;
406 }
407 /* if the above loop is broken, this permutation does not qualify for the stabilizer */
408 if ( i < orcdata->nsymbrokenvarids )
409 continue;
410
411 /* iterate over each branched variable and check */
412 for (i = 0; i < nbranchedvarindices; ++i)
413 {
414 varid = branchedvarindices[i];
415 assert( varid >= 0 );
416 assert( varid < orcdata->npermvars );
417 assert( orcdata->permvars[varid] != NULL );
418 varidimage = perm[varid];
419 assert( varidimage >= 0 );
420 assert( varidimage < orcdata->npermvars );
421 assert( orcdata->permvars[varidimage] != NULL );
422
423 /* branching variable is not affected by this permutation */
424 if ( varidimage == varid )
425 continue;
426
427 if ( SCIPsymGT(scip,
428 varubs ? varubs[varid] : SCIPvarGetUbLocal(orcdata->permvars[varid]),
429 varlbs ? varlbs[varidimage] : SCIPvarGetLbLocal(orcdata->permvars[varidimage]) )
430 )
431 break;
432 }
433 /* if the above loop is broken, this permutation does not qualify for the stabilizer */
434 if ( i < nbranchedvarindices )
435 continue;
436
437 /* permutation qualifies for the stabilizer. Add permutation */
438 chosenperms[(*nchosenperms)++] = perm;
439 }
440
441 return SCIP_OKAY;
442}
443
444/** using bisection, finds the minimal index k (frameleft <= k < frameright) such that ids[idssort[k]] >= findid
445 *
446 * If for all k (frameleft <= k < frameright) holds ids[idssort[k]] < findid, returns frameright.
447 */
448static
450 int* ids, /**< int array with entries */
451 int* idssort, /**< array of indices of ids that sort ids */
452 int frameleft, /**< search in idssort for index range [frameleft, frameright) */
453 int frameright, /**< search in idssort for index range [frameleft, frameright) */
454 int findid /**< entry value to find */
455 )
456{
457 int center;
458 int id;
459
460#ifndef NDEBUG
461 int origframeleft;
462 int origframeright;
463 origframeleft = frameleft;
464 origframeright = frameright;
465#endif
466
467 assert( ids != NULL );
468 assert( idssort != NULL );
469 assert( frameleft >= 0 );
470 assert( frameright >= frameleft );
471
472 /* empty frame case */
473 if ( frameright == frameleft )
474 return frameright;
475
476 while (frameright - frameleft >= 2)
477 {
478 /* split [frameleft, frameright) in [frameleft, center) and [center, frameright) */
479 center = frameleft + ((frameright - frameleft) / 2);
480 assert( center > frameleft );
481 assert( center < frameright );
482 id = idssort[center];
483 if ( ids[id] < findid )
484 {
485 /* first instance greater or equal to findid is in [center, frameright) */
486 frameleft = center;
487 }
488 else
489 {
490 /* first instance greater or equal to findid is in [frameleft, center) */
491 frameright = center;
492 }
493 }
494
495 assert( frameright - frameleft == 1 );
496 id = idssort[frameleft];
497 if ( ids[id] < findid )
498 ++frameleft;
499
500 assert( frameleft >= origframeleft );
501 assert( frameright <= origframeright );
502 assert( frameleft >= origframeright || ids[idssort[frameleft]] >= findid );
503 assert( frameleft - 1 < origframeleft || ids[idssort[frameleft - 1]] < findid );
504 return frameleft;
505}
506
507
508/** applies the orbital reduction steps for precomputed orbits
509 *
510 * Either use the local variable bounds, or variable bounds determined by the varlbs and varubs arrays.
511 * @pre varubs is NULL if and only if varlbs is NULL.
512 */
513static
515 SCIP* scip, /**< pointer to SCIP data structure */
516 ORCDATA* orcdata, /**< pointer to data for orbital reduction data */
517 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is detected */
518 int* nred, /**< pointer to store the number of determined domain reductions */
519 int* varorbitids, /**< array specifying the orbit IDs for variables in array orcdata->vars */
520 int* varorbitidssort, /**< an index array that sorts the varorbitids array */
521 SCIP_Real* varlbs, /**< array of lower bounds for variable array orcdata->vars to compute with
522 * or NULL, if local bounds are used */
523 SCIP_Real* varubs /**< array of upper bounds for variable array orcdata->vars to compute with
524 * or NULL, if local bounds are used. */
525 )
526{
527 int i;
528 int varid;
529 int orbitid;
530 int orbitbegin;
531 int orbitend;
532 SCIP_Real orbitlb;
533 SCIP_Real orbitub;
534 SCIP_Real lb;
535 SCIP_Real ub;
536
537 assert( scip != NULL );
538 assert( orcdata != NULL );
539 assert( infeasible != NULL );
540 assert( nred != NULL );
541 assert( varorbitids != NULL );
542 assert( varorbitidssort != NULL );
543 assert( ( varlbs == NULL ) == ( varubs == NULL ) );
544
545 /* infeasible and nred are defined by the function that calls this function,
546 * and this function only gets called if no infeasibility is found so far.
547 */
548 assert( !*infeasible );
549 assert( *nred >= 0 );
550
551 for (orbitbegin = 0; orbitbegin < orcdata->npermvars; orbitbegin = orbitend)
552 {
553 /* get id of the orbit, and scan how large the orbit is */
554 orbitid = varorbitids[varorbitidssort[orbitbegin]];
555 for (orbitend = orbitbegin + 1; orbitend < orcdata->npermvars; ++orbitend)
556 {
557 if ( varorbitids[varorbitidssort[orbitend]] != orbitid )
558 break;
559 }
560
561 /* orbits consisting of only one element cannot yield reductions */
562 if ( orbitend - orbitbegin <= 1 )
563 continue;
564
565 /* get upper and lower bounds in orbit */
566 orbitlb = -SCIPinfinity(scip);
567 orbitub = SCIPinfinity(scip);
568 for (i = orbitbegin; i < orbitend; ++i)
569 {
570 varid = varorbitidssort[i];
571 assert( varid >= 0 );
572 assert( varid < orcdata->npermvars );
573 assert( orcdata->permvars[varid] != NULL );
574
575 lb = varlbs ? varlbs[varid] : SCIPvarGetLbLocal(orcdata->permvars[varid]);
576 if ( SCIPsymGT(scip, lb, orbitlb) )
577 orbitlb = lb;
578 ub = varubs ? varubs[varid] : SCIPvarGetUbLocal(orcdata->permvars[varid]);
579 if ( SCIPsymLT(scip, ub, orbitub) )
580 orbitub = ub;
581 }
582
583 /* if bounds are incompatible, infeasibility is detected */
584 if ( SCIPsymGT(scip, orbitlb, orbitub) )
585 {
586 *infeasible = TRUE;
587 return SCIP_OKAY;
588 }
589 assert( SCIPsymLE(scip, orbitlb, orbitub) );
590
591 /* update variable bounds to be in this range */
592 for (i = orbitbegin; i < orbitend; ++i)
593 {
594 varid = varorbitidssort[i];
595 assert( varid >= 0 );
596 assert( varid < orcdata->npermvars );
597
598 if ( varlbs != NULL )
599 {
600 assert( SCIPsymLE(scip, varlbs[varid], orbitlb) );
601 varlbs[varid] = orbitlb;
602 }
603 if ( !SCIPisInfinity(scip, -orbitlb) &&
604 SCIPsymLT(scip, SCIPvarGetLbLocal(orcdata->permvars[varid]), orbitlb) )
605 {
606 SCIP_Bool tightened;
607 SCIP_CALL( SCIPtightenVarLb(scip, orcdata->permvars[varid], orbitlb, TRUE, infeasible, &tightened) );
608
609 /* propagator detected infeasibility in this node */
610 if ( *infeasible )
611 return SCIP_OKAY;
612 assert( tightened );
613 *nred += 1;
614 }
615
616 if ( varubs != NULL )
617 {
618 assert( SCIPsymGE(scip, varubs[varid], orbitub) );
619 varubs[varid] = orbitub;
620 }
621 if ( !SCIPisInfinity(scip, orbitub) &&
622 SCIPsymGT(scip, SCIPvarGetUbLocal(orcdata->permvars[varid]), orbitub) )
623 {
624 SCIP_Bool tightened;
625 SCIP_CALL( SCIPtightenVarUb(scip, orcdata->permvars[varid], orbitub, TRUE, infeasible, &tightened) );
626
627 /* propagator detected infeasibility in this node */
628 if ( *infeasible )
629 return SCIP_OKAY;
630 assert( tightened );
631 *nred += 1;
632 }
633 }
634 }
635 assert( !*infeasible );
636 return SCIP_OKAY;
637}
638
639
640/** orbital reduction, the orbital branching part */
641static
643 SCIP* scip, /**< pointer to SCIP data structure */
644 ORCDATA* orcdata, /**< pointer to data for orbital reduction data */
645 SCIP_SHADOWTREE* shadowtree, /**< pointer to shadow tree */
646 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is detected */
647 int* nred /**< pointer to store the number of determined domain reductions */
648 )
649{
650 SCIP_NODE* focusnode;
651 SCIP_NODE* parentnode;
652 SCIP_SHADOWNODE* shadowfocusnode;
653 SCIP_SHADOWNODE* tmpshadownode;
654 SCIP_SHADOWNODE** rootedshadowpath;
655 int pathlength;
656 int depth;
657 int branchstep;
658 int i;
659 SCIP_Real* varlbs;
660 SCIP_Real* varubs;
662 int* branchedvarindices;
663 SCIP_Bool* inbranchedvarindices;
664 int nbranchedvarindices;
665 int varid;
666 SCIP_SHADOWBOUNDUPDATE* branchingdecision;
667 int branchingdecisionvarid;
668 int** chosenperms;
669 int* perm;
670 int nchosenperms;
671 int p;
672 int* varorbitids;
673 int* varorbitidssort;
674 int idx;
675 int orbitbegin;
676 int orbitend;
677 SCIP_DISJOINTSET* orbitset;
678 int orbitsetcomponentid;
679
680 assert( scip != NULL );
681 assert( orcdata != NULL );
682 assert( shadowtree != NULL );
683 assert( infeasible != NULL );
684 assert( nred != NULL );
685
686 /* infeasible and nred are defined by the function that calls this function,
687 * and this function only gets called if no infeasibility is found so far.
688 */
689 assert( !*infeasible );
690 assert( *nred >= 0 );
691
692 focusnode = SCIPgetFocusNode(scip);
693 assert( focusnode == SCIPgetCurrentNode(scip) );
694 assert( focusnode != NULL );
695
696 /* do nothing if this method has already been called for this node */
697 if ( orcdata->lastnode == focusnode )
698 return SCIP_OKAY;
699
700 orcdata->lastnode = focusnode;
701 parentnode = SCIPnodeGetParent(focusnode);
702
703 /* the root node has not been generated by branching decisions */
704 if ( parentnode == NULL )
705 return SCIP_OKAY;
706
707 shadowfocusnode = SCIPshadowTreeGetShadowNode(shadowtree, focusnode);
708
709 /* do not apply orbital reduction if focusnode does not exist in the shadowtree */
710 if ( shadowfocusnode == NULL )
711 {
712 if ( !orcdata->treewarninggiven )
713 {
714 SCIPwarningMessage(scip, "Attempting orbital reduction on nodes not existing in the symmetry shadowtree"
715 " (and suppressing future warnings for this component)\n");
716 orcdata->treewarninggiven = TRUE;
717 }
718 return SCIP_OKAY;
719 }
720
721 /* get the rooted path */
722 /* @todo add depth field to shadow tree node to improve efficiency */
723 pathlength = 0;
724 tmpshadownode = shadowfocusnode;
725 do
726 {
727 tmpshadownode = tmpshadownode->parent;
728 ++pathlength;
729 }
730 while ( tmpshadownode != NULL );
731
732 SCIP_CALL( SCIPallocBufferArray(scip, &rootedshadowpath, pathlength) );
733 i = pathlength;
734 tmpshadownode = shadowfocusnode;
735 while ( i > 0 )
736 {
737 rootedshadowpath[--i] = tmpshadownode;
738 assert( tmpshadownode != NULL );
739 tmpshadownode = tmpshadownode->parent;
740 }
741 assert( tmpshadownode == NULL );
742 assert( i == 0 );
743
744 /* replay bound reductions and propagations made until just before the focusnode */
745 assert( orcdata->npermvars > 0 ); /* if it's 0, then we do not have to do anything at all */
746
747 SCIP_CALL( SCIPallocBufferArray(scip, &varlbs, orcdata->npermvars) );
748 SCIP_CALL( SCIPallocBufferArray(scip, &varubs, orcdata->npermvars) );
749 SCIP_CALL( SCIPallocBufferArray(scip, &branchedvarindices, orcdata->npermvars) );
750 SCIP_CALL( SCIPallocCleanBufferArray(scip, &inbranchedvarindices, orcdata->npermvars) );
751
752 /* start with the bounds found after computing the symmetry group */
753 for (i = 0; i < orcdata->npermvars; ++i)
754 varlbs[i] = orcdata->globalvarlbs[i];
755 for (i = 0; i < orcdata->npermvars; ++i)
756 varubs[i] = orcdata->globalvarubs[i];
757
758 nbranchedvarindices = 0;
759 for (depth = 0; depth < pathlength - 1; ++depth)
760 {
761 tmpshadownode = rootedshadowpath[depth];
762
763 /* receive propagations */
764 for (i = 0; i < tmpshadownode->npropagations; ++i)
765 {
766 update = &(tmpshadownode->propagations[i]);
767 varid = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) update->var);
768 assert( varid < orcdata->npermvars || varid == INT_MAX );
769 assert( varid >= 0 );
770 if ( varid < orcdata->npermvars )
771 {
772 assert( SCIPsymLE(scip, varlbs[varid], varubs[varid]) );
773 switch (update->boundchgtype)
774 {
776 assert( SCIPsymGE(scip, update->newbound, varlbs[varid]) );
777 varlbs[varid] = update->newbound;
778 break;
780 assert( SCIPsymLE(scip, update->newbound, varubs[varid]) );
781 varubs[varid] = update->newbound;
782 break;
783 default:
784 SCIPABORT();
785 }
786 assert( SCIPsymLE(scip, varlbs[varid], varubs[varid]) );
787 }
788 }
789
790 /* receive variable indices of branched variables */
791 for (i = 0; i < tmpshadownode->nbranchingdecisions; ++i)
792 {
793 update = &(tmpshadownode->branchingdecisions[i]);
794 varid = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) update->var);
795 assert( varid < orcdata->npermvars || varid == INT_MAX );
796 assert( varid >= 0 );
797 if ( varid < orcdata->npermvars )
798 {
799 if ( inbranchedvarindices[varid] )
800 continue;
801 branchedvarindices[nbranchedvarindices++] = varid;
802 inbranchedvarindices[varid] = TRUE;
803 }
804 }
805 }
806
807 /* determine symmetry group at this point, apply branched variable, apply orbital branching for this
808 *
809 * The branching variables are applied one-after-the-other.
810 * So, the group before branching is determined, orbital branching to the branching variable, then the branching
811 * variable is applied, and possibly repeated for other branching variables.
812 */
813 SCIP_CALL( SCIPallocBufferArray(scip, &chosenperms, orcdata->nperms) );
814 for (branchstep = 0; branchstep < shadowfocusnode->nbranchingdecisions; ++branchstep)
815 {
816 branchingdecision = &(shadowfocusnode->branchingdecisions[branchstep]);
817 branchingdecisionvarid = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) branchingdecision->var);
818 assert( branchingdecisionvarid < orcdata->npermvars || branchingdecisionvarid == INT_MAX );
819 assert( branchingdecisionvarid >= 0 );
820
821 /* branching decision will not have an effect on this */
822 if ( branchingdecisionvarid >= orcdata->npermvars )
823 continue;
824 assert( branchingdecisionvarid >= 0 && branchingdecisionvarid < orcdata->npermvars );
825 assert( branchingdecision->boundchgtype == SCIP_BOUNDTYPE_LOWER ?
826 SCIPsymLE(scip, varlbs[branchingdecisionvarid], branchingdecision->newbound) :
827 SCIPsymGE(scip, varubs[branchingdecisionvarid], branchingdecision->newbound) );
828 assert( SCIPsymLE(scip, varlbs[branchingdecisionvarid], varubs[branchingdecisionvarid]) );
829
830 /* get the generating set of permutations of a subgroup of a stabilizing symmetry subgroup.
831 *
832 * Note: All information about branching decisions is kept in varlbs, varubs, and the branchedvarindices.
833 */
834 SCIP_CALL( orbitalReductionGetSymmetryStabilizerSubgroup(scip, orcdata, chosenperms, &nchosenperms,
835 varlbs, varubs, branchedvarindices, inbranchedvarindices, nbranchedvarindices) );
836
837 /* compute orbit containing branching var */
838 SCIP_CALL( SCIPcreateDisjointset(scip, &orbitset, orcdata->npermvars) );
839
840 /* put elements mapping to each other in same orbit */
841 /* @todo a potential performance hazard; quadratic time */
842 for (p = 0; p < nchosenperms; ++p)
843 {
844 perm = chosenperms[p];
845 for (i = 0; i < orcdata->npermvars; ++i)
846 {
847 if ( i != perm[i] )
848 SCIPdisjointsetUnion(orbitset, i, perm[i], FALSE);
849 }
850 }
851
852 /* 1. ensure that the bounds are tightest possible just before the branching step (orbital reduction step)
853 *
854 * If complete propagation was applied in the previous node,
855 * then all variables in the same orbit have the same bounds just before branching,
856 * so the bounds of the branching variable should be the tightest in its orbit by now.
857 * It is possible that that is not the case. In that case, we do it here.
858 */
859 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitids, orcdata->npermvars) );
860 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitidssort, orcdata->npermvars) );
861 for (i = 0; i < orcdata->npermvars; ++i)
862 varorbitids[i] = SCIPdisjointsetFind(orbitset, i);
863 SCIPsort(varorbitidssort, SCIPsortArgsortInt, varorbitids, orcdata->npermvars);
864
865 /* apply orbital reduction to these orbits */
866 SCIP_CALL( applyOrbitalReductionPart(scip, orcdata, infeasible, nred, varorbitids,
867 varorbitidssort, varlbs, varubs) );
868 if ( *infeasible )
869 goto FREE;
870 assert( !*infeasible );
871
872 /* 2. apply branching step to varlbs or varubs array
873 *
874 * Due to the steps above, it is possible that the branching step is redundant or infeasible.
875 */
876 assert( SCIPsymLE(scip, varlbs[branchingdecisionvarid], varubs[branchingdecisionvarid]) );
877 switch (branchingdecision->boundchgtype)
878 {
880 /* incompatible upper bound */
881 if ( SCIPsymGT(scip, branchingdecision->newbound, varubs[branchingdecisionvarid]) )
882 {
883 *infeasible = TRUE;
884 goto FREE;
885 }
886
887 assert( SCIPsymLE(scip, varlbs[branchingdecisionvarid], branchingdecision->newbound) );
888 varlbs[branchingdecisionvarid] = branchingdecision->newbound;
889 break;
891 /* incompatible lower bound */
892 if ( SCIPsymLT(scip, branchingdecision->newbound, varlbs[branchingdecisionvarid]) )
893 {
894 *infeasible = TRUE;
895 goto FREE;
896 }
897
898 assert( SCIPsymGE(scip, varubs[branchingdecisionvarid], branchingdecision->newbound) );
899 varubs[branchingdecisionvarid] = branchingdecision->newbound;
900 break;
901 default:
902 SCIPABORT();
903 }
904
905 /* 3. propagate that branching variable is >= the variables in its orbit
906 *
907 * Also apply the updates to the variable arrays
908 */
909
910 /* get the orbit of the branching variable */
911 orbitsetcomponentid = SCIPdisjointsetFind(orbitset, branchingdecisionvarid);
912
913 /* find the orbit in the sorted array of orbits. npermvars can be huge, so use bisection. */
914 orbitbegin = bisectSortedArrayFindFirstGEQ(varorbitids, varorbitidssort, 0, orcdata->npermvars,
915 orbitsetcomponentid);
916 assert( orbitbegin >= 0 && orbitbegin < orcdata->npermvars );
917 assert( varorbitids[varorbitidssort[orbitbegin]] == orbitsetcomponentid );
918 assert( orbitbegin == 0 || varorbitids[varorbitidssort[orbitbegin - 1]] < orbitsetcomponentid );
919
920 orbitend = bisectSortedArrayFindFirstGEQ(varorbitids, varorbitidssort, orbitbegin + 1, orcdata->npermvars,
921 orbitsetcomponentid + 1);
922 assert( orbitend > 0 && orbitend <= orcdata->npermvars && orbitend > orbitbegin );
923 assert( orbitend == orcdata->npermvars || varorbitids[varorbitidssort[orbitend]] > orbitsetcomponentid );
924 assert( varorbitids[varorbitidssort[orbitend - 1]] == orbitsetcomponentid );
925
926 /* propagate that branching variable is >= the variables in its orbit */
927 for (idx = orbitbegin; idx < orbitend; ++idx)
928 {
929 varid = varorbitidssort[idx];
930 assert( varorbitids[varid] == orbitsetcomponentid );
931
932 /* ignore current branching variable */
933 if ( varid == branchingdecisionvarid )
934 continue;
935
936 /* is variable varid in the orbit? */
937 if ( SCIPdisjointsetFind(orbitset, varid) != orbitsetcomponentid )
938 continue;
939
940 /* all variables in the same orbit have the same bounds just before branching,
941 * due to orbital reduction. If that was not the case, these steps are applied just before applying
942 * the branching step above. After the branching step, the branching variable bounds are most restricted.
943 */
944 assert( SCIPisInfinity(scip, -varlbs[branchingdecisionvarid])
945 || SCIPsymGE(scip, varlbs[branchingdecisionvarid], varlbs[varid]) );
946 assert( SCIPisInfinity(scip, varubs[branchingdecisionvarid])
947 || SCIPsymLE(scip, varubs[branchingdecisionvarid], varubs[varid]) );
948 /* bound changes already made could only have tightened the variable domains we are thinking about */
949 assert( SCIPsymGE(scip, SCIPvarGetLbLocal(orcdata->permvars[varid]), varlbs[varid]) );
950 assert( SCIPsymLE(scip, SCIPvarGetUbLocal(orcdata->permvars[varid]), varubs[varid]) );
951
952 /* for branching variable x and variable y in its orbit, propagate x >= y. */
953 /* modify UB of y-variables */
954 assert( SCIPsymGE(scip, varubs[varid], varubs[branchingdecisionvarid]) );
955 varubs[varid] = varubs[branchingdecisionvarid];
956 if ( SCIPsymGT(scip, SCIPvarGetUbLocal(orcdata->permvars[varid]), varubs[branchingdecisionvarid]) )
957 {
958 SCIP_Bool tightened;
959 SCIP_CALL( SCIPtightenVarUb(scip, orcdata->permvars[varid], varubs[branchingdecisionvarid], TRUE,
960 infeasible, &tightened) );
961
962 /* propagator detected infeasibility in this node. */
963 if ( *infeasible )
964 goto FREE;
965 assert( tightened );
966 *nred += 1;
967 }
968
969 /* because variable domains are initially the same, the LB of the x-variables does not need to be modified. */
970 assert( SCIPsymLE(scip, varlbs[varid], varlbs[branchingdecisionvarid]) );
971 }
972
973 FREE:
974 SCIPfreeBufferArray(scip, &varorbitidssort);
975 SCIPfreeBufferArray(scip, &varorbitids);
976 SCIPfreeDisjointset(scip, &orbitset);
977
978 if ( *infeasible )
979 break;
980
981 /* for the next branched variable at this node, if it's not already added,
982 * mark the branching variable of this iteration as a branching variable. */
983 if ( !inbranchedvarindices[branchingdecisionvarid] )
984 {
985 assert( nbranchedvarindices < orcdata->npermvars );
986 branchedvarindices[nbranchedvarindices++] = branchingdecisionvarid;
987 inbranchedvarindices[branchingdecisionvarid] = TRUE;
988 }
989 }
990 SCIPfreeBufferArray(scip, &chosenperms);
991
992 /* clean inbranchedvarindices array */
993 for (i = 0; i < nbranchedvarindices; ++i)
994 {
995 varid = branchedvarindices[i];
996 assert( varid >= 0 );
997 assert( varid < orcdata->npermvars );
998 assert( inbranchedvarindices[varid] );
999 inbranchedvarindices[varid] = FALSE;
1000 }
1001#ifndef NDEBUG
1002 for (i = 0; i < orcdata->npermvars; ++i)
1003 {
1004 assert( inbranchedvarindices[i] == FALSE );
1005 }
1006#endif
1007
1008 /* free everything */
1009 SCIPfreeCleanBufferArray(scip, &inbranchedvarindices);
1010 SCIPfreeBufferArray(scip, &branchedvarindices);
1011 SCIPfreeBufferArray(scip, &varubs);
1012 SCIPfreeBufferArray(scip, &varlbs);
1013 SCIPfreeBufferArray(scip, &rootedshadowpath);
1014
1015 return SCIP_OKAY;
1016}
1017
1018/** orbital reduction, the orbital reduction part */
1019static
1021 SCIP* scip, /**< pointer to SCIP data structure */
1022 ORCDATA* orcdata, /**< pointer to data for orbital reduction data */
1023 SCIP_SHADOWTREE* shadowtree, /**< pointer to shadow tree */
1024 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is detected */
1025 int* nred /**< pointer to store the number of determined domain reductions */
1026 )
1027{
1028 SCIP_NODE* focusnode;
1029 SCIP_SHADOWNODE* shadowfocusnode;
1030 SCIP_SHADOWNODE* tmpshadownode;
1031 int i;
1032 SCIP_SHADOWBOUNDUPDATE* update;
1033 int* branchedvarindices;
1034 SCIP_Bool* inbranchedvarindices;
1035 int nbranchedvarindices;
1036 int varid;
1037 int** chosenperms;
1038 int* perm;
1039 int nchosenperms;
1040 int p;
1041 SCIP_DISJOINTSET* orbitset;
1042 int* varorbitids;
1043 int* varorbitidssort;
1044
1045 assert( scip != NULL );
1046 assert( orcdata != NULL );
1047 assert( shadowtree != NULL );
1048 assert( infeasible != NULL );
1049 assert( nred != NULL );
1050
1051 /* infeasible and nred are defined by the function that calls this function,
1052 * and this function only gets called if no infeasibility is found so far.
1053 */
1054 assert( !*infeasible );
1055 assert( *nred >= 0 );
1056
1057 focusnode = SCIPgetFocusNode(scip);
1058 assert( focusnode == SCIPgetCurrentNode(scip) );
1059 assert( focusnode != NULL );
1060
1061 shadowfocusnode = SCIPshadowTreeGetShadowNode(shadowtree, focusnode);
1062 assert( shadowfocusnode != NULL );
1063
1064 /* get the branching variables until present, so including the branchings of the focusnode */
1065 assert( orcdata->npermvars > 0 ); /* if it's 0, then we do not have to do anything at all */
1066
1067 SCIP_CALL( SCIPallocBufferArray(scip, &branchedvarindices, orcdata->npermvars) );
1068 SCIP_CALL( SCIPallocCleanBufferArray(scip, &inbranchedvarindices, orcdata->npermvars) );
1069
1070 nbranchedvarindices = 0;
1071 tmpshadownode = shadowfocusnode;
1072 while ( tmpshadownode != NULL )
1073 {
1074 /* receive variable indices of branched variables */
1075 for (i = 0; i < tmpshadownode->nbranchingdecisions; ++i)
1076 {
1077 update = &(tmpshadownode->branchingdecisions[i]);
1078 varid = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) update->var);
1079 assert( varid < orcdata->npermvars || varid == INT_MAX );
1080 assert( varid >= 0 );
1081 if ( varid < orcdata->npermvars )
1082 {
1083 if ( inbranchedvarindices[varid] )
1084 continue;
1085 branchedvarindices[nbranchedvarindices++] = varid;
1086 inbranchedvarindices[varid] = TRUE;
1087 }
1088 }
1089 tmpshadownode = tmpshadownode->parent;
1090 }
1091
1092 /* 1. compute the orbit of the branching variable of the stabilized symmetry subgroup at this point. */
1093 /* 1.1. identify the permutations of the symmetry group that are permitted */
1094 SCIP_CALL( SCIPallocBufferArray(scip, &chosenperms, orcdata->nperms) );
1095 SCIP_CALL( orbitalReductionGetSymmetryStabilizerSubgroup(scip, orcdata, chosenperms, &nchosenperms,
1096 NULL, NULL, branchedvarindices, inbranchedvarindices, nbranchedvarindices) );
1097 assert( nchosenperms >= 0 );
1098
1099 /* no reductions can be yielded by orbital reduction if the group is trivial */
1100 if ( nchosenperms == 0 )
1101 goto FREE;
1102
1103 /* 1.2. compute orbits of this subgroup */
1104 SCIP_CALL( SCIPcreateDisjointset(scip, &orbitset, orcdata->npermvars) );
1105
1106 /* put elements mapping to each other in same orbit */
1107 /* @todo this is O(nchosenperms * npermvars), which is a potential performance bottleneck.
1108 Alternative: precompute support per permutation at initialization, and iterate over these.*/
1109 for (p = 0; p < nchosenperms; ++p)
1110 {
1111 perm = chosenperms[p];
1112 for (i = 0; i < orcdata->npermvars; ++i)
1113 {
1114 if ( i != perm[i] )
1115 SCIPdisjointsetUnion(orbitset, i, perm[i], FALSE);
1116 }
1117 }
1118
1119 /* 2. for each orbit, take the intersection of the domains */
1120 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitids, orcdata->npermvars) );
1121 SCIP_CALL( SCIPallocBufferArray(scip, &varorbitidssort, orcdata->npermvars) );
1122 for (i = 0; i < orcdata->npermvars; ++i)
1123 varorbitids[i] = SCIPdisjointsetFind(orbitset, i);
1124 SCIPsort(varorbitidssort, SCIPsortArgsortInt, varorbitids, orcdata->npermvars);
1125
1126 /* apply orbital reduction to these orbits */
1127 SCIP_CALL( applyOrbitalReductionPart(scip, orcdata, infeasible, nred, varorbitids, varorbitidssort, NULL, NULL) );
1128
1129 SCIPfreeBufferArray(scip, &varorbitidssort);
1130 SCIPfreeBufferArray(scip, &varorbitids);
1131 SCIPfreeDisjointset(scip, &orbitset);
1132FREE:
1133 SCIPfreeBufferArray(scip, &chosenperms);
1134
1135 /* clean inbranchedvarindices array */
1136 for (i = 0; i < nbranchedvarindices; ++i)
1137 {
1138 varid = branchedvarindices[i];
1139 assert( varid >= 0 );
1140 assert( varid < orcdata->npermvars );
1141 assert( inbranchedvarindices[varid] );
1142 inbranchedvarindices[varid] = FALSE;
1143 }
1144#ifndef NDEBUG
1145 for (i = 0; i < orcdata->npermvars; ++i)
1146 {
1147 assert( inbranchedvarindices[i] == FALSE );
1148 }
1149#endif
1150
1151 SCIPfreeCleanBufferArray(scip, &inbranchedvarindices);
1152 SCIPfreeBufferArray(scip, &branchedvarindices);
1153
1154 return SCIP_OKAY;
1155}
1156
1157
1158/** applies orbital reduction on a symmetry group component using a two step mechanism
1159 *
1160 * 1. At the parent of our focus node (which is the current node, because we're not probing),
1161 * compute the symmetry group just before branching. Then, for our branching variable x with variable y in its
1162 * orbit, we mimic adding the constraint x >= y by variable bound propagations in this node.
1163 *
1164 * In principle, this generalizes orbital branching in the binary case: propagation of x >= y yields
1165 * 1. In the 1-branch: 1 = x >= y is a tautology (since y is in {0, 1}). Nothing happens.
1166 * 0. In the 0-branch: 0 = x >= y implies y = 0. This is an exact description of orbital branching.
1167 * REF: Ostrowski, James, et al. "Orbital branching." Mathematical Programming 126.1 (2011): 147-178.
1168 *
1169 * (This only needs to be done once per node.)
1170 *
1171 * 2. At the focus node itself, compute the symmetry group.
1172 * The symmetry group in this branch-and-bound tree node is a subgroup of the problem symmetry group
1173 * as described in the function orbitalReductionGetSymmetryStabilizerSubgroup.
1174 * For this symmetry subgroup, in each orbit, update the variable domains with the intersection of all variable
1175 * domains in the orbit.
1176 *
1177 * This generalizes orbital fixing in the binary case.
1178 * REF: Margot 2002, Margot 2003, Orbital Branching, Ostrowski's PhD thesis.
1179 */
1180static
1182 SCIP* scip, /**< SCIP data structure */
1183 ORCDATA* orcdata, /**< orbital reduction component data */
1184 SCIP_SHADOWTREE* shadowtree, /**< pointer to shadow tree */
1185 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is found */
1186 int* nred /**< pointer to store the number of domain reductions */
1187 )
1188{
1189 assert( scip != NULL );
1190 assert( orcdata != NULL );
1191 assert( shadowtree != NULL );
1192 assert( infeasible != NULL );
1193 assert( nred != NULL );
1194
1195 /* infeasible and nred are defined by the function that calls this function,
1196 * and this function only gets called if no infeasibility is found so far.
1197 */
1198 assert( !*infeasible );
1199 assert( *nred >= 0 );
1200
1201 /* orbital reduction is only propagated when branching has started */
1203
1204 /* if this is the first call, identify the orbits for which symmetry is broken */
1205 if ( !orcdata->symmetrybrokencomputed )
1206 {
1208 }
1209 assert( orcdata->symmetrybrokencomputed );
1210 assert( orcdata->nsymbrokenvarids <= orcdata->npermvars );
1211
1212 /* If symmetry is broken for all orbits, stop! */
1213 if ( orcdata->nsymbrokenvarids == orcdata->npermvars )
1214 return SCIP_OKAY;
1215
1216 /* step 1 */
1217 SCIP_CALL( applyOrbitalBranchingPropagations(scip, orcdata, shadowtree, infeasible, nred) );
1218 if ( *infeasible )
1219 return SCIP_OKAY;
1220
1221 /* step 2 */
1222 SCIP_CALL( applyOrbitalReductionPropagations(scip, orcdata, shadowtree, infeasible, nred) );
1223 if ( *infeasible )
1224 return SCIP_OKAY;
1225
1226 return SCIP_OKAY;
1227}
1228
1229
1230/** adds component */
1231static
1233 SCIP* scip, /**< SCIP data structure */
1234 SCIP_ORBITALREDDATA* orbireddata, /**< pointer to the orbital reduction data */
1235 SCIP_VAR** permvars, /**< variable array of the permutation */
1236 int npermvars, /**< number of variables in that array */
1237 int** perms, /**< permutations in the component */
1238 int nperms, /**< number of permutations in the component */
1239 SCIP_Bool* success /**< to store whether the component is successfully added */
1240 )
1241{
1242 ORCDATA* orcdata;
1243 int i;
1244 int j;
1245 int p;
1246 int* origperm;
1247 int* newperm;
1248 int newidx;
1249 int newpermidx;
1250
1251 assert( scip != NULL );
1252 assert( orbireddata != NULL );
1253 assert( permvars != NULL );
1254 assert( npermvars > 0 );
1255 assert( perms != NULL );
1256 assert( nperms > 0 );
1257 assert( success != NULL );
1258
1259 *success = TRUE;
1260 SCIP_CALL( SCIPallocBlockMemory(scip, &orcdata) );
1261
1262 /* correct indices by removing fixed points */
1263
1264 /* determine the number of vars that are moved by the component, assign to orcdata->npermvars */
1265 orcdata->npermvars = 0;
1266 for (i = 0; i < npermvars; ++i)
1267 {
1268 /* is index i moved by any of the permutations in the component? */
1269 for (p = 0; p < nperms; ++p)
1270 {
1271 if ( perms[p][i] != i )
1272 {
1273 ++orcdata->npermvars;
1274 break;
1275 }
1276 }
1277 }
1278
1279 /* do not support the setting where the component is empty */
1280 if ( orcdata->npermvars <= 0 )
1281 {
1282 SCIPfreeBlockMemory(scip, &orcdata);
1283 *success = FALSE;
1284 return SCIP_OKAY;
1285 }
1286
1287 /* require that the shadowtree is active */
1288 SCIP_CALL( SCIPactivateShadowTree(scip, orbireddata->shadowtreeeventhdlr) );
1289
1290 /* create index-corrected permvars array and the inverse */
1293
1294 j = 0;
1295 for (i = 0; i < npermvars; ++i)
1296 {
1297 /* permvars array must be unique */
1298 assert( SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) permvars[i]) == INT_MAX );
1299
1300 /* is index i moved by any of the permutations in the component? */
1301 for (p = 0; p < nperms; ++p)
1302 {
1303 if ( perms[p][i] != i )
1304 {
1305 /* var is moved by component; add, disable multiaggregation and capture */
1306 SCIP_CALL( SCIPcaptureVar(scip, permvars[i]) );
1307 orcdata->permvars[j] = permvars[i];
1308 SCIP_CALL( SCIPhashmapInsertInt(orcdata->permvarmap, (void*) permvars[i], j) );
1310 ++j;
1311 break;
1312 }
1313 }
1314 }
1315 assert( j == orcdata->npermvars );
1316
1317 /* allocate permutations */
1318 orcdata->nperms = nperms;
1319 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &orcdata->perms, nperms) );
1320 for (p = 0; p < nperms; ++p)
1321 {
1322 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &orcdata->perms[p], orcdata->npermvars) );
1323 origperm = perms[p];
1324 newperm = orcdata->perms[p];
1325
1326 for (i = 0; i < npermvars; ++i)
1327 {
1328 newidx = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) permvars[i]);
1329 if ( newidx >= orcdata->npermvars )
1330 continue;
1331 assert( newidx >= 0 );
1332 assert( newidx < orcdata->npermvars );
1333 assert( orcdata->permvars[newidx] == permvars[i] );
1334 newpermidx = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) permvars[origperm[i]]);
1335 assert( newpermidx >= 0 );
1336 assert( newidx < orcdata->npermvars ); /* this is in the orbit of any permutation, so cannot be INT_MAX */
1337 assert( orcdata->permvars[newpermidx] == permvars[origperm[i]] );
1338
1339 newperm[newidx] = newpermidx;
1340 }
1341 }
1342
1343 /* global variable bounds */
1346 for (i = 0; i < orcdata->npermvars; ++i)
1347 {
1348 orcdata->globalvarlbs[i] = SCIPvarGetLbGlobal(orcdata->permvars[i]);
1349 orcdata->globalvarubs[i] = SCIPvarGetUbGlobal(orcdata->permvars[i]);
1350 }
1351
1352 /* catch global variable bound change event */
1353 for (i = 0; i < orcdata->npermvars; ++i)
1354 {
1356 orbireddata->globalfixeventhdlr, (SCIP_EVENTDATA*) orcdata, NULL) );
1357 }
1358
1359 /* lastnode field */
1360 orcdata->lastnode = NULL;
1361
1362 /* symmetry computed field */
1363 orcdata->symmetrybrokencomputed = FALSE;
1364 orcdata->symbrokenvarids = NULL;
1365 orcdata->nsymbrokenvarids = -1;
1366
1367 /* resize component array if needed */
1368 assert( orbireddata->ncomponents >= 0 );
1369 assert( (orbireddata->ncomponents == 0) == (orbireddata->componentdatas == NULL) );
1370 assert( orbireddata->ncomponents <= orbireddata->maxncomponents );
1371 if ( orbireddata->ncomponents == orbireddata->maxncomponents )
1372 {
1373 int newsize;
1374
1375 newsize = SCIPcalcMemGrowSize(scip, orbireddata->ncomponents + 1);
1376 assert( newsize >= 0 );
1377
1378 if ( orbireddata->ncomponents == 0 )
1379 {
1380 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &orbireddata->componentdatas, newsize) );
1381 }
1382 else
1383 {
1384 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &orbireddata->componentdatas,
1385 orbireddata->ncomponents, newsize) );
1386 }
1387
1388 orbireddata->maxncomponents = newsize;
1389 }
1390
1391 /* tree warning indicator */
1392 orcdata->treewarninggiven = FALSE;
1393
1394 /* add component */
1395 assert( orbireddata->ncomponents < orbireddata->maxncomponents );
1396 orbireddata->componentdatas[orbireddata->ncomponents++] = orcdata;
1397
1398 return SCIP_OKAY;
1399}
1400
1401
1402/** frees component */
1403static
1405 SCIP* scip, /**< SCIP data structure */
1406 SCIP_ORBITALREDDATA* orbireddata, /**< pointer to the orbital reduction data */
1407 ORCDATA** orcdata /**< pointer to component data */
1408 )
1409{
1410 int i;
1411 int p;
1412
1413 assert( scip != NULL );
1414 assert( orbireddata != NULL );
1415 assert( orcdata != NULL );
1416 assert( *orcdata != NULL );
1417 assert( (*orcdata)->globalvarlbs != NULL );
1418 assert( (*orcdata)->globalvarubs != NULL );
1419 assert( (*orcdata)->nperms > 0 );
1420 assert( (*orcdata)->npermvars > 0 );
1421 assert( (*orcdata)->perms != NULL );
1422 assert( (*orcdata)->permvarmap != NULL );
1423 assert( (*orcdata)->permvars != NULL );
1424 assert( (*orcdata)->npermvars > 0 );
1425
1427
1428 /* free symmetry broken information if it has been computed */
1429 if ( (*orcdata)->symmetrybrokencomputed )
1430 {
1431 assert( ((*orcdata)->nsymbrokenvarids == 0) == ((*orcdata)->symbrokenvarids == NULL) );
1432 SCIPfreeBlockMemoryArrayNull(scip, &(*orcdata)->symbrokenvarids, (*orcdata)->nsymbrokenvarids);
1433 }
1434
1435 /* free global variable bound change event */
1437 {
1438 /* events at the freeing stage may not be dropped, because they are already getting dropped */
1439 for (i = (*orcdata)->npermvars - 1; i >= 0; --i)
1440 {
1441 SCIP_CALL( SCIPdropVarEvent(scip, (*orcdata)->permvars[i],
1443 orbireddata->globalfixeventhdlr, (SCIP_EVENTDATA*) (*orcdata), -1) );
1444 }
1445 }
1446
1447 SCIPfreeBlockMemoryArray(scip, &(*orcdata)->globalvarubs, (*orcdata)->npermvars);
1448 SCIPfreeBlockMemoryArray(scip, &(*orcdata)->globalvarlbs, (*orcdata)->npermvars);
1449
1450 for (p = (*orcdata)->nperms -1; p >= 0; --p)
1451 {
1452 SCIPfreeBlockMemoryArray(scip, &(*orcdata)->perms[p], (*orcdata)->npermvars);
1453 }
1454 SCIPfreeBlockMemoryArray(scip, &(*orcdata)->perms, (*orcdata)->nperms);
1455
1456 /* release variables */
1457 for (i = 0; i < (*orcdata)->npermvars; ++i)
1458 {
1459 assert( (*orcdata)->permvars[i] != NULL );
1460 SCIP_CALL( SCIPreleaseVar(scip, &(*orcdata)->permvars[i]) );
1461 }
1462
1463 SCIPhashmapFree(&(*orcdata)->permvarmap);
1464 SCIPfreeBlockMemoryArray(scip, &(*orcdata)->permvars, (*orcdata)->npermvars);
1465
1466 SCIPfreeBlockMemory(scip, orcdata);
1467
1468 return SCIP_OKAY;
1469}
1470
1471
1472/*
1473 * Event handler callback methods
1474 */
1475
1476/** maintains global variable bound reductions found during presolving or at the root node */
1477static
1478SCIP_DECL_EVENTEXEC(eventExecGlobalBoundChange)
1479{
1480 ORCDATA* orcdata;
1481 SCIP_VAR* var;
1482 int varidx;
1483
1484 assert( eventhdlr != NULL );
1485 assert( eventdata != NULL );
1486 assert( event != NULL );
1487
1489
1490 orcdata = (ORCDATA*) eventdata;
1491 assert( orcdata != NULL );
1492
1493 /* only update the global bounds if the propagator has not been called yet */
1494 if ( orcdata->symmetrybrokencomputed )
1495 {
1496 /* identifyOrbitalSymmetriesBroken is only called when we're propagating, which is only done for during solving */
1498 return SCIP_OKAY;
1499 }
1500
1501 var = SCIPeventGetVar(event);
1502 assert( var != NULL );
1504
1505 assert( orcdata->permvarmap != NULL );
1506 varidx = SCIPhashmapGetImageInt(orcdata->permvarmap, (void*) var);
1507
1508 switch ( SCIPeventGetType(event) )
1509 {
1511 /* can assert with equality, because no arithmetic must be applied after inheriting the value of oldbound */
1512 assert( orcdata->globalvarubs[varidx] == SCIPeventGetOldbound(event) ); /*lint !e777 */
1513 orcdata->globalvarubs[varidx] = SCIPeventGetNewbound(event);
1514 break;
1516 assert( orcdata->globalvarlbs[varidx] == SCIPeventGetOldbound(event) ); /*lint !e777 */
1517 orcdata->globalvarlbs[varidx] = SCIPeventGetNewbound(event);
1518 break;
1519 default:
1520 SCIPABORT();
1521 return SCIP_ERROR;
1522 }
1523
1524 return SCIP_OKAY;
1525}
1526
1527
1528/*
1529 * Interface methods
1530 */
1531
1532
1533/** prints orbital reduction data */
1535 SCIP* scip, /**< SCIP data structure */
1536 SCIP_ORBITALREDDATA* orbireddata, /**< orbital reduction data structure */
1537 int* nred, /**< pointer to store the total number of reductions applied */
1538 int* ncutoff /**< pointer to store the total number of cutoffs applied */
1539 )
1540{
1541 assert( scip != NULL );
1542 assert( orbireddata != NULL );
1543
1544 *nred = orbireddata->nred;
1545 *ncutoff = orbireddata->ncutoff;
1546
1547 return SCIP_OKAY;
1548}
1549
1550/** prints orbital reduction data */
1552 SCIP* scip, /**< SCIP data structure */
1553 SCIP_ORBITALREDDATA* orbireddata /**< orbital reduction data structure */
1554 )
1555{
1556 int i;
1557
1558 assert( scip != NULL );
1559 assert( orbireddata != NULL );
1560
1561 if ( orbireddata->ncomponents == 0 )
1562 {
1563 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " orbital reduction: no components\n");
1564 return SCIP_OKAY;
1565 }
1566
1568 " orbital reduction: %4d components of sizes ", orbireddata->ncomponents);
1569 for (i = 0; i < orbireddata->ncomponents; ++i)
1570 {
1571 if ( i > 0 )
1573 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "%d", orbireddata->componentdatas[i]->nperms);
1574 }
1576
1577 return SCIP_OKAY;
1578}
1579
1580
1581/** propagates orbital reduction */
1583 SCIP* scip, /**< SCIP data structure */
1584 SCIP_ORBITALREDDATA* orbireddata, /**< orbital reduction data structure */
1585 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is found */
1586 int* nred, /**< pointer to store the number of domain reductions */
1587 SCIP_Bool* didrun /**< a global pointer maintaining if any symmetry propagator has run
1588 * only set this to TRUE when a reduction is found, never set to FALSE */
1589 )
1590{
1591 ORCDATA* orcdata;
1592 SCIP_SHADOWTREE* shadowtree;
1593 int c;
1594
1595 assert( scip != NULL );
1596 assert( orbireddata != NULL );
1597 assert( (orbireddata->componentdatas == NULL) == (orbireddata->ncomponents == 0) );
1598 assert( orbireddata->ncomponents >= 0 );
1599 assert( orbireddata->ncomponents <= orbireddata->maxncomponents );
1600 assert( infeasible != NULL );
1601 assert( nred != NULL );
1602 assert( didrun != NULL );
1603
1604 *infeasible = FALSE;
1605 *nred = 0;
1606 *didrun = FALSE;
1607
1608 /* early termination */
1609 if ( orbireddata->ncomponents == 0 )
1610 return SCIP_OKAY;
1611
1612 /* do nothing if we are in a probing node */
1613 if ( SCIPinProbing(scip) )
1614 return SCIP_OKAY;
1615
1616 /* do not run again in repropagation, since the path to the root might have changed */
1618 return SCIP_OKAY;
1619
1620 assert( orbireddata->shadowtreeeventhdlr != NULL );
1621 shadowtree = SCIPgetShadowTree(orbireddata->shadowtreeeventhdlr);
1622 assert( shadowtree != NULL );
1623
1624 for (c = 0; c < orbireddata->ncomponents; ++c)
1625 {
1626 orcdata = orbireddata->componentdatas[c];
1627 assert( orcdata != NULL );
1628 assert( orcdata->nperms > 0 );
1629 SCIP_CALL( orbitalReductionPropagateComponent(scip, orcdata, shadowtree, infeasible, nred) );
1630
1631 /* a symmetry propagator has ran, so set didrun to TRUE */
1632 *didrun = TRUE;
1633
1634 if ( *infeasible )
1635 break;
1636 }
1637
1638 orbireddata->nred += *nred;
1639 if ( *infeasible )
1640 ++orbireddata->ncutoff;
1641
1642 return SCIP_OKAY;
1643}
1644
1645
1646/** adds component for orbital reduction */
1648 SCIP* scip, /**< SCIP data structure */
1649 SCIP_ORBITALREDDATA* orbireddata, /**< orbital reduction data structure */
1650 SCIP_VAR** permvars, /**< variable array of the permutation */
1651 int npermvars, /**< number of variables in that array */
1652 int** perms, /**< permutations in the component */
1653 int nperms, /**< number of permutations in the component */
1654 SCIP_Bool* success /**< to store whether the component is successfully added */
1655 )
1656{
1657 assert( scip != NULL );
1658 assert( orbireddata != NULL );
1659 assert( permvars != NULL );
1660 assert( npermvars > 0 );
1661 assert( perms != NULL );
1662 assert( nperms > 0 );
1663 assert( success != NULL );
1664
1665 /* dynamic symmetry reductions cannot be performed on original problem */
1667
1668 SCIP_CALL( addComponent(scip, orbireddata, permvars, npermvars, perms, nperms, success) );
1669
1670 return SCIP_OKAY;
1671}
1672
1673
1674/** resets orbital reduction data structure (clears all components) */
1676 SCIP* scip, /**< SCIP data structure */
1677 SCIP_ORBITALREDDATA* orbireddata /**< orbital reduction data structure */
1678 )
1679{
1680 assert( scip != NULL );
1681 assert( orbireddata != NULL );
1682 assert( orbireddata->ncomponents >= 0 );
1683 assert( (orbireddata->ncomponents == 0) == (orbireddata->componentdatas == NULL) );
1684 assert( orbireddata->ncomponents <= orbireddata->maxncomponents );
1685 assert( orbireddata->shadowtreeeventhdlr != NULL );
1686
1687 while ( orbireddata->ncomponents > 0 )
1688 {
1689 SCIP_CALL( freeComponent(scip, orbireddata, &(orbireddata->componentdatas[--orbireddata->ncomponents])) );
1690 }
1691
1692 assert( orbireddata->ncomponents == 0 );
1693 SCIPfreeBlockMemoryArrayNull(scip, &orbireddata->componentdatas, orbireddata->maxncomponents);
1694 orbireddata->componentdatas = NULL;
1695 orbireddata->maxncomponents = 0;
1696
1697 return SCIP_OKAY;
1698}
1699
1700
1701/** frees orbital reduction data */
1703 SCIP* scip, /**< SCIP data structure */
1704 SCIP_ORBITALREDDATA** orbireddata /**< orbital reduction data structure */
1705 )
1706{
1707 assert( scip != NULL );
1708 assert( orbireddata != NULL );
1709 assert( *orbireddata != NULL );
1710
1711 SCIP_CALL( SCIPorbitalReductionReset(scip, *orbireddata) );
1712
1713 SCIPfreeBlockMemory(scip, orbireddata);
1714 return SCIP_OKAY;
1715}
1716
1717
1718/** initializes structures needed for orbital reduction
1719 *
1720 * This is only done exactly once.
1721 */
1723 SCIP* scip, /**< SCIP data structure */
1724 SCIP_ORBITALREDDATA** orbireddata, /**< pointer to orbital reduction data structure to populate */
1725 SCIP_EVENTHDLR* shadowtreeeventhdlr /**< pointer to the shadow tree eventhdlr */
1726 )
1727{
1728 assert( scip != NULL );
1729 assert( orbireddata != NULL );
1730 assert( shadowtreeeventhdlr != NULL );
1731
1732 SCIP_CALL( SCIPcheckStage(scip, "SCIPincludeOrbitalReduction", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
1734
1735 SCIP_CALL( SCIPallocBlockMemory(scip, orbireddata) );
1736
1737 (*orbireddata)->componentdatas = NULL;
1738 (*orbireddata)->ncomponents = 0;
1739 (*orbireddata)->maxncomponents = 0;
1740 (*orbireddata)->shadowtreeeventhdlr = shadowtreeeventhdlr;
1741 (*orbireddata)->nred = 0;
1742 (*orbireddata)->ncutoff = 0;
1743
1744 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(*orbireddata)->globalfixeventhdlr,
1745 EVENTHDLR_SYMMETRY_NAME, EVENTHDLR_SYMMETRY_DESC, eventExecGlobalBoundChange,
1746 (SCIP_EVENTHDLRDATA*) (*orbireddata)) );
1747
1748 return SCIP_OKAY;
1749}
methods for debugging
#define SCIPcheckStage(scip, method, init, problem, transforming, transformed, initpresolve, presolving, exitpresolve, presolved, initsolve, solving, solved, exitsolve, freetrans, freescip)
Definition debug.h:365
#define NULL
Definition def.h:257
#define SCIP_Bool
Definition def.h:100
#define SCIP_STRINGEQ(name, reference, retcode)
Definition def.h:454
#define SCIP_Real
Definition def.h:165
#define TRUE
Definition def.h:102
#define FALSE
Definition def.h:103
#define SCIPABORT()
Definition def.h:336
#define SCIP_CALL(x)
Definition def.h:364
SCIP_RETCODE SCIPactivateShadowTree(SCIP *scip, SCIP_EVENTHDLR *eventhdlr)
SCIP_SHADOWTREE * SCIPgetShadowTree(SCIP_EVENTHDLR *eventhdlr)
SCIP_SHADOWNODE * SCIPshadowTreeGetShadowNode(SCIP_SHADOWTREE *shadowtree, SCIP_NODE *node)
struct SCIP_ShadowBoundUpdate SCIP_SHADOWBOUNDUPDATE
struct SCIP_ShadowTree SCIP_SHADOWTREE
struct SCIP_ShadowNode SCIP_SHADOWNODE
void SCIPfreeDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset)
int SCIPdisjointsetFind(SCIP_DISJOINTSET *djset, int element)
Definition misc.c:11247
void SCIPdisjointsetUnion(SCIP_DISJOINTSET *djset, int p, int q, SCIP_Bool forcerepofp)
Definition misc.c:11274
SCIP_RETCODE SCIPcreateDisjointset(SCIP *scip, SCIP_DISJOINTSET **djset, int ncomponents)
SCIP_Bool SCIPisTransformed(SCIP *scip)
SCIP_STAGE SCIPgetStage(SCIP *scip)
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition misc.c:3095
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition misc.c:3304
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition misc.c:3061
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition misc.c:3179
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
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:111
const char * SCIPeventhdlrGetName(SCIP_EVENTHDLR *eventhdlr)
Definition event.c:396
SCIP_EVENTTYPE SCIPeventGetType(SCIP_EVENT *event)
Definition event.c:1194
SCIP_RETCODE SCIPcatchVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int *filterpos)
Definition scip_event.c:367
SCIP_RETCODE SCIPdropVarEvent(SCIP *scip, SCIP_VAR *var, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int filterpos)
Definition scip_event.c:413
SCIP_Real SCIPeventGetOldbound(SCIP_EVENT *event)
Definition event.c:1391
SCIP_VAR * SCIPeventGetVar(SCIP_EVENT *event)
Definition event.c:1217
SCIP_Real SCIPeventGetNewbound(SCIP_EVENT *event)
Definition event.c:1415
#define SCIPfreeCleanBufferArray(scip, ptr)
Definition scip_mem.h:146
#define SCIPallocCleanBufferArray(scip, ptr, num)
Definition scip_mem.h:142
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:110
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition scip_mem.c:57
int SCIPcalcMemGrowSize(SCIP *scip, int num)
Definition scip_mem.c:139
#define SCIPallocBufferArray(scip, ptr, num)
Definition scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition scip_mem.h:136
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:93
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition scip_mem.h:99
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition scip_mem.h:111
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
SCIP_NODE * SCIPnodeGetParent(SCIP_NODE *node)
Definition tree.c:8812
SCIP_Bool SCIPinProbing(SCIP *scip)
SCIP_Longint SCIPgetNNodes(SCIP *scip)
SCIP_Bool SCIPsymGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition symmetry.c:2350
SCIP_Bool SCIPsymEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition symmetry.c:2278
SCIP_Bool SCIPsymLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition symmetry.c:2388
SCIP_Bool SCIPsymGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition symmetry.c:2426
SCIP_Bool SCIPsymLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
Definition symmetry.c:2312
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPinRepropagation(SCIP *scip)
Definition scip_tree.c:146
SCIP_NODE * SCIPgetFocusNode(SCIP *scip)
Definition scip_tree.c:72
SCIP_NODE * SCIPgetCurrentNode(SCIP *scip)
Definition scip_tree.c:91
SCIP_RETCODE SCIPtightenVarLb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition scip_var.c:6401
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition var.c:24300
SCIP_Bool SCIPvarIsTransformed(SCIP_VAR *var)
Definition var.c:23462
SCIP_RETCODE SCIPtightenVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition scip_var.c:6651
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition var.c:24174
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition scip_var.c:1887
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition var.c:24266
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition var.c:24152
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP *scip, SCIP_VAR *var)
Definition scip_var.c:11057
SCIP_RETCODE SCIPcaptureVar(SCIP *scip, SCIP_VAR *var)
Definition scip_var.c:1853
void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
Definition misc.c:5581
return SCIP_OKAY
int c
int depth
assert(minobj< SCIPgetCutoffbound(scip))
SCIP_VAR * var
memory allocation routines
public methods for managing constraints
public methods for message output
public methods for problem variables
SCIP callable library.
public methods for branching rule plugins and branching
public methods for conflict handler plugins and conflict analysis
public methods for constraint handler plugins and constraints
public methods for problem copies
public methods for cuts and aggregation rows
general public methods
public methods for the LP relaxation, rows and columns
public methods for memory management
public methods for message handling
public methods for numerical tolerances
public methods for SCIP parameter handling
public methods for global and local (sub)problems
public methods for the probing mode
public methods for solutions
public methods for SCIP variables
SCIP_BOUNDTYPE boundchgtype
SCIP_SHADOWBOUNDUPDATE * branchingdecisions
SCIP_SHADOWBOUNDUPDATE * propagations
struct SCIP_ShadowNode * parent
datastructures for block memory pools and memory buffers
SCIP main data structure.
data structures for branch and bound tree
datastructures for problem variables
methods for handling symmetries
static SCIP_RETCODE applyOrbitalReductionPropagations(SCIP *scip, ORCDATA *orcdata, SCIP_SHADOWTREE *shadowtree, SCIP_Bool *infeasible, int *nred)
static SCIP_RETCODE applyOrbitalBranchingPropagations(SCIP *scip, ORCDATA *orcdata, SCIP_SHADOWTREE *shadowtree, SCIP_Bool *infeasible, int *nred)
SCIP_RETCODE SCIPorbitalReductionFree(SCIP *scip, SCIP_ORBITALREDDATA **orbireddata)
SCIP_RETCODE SCIPorbitalReductionGetStatistics(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, int *nred, int *ncutoff)
SCIP_RETCODE SCIPincludeOrbitalReduction(SCIP *scip, SCIP_ORBITALREDDATA **orbireddata, SCIP_EVENTHDLR *shadowtreeeventhdlr)
SCIP_RETCODE SCIPorbitalReductionPrintStatistics(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata)
static SCIP_RETCODE orbitalReductionGetSymmetryStabilizerSubgroup(SCIP *scip, ORCDATA *orcdata, int **chosenperms, int *nchosenperms, SCIP_Real *varlbs, SCIP_Real *varubs, int *branchedvarindices, SCIP_Bool *inbranchedvarindices, int nbranchedvarindices)
SCIP_RETCODE SCIPorbitalReductionAddComponent(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, SCIP_VAR **permvars, int npermvars, int **perms, int nperms, SCIP_Bool *success)
static SCIP_RETCODE applyOrbitalReductionPart(SCIP *scip, ORCDATA *orcdata, SCIP_Bool *infeasible, int *nred, int *varorbitids, int *varorbitidssort, SCIP_Real *varlbs, SCIP_Real *varubs)
SCIP_RETCODE SCIPorbitalReductionReset(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata)
struct OrbitalReductionComponentData ORCDATA
#define EVENTHDLR_SYMMETRY_NAME
SCIP_RETCODE SCIPorbitalReductionPropagate(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
static SCIP_RETCODE identifyOrbitalSymmetriesBroken(SCIP *scip, ORCDATA *orcdata)
static SCIP_RETCODE orbitalReductionPropagateComponent(SCIP *scip, ORCDATA *orcdata, SCIP_SHADOWTREE *shadowtree, SCIP_Bool *infeasible, int *nred)
static int bisectSortedArrayFindFirstGEQ(int *ids, int *idssort, int frameleft, int frameright, int findid)
static SCIP_RETCODE freeComponent(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, ORCDATA **orcdata)
#define EVENTHDLR_SYMMETRY_DESC
static SCIP_RETCODE addComponent(SCIP *scip, SCIP_ORBITALREDDATA *orbireddata, SCIP_VAR **permvars, int npermvars, int **perms, int nperms, SCIP_Bool *success)
struct SCIP_OrbitalReductionData SCIP_ORBITALREDDATA
struct SCIP_Eventhdlr SCIP_EVENTHDLR
Definition type_event.h:159
#define SCIP_EVENTTYPE_GUBCHANGED
Definition type_event.h:76
struct SCIP_EventData SCIP_EVENTDATA
Definition type_event.h:179
struct SCIP_EventhdlrData SCIP_EVENTHDLRDATA
Definition type_event.h:160
#define SCIP_DECL_EVENTEXEC(x)
Definition type_event.h:259
#define SCIP_EVENTTYPE_GLBCHANGED
Definition type_event.h:75
@ SCIP_BOUNDTYPE_UPPER
Definition type_lp.h:58
@ SCIP_BOUNDTYPE_LOWER
Definition type_lp.h:57
@ SCIP_VERBLEVEL_HIGH
struct SCIP_HashMap SCIP_HASHMAP
Definition type_misc.h:106
struct SCIP_DisjointSet SCIP_DISJOINTSET
Definition type_misc.h:166
@ SCIP_INVALIDCALL
@ SCIP_ERROR
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
@ SCIP_STAGE_FREE
Definition type_set.h:57
@ SCIP_STAGE_SOLVING
Definition type_set.h:53
struct SCIP_Node SCIP_NODE
Definition type_tree.h:63
type definitions for problem variables
struct SCIP_Var SCIP_VAR
Definition type_var.h:166