Scippy

    SCIP

    Solving Constraint Integer Programs

    symmetry_graph.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_graph.c
    26 * @ingroup OTHER_CFILES
    27 * @brief methods for dealing with symmetry detection graphs
    28 * @author Christopher Hojny
    29 */
    30
    31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    32
    33#include "scip/symmetry.h"
    34#include "scip/symmetry_graph.h"
    35#include "scip/scip.h"
    36#include "scip/misc.h"
    39
    40
    41
    42/** creates and initializes a symmetry detection graph with memory for the given number of nodes and edges
    43 *
    44 * @note at some point, the graph needs to be freed!
    45 */
    47 SCIP* scip, /**< SCIP data structure */
    48 SYM_SYMTYPE symtype, /**< type of symmetries encoded in graph */
    49 SYM_GRAPH** graph, /**< pointer to hold symmetry detection graph */
    50 SCIP_VAR** symvars, /**< variables used in symmetry detection */
    51 int nsymvars, /**< number of variables used in symmetry detection */
    52 int nopnodes, /**< number of operator nodes */
    53 int nvalnodes, /**< number of value nodes */
    54 int nconsnodes, /**< number of constraint nodes */
    55 int nedges /**< number of edges */
    56 )
    57{
    58 int nnodes;
    59
    60 assert(scip != NULL);
    61 assert(graph != NULL);
    62 assert(symvars != NULL);
    63 assert(nopnodes >= 0);
    64 assert(nvalnodes >= 0);
    65 assert(nconsnodes >= 0);
    66 assert(nedges >= 0);
    67
    68 nnodes = nopnodes + nvalnodes + nconsnodes;
    69
    71 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->nodetypes, nnodes) );
    72 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->nodeinfopos, nnodes) );
    73 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->ops, nopnodes) );
    74 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->vals, nvalnodes) );
    75 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->conss, nconsnodes) );
    76 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->lhs, nconsnodes) );
    77 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->rhs, nconsnodes) );
    78 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->edgefirst, nedges) );
    79 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->edgesecond, nedges) );
    80 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(*graph)->edgevals, nedges) );
    81 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &(*graph)->isfixedvar, nsymvars) );
    82
    83 (*graph)->nnodes = 0;
    84 (*graph)->maxnnodes = nnodes;
    85 (*graph)->nopnodes = 0;
    86 (*graph)->maxnopnodes = nopnodes;
    87 (*graph)->nvalnodes = 0;
    88 (*graph)->maxnvalnodes = nvalnodes;
    89 (*graph)->nconsnodes = 0;
    90 (*graph)->maxnconsnodes = nconsnodes;
    91 (*graph)->islocked = FALSE;
    92 (*graph)->nedges = 0;
    93 (*graph)->maxnedges = nedges;
    94 (*graph)->symvars = symvars;
    95 (*graph)->nsymvars = nsymvars;
    96 (*graph)->nvarcolors = -1;
    97 (*graph)->uniqueedgetype = FALSE;
    98 (*graph)->symtype = symtype;
    99 (*graph)->infinity = SCIPinfinity(scip);
    100 (*graph)->consnodeperm = NULL;
    101
    102 /* to avoid reallocation, allocate memory for colors later */
    103 (*graph)->varcolors = NULL;
    104 (*graph)->opcolors = NULL;
    105 (*graph)->valcolors = NULL;
    106 (*graph)->conscolors = NULL;
    107 (*graph)->edgecolors = NULL;
    108
    109 return SCIP_OKAY;
    110}
    111
    112/** frees a symmetry detection graph */
    114 SCIP* scip, /**< SCIP data structure */
    115 SYM_GRAPH** graph /**< pointer to symmetry detection graph */
    116 )
    117{
    118 assert(scip != NULL);
    119 assert(graph != NULL);
    120
    121 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->edgecolors, (*graph)->nedges);
    122 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->conscolors, (*graph)->nconsnodes);
    123 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->valcolors, (*graph)->nvalnodes);
    124 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->opcolors, (*graph)->nopnodes);
    125 switch( (*graph)->symtype )
    126 {
    127 case SYM_SYMTYPE_PERM:
    128 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->varcolors, (*graph)->nsymvars);
    129 break;
    130 default:
    131 assert((*graph)->symtype == SYM_SYMTYPE_SIGNPERM);
    132 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->varcolors, 2 * (*graph)->nsymvars);
    133 } /*lint !e788*/
    134
    135 SCIPfreeBlockMemoryArrayNull(scip, &(*graph)->consnodeperm, (*graph)->nconsnodes);
    136 SCIPfreeBlockMemoryArray(scip, &(*graph)->isfixedvar, (*graph)->nsymvars);
    137 SCIPfreeBlockMemoryArray(scip, &(*graph)->edgevals, (*graph)->maxnedges);
    138 SCIPfreeBlockMemoryArray(scip, &(*graph)->edgesecond, (*graph)->maxnedges);
    139 SCIPfreeBlockMemoryArray(scip, &(*graph)->edgefirst, (*graph)->maxnedges);
    140 SCIPfreeBlockMemoryArray(scip, &(*graph)->rhs, (*graph)->maxnconsnodes);
    141 SCIPfreeBlockMemoryArray(scip, &(*graph)->lhs, (*graph)->maxnconsnodes);
    142 SCIPfreeBlockMemoryArray(scip, &(*graph)->conss, (*graph)->maxnconsnodes);
    143 SCIPfreeBlockMemoryArray(scip, &(*graph)->vals, (*graph)->maxnvalnodes);
    144 SCIPfreeBlockMemoryArray(scip, &(*graph)->ops, (*graph)->maxnopnodes);
    145 SCIPfreeBlockMemoryArray(scip, &(*graph)->nodeinfopos, (*graph)->maxnnodes);
    146 SCIPfreeBlockMemoryArray(scip, &(*graph)->nodetypes, (*graph)->maxnnodes);
    148
    149 return SCIP_OKAY;
    150}
    151
    152/** clears data of symmetry detection graph */
    154 SCIP* scip, /**< SCIP data structure */
    155 SYM_GRAPH* graph, /**< symmetry detection graph */
    156 SCIP_VAR** symvars, /**< variables used in symmetry detection */
    157 int nsymvars, /**< number of variables used in symmetry detection */
    158 SYM_SYMTYPE symtype /**< type of symmetries encoded in graph */
    159 )
    160{
    161 assert(scip != NULL);
    162 assert(graph != NULL);
    163
    164 graph->nnodes = 0;
    165 graph->nopnodes = 0;
    166 graph->nvalnodes = 0;
    167 graph->nconsnodes = 0;
    168 graph->islocked = FALSE;
    169 graph->nedges = 0;
    170 graph->symvars = symvars;
    171 graph->nsymvars = nsymvars;
    172 graph->nvarcolors = -1;
    173 graph->uniqueedgetype = FALSE;
    174 graph->symtype = symtype;
    175 graph->infinity = SCIPinfinity(scip);
    177 graph->consnodeperm = NULL;
    178
    183 switch( graph->symtype )
    184 {
    185 case SYM_SYMTYPE_PERM:
    187 break;
    188 default:
    189 assert(graph->symtype == SYM_SYMTYPE_SIGNPERM);
    191 } /*lint !e788*/
    192
    193 graph->opcolors = NULL;
    194 graph->valcolors = NULL;
    195 graph->conscolors = NULL;
    196 graph->edgecolors = NULL;
    197 graph->varcolors = NULL;
    198
    199 return SCIP_OKAY;
    200}
    201
    202/** copies an existing graph and changes variable nodes according to a permutation */
    204 SCIP* scip, /**< SCIP data structure */
    205 SYM_GRAPH** graph, /**< pointer to hold copy of symmetry detection graph */
    206 SYM_GRAPH* origgraph, /**< graph to be copied */
    207 int* perm, /**< permutation of variables */
    208 SYM_SPEC fixedtype /**< variable types that must be fixed by symmetries */
    209 )
    210{ /*lint --e{788}*/
    211 SYM_NODETYPE nodetype;
    212 int* nodeinfopos;
    213 int nnodes;
    214 int first;
    215 int second;
    216 int nodeidx;
    217 int i;
    218
    219 assert(scip != NULL);
    220 assert(graph != NULL);
    221 assert(origgraph != NULL);
    222 assert(perm != NULL);
    223
    224 SCIP_CALL( SCIPcreateSymgraph(scip, origgraph->symtype, graph, origgraph->symvars, origgraph->nsymvars,
    225 origgraph->nopnodes, origgraph->nvalnodes, origgraph->nconsnodes, origgraph->nedges) );
    226
    227 /* copy nodes */
    228 nnodes = origgraph->nnodes;
    229 nodeinfopos = origgraph->nodeinfopos;
    230 for( i = 0; i < nnodes; ++i )
    231 {
    232 nodetype = origgraph->nodetypes[i];
    233
    234 switch( nodetype )
    235 {
    237 SCIP_CALL( SCIPaddSymgraphOpnode(scip, *graph, origgraph->ops[nodeinfopos[i]], &nodeidx) );
    238 break;
    239 case SYM_NODETYPE_VAL:
    240 SCIP_CALL( SCIPaddSymgraphValnode(scip, *graph, origgraph->vals[nodeinfopos[i]], &nodeidx) );
    241 break;
    242 default:
    243 assert(nodetype == SYM_NODETYPE_CONS);
    244 SCIP_CALL( SCIPaddSymgraphConsnode(scip, *graph, origgraph->conss[nodeinfopos[i]],
    245 origgraph->lhs[nodeinfopos[i]], origgraph->rhs[nodeinfopos[i]], &nodeidx) );
    246 }
    247 assert(0 <= nodeidx && nodeidx < nnodes);
    248 }
    249
    250 /* copy edges */
    251 for( i = 0; i < origgraph->nedges; ++i )
    252 {
    253 first = SCIPgetSymgraphEdgeFirst(origgraph, i);
    254 second = SCIPgetSymgraphEdgeSecond(origgraph, i);
    255
    256 /* possibly adapt edges due to permutation of variables */
    257 if( first < 0 )
    258 first = -perm[-first - 1] - 1;
    259 if( second < 0 )
    260 second = -perm[-second - 1] - 1;
    261
    262 SCIP_CALL( SCIPaddSymgraphEdge(scip, *graph, first, second,
    263 ! SCIPisInfinity(scip, origgraph->edgevals[i]), origgraph->edgevals[i]) );
    264 }
    265
    266 SCIP_CALL( SCIPcomputeSymgraphColors(scip, *graph, fixedtype) );
    267
    268 return SCIP_OKAY;
    269}
    270
    271/** copies a symmetry detection graph into another symmetry detection graph */
    273 SCIP* scip, /**< SCIP pointer */
    274 SYM_GRAPH* sourcegraph, /**< graph to be copied */
    275 SYM_GRAPH* targetgraph, /**< graph into which copy shall be included */
    276 SCIP_CONS* sourcecons, /**< constraint associated with sourcegraph */
    277 int* rootidx /**< pointer to hold index of root node of sourcegraph in targetgraph
    278 * (or -1 if root cannot be detected) */
    279 )
    280{ /*lint --e{788}*/
    281 SYM_NODETYPE nodetype;
    282 int* nodeinfopos;
    283 int* nodemap;
    284 int nnodes;
    285 int first;
    286 int second;
    287 int nodeidx;
    288 int i;
    289
    290 assert(scip != NULL);
    291 assert(sourcegraph != NULL);
    292 assert(targetgraph != NULL);
    293 assert(sourcecons != NULL);
    294 assert(rootidx != NULL);
    295
    296 *rootidx = -1;
    297
    298 /* copy nodes */
    299 nnodes = sourcegraph->nnodes;
    300 nodeinfopos = sourcegraph->nodeinfopos;
    301
    302 /* prepare map of node index from sourcegraph to indices in copied graph */
    304
    305 for( i = 0; i < nnodes; ++i )
    306 {
    307 nodetype = sourcegraph->nodetypes[i];
    308
    309 switch( nodetype )
    310 {
    312 SCIP_CALL( SCIPaddSymgraphOpnode(scip, targetgraph, sourcegraph->ops[nodeinfopos[i]], &nodeidx) );
    313 break;
    314 case SYM_NODETYPE_VAL:
    315 SCIP_CALL( SCIPaddSymgraphValnode(scip, targetgraph, sourcegraph->vals[nodeinfopos[i]], &nodeidx) );
    316 break;
    317 default:
    318 assert(nodetype == SYM_NODETYPE_CONS);
    319 SCIP_CALL( SCIPaddSymgraphConsnode(scip, targetgraph, sourcegraph->conss[nodeinfopos[i]],
    320 sourcegraph->lhs[nodeinfopos[i]], sourcegraph->rhs[nodeinfopos[i]], &nodeidx) );
    321
    322 if( sourcegraph->conss[nodeinfopos[i]] == sourcecons )
    323 {
    324 /* store the root node of sourcegraph; if there are multiple nodes that could qualify
    325 * as root, stop; we cannot identify root node unambiguously
    326 */
    327 if( *rootidx == -1 )
    328 *rootidx = nodeidx;
    329 else
    330 {
    331 *rootidx = -1;
    332 SCIPfreeBufferArray(scip, &nodemap);
    333 return SCIP_OKAY;
    334 }
    335 }
    336 }
    337 assert(0 <= nodeidx && nodeidx < targetgraph->nnodes);
    338
    339 nodemap[i] = nodeidx;
    340 }
    341
    342 /* terminate early if root node could not be detected */
    343 if( *rootidx == -1 )
    344 {
    345 SCIPfreeBufferArray(scip, &nodemap);
    346 return SCIP_OKAY;
    347 }
    348
    349 /* copy edges */
    350 for( i = 0; i < sourcegraph->nedges; ++i )
    351 {
    352 first = SCIPgetSymgraphEdgeFirst(sourcegraph, i);
    353 second = SCIPgetSymgraphEdgeSecond(sourcegraph, i);
    354
    355 /* adapt indices from source graph to target graph in case of non-variable nodes */
    356 if( first >= 0 )
    357 first = nodemap[first];
    358 if( second >= 0 )
    359 second = nodemap[second];
    360
    361 SCIP_CALL( SCIPaddSymgraphEdge(scip, targetgraph, first, second,
    362 !SCIPisInfinity(scip, sourcegraph->edgevals[i]), sourcegraph->edgevals[i]) );
    363 }
    364 SCIPfreeBufferArray(scip, &nodemap);
    365
    366 return SCIP_OKAY;
    367}
    368
    369/** adds a symmetry detection graph for a linear constraint to existing graph
    370 *
    371 * For permutation symmetries, a constraint node is added that is connected to all
    372 * variable nodes in the constraint. Edges are colored according to the variable coefficients.
    373 * For signed permutation symmetries, also edges connecting the constraint node and
    374 * the negated variable nodes are added, these edges are colored by the negative coefficients.
    375 */
    377 SCIP* scip, /**< SCIP data structure */
    378 SYM_GRAPH* graph, /**< symmetry detection graph */
    379 SCIP_VAR** vars, /**< variable array of linear constraint */
    380 SCIP_Real* vals, /**< coefficients of linear constraint */
    381 int nvars, /**< number of variables in linear constraint */
    382 SCIP_CONS* cons, /**< constraint for which we encode symmetries */
    383 SCIP_Real lhs, /**< left-hand side of constraint */
    384 SCIP_Real rhs, /**< right-hand side of constraint */
    385 SCIP_Bool* success /**< pointer to store whether graph could be built */
    386 )
    387{
    388 int rhsnodeidx;
    389 int varnodeidx;
    390 int i;
    391
    392 assert(scip != NULL);
    393 assert(graph != NULL);
    394 assert(vars != NULL);
    395 assert(vals != NULL);
    396 assert(nvars >= 0);
    397 assert(cons != NULL);
    398 assert(success != NULL);
    399 assert(! graph->islocked);
    400
    401 *success = TRUE;
    402
    403#ifndef NDEBUG
    404 /* check whether variable nodes exist in the graph */
    405 for( i = 0; i < nvars; ++i )
    406 {
    407 assert(0 <= SCIPvarGetProbindex(vars[i]) && SCIPvarGetProbindex(vars[i]) < graph->nsymvars);
    408 }
    409#endif
    410
    411 /* create node corresponding to right-hand side */
    412 SCIP_CALL( SCIPaddSymgraphConsnode(scip, graph, cons, lhs, rhs, &rhsnodeidx) );
    413
    414 /* create edges */
    415 for( i = 0; i < nvars; ++i )
    416 {
    418 {
    419 varnodeidx = SCIPgetSymgraphNegatedVarnodeidx(scip, graph, vars[i]);
    420 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rhsnodeidx, varnodeidx, TRUE, -vals[i]) );
    421
    422 varnodeidx = SCIPgetSymgraphVarnodeidx(scip, graph, vars[i]);
    423 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rhsnodeidx, varnodeidx, TRUE, vals[i]) );
    424 }
    425 else
    426 {
    428
    429 varnodeidx = SCIPgetSymgraphVarnodeidx(scip, graph, vars[i]);
    430 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rhsnodeidx, varnodeidx, TRUE, vals[i]) );
    431 }
    432 }
    433
    434 return SCIP_OKAY;
    435}
    436
    437/** adds nodes and edges corresponding to the aggregation of a variable to a symmetry detection graph
    438 *
    439 * For permutation symmetries, the root node is connected with all variable nodes in the aggregation.
    440 * Edges are colored according to the variable coefficients.
    441 * For signed permutation symmetries, also edges connecting the root node and the negated variable
    442 * nodes are added, these edges are colored by the negative coefficients.
    443 * If the variable is fixed, a node representing the constant value is added.
    444 */
    446 SCIP* scip, /**< SCIP data structure */
    447 SYM_GRAPH* graph, /**< symmetry detection graph */
    448 int rootidx, /**< index of root node of the aggegration */
    449 SCIP_VAR** vars, /**< array of variables in aggregation */
    450 SCIP_Real* vals, /**< coefficients of variables */
    451 int nvars, /**< number of variables in aggregation */
    452 SCIP_Real constant /**< constant of aggregation */
    453 )
    454{
    455 int nodeidx;
    456 int j;
    457
    458 assert(scip != NULL);
    459 assert(graph != NULL);
    460 assert(rootidx >= 0);
    461 assert(vars != NULL);
    462 assert(vals != NULL);
    463 assert(nvars >= 0);
    464
    465#ifndef NDEBUG
    466 /* check whether variable nodes exist in the graph */
    467 for( j = 0; j < nvars; ++j )
    468 {
    469 assert(0 <= SCIPvarGetProbindex(vars[j]) && SCIPvarGetProbindex(vars[j]) < graph->nsymvars);
    470 }
    471#endif
    472
    473 /* add edges incident to variables in aggregation */
    474 for( j = 0; j < nvars; ++j )
    475 {
    477 {
    478 nodeidx = SCIPgetSymgraphNegatedVarnodeidx(scip, graph, vars[j]);
    479 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rootidx, nodeidx, TRUE, -vals[j]) );
    480
    481 nodeidx = SCIPgetSymgraphVarnodeidx(scip, graph, vars[j]);
    482 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rootidx, nodeidx, TRUE, vals[j]) );
    483 }
    484 else
    485 {
    487
    488 nodeidx = SCIPgetSymgraphVarnodeidx(scip, graph, vars[j]);
    489 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rootidx, nodeidx, TRUE, vals[j]) );
    490 }
    491 }
    492
    493 /* possibly add node for constant */
    494 if( nvars == 0 || !SCIPisZero(scip, constant) )
    495 {
    496 SCIP_CALL( SCIPaddSymgraphValnode(scip, graph, constant, &nodeidx) );
    497 SCIP_CALL( SCIPaddSymgraphEdge(scip, graph, rootidx, nodeidx, FALSE, 0.0) );
    498 }
    499
    500 return SCIP_OKAY;
    501}
    502
    503/*
    504 * methods for adding and accessing nodes
    505 */
    506
    507/** ensures that the node-based arrays in symmetry graph are sufficiently long */
    508static
    510 SCIP* scip, /**< SCIP data structure */
    511 SYM_GRAPH* graph, /**< symmetry detection graph */
    512 int addsize /**< required additional size of node-based arrays */
    513 )
    514{
    515 assert(scip != NULL);
    516 assert(graph != NULL);
    517 assert(addsize > 0);
    518
    519 if( graph->nnodes + addsize > graph->maxnnodes )
    520 {
    521 int newsize;
    522
    523 newsize = SCIPcalcMemGrowSize(scip, graph->nnodes + addsize);
    524 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->nodetypes, graph->maxnnodes, newsize) );
    525 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->nodeinfopos, graph->maxnnodes, newsize) );
    526 graph->maxnnodes = newsize;
    527 }
    528
    529 return SCIP_OKAY;
    530}
    531
    532/** adds an operator node to a symmetry detection graph and returns its node index */
    534 SCIP* scip, /**< SCIP data structure */
    535 SYM_GRAPH* graph, /**< symmetry detection graph */
    536 int op, /**< int associated with operator of node */
    537 int* nodeidx /**< pointer to hold index of created node */
    538 )
    539{
    540 assert(scip != NULL);
    541 assert(graph != NULL);
    542 assert(nodeidx != NULL);
    543
    544 /* we can only add nodes if symmetry colors have not been computed yet */
    545 if( graph->islocked )
    546 {
    547 SCIPerrorMessage("Cannot add nodes to a graph for which colors have already been computed.\n");
    548 return SCIP_ERROR;
    549 }
    550
    551 SCIP_CALL( ensureNodeArraysSize(scip, graph, 1) );
    552
    553 if( graph->nopnodes >= graph->maxnopnodes )
    554 {
    555 int newsize;
    556
    557 newsize = SCIPcalcMemGrowSize(scip, graph->nopnodes + 1);
    558 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->ops, graph->maxnopnodes, newsize) );
    559 graph->maxnopnodes = newsize;
    560 }
    561
    562 graph->nodetypes[graph->nnodes] = SYM_NODETYPE_OPERATOR;
    563 graph->nodeinfopos[graph->nnodes] = graph->nopnodes;
    564 graph->ops[graph->nopnodes] = op;
    565
    566 *nodeidx = graph->nnodes;
    567 ++graph->nnodes;
    568 ++graph->nopnodes;
    569
    570 return SCIP_OKAY;
    571}
    572
    573/** adds a value node to a symmetry detection graph and returns its node index */
    575 SCIP* scip, /**< SCIP data structure */
    576 SYM_GRAPH* graph, /**< symmetry detection graph */
    577 SCIP_Real val, /**< value of node */
    578 int* nodeidx /**< pointer to hold index of created node */
    579 )
    580{
    581 assert(scip != NULL);
    582 assert(graph != NULL);
    583 assert(nodeidx != NULL);
    584
    585 /* we can only add nodes if symmetry colors have not been computed yet */
    586 if( graph->islocked )
    587 {
    588 SCIPerrorMessage("Cannot add nodes to a graph for which colors have already been computed.\n");
    589 return SCIP_ERROR;
    590 }
    591
    592 SCIP_CALL( ensureNodeArraysSize(scip, graph, 1) );
    593
    594 if( graph->nvalnodes >= graph->maxnvalnodes )
    595 {
    596 int newsize;
    597
    598 newsize = SCIPcalcMemGrowSize(scip, graph->nvalnodes + 1);
    599 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->vals, graph->maxnvalnodes, newsize) );
    600 graph->maxnvalnodes = newsize;
    601 }
    602
    603 graph->nodetypes[graph->nnodes] = SYM_NODETYPE_VAL;
    604 graph->nodeinfopos[graph->nnodes] = graph->nvalnodes;
    605 graph->vals[graph->nvalnodes] = val;
    606
    607 *nodeidx = graph->nnodes;
    608 ++graph->nnodes;
    609 ++graph->nvalnodes;
    610
    611 return SCIP_OKAY;
    612}
    613
    614/** adds a constraint node to a symmetry detection graph and returns its node index */
    616 SCIP* scip, /**< SCIP data structure */
    617 SYM_GRAPH* graph, /**< symmetry detection graph */
    618 SCIP_CONS* cons, /**< constraint of node */
    619 SCIP_Real lhs, /**< left-hand side of node */
    620 SCIP_Real rhs, /**< right-hand side of node */
    621 int* nodeidx /**< pointer to hold index of created node */
    622 )
    623{
    624 assert(scip != NULL);
    625 assert(graph != NULL);
    626 assert(cons != NULL);
    627 assert(nodeidx != NULL);
    628
    629 /* we can only add nodes if symmetry colors have not been computed yet */
    630 if( graph->islocked )
    631 {
    632 SCIPerrorMessage("Cannot add nodes to a graph for which colors have already been computed.\n");
    633 return SCIP_ERROR;
    634 }
    635
    636 SCIP_CALL( ensureNodeArraysSize(scip, graph, 1) );
    637
    638 if( graph->nconsnodes >= graph->maxnconsnodes )
    639 {
    640 int newsize;
    641
    642 newsize = SCIPcalcMemGrowSize(scip, graph->nconsnodes + 1);
    643 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->conss, graph->maxnconsnodes, newsize) );
    644 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->lhs, graph->maxnconsnodes, newsize) );
    645 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->rhs, graph->maxnconsnodes, newsize) );
    646 graph->maxnconsnodes = newsize;
    647 }
    648
    649 graph->nodetypes[graph->nnodes] = SYM_NODETYPE_CONS;
    650 graph->nodeinfopos[graph->nnodes] = graph->nconsnodes;
    651 graph->conss[graph->nconsnodes] = cons;
    652 graph->lhs[graph->nconsnodes] = MAX(lhs, -graph->infinity);
    653 graph->rhs[graph->nconsnodes] = MIN(rhs, graph->infinity);
    654
    655 *nodeidx = graph->nnodes;
    656 ++graph->nnodes;
    657 ++graph->nconsnodes;
    658
    659 return SCIP_OKAY;
    660}
    661
    662/** returns the (hypothetical) node index of a variable */
    664 SCIP* scip, /**< SCIP data structure */
    665 SYM_GRAPH* graph, /**< symmetry detection graph */
    666 SCIP_VAR* var /**< variable */
    667 )
    668{
    669 int nodeidx;
    670
    671 assert(scip != NULL);
    672 assert(graph != NULL);
    673 assert(var != NULL);
    674 assert(graph->symtype == SYM_SYMTYPE_PERM || graph->symtype == SYM_SYMTYPE_SIGNPERM);
    675
    676 nodeidx = -SCIPvarGetProbindex(var) - 1;
    677 assert(nodeidx != INT_MAX);
    678
    679 return nodeidx;
    680}
    681
    682/** returns the (hypothetical) node index of a negated variable */
    684 SCIP* scip, /**< SCIP data structure */
    685 SYM_GRAPH* graph, /**< symmetry detection graph */
    686 SCIP_VAR* var /**< variable */
    687 )
    688{
    689 int nodeidx;
    690
    691 assert(scip != NULL);
    692 assert(graph != NULL);
    693 assert(var != NULL);
    694 assert(graph->symtype == SYM_SYMTYPE_SIGNPERM);
    695 assert(SCIPgetSymgraphVarnodeidx(scip, graph, var) < 0 );
    696
    697 nodeidx = SCIPgetSymgraphVarnodeidx(scip, graph, var) - graph->nsymvars;
    698 assert(nodeidx != INT_MAX);
    699
    700 return nodeidx;
    701}
    702
    703/** updates the lhs of a constraint node */
    705 SYM_GRAPH* graph, /**< symmetry detection graph */
    706 int nodeidx, /**< index of constraint node in graph */
    707 SCIP_Real newlhs /**< new left-hand side of node */
    708 )
    709{
    710 assert(graph != NULL);
    711 assert(nodeidx >= 0);
    712 assert(graph->nodetypes[nodeidx] == SYM_NODETYPE_CONS);
    713
    714 graph->lhs[graph->nodeinfopos[nodeidx]] = newlhs;
    715
    716 return SCIP_OKAY;
    717}
    718
    719/** updates the rhs of a constraint node */
    721 SYM_GRAPH* graph, /**< symmetry detection graph */
    722 int nodeidx, /**< index of constraint node in graph */
    723 SCIP_Real newrhs /**< new reft-hand side of node */
    724 )
    725{
    726 assert(graph != NULL);
    727 assert(nodeidx >= 0);
    728 assert(graph->nodetypes[nodeidx] == SYM_NODETYPE_CONS);
    729
    730 graph->rhs[graph->nodeinfopos[nodeidx]] = newrhs;
    731
    732 return SCIP_OKAY;
    733}
    734
    735/** registers a variable node (corresponding to active variable) to be fixed by symmetry */
    737 SYM_GRAPH* graph, /**< symmetry detection graph */
    738 SCIP_VAR* var /**< active variable that needs to be fixed */
    739 )
    740{
    741 int varidx;
    742
    743 assert(graph != NULL);
    744 assert(var != NULL);
    745
    746 varidx = SCIPvarGetProbindex(var);
    747 assert(0 <= varidx && varidx < graph->nsymvars);
    748
    749 graph->isfixedvar[varidx] = TRUE;
    750
    751 return SCIP_OKAY;
    752}
    753
    754/*
    755 * methods for adding edges
    756 */
    757
    758/** ensures that the edge-based arrays in symmetry graph are sufficiently long */
    759static
    761 SCIP* scip, /**< SCIP data structure */
    762 SYM_GRAPH* graph, /**< symmetry detection graph */
    763 int addsize /**< required additional size of edge-based arrays */
    764 )
    765{
    766 assert(scip != NULL);
    767 assert(graph != NULL);
    768 assert(addsize > 0);
    769
    770 if( graph->nedges + addsize > graph->maxnedges )
    771 {
    772 int newsize;
    773
    774 newsize = SCIPcalcMemGrowSize(scip, graph->nedges + addsize);
    775 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->edgefirst, graph->maxnedges, newsize) );
    776 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->edgesecond, graph->maxnedges, newsize) );
    777 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &graph->edgevals, graph->maxnedges, newsize) );
    778 graph->maxnedges = newsize;
    779 }
    780
    781 return SCIP_OKAY;
    782}
    783
    784/** adds an edge to a symmetry detection graph */
    786 SCIP* scip, /**< SCIP data structure */
    787 SYM_GRAPH* graph, /**< symmetry detection graph */
    788 int first, /**< first node index of edge */
    789 int second, /**< second node index of edge */
    790 SCIP_Bool hasval, /**< whether the edge has a value */
    791 SCIP_Real val /**< value of the edge (is it has a value) */
    792 )
    793{
    794 assert(scip != NULL);
    795 assert(graph != NULL);
    796
    797 /* we can only add edges if symmetry colors have not been computed yet */
    798 if( graph->islocked )
    799 {
    800 SCIPerrorMessage("Cannot add edges to a graph for which colors have already been computed.\n");
    801 return SCIP_ERROR;
    802 }
    803
    804 SCIP_CALL( ensureEdgeArraysSize(scip, graph, 1) );
    805
    806 graph->edgefirst[graph->nedges] = first;
    807 graph->edgesecond[graph->nedges] = second;
    808 if( hasval )
    809 graph->edgevals[graph->nedges] = val;
    810 else
    811 graph->edgevals[graph->nedges] = SCIPinfinity(scip);
    812
    813 graph->nedges += 1;
    814
    815 return SCIP_OKAY;
    816}
    817
    818/*
    819 * methods to compute colors
    820 */
    821
    822/** compares two variables for permutation symmetry detection
    823 *
    824 * Variables are sorted first by their type, then by their objective coefficient,
    825 * then by their lower bound, and then by their upper bound.
    826 *
    827 * result:
    828 * < 0: ind1 comes before (is better than) ind2
    829 * = 0: both indices have the same value
    830 * > 0: ind2 comes after (is worse than) ind2
    831 */
    832static
    834 SCIP* scip, /**< SCIP pointer (or NULL for exact comparison) */
    835 SCIP_VAR* var1, /**< first variable for comparison */
    836 SCIP_VAR* var2 /**< second variable for comparison */
    837 )
    838{
    839 SCIP_VARTYPE type1;
    840 SCIP_VARTYPE type2;
    841
    842 assert(var1 != NULL);
    843 assert(var2 != NULL);
    844
    845 type1 = SCIPgetSymInferredVarType(var1);
    846 type2 = SCIPgetSymInferredVarType(var2);
    847
    848 if( type1 < type2 )
    849 return -1;
    850 if( type1 > type2 )
    851 return 1;
    852
    853 /* use SCIP's comparison functions if available */
    854 if( scip == NULL )
    855 {
    856 if( SCIPvarGetObj(var1) < SCIPvarGetObj(var2) )
    857 return -1;
    858 if( SCIPvarGetObj(var1) > SCIPvarGetObj(var2) )
    859 return 1;
    860
    861 if( SCIPvarGetLbGlobal(var1) < SCIPvarGetLbGlobal(var2) )
    862 return -1;
    863 if( SCIPvarGetLbGlobal(var1) > SCIPvarGetLbGlobal(var2) )
    864 return 1;
    865
    866 if( SCIPvarGetUbGlobal(var1) < SCIPvarGetUbGlobal(var2) )
    867 return -1;
    868 if( SCIPvarGetUbGlobal(var1) > SCIPvarGetUbGlobal(var2) )
    869 return 1;
    870 }
    871 else
    872 {
    873 if( SCIPisLT(scip, SCIPvarGetObj(var1), SCIPvarGetObj(var2)) )
    874 return -1;
    875 if( SCIPisGT(scip, SCIPvarGetObj(var1), SCIPvarGetObj(var2)) )
    876 return 1;
    877
    879 return -1;
    881 return 1;
    882
    884 return -1;
    886 return 1;
    887 }
    888
    889 return 0;
    890}
    891
    892/** compares two variables for permutation symmetry detection
    893 *
    894 * Variables are sorted first by whether they are fixed, then by their type, then by
    895 * their objective coefficient, then by their lower bound, and then by their upper bound.
    896 *
    897 * result:
    898 * < 0: ind1 comes before (is better than) ind2
    899 * = 0: both indices have the same value
    900 * > 0: ind2 comes after (is worse than) ind2
    901 */
    902static
    904 SCIP* scip, /**< SCIP pointer (or NULL for exact comparison) */
    905 SCIP_VAR* var1, /**< first variable for comparison */
    906 SCIP_VAR* var2, /**< second variable for comparison */
    907 SCIP_Bool isfixed1, /**< whether var1 needs to be fixed */
    908 SCIP_Bool isfixed2 /**< whether var2 needs to be fixed */
    909 )
    910{
    911 assert(var1 != NULL);
    912 assert(var2 != NULL);
    913
    914 if( (! isfixed1) && isfixed2 )
    915 return -1;
    916 if( isfixed1 && (! isfixed2) )
    917 return 1;
    918
    919 return compareVars(scip, var1, var2);
    920}
    921
    922/** sorts nodes of a permutation symmetry detection graph
    923 *
    924 * Variables are sorted first by whether they are fixed, then by their type, then by
    925 * their objective coefficient, then by their lower bound, and then by their upper bound.
    926 *
    927 * result:
    928 * < 0: ind1 comes before (is better than) ind2
    929 * = 0: both indices have the same value
    930 * > 0: ind2 comes after (is worse than) ind2
    931 */
    932static
    933SCIP_DECL_SORTINDCOMP(SYMsortVarnodesPermsym)
    934{
    935 SYM_GRAPH* graph;
    936 SCIP_VAR** vars;
    937 SCIP_Bool* isfixedvar;
    938
    939 graph = (SYM_GRAPH*) dataptr;
    940 assert(graph != NULL);
    941
    942 vars = graph->symvars;
    943 assert(vars != NULL);
    944
    945 isfixedvar = graph->isfixedvar;
    946 assert(isfixedvar != NULL);
    947
    948 return compareVarsFixed(NULL, vars[ind1], vars[ind2], isfixedvar[ind1], isfixedvar[ind2]);
    949}
    950
    951/** compares two variables for signed permutation symmetry detection
    952 *
    953 * Variables are sorted first by their type, then by their objective coefficient,
    954 * then by their lower bound, and then by their upper bound.
    955 * To take signed permutations into account, variable domains are centered at origin
    956 * if the domain is finite.
    957 *
    958 * result:
    959 * < 0: ind1 comes before (is better than) ind2
    960 * = 0: both indices have the same value
    961 * > 0: ind2 comes after (is worse than) ind2
    962 */
    963static
    965 SCIP* scip, /**< SCIP pointer (or NULL for exact comparison) */
    966 SCIP_VAR* var1, /**< first variable for comparison */
    967 SCIP_VAR* var2, /**< second variable for comparison */
    968 SCIP_Bool isneg1, /**< whether var1 needs to be negated */
    969 SCIP_Bool isneg2, /**< whether var2 needs to be negated */
    970 SCIP_Real infinity /**< values as least as large as this are regarded as infinite */
    971 )
    972{
    973 SCIP_Real ub1;
    974 SCIP_Real ub2;
    975 SCIP_Real lb1;
    976 SCIP_Real lb2;
    977 SCIP_Real obj1;
    978 SCIP_Real obj2;
    979 SCIP_Real mid;
    980 SCIP_VARTYPE type1;
    981 SCIP_VARTYPE type2;
    982
    983 assert(var1 != NULL);
    984 assert(var2 != NULL);
    985
    986 type1 = SCIPgetSymInferredVarType(var1);
    987 type2 = SCIPgetSymInferredVarType(var2);
    988
    989 if( type1 < type2 )
    990 return -1;
    991 if( type1 > type2 )
    992 return 1;
    993
    994 obj1 = isneg1 ? -SCIPvarGetObj(var1) : SCIPvarGetObj(var1);
    995 obj2 = isneg2 ? -SCIPvarGetObj(var2) : SCIPvarGetObj(var2);
    996
    997 /* use SCIP's comparison functions if available */
    998 if( scip == NULL )
    999 {
    1000 if( obj1 < obj2 )
    1001 return -1;
    1002 if( obj1 > obj2 )
    1003 return 1;
    1004 }
    1005 else
    1006 {
    1007 if( SCIPisLT(scip, obj1, obj2) )
    1008 return -1;
    1009 if( SCIPisGT(scip, obj1, obj2) )
    1010 return 1;
    1011 }
    1012
    1013 /* adapt lower and upper bounds if domain is finite */
    1014 lb1 = SCIPvarGetLbGlobal(var1);
    1015 lb2 = SCIPvarGetLbGlobal(var2);
    1016 ub1 = SCIPvarGetUbGlobal(var1);
    1017 ub2 = SCIPvarGetUbGlobal(var2);
    1018 if( ub1 < infinity && -lb1 < infinity )
    1019 {
    1020 mid = (lb1 + ub1) / 2;
    1021 lb1 -= mid;
    1022 ub1 -= mid;
    1023 }
    1024 if( ub2 < infinity && -lb2 < infinity )
    1025 {
    1026 mid = (lb2 + ub2) / 2;
    1027 lb2 -= mid;
    1028 ub2 -= mid;
    1029 }
    1030
    1031 /* for negated variables, flip upper and lower bounds */
    1032 if( isneg1 )
    1033 {
    1034 mid = lb1;
    1035 lb1 = -ub1;
    1036 ub1 = -mid;
    1037 }
    1038 if( isneg2 )
    1039 {
    1040 mid = lb2;
    1041 lb2 = -ub2;
    1042 ub2 = -mid;
    1043 }
    1044
    1045 /* use SCIP's comparison functions if available */
    1046 if( scip == NULL )
    1047 {
    1048 if( lb1 < lb2 )
    1049 return -1;
    1050 if( lb1 > lb2 )
    1051 return 1;
    1052
    1053 if( ub1 < ub2 )
    1054 return -1;
    1055 if( ub1 > ub2 )
    1056 return 1;
    1057 }
    1058 else
    1059 {
    1060 if( SCIPisLT(scip, lb1, lb2) )
    1061 return -1;
    1062 if( SCIPisGT(scip, lb1, lb2) )
    1063 return 1;
    1064
    1065 if( SCIPisLT(scip, ub1, ub2) )
    1066 return -1;
    1067 if( SCIPisGT(scip, ub1, ub2) )
    1068 return 1;
    1069 }
    1070
    1071 return 0;
    1072}
    1073
    1074/** compares two variables for signed permutation symmetry detection
    1075 *
    1076 * Variables are sorted first by whether they are fixed, then by their type, then
    1077 * by their objective coefficient, then by their lower bound and then by their upper bound.
    1078 * To take signed permutations into account, variable domains are centered at origin
    1079 * if the domain is finite.
    1080 *
    1081 * result:
    1082 * < 0: ind1 comes before (is better than) ind2
    1083 * = 0: both indices have the same value
    1084 * > 0: ind2 comes after (is worse than) ind2
    1085 */
    1086static
    1088 SCIP* scip, /**< SCIP pointer (or NULL for exact comparison) */
    1089 SCIP_VAR* var1, /**< first variable for comparison */
    1090 SCIP_VAR* var2, /**< second variable for comparison */
    1091 SCIP_Bool isfixed1, /**< whether var1 needs to be fixed */
    1092 SCIP_Bool isfixed2, /**< whether var2 needs to be fixed */
    1093 SCIP_Bool isneg1, /**< whether var1 needs to be negated */
    1094 SCIP_Bool isneg2, /**< whether var2 needs to be negated */
    1095 SCIP_Real infinity /**< values as least as large as this are regarded as infinite */
    1096 )
    1097{
    1098 assert(var1 != NULL);
    1099 assert(var2 != NULL);
    1100
    1101 if( (! isfixed1) && isfixed2 )
    1102 return -1;
    1103 if( isfixed1 && (! isfixed2) )
    1104 return 1;
    1105
    1106 return compareVarsSignedPerm(scip, var1, var2, isneg1, isneg2, infinity);
    1107}
    1108
    1109/** sorts nodes of a signed permutation symmetry detection graph
    1110 *
    1111 * Variables are sorted first by whether they are fixed, then by their type, then
    1112 * by their objective coefficient, then by their lower bound and then by their upper bound.
    1113 * To take signed permutations into account, variable domains are centered at origin
    1114 * if the domain is finite.
    1115 *
    1116 * result:
    1117 * < 0: ind1 comes before (is better than) ind2
    1118 * = 0: both indices have the same value
    1119 * > 0: ind2 comes after (is worse than) ind2
    1120 */
    1121static
    1122SCIP_DECL_SORTINDCOMP(SYMsortVarnodesSignedPermsym)
    1123{
    1124 SYM_GRAPH* graph;
    1125 SCIP_VAR** vars;
    1126 SCIP_Bool* isfixedvar;
    1127 int nsymvars;
    1128 int locind1;
    1129 int locind2;
    1130 SCIP_Bool isneg1 = FALSE;
    1131 SCIP_Bool isneg2 = FALSE;
    1132
    1133 graph = (SYM_GRAPH*) dataptr;
    1134 assert(graph != NULL);
    1135
    1136 nsymvars = graph->nsymvars;
    1137 vars = graph->symvars;
    1138 assert(nsymvars > 0);
    1139 assert(vars != NULL);
    1140
    1141 isfixedvar = graph->isfixedvar;
    1142 assert(isfixedvar != NULL);
    1143
    1144 locind1 = ind1;
    1145 if( locind1 >= nsymvars )
    1146 {
    1147 isneg1 = TRUE;
    1148 locind1 -= nsymvars;
    1149 }
    1150 locind2 = ind2;
    1151 if( locind2 >= nsymvars )
    1152 {
    1153 isneg2 = TRUE;
    1154 locind2 -= nsymvars;
    1155 }
    1156
    1157 return compareVarsFixedSignedPerm(NULL, vars[locind1], vars[locind2], isfixedvar[locind1], isfixedvar[locind2],
    1158 isneg1, isneg2, graph->infinity);
    1159}
    1160
    1161/** compares two operators
    1162 *
    1163 * Operators are sorted by their int values.
    1164 *
    1165 * result:
    1166 * < 0: ind1 comes before (is better than) ind2
    1167 * = 0: both indices have the same value
    1168 * > 0: ind2 comes after (is worse than) ind2
    1169 */
    1170static
    1172 int op1, /**< first operator in comparison */
    1173 int op2 /**< second operator in comparison */
    1174 )
    1175{
    1176 if( op1 < op2 )
    1177 return -1;
    1178 else if( op1 > op2 )
    1179 return 1;
    1180
    1181 return 0;
    1182}
    1183
    1184/** sorts operators corresponding to SCIP_EXPRHDLR*
    1185 *
    1186 * result:
    1187 * < 0: ind1 comes before (is better than) ind2
    1188 * = 0: both indices have the same value
    1189 * > 0: ind2 comes after (is worse than) ind2
    1190 */
    1191static
    1193{
    1194 int* vals;
    1195
    1196 vals = (int*) dataptr;
    1197
    1198 return compareOps(vals[ind1], vals[ind2]);
    1199}
    1200
    1201/** sorts real values
    1202 *
    1203 * result:
    1204 * < 0: ind1 comes before (is better than) ind2
    1205 * = 0: both indices have the same value
    1206 * > 0: ind2 comes after (is worse than) ind2
    1207 */
    1208static
    1210{
    1211 SCIP_Real* vals;
    1212
    1213 vals = (SCIP_Real*) dataptr;
    1214
    1215 if( vals[ind1] < vals[ind2] )
    1216 return -1;
    1217 if( vals[ind1] > vals[ind2] )
    1218 return 1;
    1219
    1220 return 0;
    1221}
    1222
    1223/** compares constraint nodes
    1224 *
    1225 * Nodes are sorted by their type of constraint, then by the lhs, and then by the rhs.
    1226 *
    1227 * result:
    1228 * < 0: ind1 comes before (is better than) ind2
    1229 * = 0: both indices have the same value
    1230 * > 0: ind2 comes after (is worse than) ind2
    1231 */
    1232static
    1234 SCIP* scip, /**< SCIP data structure */
    1235 SYM_GRAPH* graph, /**< underlying symmetry detection graph */
    1236 int ind1, /**< index of first constraint node */
    1237 int ind2 /**< index of second constraint node */
    1238 )
    1239{
    1240 SCIP_CONS* cons1;
    1241 SCIP_CONS* cons2;
    1242
    1243 assert(graph != NULL);
    1244 assert(0 <= ind1 && ind1 < graph->nconsnodes);
    1245 assert(0 <= ind2 && ind2 < graph->nconsnodes);
    1246
    1247 cons1 = graph->conss[ind1];
    1248 cons2 = graph->conss[ind2];
    1249
    1250 if( SCIPconsGetHdlr(cons1) < SCIPconsGetHdlr(cons2) )
    1251 return -1;
    1252 if( SCIPconsGetHdlr(cons1) > SCIPconsGetHdlr(cons2) )
    1253 return 1;
    1254
    1255 /* use SCIP's comparison functions if available */
    1256 if( scip != NULL )
    1257 {
    1258 if( SCIPisLT(scip, graph->lhs[ind1], graph->lhs[ind2]) )
    1259 return -1;
    1260 if( SCIPisGT(scip, graph->lhs[ind1], graph->lhs[ind2]) )
    1261 return 1;
    1262
    1263 if( SCIPisLT(scip, graph->rhs[ind1], graph->rhs[ind2]) )
    1264 return -1;
    1265 if( SCIPisGT(scip, graph->rhs[ind1], graph->rhs[ind2]) )
    1266 return 1;
    1267 }
    1268 else
    1269 {
    1270 if( graph->lhs[ind1] < graph->lhs[ind2] )
    1271 return -1;
    1272 if( graph->lhs[ind1] > graph->lhs[ind2] )
    1273 return 1;
    1274
    1275 if( graph->rhs[ind1] < graph->rhs[ind2] )
    1276 return -1;
    1277 if( graph->rhs[ind1] > graph->rhs[ind2] )
    1278 return 1;
    1279 }
    1280
    1281 return 0;
    1282}
    1283
    1284/** sorts constraint nodes
    1285 *
    1286 * Nodes are sorted by their type of constraint, then by the lhs, and then by the rhs.
    1287 *
    1288 * result:
    1289 * < 0: ind1 comes before (is better than) ind2
    1290 * = 0: both indices have the same value
    1291 * > 0: ind2 comes after (is worse than) ind2
    1292 */
    1293static
    1294SCIP_DECL_SORTINDCOMP(SYMsortConsnodes)
    1295{
    1296 return compareConsnodes(NULL, (SYM_GRAPH*) dataptr, ind1, ind2);
    1297}
    1298
    1299/** sorts edges
    1300 *
    1301 * Edges are sorted by their weights.
    1302 *
    1303 * result:
    1304 * < 0: ind1 comes before (is better than) ind2
    1305 * = 0: both indices have the same value
    1306 * > 0: ind2 comes after (is worse than) ind2
    1307 */
    1308static
    1310{
    1311 SYM_GRAPH* G;
    1312
    1313 G = (SYM_GRAPH*) dataptr;
    1314
    1315 if( G->edgevals[ind1] < G->edgevals[ind2] )
    1316 return -1;
    1317 if( G->edgevals[ind1] > G->edgevals[ind2] )
    1318 return 1;
    1319
    1320 return 0;
    1321}
    1322
    1323/** returns whether a node of the symmetry detection graph needs to be fixed */
    1324static
    1326 SCIP_VAR* var, /**< active problem variable */
    1327 SYM_SPEC fixedtype /**< variable types that must be fixed by symmetries */
    1328 )
    1329{
    1330 assert(var != NULL);
    1331
    1333 return TRUE;
    1334 if( (fixedtype & SYM_SPEC_BINARY) && SCIPvarGetType(var) == SCIP_VARTYPE_BINARY && !SCIPvarIsImpliedIntegral(var) )
    1335 return TRUE;
    1336 if( (fixedtype & SYM_SPEC_REAL) && ( SCIPvarGetType(var) == SCIP_VARTYPE_CONTINUOUS || SCIPvarIsImpliedIntegral(var) ) )
    1337 return TRUE;
    1338 return FALSE;
    1339}
    1340
    1341/** computes colors of nodes and edges
    1342 *
    1343 * Colors are detected by sorting different types of nodes (variables, operators, values, and constraint) and edges.
    1344 * If two consecutive nodes of the same type differ (e.g., different variable type), they are assigned a new color.
    1345 */
    1347 SCIP* scip, /**< SCIP data structure */
    1348 SYM_GRAPH* graph, /**< symmetry detection graph */
    1349 SYM_SPEC fixedtype /**< variable types that must be fixed by symmetries */
    1350 )
    1351{
    1352 SCIP_VAR* prevvar;
    1353 SCIP_VAR* thisvar;
    1354 SCIP_Real prevval;
    1355 SCIP_Real thisval;
    1356 SCIP_Bool previsneg;
    1357 SCIP_Bool thisisneg;
    1358 int* perm;
    1359 int nusedvars;
    1360 int len;
    1361 int i;
    1362 int color = 0;
    1363
    1364 assert(scip != NULL);
    1365 assert(graph != NULL);
    1366
    1367 /* terminate early if colors have already been computed */
    1368 if( graph->islocked )
    1369 return SCIP_OKAY;
    1370
    1371 /* lock graph to be extended */
    1372 graph->islocked = TRUE;
    1373
    1374 /* possibly fix variables */
    1375 for( i = 0; i < graph->nsymvars; ++i )
    1376 {
    1377 if( isFixedVar(graph->symvars[i], fixedtype) )
    1378 graph->isfixedvar[i] = TRUE;
    1379 }
    1380
    1381 /* get number of variables used in symmetry detection graph */
    1382 switch( graph->symtype )
    1383 {
    1384 case SYM_SYMTYPE_PERM:
    1385 nusedvars = graph->nsymvars;
    1386 break;
    1387 default:
    1388 assert(graph->symtype == SYM_SYMTYPE_SIGNPERM);
    1389 nusedvars = 2 * graph->nsymvars;
    1390 } /*lint !e788*/
    1391
    1392 /* allocate memory for colors */
    1393 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &graph->varcolors, nusedvars) );
    1398
    1399 /* allocate permutation of arrays, will be initialized by SCIPsort() */
    1400 len = graph->nedges;
    1401 if ( graph->nopnodes > len )
    1402 len = graph->nopnodes;
    1403 if ( graph->nvalnodes > len )
    1404 len = graph->nvalnodes;
    1405 if ( graph->nconsnodes > len )
    1406 len = graph->nconsnodes;
    1407 if ( nusedvars > len )
    1408 len = nusedvars;
    1409
    1410 SCIP_CALL( SCIPallocBufferArray(scip, &perm, len) );
    1411
    1412 /* find colors of variable nodes */
    1413 assert(graph->nsymvars > 0);
    1414 switch( graph->symtype )
    1415 {
    1416 case SYM_SYMTYPE_PERM:
    1417 SCIPsort(perm, SYMsortVarnodesPermsym, (void*) graph, nusedvars);
    1418
    1419 graph->varcolors[perm[0]] = color;
    1420 prevvar = graph->symvars[perm[0]];
    1421
    1422 for( i = 1; i < nusedvars; ++i )
    1423 {
    1424 thisvar = graph->symvars[perm[i]];
    1425
    1426 if( graph->isfixedvar[i] || compareVars(scip, prevvar, thisvar) != 0 )
    1427 ++color;
    1428
    1429 graph->varcolors[perm[i]] = color;
    1430 prevvar = thisvar;
    1431 }
    1432 graph->nvarcolors = color;
    1433 break;
    1434 default:
    1435 assert(graph->symtype == SYM_SYMTYPE_SIGNPERM);
    1436
    1437 SCIPsort(perm, SYMsortVarnodesSignedPermsym, (void*) graph, nusedvars);
    1438
    1439 graph->varcolors[perm[0]] = color;
    1440
    1441 /* store information about first variable */
    1442 if( perm[0] < graph->nsymvars )
    1443 {
    1444 previsneg = FALSE;
    1445 prevvar = graph->symvars[perm[0]];
    1446 }
    1447 else
    1448 {
    1449 previsneg = TRUE;
    1450 prevvar = graph->symvars[perm[0] - graph->nsymvars];
    1451 }
    1452
    1453 /* compute colors of remaining variables */
    1454 for( i = 1; i < nusedvars; ++i )
    1455 {
    1456 if( perm[i] < graph->nsymvars )
    1457 {
    1458 thisisneg = FALSE;
    1459 thisvar = graph->symvars[perm[i]];
    1460 }
    1461 else
    1462 {
    1463 thisisneg = TRUE;
    1464 thisvar = graph->symvars[perm[i] - graph->nsymvars];
    1465 }
    1466
    1467 if( graph->isfixedvar[i % graph->nsymvars]
    1468 || compareVarsSignedPerm(scip, prevvar, thisvar, previsneg, thisisneg, graph->infinity) != 0 )
    1469 ++color;
    1470
    1471 graph->varcolors[perm[i]] = color;
    1472 prevvar = thisvar;
    1473 previsneg = thisisneg;
    1474 }
    1475 graph->nvarcolors = color;
    1476 } /*lint !e788*/
    1477
    1478 /* find colors of operator nodes */
    1479 if( graph->nopnodes > 0 )
    1480 {
    1481 int prevop;
    1482 int thisop;
    1483
    1484 SCIPsort(perm, SYMsortOpnodes, (void*) graph->ops, graph->nopnodes);
    1485
    1486 graph->opcolors[perm[0]] = ++color;
    1487 prevop = graph->ops[perm[0]];
    1488
    1489 for( i = 1; i < graph->nopnodes; ++i )
    1490 {
    1491 thisop = graph->ops[perm[i]];
    1492
    1493 if( compareOps(prevop, thisop) != 0 )
    1494 ++color;
    1495
    1496 graph->opcolors[perm[i]] = color;
    1497 prevop = thisop;
    1498 }
    1499 }
    1500
    1501 /* find colors of value nodes */
    1502 if( graph->nvalnodes > 0 )
    1503 {
    1504 SCIPsort(perm, SYMsortReals, (void*) graph->vals, graph->nvalnodes);
    1505
    1506 graph->valcolors[perm[0]] = ++color;
    1507 prevval = graph->vals[perm[0]];
    1508
    1509 for( i = 1; i < graph->nvalnodes; ++i )
    1510 {
    1511 thisval = graph->vals[perm[i]];
    1512
    1513 if( ! SCIPisEQ(scip, prevval, thisval) )
    1514 ++color;
    1515
    1516 graph->valcolors[perm[i]] = color;
    1517 prevval = thisval;
    1518 }
    1519 }
    1520
    1521 /* find colors of constraint nodes */
    1522 if( graph->nconsnodes > 0 )
    1523 {
    1524 SCIPsort(perm, SYMsortConsnodes, (void*) graph, graph->nconsnodes);
    1525
    1526 graph->conscolors[perm[0]] = ++color;
    1527
    1528 for( i = 1; i < graph->nconsnodes; ++i )
    1529 {
    1530 if( compareConsnodes(scip, graph, perm[i-1], perm[i]) != 0 )
    1531 ++color;
    1532
    1533 graph->conscolors[perm[i]] = color;
    1534 }
    1535 }
    1536
    1537 /* find colors of edges */
    1538 if( graph->nedges > 0 )
    1539 {
    1540 SCIPsort(perm, SYMsortEdges, (void*) graph, graph->nedges);
    1541
    1542 /* check for uncolored or uniformly colored edges, which is easy due to sorting */
    1543 if( SCIPisInfinity(scip, graph->edgevals[perm[0]]) ||
    1544 SCIPisEQ(scip, graph->edgevals[perm[0]], graph->edgevals[perm[graph->nedges - 1]]) )
    1545 {
    1546 /* when all edges are uncolored or uniformly colored, we can ignore edge colors */
    1547 graph->uniqueedgetype = TRUE;
    1548 for( i = 0; i < graph->nedges; ++i )
    1549 graph->edgecolors[i] = -1;
    1550 }
    1551 else
    1552 {
    1553 /* first edge is colored */
    1554 graph->edgecolors[perm[0]] = ++color;
    1555 prevval = graph->edgevals[perm[0]];
    1556
    1557 for( i = 1; i < graph->nedges; ++i )
    1558 {
    1559 thisval = graph->edgevals[perm[i]];
    1560
    1561 /* terminate if edges are not colored anymore */
    1562 if( SCIPisInfinity(scip, thisval) )
    1563 break;
    1564
    1565 if( ! SCIPisEQ(scip, prevval, thisval) )
    1566 ++color;
    1567
    1568 graph->edgecolors[perm[i]] = color;
    1569 prevval = thisval;
    1570 }
    1571
    1572 /* assign uncolored edges color -1 */
    1573 for( ; i < graph->nedges; ++i )
    1574 graph->edgecolors[perm[i]] = -1;
    1575 }
    1576 }
    1577
    1578 SCIPfreeBufferArray(scip, &perm);
    1579
    1580 return SCIP_OKAY;
    1581}
    1582
    1583
    1584/* general methods */
    1585
    1586/** returns the type of symmetries encoded in graph */
    1588 SYM_GRAPH* graph /**< symmetry detection graph */
    1589 )
    1590{
    1591 assert(graph != NULL);
    1592
    1593 return graph->symtype;
    1594}
    1595
    1596/** returns the variables in a symmetry detection graph */
    1598 SYM_GRAPH* graph /**< symmetry detection graph */
    1599 )
    1600{
    1601 assert(graph != NULL);
    1602
    1603 return graph->symvars;
    1604}
    1605
    1606/** returns the number of variables in a symmetry detection graph */
    1608 SYM_GRAPH* graph /**< symmetry detection graph */
    1609 )
    1610{
    1611 assert(graph != NULL);
    1612
    1613 return graph->nsymvars;
    1614}
    1615
    1616/** returns the number of constraint nodes in a symmetry detection graph */
    1618 SYM_GRAPH* graph /**< symmetry detection graph */
    1619 )
    1620{
    1621 assert(graph != NULL);
    1622
    1623 return graph->nconsnodes;
    1624}
    1625
    1626/** returns the number of non-variable nodes in a graph */
    1628 SYM_GRAPH* graph /**< symmetry detection graph */
    1629 )
    1630{
    1631 assert(graph != NULL);
    1632
    1633 return graph->nnodes;
    1634}
    1635
    1636/** returns the number of edges in a graph */
    1638 SYM_GRAPH* graph /**< symmetry detection graph */
    1639 )
    1640{
    1641 assert(graph != NULL);
    1642
    1643 return graph->nedges;
    1644}
    1645
    1646/** return the first node index of an edge */
    1648 SYM_GRAPH* graph, /**< symmetry detection graph */
    1649 int edgeidx /**< index of edge */
    1650 )
    1651{
    1652 assert(graph != NULL);
    1653 assert(0 <= edgeidx && edgeidx < graph->nedges);
    1654
    1655 return graph->edgefirst[edgeidx];
    1656}
    1657
    1658/** return the second node index of an edge */
    1660 SYM_GRAPH* graph, /**< symmetry detection graph */
    1661 int edgeidx /**< index of edge */
    1662 )
    1663{
    1664 assert(graph != NULL);
    1665 assert(0 <= edgeidx && edgeidx < graph->nedges);
    1666
    1667 return graph->edgesecond[edgeidx];
    1668}
    1669
    1670/** returns the color of a variable node */
    1672 SYM_GRAPH* graph, /**< symmetry detection graph */
    1673 int nodeidx /**< index of variable node */
    1674 )
    1675{
    1676 assert(graph != NULL);
    1677 assert(graph->islocked);
    1678
    1679 switch( graph->symtype )
    1680 {
    1681 case SYM_SYMTYPE_PERM:
    1682 assert(0 <= nodeidx && nodeidx < graph->nsymvars);
    1683 break;
    1684 default:
    1685 assert(graph->symtype == SYM_SYMTYPE_SIGNPERM);
    1686 assert(0 <= nodeidx && nodeidx < 2 * graph->nsymvars);
    1687 } /*lint !e788*/
    1688
    1689 return graph->varcolors[nodeidx];
    1690}
    1691
    1692/** returns the type of a node */
    1694 SYM_GRAPH* graph, /**< symmetry detection graph */
    1695 int nodeidx /**< index of node */
    1696 )
    1697{
    1698 assert(graph != NULL);
    1699 assert(nodeidx < graph->nnodes);
    1700
    1701 if( nodeidx < 0 )
    1702 return SYM_NODETYPE_VAR;
    1703
    1704 return graph->nodetypes[nodeidx];
    1705}
    1706
    1707/** returns the color of a non-variable node */
    1709 SYM_GRAPH* graph, /**< symmetry detection graph */
    1710 int nodeidx /**< index of node */
    1711 )
    1712{ /*lint --e{788}*/
    1713 assert(graph != NULL);
    1714 assert(0 <= nodeidx && nodeidx < graph->nnodes);
    1715 assert(graph->islocked);
    1716
    1717 switch( graph->nodetypes[nodeidx] )
    1718 {
    1720 return graph->opcolors[graph->nodeinfopos[nodeidx]];
    1721 case SYM_NODETYPE_VAL:
    1722 return graph->valcolors[graph->nodeinfopos[nodeidx]];
    1723 default:
    1724 assert(graph->nodetypes[nodeidx] == SYM_NODETYPE_CONS);
    1725 }
    1726
    1727 return graph->conscolors[graph->nodeinfopos[nodeidx]];
    1728}
    1729
    1730/** returns whether an edge is colored */
    1732 SYM_GRAPH* graph, /**< symmetry detection graph */
    1733 int edgeidx /**< index of edge */
    1734 )
    1735{
    1736 assert(graph != NULL);
    1737 assert(0 <= edgeidx && edgeidx < graph->nedges);
    1738
    1739 if( ! graph->islocked || graph->edgecolors[edgeidx] == -1 )
    1740 return FALSE;
    1741
    1742 return TRUE;
    1743}
    1744
    1745/** returns color of an edge */
    1747 SYM_GRAPH* graph, /**< symmetry detection graph */
    1748 int edgeidx /**< index of edge */
    1749 )
    1750{
    1751 assert(graph != NULL);
    1752 assert(0 <= edgeidx && edgeidx < graph->nedges);
    1753 assert(SCIPisSymgraphEdgeColored(graph, edgeidx));
    1754
    1755 return graph->edgecolors[edgeidx];
    1756}
    1757
    1758/** returns the number of unique variable colors in the graph */
    1760 SYM_GRAPH* graph /**< symmetry detection graph */
    1761 )
    1762{
    1763 assert(graph != NULL);
    1764
    1765 if( graph->nvarcolors < 0 )
    1766 return graph->nsymvars;
    1767
    1768 return graph->nvarcolors;
    1769}
    1770
    1771/** returns whether the graph has a unique edge type */
    1773 SYM_GRAPH* graph /**< symmetry detection graph */
    1774 )
    1775{
    1776 assert(graph != NULL);
    1777
    1778 return graph->uniqueedgetype;
    1779}
    1780
    1781/** creates consnodeperm array for symmetry detection graph
    1782 *
    1783 * @note @p colors of symmetry detection graph must have been computed
    1784 */
    1786 SCIP* scip, /**< SCIP data structure */
    1787 SYM_GRAPH* graph /**< symmetry detection graph */
    1788 )
    1789{
    1790 assert(scip != NULL);
    1791 assert(graph != NULL);
    1792 assert(graph->islocked);
    1793
    1794 /* either there are no constraint nodes or we have already computed the permutation */
    1795 if( graph->nconsnodes <= 0 || graph->consnodeperm != NULL )
    1796 return SCIP_OKAY;
    1797
    1799 SCIPsort(graph->consnodeperm, SYMsortConsnodes, (void*) graph, graph->nconsnodes);
    1800
    1801 return SCIP_OKAY;
    1802}
    1803
    1804/** frees consnodeperm array for symmetry detection graph */
    1806 SCIP* scip, /**< SCIP data structure */
    1807 SYM_GRAPH* graph /**< symmetry detection graph */
    1808 )
    1809{
    1810 assert(scip != NULL);
    1811 assert(graph != NULL);
    1812 assert(graph->islocked);
    1813
    1815
    1816 return SCIP_OKAY;
    1817}
    1818
    1819/** returns consnodeperm array for symmetry detection graph
    1820 *
    1821 * @note @p colors of symmetry detection graph must have been computed
    1822 */
    1824 SCIP* scip, /**< SCIP data structure */
    1825 SYM_GRAPH* graph /**< symmetry detection graph */
    1826 )
    1827{
    1828 assert(scip != NULL);
    1829 assert(graph != NULL);
    1830 assert(graph->islocked);
    1831
    1833
    1834 return graph->consnodeperm;
    1835}
    1836
    1837/** Transforms given variables, scalars, and constant to the corresponding active variables, scalars, and constant.
    1838 *
    1839 * For permutation symmetries, active variables as encoded in SCIP are used. For signed permutation symmetries,
    1840 * active variables are shifted such that their domain is centered at 0 (if both their upper and lower bounds
    1841 * are finite).
    1842 *
    1843 * @note @p constant needs to be initialized!
    1844 */
    1846 SCIP* scip, /**< SCIP data structure */
    1847 SYM_SYMTYPE symtype, /**< type of symmetries for which variables are required */
    1848 SCIP_VAR*** vars, /**< pointer to vars array to get active variables for */
    1849 SCIP_Real** scalars, /**< pointer to scalars a_1, ..., a_n in linear sum a_1*x_1 + ... + a_n*x_n + c */
    1850 int* nvars, /**< pointer to number of variables and values in vars and vals array */
    1851 SCIP_Real* constant, /**< pointer to constant c in linear sum a_1*x_1 + ... + a_n*x_n + c */
    1852 SCIP_Bool transformed /**< transformed constraint? */
    1853 )
    1854{
    1855 SCIP_Real ub;
    1856 SCIP_Real lb;
    1857 int requiredsize;
    1858 int v;
    1859
    1860 assert(scip != NULL);
    1861 assert(vars != NULL);
    1862 assert(scalars != NULL);
    1863 assert(*vars != NULL);
    1864 assert(*scalars != NULL);
    1865 assert(nvars != NULL);
    1866 assert(constant != NULL);
    1867
    1868 if( transformed )
    1869 {
    1870 SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, *nvars, constant, &requiredsize) );
    1871
    1872 if( requiredsize > *nvars )
    1873 {
    1874 SCIP_CALL( SCIPreallocBufferArray(scip, vars, requiredsize) );
    1875 SCIP_CALL( SCIPreallocBufferArray(scip, scalars, requiredsize) );
    1876
    1877 SCIP_CALL( SCIPgetProbvarLinearSum(scip, *vars, *scalars, nvars, requiredsize, constant, &requiredsize) );
    1878 }
    1879 assert( requiredsize == *nvars );
    1880 }
    1881 else
    1882 {
    1883 for( v = 0; v < *nvars; ++v )
    1884 {
    1885 SCIP_CALL( SCIPvarGetOrigvarSum(&(*vars)[v], &(*scalars)[v], constant) );
    1886 }
    1887 }
    1888
    1889 /* possibly post-process active variables */
    1890 if( symtype == SYM_SYMTYPE_SIGNPERM )
    1891 {
    1892 /* center variables at origin if their domain is finite */
    1893 for (v = 0; v < *nvars; ++v)
    1894 {
    1895 ub = SCIPvarGetUbGlobal((*vars)[v]);
    1896 lb = SCIPvarGetLbGlobal((*vars)[v]);
    1897
    1898 if ( SCIPisInfinity(scip, ub) || SCIPisInfinity(scip, -lb) )
    1899 continue;
    1900
    1901 *constant += (*scalars)[v] * (ub + lb) / 2;
    1902 }
    1903 }
    1904
    1905 return SCIP_OKAY;
    1906}
    1907
    1908/** frees symmetry information of an expression */
    1910 SCIP* scip, /**< SCIP data structure */
    1911 SYM_EXPRDATA** symdata /**< symmetry information of an expression */
    1912 )
    1913{
    1914 assert(scip != NULL);
    1915 assert(symdata != NULL);
    1916
    1917 if( (*symdata)->nconstants > 0 )
    1918 {
    1919 SCIPfreeBlockMemoryArrayNull(scip, &(*symdata)->constants, (*symdata)->nconstants);
    1920 }
    1921 if( (*symdata)->ncoefficients > 0 )
    1922 {
    1923 SCIPfreeBlockMemoryArrayNull(scip, &(*symdata)->coefficients, (*symdata)->ncoefficients);
    1924 SCIPfreeBlockMemoryArrayNull(scip, &(*symdata)->children, (*symdata)->ncoefficients);
    1925 }
    1926 SCIPfreeBlockMemory(scip, symdata);
    1927
    1928 return SCIP_OKAY;
    1929}
    1930
    1931/** returns number of coefficients from exprdata */
    1933 SYM_EXPRDATA* symdata /**< symmetry information of an expression */
    1934 )
    1935{
    1936 assert(symdata != NULL);
    1937
    1938 return symdata->nconstants;
    1939}
    1940
    1941/** returns number of coefficients form exprdata */
    1943 SYM_EXPRDATA* symdata /**< symmetry information of an expression */
    1944 )
    1945{
    1946 assert(symdata != NULL);
    1947
    1948 return symdata->constants;
    1949}
    1950
    1951/** gets coefficient of expression from parent expression */
    1953 SCIP* scip, /**< SCIP data structure */
    1954 SCIP_EXPR* expr, /**< expression for which coefficient needs to be found */
    1955 SCIP_EXPR* parentexpr, /**< parent of expr */
    1956 SCIP_Real* coef, /**< buffer to store coefficient */
    1957 SCIP_Bool* success /**< whether a coefficient is found */
    1958 )
    1959{
    1960 SYM_EXPRDATA* symdata;
    1961 int i;
    1962
    1963 assert(scip != NULL);
    1964 assert(expr != NULL);
    1965 assert(parentexpr != NULL);
    1966 assert(coef != NULL);
    1967 assert(success != NULL);
    1968
    1969 *success = FALSE;
    1970
    1971 /* parent does not assign coefficients to its children */
    1972 if( ! SCIPexprhdlrHasGetSymData(SCIPexprGetHdlr(parentexpr)) )
    1973 return SCIP_OKAY;
    1974
    1975 SCIP_CALL( SCIPgetSymDataExpr(scip, parentexpr, &symdata) );
    1976
    1977 /* parent does not assign coefficients to its children */
    1978 if( symdata->ncoefficients < 1 )
    1979 {
    1980 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) );
    1981
    1982 return SCIP_OKAY;
    1983 }
    1984
    1985 /* search for expr in the children of parentexpr */
    1986 for( i = 0; i < symdata->ncoefficients; ++i )
    1987 {
    1988 if( symdata->children[i] == expr )
    1989 {
    1990 *coef = symdata->coefficients[i];
    1991 *success = TRUE;
    1992 break;
    1993 }
    1994 }
    1995
    1996 SCIP_CALL( SCIPfreeSymDataExpr(scip, &symdata) );
    1997
    1998 return SCIP_OKAY;
    1999}
    #define NULL
    Definition: def.h:248
    #define SCIP_Bool
    Definition: def.h:91
    #define MIN(x, y)
    Definition: def.h:224
    #define SCIP_Real
    Definition: def.h:156
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define MAX(x, y)
    Definition: def.h:220
    #define SCIP_CALL_ABORT(x)
    Definition: def.h:334
    #define SCIP_CALL(x)
    Definition: def.h:355
    #define infinity
    Definition: gastrans.c:80
    #define nnodes
    Definition: gastrans.c:74
    SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
    Definition: cons.c:8409
    SCIP_Bool SCIPexprhdlrHasGetSymData(SCIP_EXPRHDLR *exprhdlr)
    Definition: expr.c:685
    SCIP_RETCODE SCIPgetSymDataExpr(SCIP *scip, SCIP_EXPR *expr, SYM_EXPRDATA **symdata)
    Definition: scip_expr.c:1817
    SCIP_EXPRHDLR * SCIPexprGetHdlr(SCIP_EXPR *expr)
    Definition: expr.c:3895
    #define SCIPfreeBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:110
    #define SCIPallocClearBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:97
    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 SCIPallocBlockMemory(scip, ptr)
    Definition: scip_mem.h:89
    SCIP_VARTYPE SCIPgetSymInferredVarType(SCIP_VAR *var)
    Definition: symmetry.c:45
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_RETCODE SCIPvarGetOrigvarSum(SCIP_VAR **var, SCIP_Real *scalar, SCIP_Real *constant)
    Definition: var.c:18320
    SCIP_Bool SCIPvarIsImpliedIntegral(SCIP_VAR *var)
    Definition: var.c:23498
    SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
    Definition: var.c:23900
    SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
    Definition: var.c:23453
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24142
    int SCIPvarGetProbindex(SCIP_VAR *var)
    Definition: var.c:23662
    SCIP_RETCODE SCIPgetProbvarLinearSum(SCIP *scip, SCIP_VAR **vars, SCIP_Real *scalars, int *nvars, int varssize, SCIP_Real *constant, int *requiredsize)
    Definition: scip_var.c:2378
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24120
    void SCIPsort(int *perm, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
    Definition: misc.c:5581
    SYM_NODETYPE SCIPgetSymgraphNodeType(SYM_GRAPH *graph, int nodeidx)
    SCIP_RETCODE SCIPaddSymgraphEdge(SCIP *scip, SYM_GRAPH *graph, int first, int second, SCIP_Bool hasval, SCIP_Real val)
    SCIP_RETCODE SCIPfreeSymgraph(SCIP *scip, SYM_GRAPH **graph)
    int SCIPgetSymgraphEdgeFirst(SYM_GRAPH *graph, int edgeidx)
    SCIP_RETCODE SCIPaddSymgraphOpnode(SCIP *scip, SYM_GRAPH *graph, int op, int *nodeidx)
    int * SCIPgetSymgraphConsnodeperm(SCIP *scip, SYM_GRAPH *graph)
    SCIP_RETCODE SCIPgetSymActiveVariables(SCIP *scip, SYM_SYMTYPE symtype, SCIP_VAR ***vars, SCIP_Real **scalars, int *nvars, SCIP_Real *constant, SCIP_Bool transformed)
    SCIP_Bool SCIPhasGraphUniqueEdgetype(SYM_GRAPH *graph)
    int SCIPgetSymgraphVarnodeColor(SYM_GRAPH *graph, int nodeidx)
    SCIP_RETCODE SCIPaddSymgraphValnode(SCIP *scip, SYM_GRAPH *graph, SCIP_Real val, int *nodeidx)
    SCIP_RETCODE SCIPcreateSymgraph(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH **graph, SCIP_VAR **symvars, int nsymvars, int nopnodes, int nvalnodes, int nconsnodes, int nedges)
    int SCIPgetSymgraphNEdges(SYM_GRAPH *graph)
    int SCIPgetSymgraphVarnodeidx(SCIP *scip, SYM_GRAPH *graph, SCIP_VAR *var)
    SCIP_RETCODE SCIPcomputeSymgraphColors(SCIP *scip, SYM_GRAPH *graph, SYM_SPEC fixedtype)
    SYM_SYMTYPE SCIPgetSymgraphSymtype(SYM_GRAPH *graph)
    SCIP_RETCODE SCIPcopySymgraphAsSubgraph(SCIP *scip, SYM_GRAPH *sourcegraph, SYM_GRAPH *targetgraph, SCIP_CONS *sourcecons, int *rootidx)
    SCIP_RETCODE SCIPaddSymgraphConsnode(SCIP *scip, SYM_GRAPH *graph, SCIP_CONS *cons, SCIP_Real lhs, SCIP_Real rhs, int *nodeidx)
    SCIP_RETCODE SCIPcreateSymgraphConsnodeperm(SCIP *scip, SYM_GRAPH *graph)
    int SCIPgetSymgraphEdgeSecond(SYM_GRAPH *graph, int edgeidx)
    int SCIPgetSymgraphNConsnodes(SYM_GRAPH *graph)
    SCIP_RETCODE SCIPaddSymgraphVarAggregation(SCIP *scip, SYM_GRAPH *graph, int rootidx, SCIP_VAR **vars, SCIP_Real *vals, int nvars, SCIP_Real constant)
    int SCIPgetSymExprdataNConstants(SYM_EXPRDATA *symdata)
    SCIP_RETCODE SCIPupdateSymgraphRhs(SYM_GRAPH *graph, int nodeidx, SCIP_Real newrhs)
    SCIP_RETCODE SCIPfreeSymgraphConsnodeperm(SCIP *scip, SYM_GRAPH *graph)
    SCIP_RETCODE SCIPupdateSymgraphLhs(SYM_GRAPH *graph, int nodeidx, SCIP_Real newlhs)
    int SCIPgetSymgraphNVars(SYM_GRAPH *graph)
    int SCIPgetSymgraphNegatedVarnodeidx(SCIP *scip, SYM_GRAPH *graph, SCIP_VAR *var)
    SCIP_Bool SCIPisSymgraphEdgeColored(SYM_GRAPH *graph, int edgeidx)
    SCIP_RETCODE SCIPfreeSymDataExpr(SCIP *scip, SYM_EXPRDATA **symdata)
    SCIP_VAR ** SCIPgetSymgraphVars(SYM_GRAPH *graph)
    int SCIPgetSymgraphNodeColor(SYM_GRAPH *graph, int nodeidx)
    int SCIPgetSymgraphEdgeColor(SYM_GRAPH *graph, int edgeidx)
    SCIP_RETCODE SCIPextendPermsymDetectionGraphLinear(SCIP *scip, SYM_GRAPH *graph, SCIP_VAR **vars, SCIP_Real *vals, int nvars, SCIP_CONS *cons, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool *success)
    int SCIPgetSymgraphNNodes(SYM_GRAPH *graph)
    SCIP_Real * SCIPgetSymExprdataConstants(SYM_EXPRDATA *symdata)
    SCIP_RETCODE SCIPclearSymgraph(SCIP *scip, SYM_GRAPH *graph, SCIP_VAR **symvars, int nsymvars, SYM_SYMTYPE symtype)
    SCIP_RETCODE SCIPcopySymgraph(SCIP *scip, SYM_GRAPH **graph, SYM_GRAPH *origgraph, int *perm, SYM_SPEC fixedtype)
    SCIP_RETCODE SCIPfixSymgraphVarnode(SYM_GRAPH *graph, SCIP_VAR *var)
    int SCIPgetSymgraphNVarcolors(SYM_GRAPH *graph)
    SCIP_RETCODE SCIPgetCoefSymData(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR *parentexpr, SCIP_Real *coef, SCIP_Bool *success)
    static const SCIP_Real scalars[]
    Definition: lp.c:5959
    internal miscellaneous methods
    #define SCIPerrorMessage
    Definition: pub_message.h:64
    SCIP callable library.
    SCIP_EXPR ** children
    SCIP_Real * coefficients
    SCIP_Real * constants
    int * nodeinfopos
    int * conscolors
    SCIP_Real infinity
    SYM_NODETYPE * nodetypes
    int * valcolors
    SCIP_Real * edgevals
    int * varcolors
    int * consnodeperm
    int * edgefirst
    SCIP_VAR ** symvars
    SCIP_Bool * isfixedvar
    SCIP_Real * vals
    int * edgecolors
    SCIP_Real * rhs
    SCIP_Bool islocked
    SCIP_Bool uniqueedgetype
    SCIP_Real * lhs
    SCIP_CONS ** conss
    int * opcolors
    SYM_SYMTYPE symtype
    int * edgesecond
    structs for symmetry computations
    methods for handling symmetries
    static SCIP_RETCODE ensureNodeArraysSize(SCIP *scip, SYM_GRAPH *graph, int addsize)
    static SCIP_Bool isFixedVar(SCIP_VAR *var, SYM_SPEC fixedtype)
    static int compareOps(int op1, int op2)
    static int compareVarsFixed(SCIP *scip, SCIP_VAR *var1, SCIP_VAR *var2, SCIP_Bool isfixed1, SCIP_Bool isfixed2)
    static int compareVarsSignedPerm(SCIP *scip, SCIP_VAR *var1, SCIP_VAR *var2, SCIP_Bool isneg1, SCIP_Bool isneg2, SCIP_Real infinity)
    static int compareVars(SCIP *scip, SCIP_VAR *var1, SCIP_VAR *var2)
    static SCIP_RETCODE ensureEdgeArraysSize(SCIP *scip, SYM_GRAPH *graph, int addsize)
    static int compareConsnodes(SCIP *scip, SYM_GRAPH *graph, int ind1, int ind2)
    static int compareVarsFixedSignedPerm(SCIP *scip, SCIP_VAR *var1, SCIP_VAR *var2, SCIP_Bool isfixed1, SCIP_Bool isfixed2, SCIP_Bool isneg1, SCIP_Bool isneg2, SCIP_Real infinity)
    static SCIP_DECL_SORTINDCOMP(SYMsortVarnodesPermsym)
    methods for dealing with symmetry detection graphs
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    @ SCIP_ERROR
    Definition: type_retcode.h:43
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    type definitions for symmetry computations
    enum SYM_Symtype SYM_SYMTYPE
    Definition: type_symmetry.h:64
    #define SYM_SPEC_BINARY
    Definition: type_symmetry.h:44
    #define SYM_SPEC_INTEGER
    Definition: type_symmetry.h:43
    enum SYM_Nodetype SYM_NODETYPE
    Definition: type_symmetry.h:74
    @ SYM_NODETYPE_CONS
    Definition: type_symmetry.h:71
    @ SYM_NODETYPE_VAR
    Definition: type_symmetry.h:72
    @ SYM_NODETYPE_OPERATOR
    Definition: type_symmetry.h:69
    @ SYM_NODETYPE_VAL
    Definition: type_symmetry.h:70
    @ SYM_SYMTYPE_SIGNPERM
    Definition: type_symmetry.h:62
    @ SYM_SYMTYPE_PERM
    Definition: type_symmetry.h:61
    uint32_t SYM_SPEC
    Definition: type_symmetry.h:47
    #define SYM_SPEC_REAL
    Definition: type_symmetry.h:45
    @ SCIP_VARTYPE_INTEGER
    Definition: type_var.h:65
    @ SCIP_VARTYPE_CONTINUOUS
    Definition: type_var.h:71
    @ SCIP_VARTYPE_BINARY
    Definition: type_var.h:64
    enum SCIP_Vartype SCIP_VARTYPE
    Definition: type_var.h:73