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