Scippy

    SCIP

    Solving Constraint Integer Programs

    build_dejavu_graph.cpp
    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 build_dejavu_graph.cpp
    26 * @brief methods to build dejavu graph for symmetry detection
    27 * @author Christopher Hojny
    28 * @author Marc Pfetsch
    29 */
    30
    31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    32
    33#include <string> /* for dejvu */
    34#include "build_dejavu_graph.h"
    35#include "scip/expr_var.h"
    36#include "scip/expr_sum.h"
    37#include "scip/expr_pow.h"
    38#include "scip/expr.h"
    39#include "scip/cons_nonlinear.h"
    40#include "scip/cons_linear.h"
    41#include "scip/scip_mem.h"
    42#include "scip/symmetry_graph.h"
    43
    44
    45/* ------------------- auxiliary functions ------------------- */
    46
    47/** returns whether an edge is considered in grouping process */
    48static
    50 SYM_GRAPH* graph, /**< symmetry detection graph */
    51 int edgeidx, /**< index of edge to be checked */
    52 SCIP_Bool groupbycons /**< whether edges are grouped by constraints */
    53 )
    54{
    55 int first;
    56 int second;
    57
    58 assert(graph != NULL);
    59
    60 first = SCIPgetSymgraphEdgeFirst(graph, edgeidx);
    61 second = SCIPgetSymgraphEdgeSecond(graph, edgeidx);
    62
    63 /* uncolored edges are not grouped */
    64 if ( ! SCIPisSymgraphEdgeColored(graph, edgeidx) )
    65 return FALSE;
    66
    67 /* two variable nodes are connected */
    68 if ( first < 0 && second < 0 )
    69 return FALSE;
    70
    71 if ( ! groupbycons )
    72 {
    73 /* grouping by variables requires one variable node */
    74 if ( first < 0 || second < 0 )
    75 return TRUE;
    76 }
    77 else
    78 {
    79 /* check whether there is exactly one constraint node in edge */
    80 if ( first >= 0 && second >= 0 )
    81 {
    82 if ( (SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS
    83 && SCIPgetSymgraphNodeType(graph, second) != SYM_NODETYPE_CONS)
    85 && SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS) )
    86 return TRUE;
    87 }
    88 else if ( first >= 0 )
    89 {
    90 if ( SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS )
    91 return TRUE;
    92 }
    93 else
    94 {
    95 if ( SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS )
    96 return TRUE;
    97 }
    98 }
    99
    100 return FALSE;
    101}
    102
    103/** adds grouped edges all of which have one common endpoint to a graph
    104 *
    105 * The grouping mechanism works by sorting the edges according to their color. If two
    106 * edges have the same color, they share the same intermediate node, which is connected
    107 * to the common node and the other endpoints of equivalent edges.
    108 */
    109static
    111 SCIP* scip, /**< SCIP pointer */
    112 dejavu::static_graph* G, /**< graph which gets extended */
    113 SCIP_Bool determinesize, /**< whether only the effect of grouping on the graph shall be checked */
    114 int* internodeid, /**< (initialized) pointer to store the ID of the next intermediate node */
    115 int** degrees, /**< pointer to array of degrees for nodes in G */
    116 int* maxdegrees, /**< (initialized) pointer to maximum number of entries degrees can hold */
    117 int* nnodes, /**< (initialized) pointer to store the number of */
    118 int* nedges, /**< (initialized) pointer to store the number of */
    119 int commonnodeidx, /**< index of common node in G */
    120 int* neighbors, /**< neighbors of common node */
    121 int* colors, /**< colors of edges to neighbors */
    122 int nneighbors, /**< number of neighbors */
    123 int* naddednodes, /**< pointer to store number of nodes added to G */
    124 int* naddededges /**< pointer to store number of edges added to G */
    125 )
    126{
    127 int curcolor;
    128 int curstart;
    129 int e;
    130 int f;
    131
    132 assert( G != NULL || determinesize );
    133 assert( internodeid != NULL );
    134 assert( *internodeid >= 0 );
    135 assert( degrees != NULL );
    136 assert( maxdegrees != NULL );
    137 assert( *maxdegrees > 0 );
    138 assert( nnodes != NULL );
    139 assert( nedges != NULL );
    140 assert( neighbors != NULL );
    141 assert( colors != NULL );
    142 assert( naddednodes != NULL );
    143 assert( naddededges != NULL );
    144 assert( commonnodeidx <= *internodeid );
    145
    146 *naddednodes = 0;
    147 *naddededges = 0;
    148
    149 /* sort edges according to color */
    150 SCIPsortIntInt(colors, neighbors, nneighbors);
    151
    152 /* iterate over groups of identical edges and group them, ignoring the last group */
    153 curcolor = colors[0];
    154 curstart = 0;
    155 for (e = 1; e < nneighbors; ++e)
    156 {
    157 /* if a new group started, add edges for previous group */
    158 if ( colors[e] != curcolor )
    159 {
    160 if ( determinesize )
    161 {
    162 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
    163 (*degrees)[*internodeid] = 1;
    164 ++(*degrees)[commonnodeidx];
    165
    166 for (f = curstart; f < e; ++f)
    167 {
    168 assert( neighbors[f] <= *internodeid );
    169 ++(*degrees)[*internodeid];
    170 ++(*degrees)[neighbors[f]];
    171 }
    172 }
    173 else
    174 {
    175 (void) G->add_vertex(curcolor, (*degrees)[*internodeid]);
    176 G->add_edge((unsigned) commonnodeidx, (unsigned) *internodeid);
    177
    178 for (f = curstart; f < e; ++f)
    179 (*G).add_edge((unsigned) neighbors[f], (unsigned) *internodeid);
    180 }
    181 *naddednodes += 1;
    182 *naddededges += e - curstart + 1;
    183 ++(*internodeid);
    184
    185 curcolor = colors[e];
    186 curstart = e;
    187 }
    188 }
    189
    190 /* add edges of last group */
    191 if ( determinesize )
    192 {
    193 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
    194
    195 (*degrees)[*internodeid] = 1;
    196 ++(*degrees)[commonnodeidx];
    197
    198 for (f = curstart; f < nneighbors; ++f)
    199 {
    200 assert( neighbors[f] <= *internodeid );
    201 ++(*degrees)[*internodeid];
    202 ++(*degrees)[neighbors[f]];
    203 }
    204 }
    205 else
    206 {
    207 (void) G->add_vertex(curcolor, (*degrees)[*internodeid]);
    208 G->add_edge((unsigned) commonnodeidx, (unsigned) *internodeid);
    209
    210 for (f = curstart; f < nneighbors; ++f)
    211 G->add_edge((unsigned) neighbors[f], (unsigned) *internodeid);
    212 }
    213 *naddednodes += 1;
    214 *naddededges += nneighbors - curstart + 1;
    215 ++(*internodeid);
    216
    217 return SCIP_OKAY;
    218}
    219
    220/** either creates a graph or determines its size */
    221static
    223 SCIP* scip, /**< SCIP instance */
    224 SYM_GRAPH* graph, /**< symmetry detection graph */
    225 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
    226 dejavu::static_graph* G, /**< graph to be constructed */
    227 int* nnodes, /**< pointer to store the total number of nodes in graph */
    228 int* nedges, /**< pointer to store the total number of edges in graph */
    229 int** degrees, /**< pointer to store the degrees of the nodes */
    230 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
    231 SCIP_Bool* success /**< pointer to store whether the construction was successful */
    232 )
    233{
    234 SYM_SYMTYPE symtype;
    235 SYM_NODETYPE comparetype;
    236 SCIP_Bool groupByConstraints;
    237 int* groupfirsts = NULL;
    238 int* groupseconds = NULL;
    239 int* groupcolors = NULL;
    240 int ngroupedges = 0;
    241 int nvarnodestoadd;
    242 int internodeid;
    243 int nsymvars;
    244 int nsymedges;
    245 int first;
    246 int second;
    247 int color;
    248 int e;
    249 int j;
    250
    251 assert( scip != NULL );
    252 assert( graph != NULL );
    253 assert( G != NULL || determinesize );
    254 assert( nnodes != NULL );
    255 assert( nedges != NULL );
    256 assert( degrees != NULL );
    257 assert( maxdegrees != NULL );
    258 assert( success != NULL );
    259
    260 *success = TRUE;
    261
    262 /* collect basic information from symmetry detection graph */
    263 nsymvars = SCIPgetSymgraphNVars(graph);
    264 symtype = SCIPgetSymgraphSymtype(graph);
    265 switch ( symtype )
    266 {
    267 case SYM_SYMTYPE_PERM:
    268 nvarnodestoadd = nsymvars;
    269 break;
    270 default:
    271 assert( symtype == SYM_SYMTYPE_SIGNPERM );
    272 nvarnodestoadd = 2 * nsymvars;
    273 }
    274
    275 /* possibly find number of nodes in dejavu graph */
    276 if ( determinesize )
    277 {
    278 /* initialize counters */
    279 *nnodes = SCIPgetSymgraphNNodes(graph) + nvarnodestoadd;
    280 *nedges = 0;
    281
    282 /* possibly allocate memory for degrees (will grow dynamically) */
    283 *degrees = NULL;
    284 *maxdegrees = 0;
    285 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 100) );
    286 for (j = 0; j < *nnodes; ++j)
    287 (*degrees)[j] = 0;
    288 }
    289 else
    290 {
    291 assert( G != NULL );
    292
    293 /* add nodes for variables */
    294 for (j = 0; j < nvarnodestoadd; ++j)
    295 (void) G->add_vertex(SCIPgetSymgraphVarnodeColor(graph, j), (*degrees)[j]);
    296
    297 /* add nodes for remaining nodes of graph */
    298 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
    299 (void) G->add_vertex(SCIPgetSymgraphNodeColor(graph, j), (*degrees)[nvarnodestoadd + j]);
    300 }
    301
    302 /* determine grouping depending on the number of rhs vs. variables */
    304 groupByConstraints = TRUE;
    305 else
    306 groupByConstraints = FALSE;
    307
    308 /* allocate arrays to collect edges to be grouped */
    309 nsymedges = SCIPgetSymgraphNEdges(graph);
    310 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
    311 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
    312 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
    313
    314 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
    315 internodeid = SCIPgetSymgraphNNodes(graph) + nvarnodestoadd;
    316 for (e = 0; e < SCIPgetSymgraphNEdges(graph); ++e)
    317 {
    318 first = SCIPgetSymgraphEdgeFirst(graph, e);
    319 second = SCIPgetSymgraphEdgeSecond(graph, e);
    320
    321 /* get the first and second node in edge (corrected by variable shift) */
    322 if ( first < 0 )
    323 first = -first - 1;
    324 else
    325 first += nvarnodestoadd;
    326 if ( second < 0 )
    327 second = -second - 1;
    328 else
    329 second += nvarnodestoadd;
    330 assert(first >= 0);
    331 assert(second >= 0);
    332
    333 /* check whether edge is used for grouping */
    334 if ( ! SCIPhasGraphUniqueEdgetype(graph) && isEdgeGroupable(graph, e, groupByConstraints) )
    335 {
    336 /* store edge, first becomes the cons or var node */
    337 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
    338
    339 if ( SCIPgetSymgraphNodeType(graph, SCIPgetSymgraphEdgeFirst(graph, e)) == comparetype )
    340 {
    341 groupfirsts[ngroupedges] = first;
    342 groupseconds[ngroupedges] = second;
    343 }
    344 else
    345 {
    346 groupfirsts[ngroupedges] = second;
    347 groupseconds[ngroupedges] = first;
    348 }
    349 groupcolors[ngroupedges++] = SCIPgetSymgraphEdgeColor(graph, e);
    350 }
    351 else
    352 {
    353 /* immediately add edge or increase degrees */
    354 assert(0 <= first && first < *nnodes);
    355 assert(0 <= second && second < *nnodes);
    356
    357 /* possibly split edge if it is colored */
    358 if ( ! SCIPhasGraphUniqueEdgetype(graph) && SCIPisSymgraphEdgeColored(graph, e) )
    359 {
    360 if ( determinesize )
    361 {
    362 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, internodeid + 1) );
    363
    364 ++(*degrees)[first];
    365 ++(*degrees)[second];
    366 (*degrees)[internodeid] = 2;
    367 ++(*nnodes);
    368 *nedges += 2;
    369 ++internodeid;
    370 }
    371 else
    372 {
    373 assert( internodeid < *nnodes );
    374 assert( G != NULL );
    375
    376 color = SCIPgetSymgraphEdgeColor(graph, e);
    377 (void) G->add_vertex(color, (*degrees)[internodeid]);
    378
    379 G->add_edge((unsigned) first, (unsigned) internodeid);
    380 G->add_edge((unsigned) second, (unsigned) internodeid++);
    381 }
    382 }
    383 else
    384 {
    385 if ( determinesize )
    386 {
    387 ++(*degrees)[first];
    388 ++(*degrees)[second];
    389 ++(*nedges);
    390 }
    391 else
    392 {
    393 assert( G != NULL );
    394 if ( first < second )
    395 G->add_edge((unsigned) first, (unsigned) second);
    396 else
    397 G->add_edge((unsigned) second, (unsigned) first);
    398 }
    399 }
    400 }
    401 }
    402
    403 /* possibly add groupable edges */
    404 if ( ngroupedges > 0 )
    405 {
    406 int firstidx = 0;
    407 int firstnodeidx;
    408 int naddednodes;
    409 int naddededges;
    410
    411 /* sort edges according to their first nodes */
    412 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
    413 firstnodeidx = groupfirsts[0];
    414
    415 for (j = 1; j < ngroupedges; ++j)
    416 {
    417 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
    418 if ( groupfirsts[j] != firstnodeidx )
    419 {
    420 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
    421 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], j - firstidx,
    422 &naddednodes, &naddededges) );
    423
    424 firstidx = j;
    425 firstnodeidx = groupfirsts[j];
    426
    427 if ( determinesize )
    428 {
    429 *nnodes += naddednodes;
    430 *nedges += naddededges;
    431 }
    432 }
    433 }
    434
    435 /* process the last group */
    436 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
    437 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], ngroupedges - firstidx,
    438 &naddednodes, &naddededges) );
    439
    440 if ( determinesize )
    441 {
    442 *nnodes += naddednodes;
    443 *nedges += naddededges;
    444 }
    445 }
    446
    447 SCIPfreeBufferArray(scip, &groupcolors);
    448 SCIPfreeBufferArray(scip, &groupseconds);
    449 SCIPfreeBufferArray(scip, &groupfirsts);
    450
    451 /* for signed permutation, also add edges connecting a variable and its negation */
    452 switch ( SCIPgetSymgraphSymtype(graph) )
    453 {
    455 if ( determinesize )
    456 {
    457 for (j = 0; j < nvarnodestoadd; ++j)
    458 ++(*degrees)[j];
    459 *nedges += nsymvars;
    460 }
    461 else
    462 {
    463 assert( G != NULL );
    464 for (j = 0; j < nsymvars; ++j)
    465 G->add_edge((unsigned) j, (unsigned) (j + nsymvars));
    466 }
    467 break;
    468 default:
    469 assert( SCIPgetSymgraphSymtype(graph) == SYM_SYMTYPE_PERM );
    470 }
    471
    472 if ( determinesize )
    473 {
    474 SCIPdebugMsg(scip, "#nodes: %d\n", *nnodes);
    475 SCIPdebugMsg(scip, "#nodes for variables: %d\n", nvarnodestoadd);
    476 SCIPdebugMsg(scip, "#nodes for rhs: %d\n", SCIPgetSymgraphNConsnodes(graph));
    477 SCIPdebugMsg(scip, "#edges: %d\n", *nedges);
    478 }
    479
    480 return SCIP_OKAY;
    481}
    482
    483/** either creates a graph for checking symmetries or determines its size
    484 *
    485 * The input are two graphs and the graph to be constructed consists of copies
    486 * of the two input graphs, in which non-variable nodes are colored according
    487 * to the colors used in symmetry detection. Each variable gets a unique color.
    488 */
    489static
    491 SCIP* scip, /**< SCIP instance */
    492 SYM_GRAPH* graph1, /**< first symmetry detection graph */
    493 SYM_GRAPH* graph2, /**< second symmetry detection graph */
    494 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
    495 dejavu::static_graph* G, /**< graph to be constructed */
    496 int* nnodes, /**< pointer to store the total number of nodes in graph */
    497 int* nedges, /**< pointer to store the total number of edges in graph */
    498 int** degrees, /**< pointer to store the degrees of the nodes */
    499 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
    500 int* nnodesfromG1, /**< pointer to store number of nodes in dejavu graph arising from G1 (or NULL) */
    501 SCIP_Bool* success /**< pointer to store whether the graph could be built */
    502 )
    503{
    504 SYM_SYMTYPE symtype;
    505 SYM_NODETYPE comparetype;
    506 SCIP_Bool groupByConstraints;
    507 SYM_GRAPH* graph;
    508 int* nvarused1 = NULL;
    509 int* nvarused2 = NULL;
    510 int* varlabel = NULL;
    511 int* groupfirsts = NULL;
    512 int* groupseconds = NULL;
    513 int* groupcolors = NULL;
    514 int ngroupedges = 0;
    515 int nusedvars = 0;
    516 int nodeshift;
    517 int curnnodes;
    518 int nvarnodestoadd;
    519 int internodeid;
    520 int nsymvars;
    521 int nsymedges;
    522 int first;
    523 int second;
    524 int color;
    525 int e;
    526 int i;
    527 int j;
    528
    529 assert( scip != NULL );
    530 assert( graph1 != NULL );
    531 assert( graph2 != NULL );
    532 assert( G != NULL || determinesize );
    533 assert( nnodes != NULL );
    534 assert( nedges != NULL );
    535 assert( degrees != NULL );
    536 assert( maxdegrees != NULL );
    537 assert( success != NULL );
    538
    539 *success = FALSE;
    540
    541 /* graphs cannot be symmetric */
    542 if ( SCIPgetSymgraphNEdges(graph1) != SCIPgetSymgraphNEdges(graph2)
    543 || SCIPgetSymgraphNVars(graph1) != SCIPgetSymgraphNVars(graph2) )
    544 return SCIP_OKAY;
    545
    546 /* collect basic information from symmetry detection graph */
    547 nsymvars = SCIPgetSymgraphNVars(graph1);
    548 nsymedges = SCIPgetSymgraphNEdges(graph1);
    549 symtype = SCIPgetSymgraphSymtype(graph1);
    550 switch ( symtype )
    551 {
    552 case SYM_SYMTYPE_PERM:
    553 nvarnodestoadd = nsymvars;
    554 break;
    555 default:
    556 assert( symtype == SYM_SYMTYPE_SIGNPERM );
    557 nvarnodestoadd = 2 * nsymvars;
    558 }
    559
    560 /* find the variables that are contained in an edge */
    561 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused1, nvarnodestoadd) );
    562 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused2, nvarnodestoadd) );
    563 SCIP_CALL( SCIPallocBufferArray(scip, &varlabel, nvarnodestoadd) );
    564
    565 for (e = 0; e < nsymedges; ++e)
    566 {
    567 first = SCIPgetSymgraphEdgeFirst(graph1, e);
    568 second = SCIPgetSymgraphEdgeSecond(graph1, e);
    569 if ( first < 0 )
    570 nvarused1[-first - 1] += 1;
    571 if ( second < 0 )
    572 nvarused1[-second - 1] += 1;
    573
    574 first = SCIPgetSymgraphEdgeFirst(graph2, e);
    575 second = SCIPgetSymgraphEdgeSecond(graph2, e);
    576 if ( first < 0 )
    577 nvarused2[-first - 1] += 1;
    578 if ( second < 0 )
    579 nvarused2[-second - 1] += 1;
    580 }
    581
    582 for (j = 0; j < nvarnodestoadd; ++j)
    583 {
    584 /* graphs cannot be identical */
    585 if ( nvarused1[j] != nvarused2[j] )
    586 {
    587 SCIPfreeBufferArray(scip, &varlabel);
    588 SCIPfreeBufferArray(scip, &nvarused2);
    589 SCIPfreeBufferArray(scip, &nvarused1);
    590
    591 return SCIP_OKAY;
    592 }
    593
    594 /* relabel variables by restricting to variables used in constraint (or their negation) */
    595 if ( nvarused1[j] > 0 || nvarused1[j % SCIPgetSymgraphNVars(graph1)] > 0 )
    596 varlabel[j] = nusedvars++;
    597 else
    598 varlabel[j] = -1;
    599 }
    600
    601 /* possibly find number of nodes in dejavu graph and allocate memory for dynamic array */
    602 if ( determinesize )
    603 {
    604 *degrees = NULL;
    605 *maxdegrees = 0;
    606 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees,
    607 SCIPgetSymgraphNNodes(graph1) + SCIPgetSymgraphNNodes(graph2) + 2 * nusedvars + 100) );
    608
    609 *nnodes = 0;
    610 *nedges = 0;
    611 }
    612
    613 /* determine grouping depending on the number of rhs vs. variables */
    614 if ( SCIPgetSymgraphNConsnodes(graph1) < SCIPgetSymgraphNVars(graph1) )
    615 groupByConstraints = TRUE;
    616 else
    617 groupByConstraints = FALSE;
    618
    619 /* allocate arrays to collect edges to be grouped */
    620 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
    621 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
    622 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
    623
    624 /* collect information or generate graphs, we shift the node indices of the second graph when adding them to G */
    625 nodeshift = 0;
    626 for (i = 0; i < 2; ++i)
    627 {
    628 curnnodes = 0;
    629 graph = i == 0 ? graph1 : graph2;
    630 ngroupedges = 0;
    631
    632 /* possibly add nodes for variables and remaining nodes, each variable gets a unique color */
    633 if ( determinesize )
    634 {
    635 /* add nodes for variables */
    636 for (j = 0; j < nvarnodestoadd; ++j)
    637 {
    638 if ( varlabel[j] >= 0 )
    639 {
    640 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
    641 (*degrees)[nodeshift + varlabel[j]] = 0;
    642 ++(*nnodes);
    643 ++curnnodes;
    644 }
    645 }
    646
    647 /* add nodes for remaining nodes of graph */
    648 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
    649 {
    650 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
    651 (*degrees)[nodeshift + nusedvars + j] = 0;
    652 ++(*nnodes);
    653 ++curnnodes;
    654 }
    655 }
    656 else
    657 {
    658 assert( G != NULL );
    659
    660 /* add nodes for variables, each variable gets a unique color */
    661 for (j = 0; j < nvarnodestoadd; ++j)
    662 {
    663 if ( varlabel[j] >= 0 )
    664 {
    665 (void) G->add_vertex(j, (*degrees)[nodeshift + varlabel[j]]);
    666 ++curnnodes;
    667 }
    668 }
    669
    670 /* add nodes for remaining nodes of graph, ensure that colors do not conflict with variable colors */
    671 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
    672 {
    673 (void) G->add_vertex(nusedvars + SCIPgetSymgraphNodeColor(graph, j),
    674 (*degrees)[nodeshift + nusedvars + j]);
    675 ++curnnodes;
    676 }
    677 }
    678
    679 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
    680 internodeid = nodeshift + curnnodes;
    681 for (e = 0; e < nsymedges; ++e)
    682 {
    683 first = SCIPgetSymgraphEdgeFirst(graph, e);
    684 second = SCIPgetSymgraphEdgeSecond(graph, e);
    685
    686 /* get the first and second node in edge (corrected by variable shift) */
    687 if ( first < 0 )
    688 first = varlabel[-first - 1];
    689 else
    690 first = nusedvars + first;
    691 if ( second < 0 )
    692 second = varlabel[-second - 1];
    693 else
    694 second = nusedvars + second;
    695
    696 /* check whether edge is used for grouping */
    697 if ( ! SCIPhasGraphUniqueEdgetype(graph) && isEdgeGroupable(graph, e, groupByConstraints) )
    698 {
    699 /* store edge, first becomes the cons or var node */
    700 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
    701
    702 if ( SCIPgetSymgraphNodeType(graph, SCIPgetSymgraphEdgeFirst(graph, e)) == comparetype )
    703 {
    704 groupfirsts[ngroupedges] = nodeshift + first;
    705 groupseconds[ngroupedges] = nodeshift + second;
    706 }
    707 else
    708 {
    709 groupfirsts[ngroupedges] = nodeshift + second;
    710 groupseconds[ngroupedges] = nodeshift + first;
    711 }
    712 groupcolors[ngroupedges++] = nusedvars + SCIPgetSymgraphEdgeColor(graph, e);
    713 }
    714 else
    715 {
    716 /* immediately add edge or increase degrees */
    717 assert(0 <= first && first < *nnodes);
    718 assert(0 <= second && second < *nnodes);
    719
    720 /* possibly split edge if it is colored */
    721 if ( ! SCIPhasGraphUniqueEdgetype(graph) && SCIPisSymgraphEdgeColored(graph, e) )
    722 {
    723 if ( determinesize )
    724 {
    725 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, internodeid + 1) );
    726
    727 ++(*degrees)[nodeshift + first];
    728 ++(*degrees)[nodeshift + second];
    729 (*degrees)[internodeid] = 2;
    730 ++(*nnodes);
    731 *nedges += 2;
    732 }
    733 else
    734 {
    735 assert( internodeid < *nnodes );
    736 assert( G != NULL );
    737
    738 color = SCIPgetSymgraphEdgeColor(graph, e);
    739 (void) G->add_vertex(nusedvars + color, (*degrees)[internodeid]);
    740 G->add_edge((unsigned) (nodeshift + first), (unsigned) internodeid);
    741 G->add_edge((unsigned) (nodeshift + second), (unsigned) internodeid);
    742 }
    743 ++internodeid;
    744 ++curnnodes;
    745 }
    746 else
    747 {
    748 if ( determinesize )
    749 {
    750 ++(*degrees)[nodeshift + first];
    751 ++(*degrees)[nodeshift + second];
    752 ++(*nedges);
    753 }
    754 else
    755 {
    756 assert( G != NULL );
    757
    758 if ( first < second )
    759 G->add_edge((unsigned) (nodeshift + first), (unsigned) (nodeshift + second));
    760 else
    761 G->add_edge((unsigned) (nodeshift + second), (unsigned) (nodeshift + first));
    762 }
    763 }
    764 }
    765 }
    766
    767 /* possibly add groupable edges */
    768 if ( ngroupedges > 0 )
    769 {
    770 int firstidx = 0;
    771 int firstnodeidx;
    772 int naddednodes;
    773 int naddededges;
    774
    775 /* sort edges according to their first nodes */
    776 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
    777 firstnodeidx = groupfirsts[0];
    778
    779 for (j = 1; j < ngroupedges; ++j)
    780 {
    781 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
    782 if ( groupfirsts[j] != firstnodeidx )
    783 {
    784 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
    785 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], j - firstidx,
    786 &naddednodes, &naddededges) );
    787
    788 firstidx = j;
    789 firstnodeidx = groupfirsts[j];
    790
    791 if ( determinesize )
    792 {
    793 *nnodes += naddednodes;
    794 *nedges += naddededges;
    795 }
    796 curnnodes += naddednodes;
    797 }
    798 }
    799
    800 /* process the last group */
    801 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
    802 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], ngroupedges - firstidx,
    803 &naddednodes, &naddededges) );
    804
    805 if ( determinesize )
    806 {
    807 *nnodes += naddednodes;
    808 *nedges += naddededges;
    809 }
    810 curnnodes += naddednodes;
    811 }
    812
    813 /* for signed permutation, also add edges connecting a variable and its negation */
    815 {
    816 if ( determinesize )
    817 {
    818 for (j = 0; j < nusedvars; ++j)
    819 ++(*degrees)[nodeshift + j];
    820 (*nedges) += nusedvars / 2;
    821 }
    822 else
    823 {
    824 assert( G != NULL );
    825
    826 for (j = 0; j < nusedvars/2; ++j)
    827 G->add_edge((unsigned) (nodeshift + j), (unsigned) (nodeshift + j + nusedvars / 2));
    828 }
    829 }
    830 nodeshift = curnnodes;
    831
    832 if ( i == 0 && nnodesfromG1 != NULL )
    833 *nnodesfromG1 = curnnodes;
    834 }
    835
    836 SCIPfreeBufferArray(scip, &groupcolors);
    837 SCIPfreeBufferArray(scip, &groupseconds);
    838 SCIPfreeBufferArray(scip, &groupfirsts);
    839
    840 SCIPfreeBufferArray(scip, &varlabel);
    841 SCIPfreeBufferArray(scip, &nvarused2);
    842 SCIPfreeBufferArray(scip, &nvarused1);
    843
    844 *success = TRUE;
    845
    846 return SCIP_OKAY;
    847}
    848
    849
    850/** compute generators of symmetry group */
    852 SCIP* scip, /**< SCIP pointer */
    853 dejavu::static_graph* dejavugraph, /**< pointer to hold dejavu graph being created */
    854 SYM_GRAPH* graph, /**< symmetry detection graph */
    855 SCIP_Bool* success /**< pointer to store whether dejavugraph could be built */
    856 )
    857{
    858 int* degrees;
    859 int maxdegrees;
    860 int nnodes;
    861 int nedges;
    862
    863 assert( scip != NULL );
    864 assert( dejavugraph != NULL );
    865 assert( graph != NULL );
    866
    867 *success = FALSE;
    868
    869 /* determine number of nodes and edges */
    870 SCIP_CALL( createOrDetermineSizeGraph(scip, graph, TRUE, NULL, &nnodes, &nedges, &degrees, &maxdegrees, success) );
    871
    872 if ( ! *success )
    873 {
    875 "Stopped symmetry computation: Symmetry graph would become too large.\n");
    876 return SCIP_OKAY;
    877 }
    878
    879 /* init graph */
    880 (*dejavugraph).initialize_graph(nnodes, nedges);
    881
    882 /* add the nodes for linear and nonlinear constraints to the graph */
    883 SCIP_CALL( createOrDetermineSizeGraph(scip, graph, FALSE, dejavugraph,
    884 &nnodes, &nedges, &degrees, &maxdegrees, success) );
    885
    886 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
    887
    888 SCIPdebugMsg(scip, "Symmetry detection graph has %d nodes.\n", nnodes);
    889
    890 return SCIP_OKAY;
    891}
    892
    893/** returns whether two given graphs are identical */
    895 SCIP* scip, /**< SCIP pointer */
    896 dejavu::static_graph* dejavugraph, /**< pointer to hold dejavu graph being created */
    897 SYM_GRAPH* G1, /**< first graph */
    898 SYM_GRAPH* G2, /**< second graph */
    899 int* nnodes, /**< pointer to store number of nodes in dejavu graph */
    900 int* nnodesfromG1, /**< pointer to store number of nodes in dejavu graph arising from G1 */
    901 SCIP_Bool* success /**< pointer to store whether dejavugraph could be built */
    902 )
    903{
    904 int* degrees = NULL;
    905 int maxdegrees = 0;
    906 int nedges;
    907
    908 assert( scip != NULL );
    909 assert( dejavugraph != NULL );
    910 assert( G1 != NULL );
    911 assert( G2 != NULL );
    912 assert( nnodes != NULL );
    913 assert( nnodesfromG1 != NULL );
    914 assert( success != NULL );
    915
    916 *success = FALSE;
    917 *nnodes = 0;
    918 *nnodesfromG1 = 0;
    919
    920 /* some simple checks */
    921 if ( G1->nnodes != G2->nnodes || G1->nopnodes != G2->nopnodes || G1->nvalnodes != G2->nvalnodes
    922 || G1->nconsnodes != G2->nconsnodes || G1->nedges != G2->nedges )
    923 return SCIP_OKAY;
    924
    925 /* determine number of nodes and edges */
    927 nnodes, &nedges, &degrees, &maxdegrees, nnodesfromG1, success) );
    928
    929 if ( ! *success )
    930 {
    931 assert( degrees == NULL );
    932 assert( maxdegrees == 0 );
    933 return SCIP_OKAY;
    934 }
    935 if ( *nnodes % 2 != 0 )
    936 {
    937 assert( degrees != NULL );
    938 assert( maxdegrees > 0 );
    939
    940 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
    941 return SCIP_OKAY;
    942 }
    943
    944 /* init graph */
    945 (*dejavugraph).initialize_graph(*nnodes, nedges);
    946
    947 /* add the nodes for linear and nonlinear constraints to the graph */
    949 nnodes, &nedges, &degrees, &maxdegrees, NULL, success) );
    950 assert( *success );
    951
    952 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
    953
    954 return SCIP_OKAY;
    955}
    SCIP_RETCODE SYMbuildDejavuGraphCheck(SCIP *scip, dejavu::static_graph *dejavugraph, SYM_GRAPH *G1, SYM_GRAPH *G2, int *nnodes, int *nnodesfromG1, SCIP_Bool *success)
    static SCIP_RETCODE addOrDetermineEffectOfGroupedEdges(SCIP *scip, dejavu::static_graph *G, SCIP_Bool determinesize, int *internodeid, int **degrees, int *maxdegrees, int *nnodes, int *nedges, int commonnodeidx, int *neighbors, int *colors, int nneighbors, int *naddednodes, int *naddededges)
    static SCIP_Bool isEdgeGroupable(SYM_GRAPH *graph, int edgeidx, SCIP_Bool groupbycons)
    static SCIP_RETCODE createOrDetermineSizeGraphCheck(SCIP *scip, SYM_GRAPH *graph1, SYM_GRAPH *graph2, SCIP_Bool determinesize, dejavu::static_graph *G, int *nnodes, int *nedges, int **degrees, int *maxdegrees, int *nnodesfromG1, SCIP_Bool *success)
    static SCIP_RETCODE createOrDetermineSizeGraph(SCIP *scip, SYM_GRAPH *graph, SCIP_Bool determinesize, dejavu::static_graph *G, int *nnodes, int *nedges, int **degrees, int *maxdegrees, SCIP_Bool *success)
    SCIP_RETCODE SYMbuildDejavuGraph(SCIP *scip, dejavu::static_graph *dejavugraph, SYM_GRAPH *graph, SCIP_Bool *success)
    methods to build dejavu graph for symmetry detection
    Constraint handler for linear constraints in their most general form, .
    constraint handler for nonlinear constraints specified by algebraic expressions
    #define NULL
    Definition: def.h:248
    #define SCIP_Bool
    Definition: def.h:91
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define SCIP_CALL_ABORT(x)
    Definition: def.h:334
    #define SCIP_CALL(x)
    Definition: def.h:355
    private functions to work with algebraic expressions
    power and signed power expression handlers
    sum expression handler
    variable expression handler
    #define nnodes
    Definition: gastrans.c:74
    void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:225
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    #define SCIPfreeBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:110
    #define SCIPensureBlockMemoryArray(scip, ptr, arraysizeptr, minsize)
    Definition: scip_mem.h:107
    #define SCIPallocClearBufferArray(scip, ptr, num)
    Definition: scip_mem.h:126
    #define SCIPallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:124
    #define SCIPfreeBufferArray(scip, ptr)
    Definition: scip_mem.h:136
    void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
    void SCIPsortIntIntInt(int *intarray1, int *intarray2, int *intarray3, int len)
    SYM_NODETYPE SCIPgetSymgraphNodeType(SYM_GRAPH *graph, int nodeidx)
    int SCIPgetSymgraphEdgeFirst(SYM_GRAPH *graph, int edgeidx)
    SCIP_Bool SCIPhasGraphUniqueEdgetype(SYM_GRAPH *graph)
    int SCIPgetSymgraphVarnodeColor(SYM_GRAPH *graph, int nodeidx)
    int SCIPgetSymgraphNEdges(SYM_GRAPH *graph)
    SYM_SYMTYPE SCIPgetSymgraphSymtype(SYM_GRAPH *graph)
    int SCIPgetSymgraphEdgeSecond(SYM_GRAPH *graph, int edgeidx)
    int SCIPgetSymgraphNConsnodes(SYM_GRAPH *graph)
    int SCIPgetSymgraphNVars(SYM_GRAPH *graph)
    SCIP_Bool SCIPisSymgraphEdgeColored(SYM_GRAPH *graph, int edgeidx)
    int SCIPgetSymgraphNodeColor(SYM_GRAPH *graph, int nodeidx)
    int SCIPgetSymgraphEdgeColor(SYM_GRAPH *graph, int edgeidx)
    int SCIPgetSymgraphNNodes(SYM_GRAPH *graph)
    public methods for memory management
    methods for dealing with symmetry detection graphs
    @ SCIP_VERBLEVEL_MINIMAL
    Definition: type_message.h:59
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    enum SYM_Symtype SYM_SYMTYPE
    Definition: type_symmetry.h:64
    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_SYMTYPE_SIGNPERM
    Definition: type_symmetry.h:62
    @ SYM_SYMTYPE_PERM
    Definition: type_symmetry.h:61