Scippy

SCIP

Solving Constraint Integer Programs

symmetry_orbitopal.c
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file symmetry_orbitopal.c
26 * @ingroup OTHER_CFILES
27 * @brief methods for handling orbitopal symmetries
28 * @author Jasper van Doornmalen
29 *
30 * This implements orbitopal reducion, which generalizes full orbitope propagation to work for non-binary variable
31 * domains, and is dynamified. See cons_orbitope.c for the variant for binary variables, both the static and partially
32 * dynamic variant.
33 * Just as in orbital reduction (cf. symmetry_orbital.c), the variable order is chosen as the variables
34 * branched on from the root node to the focus node.
35 *
36 * See Section 4.2, Example 12 and Section 5.1 in [vD,H]:@n
37 * J. van Doornmalen, C. Hojny, "A Unified Framework for Symmetry Handling", preprint, 2023,
38 * https://doi.org/10.48550/arXiv.2211.01295.
39 *
40 * Orbitopal reduction can be used to handle symmetries of the following type.
41 * If the variables can be arranged in a matrix and the symmetries correspond to all column permutations of this matrix,
42 * then these symmetries are called orbitopal.
43 * Symmetry is completely handled by enforcing that the columns are lexicographically decreasing.
44 * If a reduction on a variable is applied, and if this variable is high up in the variable matrix, then this has a
45 * relatively large impact on the lexicographic ordering. Moreover, the ordering of the columns also matter.
46 * Dynamification allows for choosing a custom ordering of a subset of rows and a permutation of the columns.
47 * For every node, we maintain the ordered subset of rows and the column order.
48 * The root node assumes no rows and an arbitrary column order (we choose the identity).
49 * If towards a new node it is branched on a variable, that appears in a row which is not included in the subset of
50 * rows for the current node, then the row set of the new children is the ordered row set of the current node, appended
51 * by this new row.
52 * For the column order, if at the current node columns are symmetrically equivalent, then these can be permuted for
53 * the sake of symmetry handling. In the implementation, we only swap the column containing the branching variable
54 * with a symmetrically equivalent column elsewhere. We use one of the following heuristics:
55 *
56 * - None: Keep the column-order as-is.
57 * - First: Permute such that the column containing the branching variable is in the symmetrically equivalent column
58 * with minimal index.
59 * - Last: The same, but to the symmetrically equivalent column with maximal index.
60 * - Centre: The same, but to the symmetrically equivalent column closest to to the middlemost column among all columns.
61 * - Median: The same, but to the median of all symmetrically equivalent columns. (This is the default)
62 *
63 * Since the dynamic row and column ordering rule for a branch-and-bound tree node depends on the decisions made up to
64 * that branch-and-bound tree node, we compute and store the row and column order for the branch-and-bound tree children
65 * at the moment of branching. This is done by the eventhandler in this file.
66 * Instead of storing those, we could have chosen to reconstruct this row and column ordering to save memory.
67 * However, we cannot reliably reconstruct this order from the branch-and-bound tree itself,
68 * because the row and column ordering depends on symmetrical equivalence of columns in the orbitope matrix,
69 * and because SCIP can change the tree structure during solving that may re-write historic variable bound changes
70 * (for instance when global variable bound changes are found, or when the root node is moved down the tree to become
71 * the new effective root node).
72 * We are not concerned about storing the row and column ordering, since we only store the mutations with its parent.
73 * These are usually at most one column swap and usually at most one additional row.
74 *
75 * @todo Exploiting packing-partitioning structures
76 * @todo for packing-partitioning rows, use the FIRST column swap heuristic.
77 */
78
79/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
80
83#include "scip/pub_cons.h"
84#include "scip/pub_message.h"
85#include "scip/pub_var.h"
86#include "scip/struct_var.h"
87#include "scip/type_var.h"
88#include "scip/scip.h"
89#include "scip/scip_branch.h"
90#include "scip/scip_conflict.h"
91#include "scip/scip_cons.h"
92#include "scip/scip_copy.h"
93#include "scip/scip_cut.h"
94#include "scip/scip_general.h"
95#include "scip/scip_lp.h"
96#include "scip/scip_mem.h"
97#include "scip/scip_message.h"
98#include "scip/scip_numerics.h"
99#include "scip/scip_param.h"
100#include "scip/scip_prob.h"
101#include "scip/scip_probing.h"
102#include "scip/scip_sol.h"
103#include "scip/scip_var.h"
104#include "scip/struct_scip.h"
105#include "scip/struct_mem.h"
106#include "scip/struct_tree.h"
107#include "scip/symmetry.h"
108#include "scip/debug.h"
110#include <string.h>
111
112
113/* symmetry handler properties */
114#define SYMHDLR_NAME "orbitopalreduction"
115
116/* orbitopal symmetry handler properties */
117#define EVENTHDLR_NAME "symmetry_orbitopal_eventhdlr"
118#define EVENTHDLR_DESC "event handler for maintaining the branch-and-bound tree"
119#define DEFAULT_COLUMNORDERING SCIP_COLUMNORDERING_MEDIAN /**< the column ordering variant */
120
121/*
122 * Data structures
123 */
124
125/** orbitopal symmetry handling data for a single orbitope */
127{
128 SCIP_VAR** vars; /**< orbitope variable array representing orbitope matrix row-wise */
129 int nrows; /**< number of rows */
130 int ncols; /**< number of columns */
131 int nbranchrows; /**< number of rows containing variables potentially used for branching */
132 SCIP_HASHMAP* rowindexmap; /**< map of variables to row index in orbitope matrix */
133 SCIP_HASHMAP* colindexmap; /**< map of variables to column index in orbitope matrix */
134#ifndef NDEBUG
135 SCIP_Longint lastnodenumber; /**< the last node number where the row and column order is computed */
136 int dbghash; /**< a hash for the column order in the last iteration */
137#endif
138 SCIP_HASHTABLE* nodeinfos; /**< symmetry handling information per branch-and-bound tree node */
139 SCIP_COLUMNORDERING columnordering; /**< policy for the column ordering */
140 SCIP_ROWORDERING rowordering; /**< policy for the row ordering */
141};
142typedef struct OrbitopeData ORBITOPEDATA; /**< orbitopal symmetry handling data for a single orbitope */
143
144/** wrapper for all orbitopes in orbitopal symmetry handling data */
145struct SCIP_OrbitopalReductionData
146{
147 SCIP_COLUMNORDERING defaultcolumnordering; /**< default policy for the column ordering */
148 SCIP_EVENTHDLR* eventhdlr; /**< pointer to the event handler for managing the branching tree */
149 ORBITOPEDATA** orbitopes; /**< array of pointers to orbitope data structs */
150 int norbitopes; /**< number of orbitope data structs in array */
151 int maxnorbitopes; /**< allocated orbitopes array size */
152 SCIP_CONSHDLR* conshdlr_nonlinear; /**< nonlinear constraint handler,
153 * is used to determine if a variable is a branching variable */
154 SCIP_Bool conshdlr_nonlinear_checked; /**< nonlinear constraint handler is already added? */
155 int nred; /**< total number of reductions */
156 int ncutoff; /**< total number of cutoffs */
157};
158
159/*
160 * Local methods
161 */
162
163/** gets whether a variable type is a branchrow-type */
164static
166 SCIP_ORBITOPALREDDATA* orbireddata, /**< pointer to the dynamic orbitopal reduction data */
167 SCIP_VAR* var /**< variable whose type is checked */
168)
169{
170 assert( orbireddata != NULL );
171 assert( orbireddata->conshdlr_nonlinear_checked );
172
173 /* if nonlinear constraints are present, also continuous variables can be branching variables */
174 if ( orbireddata->conshdlr_nonlinear != NULL && SCIPconshdlrGetNActiveConss(orbireddata->conshdlr_nonlinear) > 0 )
175 return TRUE;
176
177 /* otherwise, only integral variables are used for branching */
178 if ( SCIPvarIsIntegral(var) )
179 {
182 return TRUE;
183 }
184 return FALSE;
185}
186
187
188/** container for column index permutations */
190{
191 int from; /**< from which column ID */
192 int to; /**< to which column ID */
193};
194typedef struct ColSwap COLSWAP;
195
196/** information stored for branch-and-bound nodes */
198{
199 SCIP_Longint nodenumber; /**< node number of the branch-and-bound tree node */
200 COLSWAP* colswaps; /**< list containing column swaps by node branching decisions */
201 int ncolswaps; /**< number of elements in colswaps. ncolswaps == 0 <=> colswaps == NULL */
202 int* rows; /**< list of new variable rows by node branching decisions */
203 int nrows; /**< number of new variable rows added. nrows == 0 <=> rows == NULL */
204};
206
207/** hash key for virtual branch and bound nodeinfo struct */
208static
209SCIP_DECL_HASHGETKEY(hashGetKeyBnbnodeinfo)
210{ /*lint --e{715}*/
211 return elem;
212}
213
214/** returns TRUE iff the indices of both node numbers are equal */
215static
216SCIP_DECL_HASHKEYEQ(hashKeyEqBnbnodeinfo)
217{ /*lint --e{715}*/
218 BNBNODEINFO* nodeinfo1 = (BNBNODEINFO*) key1;
219 BNBNODEINFO* nodeinfo2 = (BNBNODEINFO*) key2;
220 return nodeinfo1->nodenumber == nodeinfo2->nodenumber;
221}
222
223/** returns the hash value of the key */
224static
225SCIP_DECL_HASHKEYVAL(hashKeyValBnbnodeinfo)
226{ /*lint --e{715}*/
227 BNBNODEINFO* nodeinfo = (BNBNODEINFO*) key;
228 return (unsigned int) nodeinfo->nodenumber;
229}
230
231
232/** tests if two columns are symmetrically equivalent
233 *
234 * We test if the columns with index col1 and col2 have elementwise the same bounds.
235 * If all symmetry-compatible reductions are applied, then it suffices to check only as many rows as are selected
236 * for orbitopal reduction. However, to be resilient to reductions that are not symmetry-compatible,
237 * we test all variables in the columns.
238 */
239static
241 SCIP* scip, /**< SCIP data structure */
242 ORBITOPEDATA* orbidata, /**< orbitope information */
243 int col1, /**< first column to compare */
244 int col2 /**< second column to compare */
245 )
246{
247 int i;
248 SCIP_VAR* var1;
249 SCIP_VAR* var2;
250
251 assert( scip != NULL );
252 assert( orbidata != NULL );
253 assert( col1 >= 0 );
254 assert( col1 < orbidata->ncols );
255 assert( col2 >= 0 );
256 assert( col2 < orbidata->ncols );
257
258 /* @todo test only for the selected rows (see function description) */
259 for (i = 0; i < orbidata->nrows; ++i)
260 {
261 var1 = orbidata->vars[i * orbidata->ncols + col1];
262 var2 = orbidata->vars[i * orbidata->ncols + col2];
263
264 /* if variable bounds differ: columns c and origcolid are not the same */
265 if (
267 ||
269 )
270 return FALSE;
271 }
272
273 /* loop terminated, so columns are equal */
274 return TRUE;
275}
276
277/** updates the column order with a bound change
278 *
279 * When it is branched on a variable in a column, update the column order for the children of the focusnode.
280 * Symmetrically equivalent columns, that is the columns where the variables have elementwise the same domain,
281 * at the focusnode at the moment of branching can be permuted.
282 * In this function, we select such a permutation, based on the column containing the branching variable(s).
283 * In all cases, we swap the column containing the branching variable with a symmetrically equivalent column,
284 * and the columnordering specifies if we prefer it to be the leftmost, rightmost, centermost symmetrically
285 * equivalent column, or the median column among the symmetrically equivalent columns.
286 *
287 * The column ordering is determined and stored at the moment of branching.
288 */
289static
291 SCIP* scip, /**< SCIP data structure */
292 ORBITOPEDATA* orbidata, /**< orbitope data */
293 int* colorder, /**< array to populate with column order, of size colorder */
294 int* colorderinv, /**< inverse array of the column order, of size colorder */
295 SCIP_VAR* var, /**< variable that we branch on */
296 COLSWAP* thiscolswap /**< the colswap to populate */
297 )
298{
299 int origcolid;
300 int swaporigcolid;
301 int c;
302 int ncols;
303 int* origequalcolids;
304 int norigequalcolids;
305 int middlecolumn = 0;
306 int positionorigcolidincolorder;
307 int positionswaporigcolidincolorder;
308
309#ifndef NDEBUG
310 SCIP_VAR* var1;
311 SCIP_VAR* var2;
312 int i;
313 int nrows;
314#endif
315
316 assert( scip != NULL );
317 assert( orbidata != NULL );
318 assert( colorder != NULL );
319 assert( colorderinv != NULL );
320 assert( var != NULL );
321 assert( thiscolswap != NULL );
322 assert( orbidata->ncols > 0 );
323 assert( orbidata->nrows > 0 );
324
325 ncols = orbidata->ncols;
326 assert( ncols > 0 );
327#ifndef NDEBUG
328 nrows = orbidata->nrows > 0;
329 assert( nrows > 0 );
330#endif
331
332 /* do not apply a column swap if no column permutations are applied */
333 if ( orbidata->columnordering == SCIP_COLUMNORDERING_NONE )
334 {
335 thiscolswap->from = 0;
336 thiscolswap->to = 0;
337 return SCIP_OKAY;
338 }
339
340 /* only variables from the orbitope matrix are of interest */
341 origcolid = SCIPhashmapGetImageInt(orbidata->colindexmap, (void*) var);
342 if ( origcolid == INT_MAX )
343 {
344 thiscolswap->from = 0;
345 thiscolswap->to = 0;
346 return SCIP_OKAY;
347 }
348 assert( origcolid >= 0 );
349 assert( origcolid < ncols );
350
351 /* policy: swap with identical column that is closest to the center in relabeled order */
352 /* @todo other policies: If the variable is in a ppc-row, then select the minimal/second to minimal to branch on */
353 swaporigcolid = origcolid;
354
355 switch (orbidata->columnordering)
356 {
358 /* CENTRE uses the same code as FIRST and LAST, but requires that middlecolumn = ncols / 2 is set */
359 middlecolumn = ncols / 2;
360 /*lint -fallthrough*/
363 /* for each column, test column ordering condition, then if the column is identical to the var-column */
364 for (c = 0; c < ncols; ++c)
365 {
366 /* origcolid is not interesting */
367 if ( c == origcolid )
368 continue;
369
370 /* test if c is a better choice than swaporigcolid,
371 * otherwise continue to next iteration through CONDITIONFAIL
372 */
373 switch (orbidata->columnordering)
374 {
376 /* only swap with c if c is earlier in column order than swaporigcolid */
377 if ( colorderinv[c] >= colorderinv[swaporigcolid] )
378 continue;
379 break;
381 /* only swap with c if c is later in column order than swaporigcolid */
382 if ( colorderinv[c] <= colorderinv[swaporigcolid] )
383 continue;
384 break;
386 /* if the column is not more central than swaporigcolid, ignore */
387 if ( ABS(colorderinv[c] - middlecolumn) >=
388 ABS(colorderinv[swaporigcolid] - middlecolumn) )
389 continue;
390 break;
391 default:
392 return SCIP_ERROR;
393 }
394
395 /* test: are c and origcolid the same columns w.r.t. the variable domain restrictions? */
396 if ( !testColumnsAreSymmetricallyEquivalent(scip, orbidata, c, origcolid) )
397 continue;
398
399 /* the variable domain reductions in c and origcolid are the same */
400 swaporigcolid = c;
401 }
402
403 /* end switch */
404 break;
405
407 /* collect columns identical to the var-column, then select column satisfying ordering condition */
408 norigequalcolids = 0;
409 SCIP_CALL( SCIPallocBufferArray(scip, &origequalcolids, ncols) );
410
411 /* collect equal columns */
412#ifdef SCIP_MORE_DEBUG
413 SCIPdebugMessage("Detect columns identical to original column %d: ", origcolid);
414#endif
415 for (c = 0; c < ncols; ++c)
416 {
417 /* column origcolid is always equal to itself */
418 if ( c == origcolid )
419 {
420 origequalcolids[norigequalcolids++] = c;
421#ifdef SCIP_MORE_DEBUG
422 SCIPdebugPrintf("%d ", c);
423#endif
424 assert( norigequalcolids <= ncols );
425 continue;
426 }
427
428 /* test: are c and origcolid the same columns w.r.t. the variable domain restrictions? */
429 if ( !testColumnsAreSymmetricallyEquivalent(scip, orbidata, c, origcolid) )
430 continue;
431
432 /* the variable domain reductions in c and origcolid are the same */
433 origequalcolids[norigequalcolids++] = c;
434#ifdef SCIP_MORE_DEBUG
435 SCIPdebugPrintf("%d ", c);
436#endif
437 assert( norigequalcolids <= ncols );
438 }
439#ifdef SCIP_MORE_DEBUG
440 SCIPdebugPrintf("\n");
441#endif
442
443 /* we should have found origcolid, at least */
444 assert( norigequalcolids >= 1 );
445
446 /* from origequalcolids, select the column satisfying the column ordering policy */
447
448 /* get median column; since colorder maps origcolids to our ordering,
449 * we need to use colorderinv as the argument. */
450 /* @todo computing the median is O(n) by repeated median-of-medians (CLRS, Ch9), but let's just sort things */
451 SCIPsortInd(origequalcolids, SCIPsortArgsortInt, colorderinv, norigequalcolids);
452 /* get the median, that is swaporigcolid */
453 swaporigcolid = origequalcolids[norigequalcolids / 2];
454
455 SCIPfreeBufferArray(scip, &origequalcolids);
456
457 /* end switch */
458 break;
459
461 /* already handled earlier in this function */
462 default:
463 /* unknown column ordering variant */
464 return SCIP_ERROR;
465 }
466
467 thiscolswap->from = swaporigcolid;
468 thiscolswap->to = origcolid;
469
470 /* if we do not replace origcolid */
471 if ( swaporigcolid == origcolid )
472 return SCIP_OKAY;
473
474#ifndef NDEBUG
475 /* swapped columns should be equivalent */
476 for (i = 0; i < nrows; ++i)
477 {
478 var1 = orbidata->vars[i * ncols + swaporigcolid];
479 var2 = orbidata->vars[i * ncols + origcolid];
480 assert( SCIPsymEQ(scip, SCIPvarGetLbLocal(var1), SCIPvarGetLbLocal(var2)) );
481 assert( SCIPsymEQ(scip, SCIPvarGetUbLocal(var1), SCIPvarGetUbLocal(var2)) );
482 }
483#endif
484
485 /* now swap the permuted column indices of swaporigcolid and origcolid */
486
487 /* at which column is origcolid? */
488 positionorigcolidincolorder = colorderinv[origcolid];
489 assert( positionorigcolidincolorder >= 0 );
490 assert( positionorigcolidincolorder < ncols );
491 assert( colorder[positionorigcolidincolorder] == origcolid );
492
493 /* at which column is swaporigcolid? */
494 positionswaporigcolidincolorder = colorderinv[swaporigcolid];
495 assert( positionswaporigcolidincolorder >= 0 );
496 assert( positionswaporigcolidincolorder < ncols );
497 assert( colorder[positionswaporigcolidincolorder] == swaporigcolid );
498
499 SCIPdebugMessage("Orbitope %p: Swapping column %d (at position %d) with column %d (at position %d)\n",
500 (void*) orbidata, origcolid, positionorigcolidincolorder, swaporigcolid, positionswaporigcolidincolorder);
501
502 /* swap them, also keep track of the inverses */
503 colorder[positionswaporigcolidincolorder] = origcolid;
504 colorder[positionorigcolidincolorder] = swaporigcolid;
505 colorderinv[origcolid] = positionswaporigcolidincolorder;
506 colorderinv[swaporigcolid] = positionorigcolidincolorder;
507
508 return SCIP_OKAY;
509}
510
511
512/** yields entry at index in array, or returns entry if array is NULL */
513static
515 int* arr, /**< array */
516 int idx /**< index */
517 )
518{
519 assert( idx >= 0 );
520 if ( arr == NULL )
521 return idx;
522 return arr[idx];
523}
524
525/** frees the row order */
526static
528 SCIP* scip, /**< SCIP data structure */
529 ORBITOPEDATA* orbidata, /**< orbitope data */
530 int** roworder /**< roworder array that is initialized with the roworder in the dynamic
531 * case, and NULL in the static case */
532 )
533{
534 assert( scip != NULL );
535 assert( orbidata != NULL );
536 assert( roworder != NULL );
537
538 if ( orbidata->rowordering == SCIP_ROWORDERING_NONE )
539 {
540 assert( *roworder == NULL );
541 return;
542 }
543
544 assert( *roworder != NULL );
545 assert( orbidata->rowordering == SCIP_ROWORDERING_BRANCHING );
546 SCIPfreeBlockMemoryArray(scip, roworder, orbidata->nrows);
547
548 return;
549}
550
551/** gets the row order at the node
552 *
553 * this is NULL (i.e., the identity map) in the static (none) setting.
554 * this is an array of size orbidata->nrows in the dynamic (branching) setting.
555 *
556 * The row order is given in the order of the variables that is branched on.
557 * @todo combine with variant of cons_orbitope.c
558 */
559static
561 SCIP* scip, /**< SCIP data structure */
562 ORBITOPEDATA* orbidata, /**< orbitope data */
563 SCIP_NODE* node, /**< node for which the row order should be detected */
564 int** roworder, /**< array to populate with row order */
565 int* nselrows /**< pointer to populate with the number of rows part of the row order */
566 )
567{
568 int i;
569 int j;
570 BNBNODEINFO* ancestornodeinfo;
571 BNBNODEINFO tmpnodeinfo; /* used for lookups in hash table */
572
573 assert( orbidata != NULL );
574 assert( orbidata->nrows > 0 );
575 assert( orbidata->ncols > 0 );
576 assert( node != NULL );
577 assert( roworder != NULL );
578 assert( nselrows != NULL );
579
580 if ( orbidata->rowordering == SCIP_ROWORDERING_NONE )
581 {
582 *roworder = NULL;
583 *nselrows = orbidata->nrows;
584 return SCIP_OKAY;
585 }
586
587 assert( orbidata->rowordering == SCIP_ROWORDERING_BRANCHING );
588
589 /* allocate number of rows */
590 SCIP_CALL( SCIPallocBlockMemoryArray(scip, roworder, orbidata->nrows) );
591
592 *nselrows = 0;
593
594 /* get the present row order up to this node (excluding the node itself) */
595 node = SCIPnodeGetParent(node);
596 while (node != NULL)
597 {
598 /* retrieve the nodeinfo of this ancestor node */
599 tmpnodeinfo.nodenumber = SCIPnodeGetNumber(node);
600 ancestornodeinfo = (BNBNODEINFO*) SCIPhashtableRetrieve(orbidata->nodeinfos, (void*) &tmpnodeinfo);
601 if ( ancestornodeinfo != NULL )
602 {
603 assert( ancestornodeinfo->nrows >= 0 );
604 for (i = ancestornodeinfo->nrows - 1; i >= 0; --i)
605 {
606 (*roworder)[(*nselrows)++] = ancestornodeinfo->rows[i];
607#ifndef NDEBUG
608 {
609 /* check if this row is not featured earlier */
610 for (j = 0; j < (*nselrows) - 1; ++j)
611 {
612 assert( ancestornodeinfo->rows[i] != (*roworder)[j] );
613 }
614 }
615#endif
616 }
617 }
618
619 node = SCIPnodeGetParent(node);
620 }
621
622 /* row order is in reverse order now, so reverse the array */
623 for (i = 0; i < (*nselrows) / 2; ++i)
624 {
625 /* swap entry i with nselrows - 1 - i */
626 j = (*roworder)[i];
627 (*roworder)[i] = (*roworder)[(*nselrows) - 1 - i];
628 (*roworder)[(*nselrows) - 1 - i] = j;
629 }
630
631 return SCIP_OKAY;
632}
633
634
635/** gets rooted path up to node and populates column ordering array */
636static
638 ORBITOPEDATA* orbidata, /**< orbitope data */
639 SCIP_NODE* node, /**< node considered */
640 SCIP_NODE** rootedpath, /**< array to populate with the rooted path, must be sufficiently long */
641 int* colorder, /**< array to populate with the column order, must be nvars long */
642 int* colorderinv /**< array to populate with the inverse column order, must be nvars long */
643 )
644{
645 int i;
646 int j;
647 int depth;
648 BNBNODEINFO* ancestornodeinfo;
649 BNBNODEINFO tmpnodeinfo;
650 COLSWAP* thiscolswap;
651
652 assert( orbidata != NULL );
653 assert( node != NULL );
654 assert( rootedpath != NULL );
655 assert( colorder != NULL );
656 assert( colorderinv != NULL );
657
658 depth = SCIPnodeGetDepth(node);
659 i = depth;
660 while ( node != NULL )
661 {
662 assert( SCIPnodeGetDepth(node) == i );
663 rootedpath[i--] = node;
664 node = SCIPnodeGetParent(node);
665 }
666 assert( i == -1 );
667
668 for (i = 0; i <= depth; ++i)
669 {
670 node = rootedpath[i];
671
672 assert( SCIPnodeGetDepth(node) == i );
673
674 /* get the node info of that node */
675 tmpnodeinfo.nodenumber = SCIPnodeGetNumber(node);
676 ancestornodeinfo = (BNBNODEINFO*) SCIPhashtableRetrieve(orbidata->nodeinfos, (void*) &tmpnodeinfo);
677
678 /* skip nodes that do not imply any row or column swaps */
679 if ( ancestornodeinfo == NULL )
680 continue;
681
682 /* ncolswaps == 0 iff colswaps == NULL */
683 assert( (ancestornodeinfo->ncolswaps == 0) != (ancestornodeinfo->colswaps != NULL) );
684
685 for (j = 0; j < ancestornodeinfo->ncolswaps; ++j)
686 {
687 int positionfromincolorder;
688 int positiontoincolorder;
689
690 thiscolswap = &ancestornodeinfo->colswaps[j];
691 assert( thiscolswap->from != thiscolswap->to ); /* there are no trivial swaps in the list */
692 assert( thiscolswap->from >= 0 && thiscolswap->from < orbidata->ncols );
693 assert( thiscolswap->to >= 0 && thiscolswap->to < orbidata->ncols );
694
695 /* at which column is origcolid? */
696 positionfromincolorder = colorderinv[thiscolswap->from];
697 assert( positionfromincolorder >= 0 );
698 assert( positionfromincolorder < orbidata->ncols );
699 assert( colorder[positionfromincolorder] == thiscolswap->from );
700
701 /* at which column is swaporigcolid? */
702 positiontoincolorder = colorderinv[thiscolswap->to];
703 assert( positiontoincolorder >= 0 );
704 assert( positiontoincolorder < orbidata->ncols );
705 assert( colorder[positiontoincolorder] == thiscolswap->to );
706
707 /* swap them, also keep track of the inverses */
708 colorder[positiontoincolorder] = thiscolswap->from;
709 colorder[positionfromincolorder] = thiscolswap->to;
710 colorderinv[thiscolswap->from] = positiontoincolorder;
711 colorderinv[thiscolswap->to] = positionfromincolorder;
712 }
713 }
714
715 return SCIP_OKAY;
716}
717
718/** at branching decisions, maintains the column swap and potential new rows in the orbitope */
719static
720SCIP_DECL_EVENTEXEC(eventExecNodeBranched)
721{
722 ORBITOPEDATA* orbidata;
723 SCIP_NODE* node;
724 SCIP_NODE* eventnode;
725 SCIP_NODE** children;
726 SCIP_NODE* childnode;
727 SCIP_DOMCHG* domchg;
728 SCIP_BOUNDCHG* boundchg;
729 SCIP_VAR* var;
730 SCIP_VAR** branchvars;
731 int maxnbranchvars;
732 int nbranchvars;
733 int nboundchgs;
734 int nchildren;
735 int i;
736 int j;
737 int c;
738 int rowid;
739 BNBNODEINFO* newnodeinfo;
740 SCIP_NODE** rootedpath;
741
742 assert( eventdata != NULL );
743 assert( !SCIPinProbing(scip) );
744
745 eventnode = SCIPeventGetNode(event);
746 assert( SCIPgetFocusNode(scip) == eventnode );
747
748 orbidata = (ORBITOPEDATA*) eventdata;
749 assert( orbidata != NULL );
750 assert( orbidata->nrows > 0 );
751 assert( orbidata->ncols > 0 );
752 assert( orbidata->vars != NULL );
753 assert( orbidata->colindexmap != NULL );
754 assert( orbidata->rowindexmap != NULL );
755
756 SCIP_CALL( SCIPgetChildren(scip, &children, &nchildren) );
757
758 /* arrays used within the loop */
759 maxnbranchvars = 1; /* it's a good guess that there's one branching variable, because that's likely the number */
760 SCIP_CALL( SCIPallocBufferArray(scip, &branchvars, maxnbranchvars) );
761 SCIP_CALL( SCIPallocBufferArray(scip, &rootedpath, SCIPnodeGetDepth(eventnode)) );
762
763 /* get all variables branched upon (check all branches) */
764 nbranchvars = 0;
765 for (c = 0; c < nchildren; ++c)
766 {
767 childnode = children[c];
768 domchg = SCIPnodeGetDomchg(childnode);
769
770 /* loop through all bound changes */
771 nboundchgs = SCIPdomchgGetNBoundchgs(domchg);
772 for (i = 0; i < nboundchgs; ++i)
773 {
774 /* get bound change info */
775 boundchg = SCIPdomchgGetBoundchg(domchg, i);
776 assert( boundchg != NULL );
777
778 /* branching decisions have to be in the beginning of the bound change array */
780 break;
781
782 /* get corresponding branching variable */
783 var = SCIPboundchgGetVar(boundchg);
784
785 /* only variables from the orbitope matrix are of interest */
786 if ( ! SCIPhashmapExists(orbidata->rowindexmap, (void*) var) )
787 continue;
788
789 /* skip variables that are already stored */
790 if ( nbranchvars > 0 )
791 {
792 for (j = 0; j < nbranchvars; ++j)
793 {
794 if ( branchvars[j] == var )
795 break;
796 }
797 /* if the loop above is stopped with `break`, `j < nbranchvars` is not satisfied.
798 * then, go to the next iteration
799 */
800 if ( j < nbranchvars )
801 continue;
802 }
803
804 /* the variable is not already in the array, so store it */
805 if ( nbranchvars >= maxnbranchvars )
806 {
807 assert( nbranchvars == maxnbranchvars );
808 assert( maxnbranchvars > 0 );
809 maxnbranchvars = SCIPcalcMemGrowSize(scip, maxnbranchvars + 1);
810 SCIP_CALL( SCIPreallocBufferArray(scip, &branchvars, maxnbranchvars) );
811 }
812 assert( nbranchvars < maxnbranchvars );
813 branchvars[nbranchvars++] = var;
814 }
815 }
816
817 /* skip orbitopes whose variable matrices do not contain any branching variable */
818 if ( nbranchvars <= 0 )
819 goto FREE;
820
821 SCIP_CALL( SCIPallocBlockMemory(scip, &newnodeinfo) );
822 newnodeinfo->nodenumber = SCIPnodeGetNumber(eventnode);
823 newnodeinfo->colswaps = NULL;
824 newnodeinfo->ncolswaps = 0;
825 newnodeinfo->rows = NULL;
826 newnodeinfo->nrows = 0;
827
828 /* store data about row ordering */
829 if ( orbidata->rowordering != SCIP_ROWORDERING_NONE )
830 {
831 int* roworder;
832 int nselrows;
833
834 assert( orbidata->nrows > 0 );
835 assert( orbidata->rowordering == SCIP_ROWORDERING_BRANCHING );
836
837 /* get the present row order up to this node */
838 SCIP_CALL( getRowOrder(scip, orbidata, eventnode, &roworder, &nselrows) );
839 assert( roworder != NULL );
840
841 /* extend the row fixings with the steps from this node */
842 for (i = 0; i < nbranchvars; ++i)
843 {
844 var = branchvars[i];
845
846 assert( SCIPhashmapExists(orbidata->rowindexmap, (void*) var) ); /* otherwise was not added to branchvars */
847 rowid = SCIPhashmapGetImageInt(orbidata->rowindexmap, (void*) var);
848 assert( rowid >= 0 );
849 assert( rowid < orbidata->nrows );
850
851 /* avoid adding row to row order twice */
852 if ( nselrows > 0 )
853 {
854 for (j = 0; j < nselrows; ++j)
855 {
856 if ( rowid == roworder[j] )
857 break;
858 }
859 if ( j < nselrows ) /* if the loop is interrupted */
860 continue;
861 }
862
863 /* if we end up here, the row index does not appear for any ancestor or the present row order */
864
865 /* append rowid to present roworder */
866 roworder[nselrows++] = rowid;
867
868 /* mark that this row index is the new one in the node */
869 if ( newnodeinfo->rows == NULL )
870 {
871 assert( newnodeinfo->nrows == 0 );
872 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &newnodeinfo->rows, newnodeinfo->nrows + 1) );
873 }
874 else
875 {
876 /* reallocate with linear increments, because we expect 1 branching variable most of the time */
878 newnodeinfo->nrows, newnodeinfo->nrows + 1) );
879 }
880 newnodeinfo->rows[newnodeinfo->nrows++] = rowid;
881 }
882
883 freeRowOrder(scip, orbidata, &roworder);
884 }
885
886 /* store data about column ordering */
887 if ( orbidata->columnordering != SCIP_COLUMNORDERING_NONE )
888 {
889 int* colorder;
890 int* colorderinv;
891 COLSWAP* thiscolswap;
892 COLSWAP tmpcolswap;
893
894 assert( orbidata->ncols > 0 );
895 SCIP_CALL( SCIPallocBufferArray(scip, &colorder, orbidata->ncols) );
896 SCIP_CALL( SCIPallocBufferArray(scip, &colorderinv, orbidata->ncols) );
897
898 /* populate colorder with standard ordering */
899 for (i = 0; i < orbidata->ncols; ++i)
900 colorder[i] = i;
901
902 /* introduce inverse column ordering */
903 for (i = 0; i < orbidata->ncols; ++i)
904 colorderinv[i] = i;
905
906 /* get the rooted path
907 *
908 * We want to iterate through the bound changes in the order of the rooted path to this node.
909 */
910 node = SCIPnodeGetParent(eventnode);
911 if ( node != NULL )
912 {
913 SCIP_CALL( populateRootedPathColumnOrder(orbidata, node, rootedpath, colorder, colorderinv) );
914 }
915
916 /* get the swap for this node */
917 for (i = 0; i < nbranchvars; ++i)
918 {
920 colorderinv, branchvars[i], &tmpcolswap) );
921
922 /* skip trivial swaps of columns */
923 if ( tmpcolswap.from == tmpcolswap.to ) /*lint !e644*/
924 continue;
925
926 /* mark that this row index is the new one in the node */
927 if ( newnodeinfo->rows == NULL )
928 {
929 assert( newnodeinfo->nrows == 0 );
930 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &newnodeinfo->colswaps, newnodeinfo->ncolswaps + 1) );
931 }
932 else
933 {
934 /* reallocate with linear increments, because we expect 1 branching variable most of the time */
935 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &newnodeinfo->colswaps, newnodeinfo->ncolswaps,
936 newnodeinfo->ncolswaps + 1) );
937 }
938 thiscolswap = &(newnodeinfo->colswaps[newnodeinfo->ncolswaps++]);
939 thiscolswap->from = tmpcolswap.from; /*lint !e644*/
940 thiscolswap->to = tmpcolswap.to; /*lint !e644*/
941 }
942
943 SCIPfreeBufferArray(scip, &colorder);
944 SCIPfreeBufferArray(scip, &colorderinv);
945 }
946
947 /* store updates of row/column order or free memory if no change applied */
948 if ( newnodeinfo->nrows > 0 || newnodeinfo->ncolswaps > 0 )
949 {
950 SCIP_CALL( SCIPhashtableSafeInsert(orbidata->nodeinfos, newnodeinfo) );
951 }
952 else
953 {
954 SCIPfreeBlockMemory(scip, &newnodeinfo);
955 }
956
957FREE:
958 SCIPfreeBufferArray(scip, &rootedpath);
959 SCIPfreeBufferArray(scip, &branchvars);
960
961 return SCIP_OKAY;
962} /*lint !e715*/
963
964
965/** at branching decisions, maintains the column swap and potential new rows in the orbitope */
966static
968{
969 switch (SCIPeventGetType(event))
970 {
972 return eventExecNodeBranched(scip, eventhdlr, event, eventdata);
973 default:
974 SCIPerrorMessage("Eventhandler " EVENTHDLR_NAME " is called with an unsupported eventtype.\n");
975 return SCIP_ERROR;
976 }
977}
978
979
980/** returns whether a row contains potential branching variables */
981static
983 SCIP* scip, /**< SCIP data structure */
984 SCIP_ORBITOPALREDDATA* orbireddata, /**< pointer to the dynamic orbitopal reduction data */
985 ORBITOPEDATA* orbidata, /**< symmetry handling data for orbitopal structure */
986 int rowid /**< row id for which to check */
987 )
988{
989#ifndef NDEBUG
990 int c;
991#endif
992 SCIP_VAR* var;
993
994 assert( scip != NULL );
995 assert( orbireddata != NULL );
996 assert( orbidata != NULL );
997 assert( orbidata->nrows > 0 );
998 assert( orbidata->ncols > 0 );
999 assert( rowid >= 0 );
1000 assert( rowid < orbidata->nrows );
1001 assert( orbidata->vars != NULL );
1002
1003 /* get the first variable from the row */
1004 var = orbidata->vars[rowid * orbidata->ncols];
1005 assert( var != NULL );
1006
1007 /* debugging: the variable types in a row should all be the same */
1008#ifndef NDEBUG
1009 for (c = 1; c < orbidata->ncols; ++c)
1010 {
1011 assert( SCIPgetSymInferredVarType(var)
1012 == SCIPgetSymInferredVarType(orbidata->vars[rowid * orbidata->ncols + c]) );
1013 }
1014#endif
1015
1016 /* check whether the row contains a potential branching variable (all variables within a row are symmetric) */
1017 return vartypeIsBranchRowType(orbireddata, var);
1018}
1019
1020
1021/** frees orbitope data */
1022static
1024 SCIP* scip, /**< SCIP data structure */
1025 SCIP_ORBITOPALREDDATA* orbireddata, /**< pointer to the dynamic orbitopal reduction data */
1026 ORBITOPEDATA** orbidata /**< pointer to orbitope data */
1027 )
1028{
1029 BNBNODEINFO* nodeinfo;
1030 int i;
1031 int nentries;
1032 int nelem;
1033
1034 assert( scip != NULL );
1035 assert( orbireddata != NULL );
1036 assert( orbidata != NULL );
1037 assert( *orbidata != NULL );
1038 assert( (*orbidata)->vars != NULL );
1039 assert( (*orbidata)->nrows > 0 );
1040 assert( (*orbidata)->ncols > 0 );
1041 assert( (*orbidata)->nrows * (*orbidata)->ncols > 0 );
1042 assert( SCIPisTransformed(scip) );
1043
1044 /* free data if orbitopal reduction is dynamic */
1045 if ( (*orbidata)->columnordering != SCIP_COLUMNORDERING_NONE || (*orbidata)->rowordering != SCIP_ROWORDERING_NONE )
1046 {
1047 /* drop event */
1048 SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_NODEBRANCHED, orbireddata->eventhdlr,
1049 (SCIP_EVENTDATA*) *orbidata, -1 ) );
1050
1051 /* free nodeinfos */
1052 nentries = SCIPhashtableGetNEntries((*orbidata)->nodeinfos);
1053 for (i = 0; i < nentries; ++i)
1054 {
1055 /* @todo in principle, can deal with memory sparsity by first getting all nodeinfos,
1056 * then sorting by address and free them in descending order
1057 */
1058 nodeinfo = (BNBNODEINFO*) (SCIPhashtableGetEntry((*orbidata)->nodeinfos, i));
1059 if ( nodeinfo == NULL )
1060 continue;
1061
1062 assert( nodeinfo != NULL );
1063 assert( nodeinfo->nrows > 0 || nodeinfo->ncolswaps > 0 );
1064
1065 assert( (nodeinfo->ncolswaps == 0) != (nodeinfo->colswaps != NULL) );
1066 SCIPfreeBlockMemoryArrayNull(scip, &(nodeinfo->colswaps), nodeinfo->ncolswaps);
1067
1068 assert( (nodeinfo->nrows == 0) != (nodeinfo->rows != NULL) );
1069 SCIPfreeBlockMemoryArrayNull(scip, &(nodeinfo->rows), nodeinfo->nrows);
1070
1071 SCIPfreeBlockMemory(scip, &nodeinfo);
1072 }
1073 SCIPhashtableFree(&((*orbidata)->nodeinfos));
1074 }
1075
1076 /* free index lookup hashsets */
1077 SCIPhashmapFree(&((*orbidata)->colindexmap));
1078 SCIPhashmapFree(&((*orbidata)->rowindexmap));
1079
1080 /* free and release vars */
1081 nelem = (*orbidata)->nrows * (*orbidata)->ncols;
1082 assert( nelem > 0 );
1083 for (i = 0; i < nelem; ++i)
1084 {
1085 SCIP_CALL( SCIPreleaseVar(scip, &(*orbidata)->vars[i]) );
1086 }
1087 SCIPfreeBlockMemoryArray(scip, &((*orbidata)->vars), (*orbidata)->nrows * (*orbidata)->ncols); /*lint !e647*/
1088
1089 SCIPfreeBlockMemory(scip, orbidata);
1090
1091 return SCIP_OKAY;
1092}
1093
1094
1095/** adds an orbitope to the orbitopal reduction data */
1096static
1098 SCIP* scip, /**< SCIP data structure */
1099 SCIP_ORBITOPALREDDATA* orbireddata, /**< pointer to the dynamic orbitopal reduction data */
1100 SCIP_ROWORDERING rowordering, /**< specifies how rows of orbitope are ordered */
1101 SCIP_COLUMNORDERING colordering, /**< specifies how columnss of orbitope are ordered */
1102 SCIP_VAR** vars, /**< variables array, must have size nrows * ncols */
1103 int nrows, /**< number of rows in orbitope */
1104 int ncols, /**< number of columns in orbitope */
1105 SCIP_Bool* success /**< to store whether the component is successfully added */
1106 )
1107{
1108 ORBITOPEDATA* orbidata;
1109 SCIP_VAR* var;
1110 int i;
1111 int rowid;
1112 int colid;
1113 int nelem;
1114
1115 assert( scip != NULL );
1116 assert( orbireddata != NULL );
1117 assert( orbireddata->eventhdlr != NULL );
1118 assert( vars != NULL );
1119 assert( nrows >= 0 );
1120 assert( ncols >= 0 );
1121
1122 nelem = nrows * ncols;
1123 assert( nelem >= 0 );
1124
1125 /* prevent trivial case with empty orbitope */
1126 if ( nelem == 0 )
1127 {
1128 *success = FALSE;
1129 return SCIP_OKAY;
1130 }
1131
1132 *success = TRUE;
1133
1134 SCIP_CALL( SCIPallocBlockMemory(scip, &orbidata) );
1135
1136 orbidata->nrows = nrows;
1137 orbidata->ncols = ncols;
1138 orbidata->columnordering = colordering;
1139 orbidata->rowordering = rowordering;
1140
1141#ifndef NDEBUG
1142 orbidata->lastnodenumber = -1;
1143 orbidata->dbghash = 0;
1144#endif
1145
1146 /* variable array enumerates the orbitope matrix row-wise */
1147 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &orbidata->vars, nelem) );
1148
1149 /* create row and column index lookup maps */
1151 SCIP_CALL( SCIPhashmapCreate(&orbidata->colindexmap, SCIPblkmem(scip), ncols) );
1152
1153 SCIPdebugMessage("Orbitope variables for (%dx%d) orbitope with orbidata %p\n", nrows, ncols, (void*) orbidata);
1154
1155 /* populate variable array defining orbitope matrix for orbitope data */
1156 for (i = 0, rowid = 0, colid = 0; i < nelem; ++i, ++colid)
1157 {
1158 if ( colid == ncols )
1159 {
1160 colid = 0;
1161 ++rowid;
1162 }
1163 assert( nrows > 0 );
1164 assert( ncols > 0 );
1165 assert( rowid == i / ncols );
1166 assert( colid == i % ncols );
1167
1168 var = vars[i];
1169 assert( var != NULL );
1170 assert( SCIPvarIsTransformed(var) );
1171
1173 SCIP_CALL( SCIPcaptureVar(scip, var) );
1174
1175 orbidata->vars[i] = var;
1176
1177 /* variables cannot be repeated in the variable matrix */
1178 assert( ! SCIPhashmapExists(orbidata->rowindexmap, var) );
1179 SCIP_CALL( SCIPhashmapInsertInt(orbidata->rowindexmap, var, rowid) );
1180
1181 assert( ! SCIPhashmapExists(orbidata->colindexmap, var) );
1182 SCIP_CALL( SCIPhashmapInsertInt(orbidata->colindexmap, var, colid) );
1183
1184 SCIPdebugMessage("%4d %4d -> %s\n", rowid, colid, var->name);
1185 }
1186
1187 /* count number of branchable rows in orbitope */
1188 orbidata->nbranchrows = 0;
1189 /* @todo at getRowData: If nselrows == nbranchrows, append the non-branch rows (like before) */
1190 for (i = 0; i < nrows; ++i)
1191 {
1192 if ( rowIsBranchRow(scip, orbireddata, orbidata, i) )
1193 ++orbidata->nbranchrows;
1194 }
1195
1196 /* cannot add orbitope when already branching */
1198
1199 /* possibly create data needed for dynamic orbitopal reduction */
1201 {
1202 /* add the event to store the row and column updates of nodes in the branch-and-bound tree */
1203 SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_NODEBRANCHED, orbireddata->eventhdlr,
1204 (SCIP_EVENTDATA*) orbidata, NULL) );
1205
1206 /* nodeinfos: every node that implies a column swap is represented
1207 *
1208 * Assuming at most one branching on every variable implying a column swap, initial hashtable size nelem.
1209 * In case that there are many more rows than columns, we do not expect too many column swaps.
1210 */
1211 SCIP_CALL( SCIPhashtableCreate(&orbidata->nodeinfos, scip->mem->probmem, MIN(16 * ncols + 64, nelem),
1212 hashGetKeyBnbnodeinfo, hashKeyEqBnbnodeinfo, hashKeyValBnbnodeinfo, NULL) );
1213 }
1214
1215 /* resize orbitope array if needed */
1216 assert( orbireddata->norbitopes >= 0 );
1217 assert( (orbireddata->norbitopes == 0) == (orbireddata->orbitopes == NULL) );
1218 assert( orbireddata->norbitopes <= orbireddata->maxnorbitopes );
1219 if ( orbireddata->norbitopes == orbireddata->maxnorbitopes )
1220 {
1221 int newsize;
1222
1223 newsize = SCIPcalcMemGrowSize(scip, orbireddata->norbitopes + 1);
1224 assert( newsize >= 0 );
1225
1226 if ( orbireddata->norbitopes == 0 )
1227 {
1228 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &orbireddata->orbitopes, newsize) );
1229 }
1230 else
1231 {
1232 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &orbireddata->orbitopes, orbireddata->norbitopes, newsize) );
1233 }
1234
1235 orbireddata->maxnorbitopes = newsize;
1236 }
1237 assert( orbireddata->orbitopes != NULL );
1238 assert( orbireddata->norbitopes < orbireddata->maxnorbitopes );
1239
1240 /* add orbitope to orbitopal reduction data */
1241 assert( orbireddata->norbitopes < orbireddata->maxnorbitopes );
1242 orbireddata->orbitopes[orbireddata->norbitopes++] = orbidata;
1243
1244 SCIPdebugMsg(scip, "Added orbitope for orbitopal reduction of size %d by %d\n", nrows, ncols);
1245
1246 return SCIP_OKAY;
1247}
1248
1249
1250/** frees the column order */
1251static
1253 SCIP* scip, /**< SCIP data structure */
1254 ORBITOPEDATA* orbidata, /**< orbitope data */
1255 int** colorder, /**< colorder array that is initialized with the colorder in the dynamic
1256 * case, of size ncols, and NULL in the static case */
1257 int** colorderinv /**< array with the inverse column order, of size ncols */
1258 )
1259{
1260 assert( scip != NULL );
1261 assert( orbidata != NULL );
1262 assert( colorder != NULL );
1263 assert( colorderinv != NULL );
1264
1265 if ( orbidata->columnordering == SCIP_COLUMNORDERING_NONE )
1266 {
1267 assert( *colorder == NULL );
1268 assert( *colorderinv == NULL );
1269 return;
1270 }
1271 assert( *colorder != NULL );
1272 assert( *colorderinv != NULL );
1273
1274 SCIPfreeBlockMemoryArray(scip, colorder, orbidata->ncols);
1275 SCIPfreeBlockMemoryArray(scip, colorderinv, orbidata->ncols);
1276}
1277
1278
1279/** gets the column order at the node
1280 *
1281 * The column order is (deterministically) dynamically decided based on the policy for column ordering.
1282 */
1283static
1285 SCIP* scip, /**< SCIP data structure */
1286 ORBITOPEDATA* orbidata, /**< orbitope data */
1287 SCIP_NODE* eventnode, /**< node where this should be determined at */
1288 int** colorder, /**< array to populate with column order, of size ncols */
1289 int** colorderinv /**< array to populate with inverse column order, of size ncols */
1290 )
1291{
1292 SCIP_NODE* node;
1293 SCIP_NODE** rootedpath;
1294 int i;
1295 int depth;
1296 int ncols;
1297
1298 assert( scip != NULL );
1299 assert( orbidata != NULL );
1300 assert( eventnode != NULL );
1301 assert( colorder != NULL );
1302 assert( colorderinv != NULL );
1303
1304 if ( orbidata->columnordering == SCIP_COLUMNORDERING_NONE )
1305 {
1306 *colorder = NULL;
1307 *colorderinv = NULL;
1308 return SCIP_OKAY;
1309 }
1310 ncols = orbidata->ncols;
1311 assert( ncols > 0 );
1312
1313 SCIP_CALL( SCIPallocBlockMemoryArray(scip, colorder, ncols) );
1314 SCIP_CALL( SCIPallocBlockMemoryArray(scip, colorderinv, ncols) );
1315
1316 /* populate colorder with standard ordering */
1317 for (i = 0; i < ncols; ++i)
1318 (*colorder)[i] = i;
1319
1320 /* introduce inverse column ordering */
1321 for (i = 0; i < ncols; ++i)
1322 (*colorderinv)[i] = i;
1323
1324 /* get the rooted path
1325 *
1326 * We want to iterate through the bound changes in the order of the rooted path to this node.
1327 */
1328 node = SCIPnodeGetParent(eventnode);
1329 if ( node != NULL )
1330 {
1331 depth = SCIPnodeGetDepth(node);
1332 SCIP_CALL( SCIPallocBufferArray(scip, &rootedpath, depth + 1) );
1333 SCIP_CALL( populateRootedPathColumnOrder(orbidata, node, rootedpath, *colorder, *colorderinv) );
1334 SCIPfreeBufferArray(scip, &rootedpath);
1335 }
1336
1337 return SCIP_OKAY;
1338}
1339
1340
1341#ifndef NDEBUG
1342/** checks if the columns of the matrix are lexicographically decreasing, using the specified row and column ordering */
1343static
1345 SCIP* scip, /**< SCIP data structure */
1346 ORBITOPEDATA* orbidata, /**< orbitope data */
1347 int* roworder, /**< array with the row order */
1348 int* colorder, /**< array with the column order */
1349 SCIP_Real* matrix, /**< a matrix */
1350 int nrows, /**< number of rows of matrix */
1351 int ncols, /**< number of cols of matrix */
1352 int* infinitesimal, /**< array specifying where the infinitesimals are at */
1353 SCIP_Bool addinfinitesimals /**< whether infinitesimals are added (TRUE) or subtracted (FALSE) */
1354 )
1355{
1356 int rowid;
1357 int colid;
1358 int idx;
1359 int origrowid;
1360 int origcolid;
1361 int origidx;
1362 SCIP_VAR* var;
1363
1364 assert( scip != NULL );
1365 assert( matrix != NULL );
1366 assert( orbidata != NULL );
1367 assert( orbidata->vars != NULL );
1368 assert( nrows >= 0 );
1369 assert( nrows <= orbidata->nrows );
1370 assert( ncols >= 0 );
1371 assert( ncols <= orbidata->ncols );
1372 assert( infinitesimal != NULL );
1373
1374 /* respect variable bounds */
1375 for (rowid = 0; rowid < nrows; ++rowid)
1376 {
1377 origrowid = getArrayEntryOrIndex(roworder, rowid);
1378 for (colid = 0; colid < ncols; ++colid)
1379 {
1380 origcolid = getArrayEntryOrIndex(colorder, colid);
1381 idx = rowid * ncols + colid;
1382 origidx = origrowid * ncols + origcolid;
1383 var = orbidata->vars[origidx];
1384 assert( SCIPsymGE(scip, matrix[idx], SCIPvarGetLbLocal(var)) );
1385 assert( SCIPsymLE(scip, matrix[idx], SCIPvarGetUbLocal(var)) );
1386 }
1387 }
1388
1389 /* is orbitope */
1390 for (colid = 0; colid < ncols - 1; ++colid)
1391 {
1392 /* compare column colid with colid + 1 */
1393 for (rowid = 0; rowid < nrows; ++rowid)
1394 {
1395 /* entry is >= entry to the right */
1396 assert( SCIPsymGE(scip, matrix[rowid * ncols + colid], matrix[rowid * ncols + colid + 1]) );
1397
1398 if ( SCIPsymGT(scip, matrix[rowid * ncols + colid], matrix[rowid * ncols + colid + 1]) )
1399 {
1400 /* critical row */
1401 break;
1402 }
1403 else
1404 {
1405 /* check for infinitesimal values
1406 * If infinitesimals are added (lexminface case), then if the left column has a +epsilon,
1407 * it does not matter whether the right column has +epsilon or not, then the left column is >,
1408 * due to the axioms x + epsilon > x + epsilon and x + epsilon > x.
1409 * Analogously, x > x - epsilon and x - epsilon > x - epsilon.
1410 */
1411 assert( SCIPsymEQ(scip, matrix[rowid * ncols + colid], matrix[rowid * ncols + colid + 1]) );
1412 if ( addinfinitesimals
1413 ? (infinitesimal[colid] == rowid) /* left has +epsilon term */
1414 : (infinitesimal[colid + 1] == rowid) /* right has -epsilon term */
1415 )
1416 {
1417 /* critical row */
1418 break;
1419 }
1420 }
1421 }
1422 }
1423}
1424#endif
1425
1426#ifndef NDEBUG
1427/** to test if arrays are the same, generates some hash for an array of integers */
1428static
1430 int* array, /**< array */
1431 int len /**< array length */
1432 )
1433{
1434 int i;
1435 unsigned int hash = 0;
1436
1437 assert( array != NULL );
1438 assert( len >= 0 );
1439
1440 for (i = 0; i < len; i++)
1441 {
1442 hash ^= (unsigned int) (array[i]);
1443 hash = (hash << 1) ^ (hash >> 1);
1444 }
1445
1446 return (int) hash;
1447}
1448#endif
1449
1450#ifdef SCIP_MORE_DEBUG
1451/** prints nrows × ncols matrix of floats with 2 decimals */
1452static
1453void debugPrintMatrix(
1454 SCIP_Real* matrix, /**< matrix, encoded as array enumerating the elements row-wise */
1455 int nrows, /**< number of rows */
1456 int ncols /**< number of rows */
1457 )
1458{
1459 int row;
1460 int col;
1461
1462 assert( matrix != NULL );
1463 assert( nrows >= 0 );
1464 assert( ncols >= 0 );
1465
1466 for (row = 0; row < nrows; ++row)
1467 {
1468 SCIPdebugPrintf("[");
1469 for (col = 0; col < ncols; ++col)
1470 {
1471 SCIPdebugPrintf(" %+10.2f", matrix[row * ncols + col]);
1472 }
1473 SCIPdebugPrintf(" ]\n");
1474 }
1475}
1476#endif
1477
1478
1479/** gets the column order at the node */
1480static
1482 SCIP* scip, /**< SCIP data structure */
1483 ORBITOPEDATA* orbidata, /**< orbitope data */
1484 int* roworder, /**< array with the row order (or NULL if identity function is used) */
1485 int nselrows, /**< number of selected rows */
1486 int* colorder, /**< array with the column order (or NULL if identity function is used) */
1487 SCIP_Bool* infeasible, /**< pointer to store whether the problem is infeasible */
1488 int* nfixedvars /**< pointer to counter of number of variable domain reductions */
1489 )
1490{
1491 /* @todo also make "nselcols" to allow for colorders smaller than orbidata->ncols */
1492 SCIP_Real* lexminface = NULL;
1493 int* lexminepsrow = NULL;
1494 SCIP_Real* lexmaxface = NULL;
1495 int* lexmaxepsrow = NULL;
1496 int nelem;
1497 int rowid;
1498 int colid;
1499 int ncols;
1500 int origrowid;
1501 int origcolid;
1502 int origidx;
1503 int i;
1504 int lastunfixed;
1505 SCIP_Real lb;
1506 SCIP_Real ub;
1507 SCIP_Bool iseq;
1508 SCIP_Bool success;
1509 SCIP_VAR* var;
1510 SCIP_VAR* othervar;
1511
1512 assert( scip != NULL );
1513 assert( orbidata != NULL );
1514 assert( orbidata->vars != NULL );
1515 assert( nselrows >= 0 );
1516 assert( infeasible != NULL );
1517 assert( nfixedvars != NULL );
1518
1519 *infeasible = FALSE;
1520
1521 assert( orbidata->nrows > 0 );
1522 assert( orbidata->nrows >= nselrows );
1523 ncols = orbidata->ncols;
1524 assert( ncols > 1 );
1525
1526 /* nothing to propagate without any rows */
1527 if ( nselrows <= 0 )
1528 return SCIP_OKAY;
1529
1530#ifdef SCIP_MORE_DEBUG
1531 /* print matrix for debugging purposes */
1532 {
1533 int k;
1534 int r;
1535 SCIP_VAR* thisvar;
1536 SCIPdebugMessage("Start propagating static orbitope: \n");
1537 SCIPdebugPrintf(">");
1538 for (k = 0; k < ncols; ++k)
1539 {
1540 SCIPdebugPrintf("%12d ", colorder[k]);
1541 }
1542 SCIPdebugPrintf("< (IDs)\n");
1543
1544 for (r = 0; r < nselrows; ++r)
1545 {
1546 SCIPdebugPrintf("[");
1547 for (k = 0; k < ncols; ++k)
1548 {
1549 thisvar = orbidata->vars[roworder[r] * ncols + colorder[k]];
1550 SCIPdebugPrintf("%4s %+1.2f,%+1.2f ", SCIPvarGetName(thisvar),
1551 SCIPvarGetLbLocal(thisvar), SCIPvarGetUbLocal(thisvar));
1552 }
1553 SCIPdebugPrintf("] (row %d)\n", roworder[r]);
1554 }
1555 }
1556#endif
1557
1558 nelem = nselrows * ncols;
1559
1560 /* compute lexmin face */
1561 SCIP_CALL( SCIPallocBufferArray(scip, &lexminface, nelem) );
1562
1563 /* array to store for each column at which row we add an infinitesimal value, initially at none (-1) */
1564 SCIP_CALL( SCIPallocBufferArray(scip, &lexminepsrow, ncols) );
1565 for (colid = 0; colid < ncols; ++colid)
1566 lexminepsrow[colid] = -1;
1567
1568 /* last column takes the minimally possible values. */
1569 colid = ncols - 1;
1570 origcolid = getArrayEntryOrIndex(colorder, colid);
1571 for (rowid = 0, i = colid; rowid < nselrows; ++rowid, i += ncols)
1572 {
1573 origrowid = getArrayEntryOrIndex(roworder, rowid);
1574 origidx = origrowid * ncols + origcolid;
1575 var = orbidata->vars[origidx];
1576
1577 assert( i == rowid * ncols + colid );
1578 assert( var != NULL );
1579
1580 lexminface[i] = SCIPvarGetLbLocal(var);
1581 }
1582 /* all previous columns: One-column replacement algorithm */
1583 for (colid = ncols - 2; colid >= 0; --colid)
1584 {
1585 /* "rowid" of the last unfixed entry whose domain allows for larger values than the current chosen.
1586 * If there is none, -1. */
1587 lastunfixed = -1;
1588 /* whether column "colid" is the same as column "colid + 1" up (but excluding) to "rowid" */
1589 iseq = TRUE;
1590
1591 origcolid = getArrayEntryOrIndex(colorder, colid);
1592 for (rowid = 0, i = colid; rowid < nselrows; ++rowid, i += ncols)
1593 {
1594 origrowid = getArrayEntryOrIndex(roworder, rowid);
1595 origidx = origrowid * ncols + origcolid;
1596 assert( i == rowid * ncols + colid );
1597
1598 /* the entry one to the right is not the first column */
1599 assert( (i + 1) % ncols > 0 );
1600
1601 var = orbidata->vars[origidx];
1602 assert( var != NULL );
1603
1604 if ( iseq )
1605 {
1606 /* equality holds up to this row
1607 * Compare to the entry value on the column immediately right.
1608 * The value we choose on the left must be at least this.
1609 * 2 Options:
1610 * Option 1: The upper bound is smaller. Then we're in an infeasible situation. Resolve as described below.
1611 * Option 2: The upper bound is greater or equal.
1612 */
1613 ub = SCIPvarGetUbLocal(var);
1614
1615 /* compare to the value in the column right of it */
1616 if ( SCIPsymLT(scip, ub, lexminface[i + 1]) ||
1617 ( lexminepsrow[colid + 1] == rowid && SCIPsymEQ(scip, ub, lexminface[i + 1]) ) )
1618 {
1619 /* value of this column can only be strictly smaller than the value in the column to its right
1620 * This may not be possible.
1621 * Try to repair: Go back to the last row with "room" left, and make the value minimally larger.
1622 */
1623 if ( lastunfixed >= 0 )
1624 {
1625 /* repair: return to the last row with "room", and increase the lexmin-value at that row. */
1626 assert( SCIPsymEQ(scip, lexminface[lastunfixed * ncols + colid],
1627 lexminface[lastunfixed * ncols + colid + 1]) );
1628 othervar = orbidata->vars[getArrayEntryOrIndex(roworder, lastunfixed) * ncols + origcolid];
1629 if( SCIPvarIsIntegral(othervar) )
1630 {
1631 /* discrete type with unit steps: Add one to the bound. */
1632 /* @todo @question Are variable bounds for SCIP_VARTYPE_IMPLINT always integral? */
1633 assert( SCIPisIntegral(scip, lexminface[lastunfixed * ncols + colid]) );
1634 lexminface[lastunfixed * ncols + colid] += 1.0;
1635 assert( SCIPisIntegral(scip, lexminface[lastunfixed * ncols + colid]) );
1636 assert( SCIPsymLE(scip, lexminface[lastunfixed * ncols + colid], SCIPvarGetUbLocal(othervar)) );
1637 }
1638 else
1639 {
1640 /* continuous type, so add an infinitesimal value to the bound */
1641 assert( SCIPsymLE(scip, lexminface[lastunfixed * ncols + colid], SCIPvarGetUbLocal(othervar)) );
1642 assert( lexminepsrow[colid] == -1 );
1643 lexminepsrow[colid] = lastunfixed;
1644 }
1645 /* now row "lastunfixed" is greater. Restart from here. */
1646 iseq = FALSE;
1647 rowid = lastunfixed; /*lint !e850*/ /* the next iteration considers "lastunfixed + 1" */
1648 i = rowid * ncols + colid;
1649 continue;
1650 }
1651 else
1652 {
1653 /* cannot repair. It is infeasible. */
1654 *infeasible = TRUE;
1655 SCIPdebugMessage("Cannot repair infeasibility for column %d (original: %d), min\n", colid, origcolid);
1656 goto FREE;
1657 }
1658 }
1659 else
1660 {
1661 assert( SCIPsymGE(scip, ub, lexminface[i + 1]) );
1662 lb = SCIPvarGetLbLocal(var);
1663 assert( SCIPsymLE(scip, lb, ub) );
1664 lexminface[i] = MAX(lexminface[i + 1], lb);
1665 assert( SCIPsymGE(scip, lexminface[i], lexminface[i + 1]) );
1666
1667 /* are we still equal? */
1668 if ( SCIPsymGT(scip, lexminface[i], lexminface[i + 1]) )
1669 iseq = FALSE;
1670 else if ( lexminepsrow[colid + 1] == rowid )
1671 {
1672 assert( SCIPsymEQ(scip, lexminface[i], lexminface[i + 1]) );
1673 assert( !SCIPvarIsIntegral(orbidata->vars[getArrayEntryOrIndex(roworder, rowid) * ncols + origcolid]) );
1674 assert( !SCIPvarIsIntegral(var) );
1675 /* right column (colid+1) has value x + epsilon, left column (colid) has value x, now
1676 * must also become x + epsilon in order to be larger or equal
1677 * by axioms, we can squeeze infinitesimals between one other; epsilon > epsilon.
1678 */
1679 iseq = FALSE;
1680 assert( lexminepsrow[colid] == -1 );
1681 lexminepsrow[colid] = rowid;
1682 }
1683
1684 /* is there room left to increase this variable further? */
1685 if( SCIPvarIsIntegral(var) )
1686 {
1687 /* discrete type with unit steps: Add one to the bound. */
1688 /* @todo @question Are variable bounds for SCIP_VARTYPE_IMPLINT always integral? */
1689 /* @todo in principle, this can be made more tight using the hole-lists... */
1690 assert( SCIPisIntegral(scip, lexminface[i]) );
1691 if ( SCIPsymLE(scip, lexminface[i] + 1.0, ub) )
1692 lastunfixed = rowid;
1693 }
1694 else
1695 {
1696 /* continuous type: if we can add an infinitesimal value to the current lexminface[i] value,
1697 * mark row as 'lastunfixed'
1698 */
1699 if ( SCIPsymLT(scip, lexminface[i], ub) )
1700 lastunfixed = rowid;
1701 }
1702 }
1703 }
1704 else
1705 {
1706 /* there had been a row before which breaks the equality-condition, choose minimally possible value */
1707 lexminface[i] = SCIPvarGetLbLocal(var);
1708 }
1709 }
1710 }
1711
1712#ifndef NDEBUG
1713 /* sanity checks */
1714 assertIsOrbitopeMatrix(scip, orbidata, roworder, colorder, lexminface, nselrows, ncols, lexminepsrow, TRUE);
1715#endif
1716
1717 /* compute lexmax face */
1718 SCIP_CALL( SCIPallocBufferArray(scip, &lexmaxface, nelem) );
1719
1720 /* array to store for each column at which row we subtract an infinitesimal value, initially at none (-1) */
1721 SCIP_CALL( SCIPallocBufferArray(scip, &lexmaxepsrow, ncols) );
1722 for (colid = 0; colid < ncols; ++colid)
1723 lexmaxepsrow[colid] = -1;
1724
1725 /* first column, fill all unfixed entries with maximally possible values */
1726 colid = 0;
1727 origcolid = getArrayEntryOrIndex(colorder, colid);
1728 for (rowid = 0, i = colid; rowid < nselrows; ++rowid, i += ncols)
1729 {
1730 origrowid = getArrayEntryOrIndex(roworder, rowid);
1731 origidx = origrowid * ncols + origcolid;
1732 var = orbidata->vars[origidx];
1733
1734 assert( i == rowid * ncols + colid );
1735 assert( var != NULL );
1736
1737 lexmaxface[i] = SCIPvarGetUbLocal(var);
1738 }
1739 /* all next columns: One-column replacement algorithm */
1740 for (colid = 1; colid < ncols; ++colid)
1741 {
1742 /* "rowid" of the last unfixed entry whose domain allows for smaller values than the current chosen.
1743 * If there is none, -1. */
1744 lastunfixed = -1;
1745 /* whether column "colid" is the same as column "colid - 1" up (but excluding) to "rowid" */
1746 iseq = TRUE;
1747
1748 origcolid = getArrayEntryOrIndex(colorder, colid);
1749 for (rowid = 0, i = colid; rowid < nselrows; ++rowid, i += ncols)
1750 {
1751 origrowid = getArrayEntryOrIndex(roworder, rowid);
1752 origidx = origrowid * ncols + origcolid;
1753 assert( i == rowid * ncols + colid );
1754
1755 /* the entry one to the left is not the last column, i.e., this column cannot be the first column */
1756 assert( i % ncols > 0 );
1757
1758 var = orbidata->vars[origidx];
1759 assert( var != NULL );
1760
1761 if ( iseq )
1762 {
1763 /* equality holds up to this row
1764 * Compare to the entry value on the column immediately left.
1765 * The value we choose on the right must be at most this.
1766 * 2 Options:
1767 * Option 1: The lower bound is larger. Then we're in an infeasible situation. Resolve as described below.
1768 * Option 2: The lower bound is smaller or equal.
1769 */
1770 lb = SCIPvarGetLbLocal(var);
1771
1772 /* compare to the value in the column left of it */
1773 if ( SCIPsymGT(scip, lb, lexmaxface[i - 1]) ||
1774 ( lexmaxepsrow[colid - 1] == rowid && SCIPsymEQ(scip, lb, lexmaxface[i - 1]) ) )
1775 {
1776 /* value of this column can only be strictly larger than the value in the column to its left
1777 * This may not be possible.
1778 * Try to repair: Go back to the last row with "room" left, and make the value minimally smaller.
1779 */
1780 if ( lastunfixed >= 0 )
1781 {
1782 /* repair: return to the last row with "room", and decrease the lexmax-value at that row. */
1783 assert( SCIPsymEQ(scip, lexmaxface[lastunfixed * ncols + colid],
1784 lexmaxface[lastunfixed * ncols + colid - 1]) );
1785 othervar = orbidata->vars[getArrayEntryOrIndex(roworder, lastunfixed) * ncols + origcolid];
1786 if( SCIPvarIsIntegral(othervar) )
1787 {
1788 /* discrete type with unit steps: Remove one from the lexmax-value. */
1789 /* @todo @question Are variable bounds for SCIP_VARTYPE_IMPLINT always integral? */
1790 assert( SCIPisIntegral(scip, lexmaxface[lastunfixed * ncols + colid]) );
1791 lexmaxface[lastunfixed * ncols + colid] -= 1.0;
1792 assert( SCIPisIntegral(scip, lexmaxface[lastunfixed * ncols + colid]) );
1793 assert( SCIPsymGE(scip, lexmaxface[lastunfixed * ncols + colid], SCIPvarGetLbLocal(othervar)) );
1794 assert( SCIPsymLE(scip, lexmaxface[lastunfixed * ncols + colid], SCIPvarGetUbLocal(othervar)) );
1795 }
1796 else
1797 {
1798 /* continuous type, so subtract an infinitesimal value to the bound */
1799 assert( SCIPsymGE(scip, lexmaxface[lastunfixed * ncols + colid], SCIPvarGetLbLocal(othervar)) );
1800 assert( SCIPsymLE(scip, lexmaxface[lastunfixed * ncols + colid], SCIPvarGetUbLocal(othervar)) );
1801 assert( lexmaxepsrow[colid] == -1 );
1802 lexmaxepsrow[colid] = lastunfixed;
1803 }
1804 /* now row "lastunfixed" is greater. Restart from here. */
1805 iseq = FALSE;
1806 rowid = lastunfixed; /*lint !e850*/ /* the next iteration considers "lastunfixed + 1" */
1807 i = rowid * ncols + colid;
1808 continue;
1809 }
1810 else
1811 {
1812 /* cannot repair. It is infeasible. */
1813 *infeasible = TRUE;
1814 SCIPdebugMessage("Cannot repair infeasibility for column %d (original: %d), max\n", colid, origcolid);
1815 goto FREE;
1816 }
1817 }
1818 else
1819 {
1820 assert( SCIPsymLE(scip, lb, lexmaxface[i - 1]) );
1821 ub = SCIPvarGetUbLocal(var);
1822 assert( SCIPsymLE(scip, lb, ub) );
1823 lexmaxface[i] = MIN(lexmaxface[i - 1], ub);
1824 assert( SCIPsymGE(scip, lexmaxface[i - 1], lexmaxface[i]) );
1825
1826 /* are we still equal? */
1827 if ( SCIPsymGT(scip, lexmaxface[i - 1], lexmaxface[i]) )
1828 iseq = FALSE;
1829 else if ( lexmaxepsrow[colid - 1] == rowid )
1830 {
1831 assert( SCIPsymEQ(scip, lexmaxface[i - 1], lexmaxface[i]) );
1832 assert( !SCIPvarIsIntegral(orbidata->vars[getArrayEntryOrIndex(roworder, rowid) * ncols + origcolid]) );
1833 assert( !SCIPvarIsIntegral(var) );
1834 /* left column (colid-1) has value x - epsilon, right column (colid) has value x, now
1835 * must also become x - epsilon in order to be larger or equal
1836 * by axioms, we can squeeze infinitesimals between one other; epsilon > epsilon.
1837 */
1838 iseq = FALSE;
1839 assert( lexmaxepsrow[colid] == -1 );
1840 lexmaxepsrow[colid] = rowid;
1841 }
1842
1843 /* is there room left to decrease this variable further? */
1844 if( SCIPvarIsIntegral(var) )
1845 {
1846 /* discrete type with unit steps: Remove one from the lexmax-value. */
1847 /* @todo @question Are variable bounds for SCIP_VARTYPE_IMPLINT always integral? */
1848 /* @todo in principle, this can be made more tight using the hole-lists... */
1849 assert( SCIPisIntegral(scip, lexmaxface[i]) );
1850 if ( SCIPsymGE(scip, lexmaxface[i] - 1.0, lb) )
1851 lastunfixed = rowid;
1852 }
1853 else
1854 {
1855 /* continuous type: if we can subtract an infinitesimal value to the current lexmaxface[i] value,
1856 * mark row as 'lastunfixed'
1857 */
1858 if ( SCIPsymGT(scip, lexmaxface[i], lb) )
1859 lastunfixed = rowid;
1860 }
1861 }
1862 }
1863 else
1864 {
1865 /* there had been a row before which breaks the equality-condition, choose maximally possible value */
1866 lexmaxface[i] = SCIPvarGetUbLocal(var);
1867 }
1868 }
1869 }
1870
1871#ifndef NDEBUG
1872 /* sanity checks */
1873 assertIsOrbitopeMatrix(scip, orbidata, roworder, colorder, lexmaxface, nselrows, ncols, lexmaxepsrow, FALSE);
1874#endif
1875
1876#ifdef SCIP_MORE_DEBUG
1877 /* show lexmin and lexmax face */
1878 SCIPdebugMessage("Lex min face\n");
1879 debugPrintMatrix(lexminface, nselrows, ncols);
1880 SCIPdebugMessage("Lex max face\n");
1881 debugPrintMatrix(lexmaxface, nselrows, ncols);
1882#endif
1883
1884 /* compare the two column-wise and apply domain reductions */
1885 for (colid = 0; colid < ncols; ++colid)
1886 {
1887 for (rowid = 0, i = colid; rowid < nselrows; ++rowid, i += ncols)
1888 {
1889 assert( i == rowid * ncols + colid );
1890
1891 /* get var */
1892 origrowid = getArrayEntryOrIndex(roworder, rowid);
1893 origcolid = getArrayEntryOrIndex(colorder, colid);
1894 origidx = origrowid * ncols + origcolid;
1895 var = orbidata->vars[origidx];
1896
1897 if ( SCIPsymEQ(scip, lexminface[i], lexmaxface[i]) )
1898 {
1899 /* tighten LB and UB to same value (i.e. fixing) */
1900 SCIP_CALL( SCIPtightenVarLb(scip, var, lexminface[i], FALSE, infeasible, &success) );
1901 if ( success )
1902 {
1903 SCIPdebugMessage("Fixing variable LB %12s (%3d,%3d) to %5.2f\n", var->name, rowid, colid, lexminface[i]);
1904 *nfixedvars += 1;
1905 }
1906 else
1907 {
1908 SCIPdebugMessage("Fixing variable LB %12s (%3d,%3d) to %5.2f (no success)\n", var->name, rowid, colid,
1909 lexminface[i]);
1910 }
1911 if ( *infeasible )
1912 {
1913 SCIPdebugMessage("Detected infeasibility fixing variable %12s (%3d,%3d) to %5.2f\n",
1914 var->name, rowid, colid, lexminface[i]);
1915 goto FREE;
1916 }
1917
1918 SCIP_CALL( SCIPtightenVarUb(scip, var, lexminface[i], FALSE, infeasible, &success) );
1919 if ( success )
1920 {
1921 SCIPdebugMessage("Fixing variable UB %12s (%3d,%3d) to %5.2f\n", var->name, rowid, colid, lexminface[i]);
1922 *nfixedvars += 1;
1923 }
1924 else
1925 {
1926 SCIPdebugMessage("Fixing variable UB %12s (%3d,%3d) to %5.2f (no success)\n", var->name, rowid, colid,
1927 lexminface[i]);
1928 }
1929 if ( *infeasible )
1930 {
1931 SCIPdebugMessage("Detected infeasibility fixing variable %12s (%3d,%3d) to %5.2f\n",
1932 var->name, rowid, colid, lexminface[i]);
1933 goto FREE;
1934 }
1935 }
1936 else
1937 {
1938 /* This is the row index where the min- and max-face have a different value for this column entry.
1939 * Update the lower bound and upper bound */
1940
1941 /* lower bound, based on lexminface */
1942 SCIP_CALL( SCIPtightenVarLb(scip, var, lexminface[i], FALSE, infeasible, &success) );
1943 if ( success )
1944 {
1945 SCIPdebugMessage("Restricting variable LB %12s (%3d,%3d) to %5.2f\n", var->name, rowid, colid,
1946 lexminface[i]);
1947 *nfixedvars += 1;
1948 }
1949 else
1950 {
1951 SCIPdebugMessage("Restricting variable LB %12s (%3d,%3d) to %5.2f (no success)\n", var->name,
1952 rowid, colid, lexminface[i]);
1953 }
1954 if ( *infeasible )
1955 {
1956 SCIPdebugMessage("Detected infeasibility restricting variable LB %12s (%3d,%3d) to %5.2f\n",
1957 var->name, rowid, colid, lexminface[i]);
1958 goto FREE;
1959 }
1960
1961 /* upper bound, based on lexmaxface */
1962 SCIP_CALL( SCIPtightenVarUb(scip, var, lexmaxface[i], FALSE, infeasible, &success) );
1963 if ( success )
1964 {
1965 SCIPdebugMessage("Restricting variable UB %12s (%3d,%3d) to %5.2f\n", var->name, rowid, colid,
1966 lexmaxface[i]);
1967 *nfixedvars += 1;
1968 }
1969 else
1970 {
1971 SCIPdebugMessage("Restricting variable UB %12s (%3d,%3d) to %5.2f (no success)\n", var->name,
1972 rowid, colid, lexmaxface[i]);
1973 }
1974 if ( *infeasible )
1975 {
1976 SCIPdebugMessage("Detected infeasibility restricting variable UB %12s (%3d,%3d) to %5.2f\n",
1977 var->name, rowid, colid, lexmaxface[i]);
1978 goto FREE;
1979 }
1980 break;
1981 }
1982 }
1983 }
1984
1985FREE:
1986 SCIPfreeBufferArrayNull(scip, &lexmaxepsrow);
1987 SCIPfreeBufferArrayNull(scip, &lexmaxface);
1988 SCIPfreeBufferArrayNull(scip, &lexminepsrow);
1989 SCIPfreeBufferArrayNull(scip, &lexminface);
1990
1991 return SCIP_OKAY;
1992}
1993
1994
1995/** propagation method for a single orbitope matrix */
1996static
1998 SCIP* scip, /**< SCIP data structure */
1999 ORBITOPEDATA* orbidata, /**< orbitope data */
2000 SCIP_Bool* infeasible, /**< pointer to store whether the problem is infeasible */
2001 int* nfixedvars /**< pointer to store the number of found domain reductions */
2002 )
2003{
2004 SCIP_NODE* focusnode;
2005 int* roworder;
2006 int nselrows;
2007 int* colorder;
2008 int* colorderinv;
2009
2010 assert( scip != NULL );
2011 assert( orbidata != NULL );
2012 assert( infeasible != NULL );
2013 assert( nfixedvars != NULL );
2014
2015 *nfixedvars = 0;
2016 *infeasible = FALSE;
2017
2018 assert( orbidata->ncols > 0 );
2019 assert( orbidata->nrows > 0 );
2020
2021 focusnode = SCIPgetFocusNode(scip);
2022 assert( focusnode != NULL );
2023
2024 /* get row order */
2025 SCIP_CALL( getRowOrder(scip, orbidata, focusnode, &roworder, &nselrows) );
2026 assert( nselrows >= 0 );
2027 assert( nselrows <= orbidata->nrows );
2028 if ( nselrows == 0 )
2029 goto FREEROWS;
2030
2031 /* get column order */
2032 SCIP_CALL( getColumnOrder(scip, orbidata, focusnode, &colorder, &colorderinv) );
2033
2034#ifndef NDEBUG
2035 /* DEBUG: if propagation is repeated in the same node, the same column order and row order is needed */
2036 /* @todo: performance: move roworder and colorder to orbidata, then re-use */
2037 {
2038 int colhash = (colorder == NULL) ? 1 : debugGetArrayHash(colorder, orbidata->ncols);
2039 int rowhash = (roworder == NULL) ? 0 : debugGetArrayHash(roworder, nselrows);
2040 int hash = colhash ^ rowhash;
2041
2042#ifdef SCIP_DEBUG
2043 SCIPdebugPrintf("Col hash %32d; Row hash %32d; Hash %32d\n", colhash, rowhash, hash);
2044 {
2045 SCIP_NODE* tmpnode;
2046 tmpnode = SCIPgetFocusNode(scip);
2047 while ( tmpnode != NULL )
2048 {
2049 int nbranchings, nconsprop, nprop;
2050 SCIPnodeGetDomchg(tmpnode);
2051 SCIPnodeGetNDomchg(tmpnode, &nbranchings, &nconsprop, &nprop);
2052 SCIPdebugPrintf(" node %lld: (%d, %d, %d) \n", tmpnode->number, nbranchings, nconsprop, nprop);
2053 tmpnode = SCIPnodeGetParent(tmpnode);
2054 }
2055 }
2056#endif
2057
2058 assert( SCIPgetCurrentNode(scip) == SCIPgetFocusNode(scip) ); /* no probing */
2060 {
2061 assert( orbidata->dbghash == hash );
2062 }
2063 orbidata->dbghash = hash;
2064 }
2066#endif
2067
2068 SCIP_CALL( propagateStaticOrbitope(scip, orbidata, roworder, nselrows, colorder, infeasible, nfixedvars) );
2069
2070 freeColumnOrder(scip, orbidata, &colorder, &colorderinv);
2071FREEROWS:
2072 freeRowOrder(scip, orbidata, &roworder);
2073
2074#ifdef SCIP_MORE_DEBUG
2075 SCIPdebugPrintf("\n\n");
2076#endif
2077
2078 return SCIP_OKAY;
2079}
2080
2081
2082/*
2083 * Interface methods
2084 */
2085
2086
2087/** gets the number of reductions */
2089 SCIP* scip, /**< SCIP data structure */
2090 SCIP_ORBITOPALREDDATA* orbireddata, /**< orbitopal reduction data structure */
2091 int* nred, /**< total number of reductions applied */
2092 int* ncutoff /**< total number of cutoffs applied */
2093 )
2094{
2095 assert( scip != NULL );
2096 assert( orbireddata != NULL );
2097 assert( nred != NULL );
2098
2099 *nred = orbireddata->nred;
2100 *ncutoff = orbireddata->ncutoff;
2101
2102 return SCIP_OKAY;
2103}
2104
2105
2106/** prints orbitopal reduction data */
2108 SCIP* scip, /**< SCIP data structure */
2109 SCIP_ORBITOPALREDDATA* orbireddata /**< orbitopal reduction data structure */
2110 )
2111{
2112 int i;
2113
2114 assert( scip != NULL );
2115 assert( orbireddata != NULL );
2116
2117 if ( orbireddata->norbitopes == 0 )
2118 {
2119 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, " orbitopal reduction: no components\n");
2120 return SCIP_OKAY;
2121 }
2122
2124 " orbitopal reduction: %4d components: ", orbireddata->norbitopes);
2125 for (i = 0; i < orbireddata->norbitopes; ++i)
2126 {
2127 if ( i > 0 )
2130 "%dx%d", orbireddata->orbitopes[i]->nrows, orbireddata->orbitopes[i]->ncols);
2131 }
2133
2134 return SCIP_OKAY;
2135}
2136
2137
2138/** propagates orbitopal reduction */
2140 SCIP* scip, /**< SCIP data structure */
2141 SCIP_ORBITOPALREDDATA* orbireddata, /**< orbitopal reduction data structure */
2142 SCIP_Bool* infeasible, /**< pointer to store whether infeasibility is found */
2143 int* nred, /**< pointer to store the number of domain reductions */
2144 SCIP_Bool* didrun /**< a global pointer maintaining if any symmetry propagator has run
2145 * only set this to TRUE when a reduction is found, never set to FALSE */
2146 )
2147{
2148 ORBITOPEDATA* orbidata;
2149 int c;
2150 int thisfixedvars;
2151
2152 assert( scip != NULL );
2153 assert( orbireddata != NULL );
2154 assert( (orbireddata->orbitopes == NULL) == (orbireddata->norbitopes == 0) );
2155 assert( orbireddata->norbitopes >= 0 );
2156 assert( orbireddata->norbitopes <= orbireddata->maxnorbitopes );
2157 assert( infeasible != NULL );
2158 assert( nred != NULL );
2159 assert( didrun != NULL );
2160
2161 *infeasible = FALSE;
2162 *nred = 0;
2163 *didrun = FALSE;
2164
2165 /* early termination */
2166 if ( orbireddata->norbitopes == 0 )
2167 return SCIP_OKAY;
2168
2169 /* @todo Can the following be removed? */
2170 /* @todo shouldn't this be SCIPallowWeakDualReds, since we do not regard the objective */
2172 return SCIP_OKAY;
2173
2174 /* cannot do anything during probing
2175 * @todo can find deductions for the probing node, maybe?
2176 */
2177 if ( SCIPinProbing(scip) )
2178 return SCIP_OKAY;
2179
2180 /* propagate all orbitopes */
2181 for (c = 0; c < orbireddata->norbitopes; ++c)
2182 {
2183 orbidata = orbireddata->orbitopes[c];
2184 assert( orbidata != NULL );
2185
2186 SCIP_CALL( propagateOrbitope(scip, orbidata, infeasible, &thisfixedvars) );
2187 SCIPdebugMessage("Found %d reductions during orbitopal reduction for orbitope %d\n", thisfixedvars, c);
2188 *nred += thisfixedvars;
2189
2190 /* a symmetry propagator has ran, so set didrun to TRUE */
2191 *didrun = TRUE;
2192
2193 /* stop if we find infeasibility in one of the methods */
2194 if ( *infeasible )
2195 {
2196 SCIPdebugMessage("Detected infeasibility during orbitopal reduction for orbitope %d\n", c);
2197 break;
2198 }
2199 }
2200
2201 orbireddata->nred += *nred;
2202 if ( *infeasible )
2203 ++orbireddata->ncutoff;
2204
2205 return SCIP_OKAY;
2206}
2207
2208/** adds orbitopal component to orbitopal symmetry handler */
2210 SCIP* scip, /**< SCIP data structure */
2211 SCIP_ORBITOPALREDDATA* orbireddata, /**< orbitopal reduction data structure */
2212 SCIP_ROWORDERING rowordering, /**< specifies how rows of orbitope are ordered */
2213 SCIP_COLUMNORDERING colordering, /**< specifies how columnss of orbitope are ordered */
2214 SCIP_VAR** vars, /**< matrix of variables on which the symmetry acts */
2215 int nrows, /**< number of rows */
2216 int ncols, /**< number of columns */
2217 SCIP_Bool* success /**< to store whether the component is successfully added */
2218 )
2219{
2220 assert( scip != NULL );
2221 assert( orbireddata != NULL );
2222 assert( vars != NULL );
2223 assert( nrows > 0 );
2224 assert( ncols > 0 );
2225
2226 /* dynamic symmetry reductions cannot be performed on original problem */
2227 assert( SCIPisTransformed(scip) );
2228
2229 /* if this is the first time adding an orbitope, check if the nonlinear conshlr exists */
2230 if ( !orbireddata->conshdlr_nonlinear_checked )
2231 {
2232 orbireddata->conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
2233 orbireddata->conshdlr_nonlinear_checked = TRUE;
2234 }
2235
2236 /* create orbitope data */
2237 SCIP_CALL( addOrbitope(scip, orbireddata, rowordering, colordering, vars, nrows, ncols, success) );
2238
2239 return SCIP_OKAY;
2240}
2241
2242
2243/** resets orbitopal reduction data structure (clears all orbitopes) */
2245 SCIP* scip, /**< SCIP data structure */
2246 SCIP_ORBITOPALREDDATA* orbireddata /**< pointer to orbitopal reduction structure to populate */
2247 )
2248{
2249 assert( scip != NULL );
2250 assert( orbireddata != NULL );
2251 assert( orbireddata->norbitopes >= 0 );
2252 assert( (orbireddata->norbitopes == 0) == (orbireddata->orbitopes == NULL) );
2253 assert( orbireddata->norbitopes <= orbireddata->maxnorbitopes );
2254 assert( orbireddata->eventhdlr != NULL );
2255
2256 /* free orbitopes that are added */
2257 while (orbireddata->norbitopes > 0)
2258 {
2259 SCIP_CALL( freeOrbitope(scip, orbireddata, &(orbireddata->orbitopes[--orbireddata->norbitopes])) );
2260 }
2261 assert( orbireddata->norbitopes == 0 );
2262 SCIPfreeBlockMemoryArrayNull(scip, &orbireddata->orbitopes, orbireddata->maxnorbitopes);
2263 orbireddata->orbitopes = NULL;
2264 orbireddata->maxnorbitopes = 0;
2265
2266 return SCIP_OKAY;
2267}
2268
2269
2270/** frees orbitopal reduction data */
2272 SCIP* scip, /**< SCIP data structure */
2273 SCIP_ORBITOPALREDDATA** orbireddata /**< pointer to orbitopal reduction structure to populate */
2274 )
2275{
2276 assert( scip != NULL );
2277 assert( orbireddata != NULL );
2278 assert( *orbireddata != NULL );
2279
2280 SCIP_CALL( SCIPorbitopalReductionReset(scip, *orbireddata) );
2281
2282 SCIPfreeBlockMemory(scip, orbireddata);
2283 return SCIP_OKAY;
2284}
2285
2286
2287/** initializes structures needed for orbitopal reduction
2288 *
2289 * This is only done exactly once.
2290 */
2292 SCIP* scip, /**< SCIP data structure */
2293 SCIP_ORBITOPALREDDATA** orbireddata /**< pointer to orbitopal reduction structure to populate */
2294 )
2295{
2296 SCIP_EVENTHDLR* eventhdlr;
2297
2298 assert( scip != NULL );
2299 assert( orbireddata != NULL );
2300
2301 SCIP_CALL( SCIPcheckStage(scip, "SCIPincludeOrbitopalReduction", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
2303
2304 /* create orbitope handler data */
2305 SCIP_CALL( SCIPallocBlockMemory(scip, orbireddata) );
2306
2307 /* default column ordering param */
2308 SCIP_CALL( SCIPaddIntParam(scip, "propagating/symmetry/" SYMHDLR_NAME "/columnordering",
2309 "The column ordering variant, respects enum SCIP_ColumnOrdering.",
2310 (int*) &(*orbireddata)->defaultcolumnordering, TRUE, DEFAULT_COLUMNORDERING, 0, 4,
2311 NULL, NULL) ); /*lint !e641*/
2312
2313 /* initialize event handler. */
2316 assert( eventhdlr != NULL );
2317 (*orbireddata)->eventhdlr = eventhdlr;
2318
2319 /* orbitopes array */
2320 (*orbireddata)->orbitopes = NULL;
2321 (*orbireddata)->norbitopes = 0;
2322 (*orbireddata)->maxnorbitopes = 0;
2323
2324 /* conshdlr nonlinear */
2325 (*orbireddata)->conshdlr_nonlinear = NULL;
2326 (*orbireddata)->conshdlr_nonlinear_checked = FALSE;
2327
2328 /* counter of total number of reductions and cutoffs */
2329 (*orbireddata)->nred = 0;
2330 (*orbireddata)->ncutoff = 0;
2331
2332 return SCIP_OKAY;
2333}
2334
2335
2336/** returns the default column ordering */
2338 SCIP_ORBITOPALREDDATA* orbireddata /**< pointer to orbitopal reduction structure to populate */
2339 )
2340{
2341 assert( orbireddata != NULL );
2342
2343 return orbireddata->defaultcolumnordering;
2344}
SCIP_Real * r
Definition: circlepacking.c:59
methods for debugging
#define SCIPcheckStage(scip, method, init, problem, transforming, transformed, initpresolve, presolving, exitpresolve, presolved, initsolve, solving, solved, exitsolve, freetrans, freescip)
Definition: debug.h:364
#define NULL
Definition: def.h:248
#define SCIP_Longint
Definition: def.h:141
#define SCIP_Bool
Definition: def.h:91
#define MIN(x, y)
Definition: def.h:224
#define SCIP_Real
Definition: def.h:156
#define ABS(x)
Definition: def.h:216
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define MAX(x, y)
Definition: def.h:220
#define SCIP_CALL(x)
Definition: def.h:355
SCIP_Bool SCIPisTransformed(SCIP *scip)
Definition: scip_general.c:647
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:444
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition: misc.c:3095
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3304
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:3061
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3466
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition: misc.c:3179
void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
Definition: misc.c:2348
int SCIPhashtableGetNEntries(SCIP_HASHTABLE *hashtable)
Definition: misc.c:2765
SCIP_RETCODE SCIPhashtableSafeInsert(SCIP_HASHTABLE *hashtable, void *element)
Definition: misc.c:2567
void * SCIPhashtableGetEntry(SCIP_HASHTABLE *hashtable, int entryidx)
Definition: misc.c:2773
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:2298
void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
Definition: misc.c:2596
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:225
#define SCIPdebugMsg
Definition: scip_message.h:78
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_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:940
int SCIPconshdlrGetNActiveConss(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4812
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
SCIP_EVENTHDLR * SCIPfindEventhdlr(SCIP *scip, const char *name)
Definition: scip_event.c:241
SCIP_EVENTTYPE SCIPeventGetType(SCIP_EVENT *event)
Definition: event.c:1194
SCIP_RETCODE SCIPcatchEvent(SCIP *scip, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int *filterpos)
Definition: scip_event.c:293
SCIP_NODE * SCIPeventGetNode(SCIP_EVENT *event)
Definition: event.c:1530
SCIP_RETCODE SCIPdropEvent(SCIP *scip, SCIP_EVENTTYPE eventtype, SCIP_EVENTHDLR *eventhdlr, SCIP_EVENTDATA *eventdata, int filterpos)
Definition: scip_event.c:333
#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 SCIPreallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:128
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:136
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:93
#define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
Definition: scip_mem.h:99
#define SCIPfreeBlockMemory(scip, ptr)
Definition: scip_mem.h:108
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition: scip_mem.h:111
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition: scip_mem.h:137
#define SCIPallocBlockMemory(scip, ptr)
Definition: scip_mem.h:89
void SCIPnodeGetNDomchg(SCIP_NODE *node, int *nbranchings, int *nconsprop, int *nprop)
Definition: tree.c:8598
SCIP_DOMCHG * SCIPnodeGetDomchg(SCIP_NODE *node)
Definition: tree.c:8588
SCIP_Longint SCIPnodeGetNumber(SCIP_NODE *node)
Definition: tree.c:8483
SCIP_NODE * SCIPnodeGetParent(SCIP_NODE *node)
Definition: tree.c:8782
int SCIPnodeGetDepth(SCIP_NODE *node)
Definition: tree.c:8493
SCIP_Bool SCIPinProbing(SCIP *scip)
Definition: scip_probing.c:98
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_VARTYPE SCIPgetSymInferredVarType(SCIP_VAR *var)
Definition: symmetry.c:45
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_Bool SCIPisIntegral(SCIP *scip, SCIP_Real val)
SCIP_RETCODE SCIPgetChildren(SCIP *scip, SCIP_NODE ***children, int *nchildren)
Definition: scip_tree.c:164
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_VAR * SCIPboundchgGetVar(SCIP_BOUNDCHG *boundchg)
Definition: var.c:23174
SCIP_BOUNDCHG * SCIPdomchgGetBoundchg(SCIP_DOMCHG *domchg, int pos)
Definition: var.c:23222
SCIP_BOUNDCHGTYPE SCIPboundchgGetBoundchgtype(SCIP_BOUNDCHG *boundchg)
Definition: var.c:23184
SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
Definition: var.c:24268
SCIP_Bool SCIPvarIsTransformed(SCIP_VAR *var)
Definition: var.c:23430
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
int SCIPdomchgGetNBoundchgs(SCIP_DOMCHG *domchg)
Definition: var.c:23214
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:23267
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1887
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition: var.c:23490
SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
Definition: var.c:24234
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
SCIP_Bool SCIPallowStrongDualReds(SCIP *scip)
Definition: scip_var.c:10984
void SCIPsortInd(int *indarray, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
memory allocation routines
public methods for managing constraints
public methods for message output
#define SCIPerrorMessage
Definition: pub_message.h:64
#define SCIPdebugMessage
Definition: pub_message.h:96
#define SCIPdebugPrintf
Definition: pub_message.h:99
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_Longint nodenumber
COLSWAP * colswaps
SCIP_HASHMAP * rowindexmap
SCIP_Longint lastnodenumber
SCIP_HASHTABLE * nodeinfos
SCIP_ROWORDERING rowordering
SCIP_HASHMAP * colindexmap
SCIP_VAR ** vars
SCIP_COLUMNORDERING columnordering
SCIP_Longint number
Definition: struct_tree.h:143
char * name
Definition: struct_var.h:291
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 propagateStaticOrbitope(SCIP *scip, ORBITOPEDATA *orbidata, int *roworder, int nselrows, int *colorder, SCIP_Bool *infeasible, int *nfixedvars)
static SCIP_RETCODE getRowOrder(SCIP *scip, ORBITOPEDATA *orbidata, SCIP_NODE *node, int **roworder, int *nselrows)
static SCIP_DECL_EVENTEXEC(eventExecNodeBranched)
static SCIP_RETCODE populateRootedPathColumnOrder(ORBITOPEDATA *orbidata, SCIP_NODE *node, SCIP_NODE **rootedpath, int *colorder, int *colorderinv)
static SCIP_RETCODE freeOrbitope(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, ORBITOPEDATA **orbidata)
static int debugGetArrayHash(int *array, int len)
static void freeColumnOrder(SCIP *scip, ORBITOPEDATA *orbidata, int **colorder, int **colorderinv)
SCIP_RETCODE SCIPincludeOrbitopalReduction(SCIP *scip, SCIP_ORBITOPALREDDATA **orbireddata)
static SCIP_DECL_HASHGETKEY(hashGetKeyBnbnodeinfo)
SCIP_RETCODE SCIPorbitopalReductionFree(SCIP *scip, SCIP_ORBITOPALREDDATA **orbireddata)
SCIP_RETCODE SCIPorbitopalReductionReset(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata)
static SCIP_RETCODE propagateOrbitope(SCIP *scip, ORBITOPEDATA *orbidata, SCIP_Bool *infeasible, int *nfixedvars)
#define SYMHDLR_NAME
SCIP_RETCODE SCIPorbitopalReductionAddOrbitope(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, SCIP_ROWORDERING rowordering, SCIP_COLUMNORDERING colordering, SCIP_VAR **vars, int nrows, int ncols, SCIP_Bool *success)
static SCIP_Bool rowIsBranchRow(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, ORBITOPEDATA *orbidata, int rowid)
SCIP_RETCODE SCIPorbitopalReductionPropagate(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, SCIP_Bool *infeasible, int *nred, SCIP_Bool *didrun)
static void freeRowOrder(SCIP *scip, ORBITOPEDATA *orbidata, int **roworder)
SCIP_RETCODE SCIPorbitopalReductionGetStatistics(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, int *nred, int *ncutoff)
static SCIP_RETCODE updateColumnOrderWhenBranchingOnColumn(SCIP *scip, ORBITOPEDATA *orbidata, int *colorder, int *colorderinv, SCIP_VAR *var, COLSWAP *thiscolswap)
static SCIP_DECL_HASHKEYVAL(hashKeyValBnbnodeinfo)
static SCIP_RETCODE addOrbitope(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata, SCIP_ROWORDERING rowordering, SCIP_COLUMNORDERING colordering, SCIP_VAR **vars, int nrows, int ncols, SCIP_Bool *success)
SCIP_RETCODE SCIPorbitopalReductionPrintStatistics(SCIP *scip, SCIP_ORBITOPALREDDATA *orbireddata)
static SCIP_Bool testColumnsAreSymmetricallyEquivalent(SCIP *scip, ORBITOPEDATA *orbidata, int col1, int col2)
static SCIP_RETCODE getColumnOrder(SCIP *scip, ORBITOPEDATA *orbidata, SCIP_NODE *eventnode, int **colorder, int **colorderinv)
#define EVENTHDLR_DESC
static SCIP_DECL_HASHKEYEQ(hashKeyEqBnbnodeinfo)
SCIP_COLUMNORDERING SCIPorbitopalReductionGetDefaultColumnOrdering(SCIP_ORBITOPALREDDATA *orbireddata)
static SCIP_Bool vartypeIsBranchRowType(SCIP_ORBITOPALREDDATA *orbireddata, SCIP_VAR *var)
#define EVENTHDLR_NAME
static int getArrayEntryOrIndex(int *arr, int idx)
#define DEFAULT_COLUMNORDERING
static void assertIsOrbitopeMatrix(SCIP *scip, ORBITOPEDATA *orbidata, int *roworder, int *colorder, SCIP_Real *matrix, int nrows, int ncols, int *infinitesimal, SCIP_Bool addinfinitesimals)
@ SCIP_COLUMNORDERING_CENTRE
@ SCIP_COLUMNORDERING_FIRST
@ SCIP_COLUMNORDERING_NONE
@ SCIP_COLUMNORDERING_LAST
@ SCIP_COLUMNORDERING_MEDIAN
enum SCIP_ColumnOrdering SCIP_COLUMNORDERING
@ SCIP_ROWORDERING_BRANCHING
@ SCIP_ROWORDERING_NONE
struct SCIP_OrbitopalReductionData SCIP_ORBITOPALREDDATA
enum SCIP_RowOrdering SCIP_ROWORDERING
struct SCIP_EventData SCIP_EVENTDATA
Definition: type_event.h:179
#define SCIP_EVENTTYPE_NODEBRANCHED
Definition: type_event.h:96
@ SCIP_VERBLEVEL_HIGH
Definition: type_message.h:61
@ SCIP_OKAY
Definition: type_retcode.h:42
@ SCIP_ERROR
Definition: type_retcode.h:43
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63
@ SCIP_STAGE_SOLVING
Definition: type_set.h:53
type definitions for symmetry computations
type definitions for problem variables
@ SCIP_VARTYPE_INTEGER
Definition: type_var.h:65
@ SCIP_VARTYPE_BINARY
Definition: type_var.h:64
@ SCIP_BOUNDCHGTYPE_BRANCHING
Definition: type_var.h:131