Scippy

    SCIP

    Solving Constraint Integer Programs

    compute_symmetry_nauty.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 compute_symmetry_nauty.c
    26 * @brief interface for symmetry computations to nauty/traces
    27 * @author Marc Pfetsch
    28 */
    29
    30/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    31
    32#include "compute_symmetry.h"
    33
    34/* the following determines whether nauty or traces is used: */
    35#define NAUTY
    36
    37/* include nauty/traces */
    38/* turn off warning (just for including nauty/traces) */
    39#pragma GCC diagnostic ignored "-Wunused-variable"
    40#pragma GCC diagnostic ignored "-Wredundant-decls"
    41#pragma GCC diagnostic ignored "-Wpedantic"
    42
    43#ifdef NAUTY
    44#include "nauty/nauty.h"
    45#include "nauty/nausparse.h"
    46#else
    47#include "nauty/traces.h"
    48#endif
    49
    50#pragma GCC diagnostic warning "-Wunused-variable"
    51#pragma GCC diagnostic warning "-Wredundant-decls"
    52#pragma GCC diagnostic warning "-Wpedantic"
    53
    54#include "scip/symmetry_graph.h"
    55#include "scip/expr_var.h"
    56#include "scip/expr_sum.h"
    57#include "scip/expr_pow.h"
    58#include "scip/expr.h"
    59#include "scip/cons_nonlinear.h"
    60#include "scip/cons_linear.h"
    61#include "scip/scip_mem.h"
    62#include "tinycthread/tinycthread.h"
    63
    64/** struct for nauty callback */
    66{
    67 SCIP* scip; /**< SCIP pointer */
    68 SYM_SYMTYPE symtype; /**< type of symmetries that need to be computed */
    69 int npermvars; /**< number of variables for permutations */
    70 int nperms; /**< number of permutations */
    71 int** perms; /**< permutation generators as (nperms x npermvars) matrix */
    72 int nmaxperms; /**< maximal number of permutations */
    73 int maxgenerators; /**< maximal number of generators to be constructed (= 0 if unlimited) */
    74 SCIP_Bool restricttovars; /**< whether permutations shall be restricted to variables */
    75 int maxlevel; /**< maximum depth level of nauty's search tree (-1: unlimited) */
    76};
    77typedef struct NAUTY_Data NAUTY_DATA;
    78
    79/** static data for nauty callback */
    80#if defined(_Thread_local)
    81static _Thread_local NAUTY_DATA data_;
    82#else
    83static NAUTY_DATA data_;
    84#endif
    85
    86/* ------------------- hook functions ------------------- */
    87
    88#ifdef NAUTY
    89
    90/** callback function for nauty */ /*lint -e{715}*/
    91static
    93 int count, /**< ID of this generator */
    94 int* p, /**< generator (permutation) that nauty found */
    95 int* orbits, /**< orbits generated by the group found so far */
    96 int numorbits, /**< number of orbits */
    97 int stabvertex, /**< stabilizing node */
    98 int n /**< number of nodes in the graph */
    99 )
    100{ /* lint --e{715} */
    101 SCIP_Bool isidentity = TRUE;
    102 int permlen;
    103 int* pp;
    104
    105 assert( p != NULL );
    106
    107 /* make sure we do not generate more than maxgenerators many permutations */
    109 {
    110 /* request a kill from nauty */
    111 nauty_kill_request = 1;
    112 return;
    113 }
    114
    115 /* copy first part of automorphism */
    116 if ( data_.restricttovars )
    117 {
    119 permlen = data_.npermvars;
    120 else
    121 permlen = 2 * data_.npermvars;
    122 }
    123 else
    124 permlen = n;
    125
    126 /* check whether permutation is identity */
    127 for (int j = 0; j < permlen; ++j)
    128 {
    129 if ( (int) p[j] != j )
    130 isidentity = FALSE;
    131 }
    132
    133 /* don't store identity permutations */
    134 if ( isidentity )
    135 return;
    136
    137 /* check whether we should allocate space for perms */
    138 if ( data_.nmaxperms <= 0 )
    139 {
    140 if ( data_.maxgenerators == 0 )
    141 data_.nmaxperms = 100; /* seems to cover many cases */
    142 else
    144
    146 return;
    147 }
    148 else if ( data_.nperms >= data_.nmaxperms ) /* check whether we need to resize */
    149 {
    150 int newsize;
    151
    152 newsize = SCIPcalcMemGrowSize(data_.scip, data_.nperms + 1);
    153 assert( newsize >= data_.nperms );
    154 assert( data_.maxgenerators == 0 );
    155
    157 return;
    158
    159 data_.nmaxperms = newsize;
    160 }
    161
    162 if ( SCIPduplicateBlockMemoryArray(data_.scip, &pp, p, permlen) != SCIP_OKAY )
    163 return;
    164 data_.perms[data_.nperms++] = pp;
    165}
    166
    167/** callback function for nauty to terminate early */ /*lint -e{715}*/
    168static
    170 graph* g, /**< sparse graph for nauty */
    171 int* lab, /**< labels of node */
    172 int* ptn, /**< array indicating change of set in node parition of graph */
    173 int level, /**< level of current node in nauty's tree */
    174 int numcells, /**< number of cells in current partition */
    175 int tc, /**< index of target cells in lab if children need to be explored */
    176 int code, /**< code produced by refinement and vertex-invariant procedures */
    177 int m, /**< number of edges in the graph */
    178 int n /**< number of nodes in the graph */
    179 )
    180{ /* lint --e{715} */
    181 /* add level limit to work around call stack overflow in nauty */
    182 if ( level > data_.maxlevel && data_.maxlevel >= 0 )
    183 {
    185 "symmetry computation terminated early because Nauty level limit %d is exceeded\n",
    188 "for running full symmetry detection, increase value of parameter propagating/symmetry/nautymaxlevel\n");
    189 nauty_kill_request = 1;
    190 }
    191}
    192
    193#else
    194
    195/** callback function for traces */
    196static
    197void traceshook(
    198 int count, /**< number of generator */
    199 int* p, /**< generator that traces found */
    200 int n /**< number of nodes in the graph */
    201 )
    202{
    203 SCIP_Bool isidentity = TRUE;
    204 int permlen;
    205 int* pp;
    206 int j;
    207
    208 assert( p != NULL );
    209
    210 /* make sure we do not generate more than maxgenerators many permutations */
    212 {
    213 /* request a kill from traces */
    214 nauty_kill_request = 1;
    215 return;
    216 }
    217
    218 /* copy first part of automorphism */
    219 if ( data_.restricttovars )
    220 {
    222 permlen = data_.npermvars;
    223 else
    224 permlen = 2 * data_.npermvars;
    225 }
    226 else
    227 permlen = n;
    228
    229 /* check whether permutation is identity */
    230 for (j = 0; j < permlen; ++j)
    231 {
    232 if ( (int) p[j] != j )
    233 isidentity = FALSE;
    234 }
    235
    236 /* ignore trivial generators, i.e. generators that only permute the constraints */
    237 if ( isidentity )
    238 return;
    239
    240 /* check whether we should allocate space for perms */
    241 if ( data_.nmaxperms <= 0 )
    242 {
    243 if ( data_.maxgenerators == 0 )
    244 data_.nmaxperms = 100; /* seems to cover many cases */
    245 else
    247
    249 return;
    250 }
    251 else if ( data_.nperms >= data_.nmaxperms ) /* check whether we need to resize */
    252 {
    253 int newsize;
    254
    255 newsize = SCIPcalcMemGrowSize(data_.scip, data_.nperms + 1);
    256 assert( newsize >= data_.nperms );
    257 assert( data_.maxgenerators == 0 );
    258
    260 return;
    261
    262 data_.nmaxperms = newsize;
    263 }
    264
    265 if ( SCIPduplicateBlockMemoryArray(data_.scip, &pp, p, permlen) != SCIP_OKAY )
    266 return;
    267 data_.perms[data_.nperms++] = pp;
    268}
    269
    270#endif
    271
    272/** returns whether an edge is considered in grouping process */
    273static
    275 SYM_GRAPH* symgraph, /**< symmetry detection graph */
    276 int edgeidx, /**< index of edge to be checked */
    277 SCIP_Bool groupbycons /**< whether edges are grouped by constraints */
    278 )
    279{
    280 int first;
    281 int second;
    282
    283 assert(symgraph != NULL);
    284
    285 first = SCIPgetSymgraphEdgeFirst(symgraph, edgeidx);
    286 second = SCIPgetSymgraphEdgeSecond(symgraph, edgeidx);
    287
    288 /* uncolored edges are not grouped */
    289 if ( ! SCIPisSymgraphEdgeColored(symgraph, edgeidx) )
    290 return FALSE;
    291
    292 /* two variable nodes are connected */
    293 if ( first < 0 && second < 0 )
    294 return FALSE;
    295
    296 if ( ! groupbycons )
    297 {
    298 /* grouping by variables requires one variable node */
    299 if ( first < 0 || second < 0 )
    300 return TRUE;
    301 }
    302 else
    303 {
    304 /* check whether there is exactly one constraint node in edge */
    305 if ( first >= 0 && second >= 0 )
    306 {
    307 if ( (SCIPgetSymgraphNodeType(symgraph, first) == SYM_NODETYPE_CONS
    308 && SCIPgetSymgraphNodeType(symgraph, second) != SYM_NODETYPE_CONS)
    309 || (SCIPgetSymgraphNodeType(symgraph, first) != SYM_NODETYPE_CONS
    310 && SCIPgetSymgraphNodeType(symgraph, second) == SYM_NODETYPE_CONS) )
    311 return TRUE;
    312 }
    313 else if ( first >= 0 )
    314 {
    315 if ( SCIPgetSymgraphNodeType(symgraph, first) == SYM_NODETYPE_CONS )
    316 return TRUE;
    317 }
    318 else
    319 {
    320 if ( SCIPgetSymgraphNodeType(symgraph, second) == SYM_NODETYPE_CONS )
    321 return TRUE;
    322 }
    323 }
    324
    325 return FALSE;
    326}
    327
    328/** adds grouped edges all of which have one common endpoint to a graph
    329 *
    330 * The grouping mechanism works by sorting the edges according to their color. If two
    331 * edges have the same color, they share the same intermediate node, which is connected
    332 * to the common node and the other endpoints of equivalent edges.
    333 */
    334static
    336 SCIP* scip, /**< SCIP pointer */
    337 sparsegraph* SG, /**< graph to be constructed */
    338 int* edgestartpos, /**< initialized array of starting positions of new edges for each node */
    339 SCIP_Bool determinesize, /**< whether only the effect of grouping on the graph shall be checked */
    340 int* internodeid, /**< (initialized) pointer to store the ID of the next intermediate node */
    341 int** degrees, /**< pointer to array of degrees for nodes in G */
    342 int* maxdegrees, /**< (initialized) pointer to maximum number of entries degrees can hold */
    343 int** colorstostore, /**< pointer to array of colors of graph to be constructed */
    344 int* ncolorstostore, /**< (initialized) pointer to maximum number of entries in colorstostore */
    345 int* nnodes, /**< (initialized) pointer to store the number of */
    346 int* nedges, /**< (initialized) pointer to store the number of */
    347 int commonnodeidx, /**< index of common node in G */
    348 int* neighbors, /**< neighbors of common node */
    349 int* colors, /**< colors of edges to neighbors */
    350 int nneighbors, /**< number of neighbors */
    351 int* naddednodes, /**< pointer to store number of nodes added to G */
    352 int* naddededges /**< pointer to store number of edges added to G */
    353 )
    354{
    355 int curcolor;
    356 int curstart;
    357 int e;
    358 int f;
    359
    360 assert( SG != NULL || determinesize );
    361 assert( internodeid != NULL );
    362 assert( *internodeid >= 0 );
    363 assert( degrees != NULL );
    364 assert( maxdegrees != NULL );
    365 assert( *maxdegrees > 0 );
    366 assert( nnodes != NULL );
    367 assert( nedges != NULL );
    368 assert( neighbors != NULL );
    369 assert( colors != NULL );
    370 assert( naddednodes != NULL );
    371 assert( naddededges != NULL );
    372 assert( commonnodeidx <= *internodeid );
    373
    374 *naddednodes = 0;
    375 *naddededges = 0;
    376
    377 /* sort edges according to color */
    378 SCIPsortIntInt(colors, neighbors, nneighbors);
    379
    380 /* iterate over groups of identical edges and group them, ignoring the last group */
    381 curcolor = colors[0];
    382 curstart = 0;
    383 for (e = 1; e < nneighbors; ++e)
    384 {
    385 /* if a new group started, add edges for previous group */
    386 if ( colors[e] != curcolor )
    387 {
    388 if ( determinesize )
    389 {
    390 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
    391 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colorstostore, ncolorstostore, *internodeid + 1) );
    392 (*degrees)[*internodeid] = 1;
    393 ++(*degrees)[commonnodeidx];
    394 (*colorstostore)[*internodeid] = curcolor;
    395
    396 for (f = curstart; f < e; ++f)
    397 {
    398 assert( neighbors[f] <= *internodeid );
    399 ++(*degrees)[*internodeid];
    400 ++(*degrees)[neighbors[f]];
    401 }
    402 }
    403 else
    404 {
    405 SG->e[edgestartpos[commonnodeidx]++] = *internodeid;
    406 SG->e[edgestartpos[*internodeid]++] = commonnodeidx;
    407
    408 for (f = curstart; f < e; ++f)
    409 {
    410 SG->e[edgestartpos[neighbors[f]]++] = *internodeid;
    411 SG->e[edgestartpos[*internodeid]++] = neighbors[f];
    412 }
    413 }
    414 *naddednodes += 1;
    415 *naddededges += e - curstart + 1;
    416 ++(*internodeid);
    417
    418 curcolor = colors[e];
    419 curstart = e;
    420 }
    421 }
    422
    423 /* add edges of last group */
    424 if ( determinesize )
    425 {
    426 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
    427 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colorstostore, ncolorstostore, *internodeid + 1) );
    428
    429 (*degrees)[*internodeid] = 1;
    430 ++(*degrees)[commonnodeidx];
    431 (*colorstostore)[*internodeid] = curcolor;
    432
    433 for (f = curstart; f < nneighbors; ++f)
    434 {
    435 assert( neighbors[f] <= *internodeid );
    436 ++(*degrees)[*internodeid];
    437 ++(*degrees)[neighbors[f]];
    438 }
    439 }
    440 else
    441 {
    442 SG->e[edgestartpos[commonnodeidx]++] = *internodeid;
    443 SG->e[edgestartpos[*internodeid]++] = commonnodeidx;
    444
    445 for (f = curstart; f < nneighbors; ++f)
    446 {
    447 SG->e[edgestartpos[*internodeid]++] = neighbors[f];
    448 SG->e[edgestartpos[neighbors[f]]++] = *internodeid;
    449 }
    450 }
    451 *naddednodes += 1;
    452 *naddededges += nneighbors - curstart + 1;
    453 ++(*internodeid);
    454
    455 return SCIP_OKAY;
    456}
    457
    458/** either creates a graph or determines its size */
    459static
    461 SCIP* scip, /**< SCIP instance */
    462 SYM_GRAPH* symgraph, /**< symmetry detection graph */
    463 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
    464 sparsegraph* SG, /**< graph to be constructed */
    465 int* nnodes, /**< pointer to store the total number of nodes in graph */
    466 int* nedges, /**< pointer to store the total number of edges in graph */
    467 int** degrees, /**< pointer to store the degrees of the nodes */
    468 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
    469 int** colors, /**< pointer to store the colors of the nodes */
    470 int* ncolors, /**< pointer to store number of different colors in graph */
    471 SCIP_Bool* success /**< pointer to store whether the construction was successful */
    472 )
    473{ /*lint !e438*/
    475 SYM_NODETYPE comparetype;
    476 SCIP_Bool groupByConstraints;
    477 int* groupfirsts = NULL;
    478 int* groupseconds = NULL;
    479 int* groupcolors = NULL;
    480 int* pos = NULL;
    481 int edgebegincnt = 0;
    482 int ngroupedges = 0;
    483 int nvarnodestoadd;
    484 int internodeid;
    485 int nsymvars;
    486 int nsymedges;
    487 int first;
    488 int second;
    489 int e;
    490 int j;
    491
    492 assert( scip != NULL );
    493 assert( symgraph != NULL );
    494 assert( SG != NULL || determinesize );
    495 assert( nnodes != NULL );
    496 assert( nedges != NULL );
    497 assert( degrees != NULL );
    498 assert( maxdegrees != NULL );
    499 assert( colors != NULL );
    500 assert( ncolors != NULL );
    501 assert( success != NULL );
    502
    503 *success = TRUE;
    504
    505 /* collect basic information from symmetry detection graph */
    506 nsymvars = SCIPgetSymgraphNVars(symgraph);
    508 switch ( symtype )
    509 {
    510 case SYM_SYMTYPE_PERM:
    511 nvarnodestoadd = nsymvars;
    512 break;
    513 default:
    514 assert( symtype == SYM_SYMTYPE_SIGNPERM );
    515 nvarnodestoadd = 2 * nsymvars;
    516 } /*lint !e788*/
    517
    518 /* possibly find number of nodes in sassy graph */
    519 if ( determinesize )
    520 {
    521 int cnt = 0;
    522
    523 /* initialize counters */
    524 *nnodes = SCIPgetSymgraphNNodes(symgraph) + nvarnodestoadd;
    525 *nedges = 0;
    526
    527 /* possibly allocate memory for degrees (will grow dynamically) */
    528 *degrees = NULL;
    529 *maxdegrees = 0;
    530 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 100) );
    531 for (j = 0; j < *nnodes; ++j)
    532 (*degrees)[j] = 0;
    533
    534 /* possibly allocate memory for colors (will grow dynamically) */
    535 *colors = NULL;
    536 *ncolors = 0;
    537 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colors, ncolors, *nnodes + 100) );
    538 for (j = 0; j < nvarnodestoadd; ++j)
    539 (*colors)[cnt++] = SCIPgetSymgraphVarnodeColor(symgraph, j);
    540 for (j = 0; j < SCIPgetSymgraphNNodes(symgraph); ++j)
    541 (*colors)[cnt++] = SCIPgetSymgraphNodeColor(symgraph, j);
    542 }
    543 else
    544 {
    546
    547 /* add nodes for variables and remaining (axuiliary) nodes in graph */
    548 for (j = 0; j < *nnodes; ++j)
    549 {
    550 SG->d[j] = (*degrees)[j];
    551 SG->v[j] = (size_t) (unsigned) edgebegincnt;
    552 pos[j] = edgebegincnt;
    553 edgebegincnt += (*degrees)[j];
    554 }
    555 }
    556
    557 /* determine grouping depending on the number of rhs vs. variables */
    558 groupByConstraints = SCIPgetSymgraphNConsnodes(symgraph) < SCIPgetSymgraphNVars(symgraph);
    559
    560 /* allocate arrays to collect edges to be grouped */
    561 nsymedges = SCIPgetSymgraphNEdges(symgraph);
    562 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
    563 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
    564 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
    565
    566 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
    567 internodeid = SCIPgetSymgraphNNodes(symgraph) + nvarnodestoadd;
    568 for (e = 0; e < SCIPgetSymgraphNEdges(symgraph); ++e)
    569 {
    570 first = SCIPgetSymgraphEdgeFirst(symgraph, e);
    571 second = SCIPgetSymgraphEdgeSecond(symgraph, e);
    572
    573 /* get the first and second node in edge (corrected by variable shift) */
    574 if ( first < 0 )
    575 first = -first - 1;
    576 else
    577 first += nvarnodestoadd;
    578 if ( second < 0 )
    579 second = -second - 1;
    580 else
    581 second += nvarnodestoadd;
    582 assert(first >= 0);
    583 assert(second >= 0);
    584
    585 /* check whether edge is used for grouping */
    586 if ( ! SCIPhasGraphUniqueEdgetype(symgraph) && isEdgeGroupable(symgraph, e, groupByConstraints) )
    587 {
    588 /* store edge, first becomes the cons or var node */
    589 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
    590
    591 if ( SCIPgetSymgraphNodeType(symgraph, SCIPgetSymgraphEdgeFirst(symgraph, e)) == comparetype )
    592 {
    593 groupfirsts[ngroupedges] = first;
    594 groupseconds[ngroupedges] = second;
    595 }
    596 else
    597 {
    598 groupfirsts[ngroupedges] = second;
    599 groupseconds[ngroupedges] = first;
    600 }
    601 groupcolors[ngroupedges++] = SCIPgetSymgraphEdgeColor(symgraph, e);
    602 }
    603 else
    604 {
    605 /* immediately add edge or increase degrees */
    606 assert(0 <= first && first < *nnodes);
    607 assert(0 <= second && second < *nnodes);
    608
    609 /* possibly split edge if it is colored */
    610 if ( ! SCIPhasGraphUniqueEdgetype(symgraph) && SCIPisSymgraphEdgeColored(symgraph, e) )
    611 {
    612 if ( determinesize )
    613 {
    614 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, internodeid + 1) );
    615 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colors, ncolors, internodeid + 1) );
    616 ++(*degrees)[first];
    617 ++(*degrees)[second];
    618 (*degrees)[internodeid] = 2;
    619 ++(*nnodes);
    620 *nedges += 2;
    621 (*colors)[internodeid] = SCIPgetSymgraphEdgeColor(symgraph, e);
    622 ++internodeid;
    623 }
    624 else
    625 {
    626 assert( internodeid < *nnodes );
    627
    628 /* add (bidirected) edges */
    629 SG->e[pos[first]++] = internodeid;
    630 SG->e[pos[internodeid]++] = first;
    631 SG->e[pos[second]++] = internodeid;
    632 SG->e[pos[internodeid]++] = second;
    633
    634 assert( first == *nnodes - 1 || pos[first] <= (int) SG->v[first+1] );
    635 assert( second == *nnodes - 1 || pos[second] <= (int) SG->v[second+1] );
    636 assert( internodeid == *nnodes - 1 || pos[internodeid] <= (int) SG->v[internodeid+1] );
    637
    638 ++internodeid;
    639 }
    640 }
    641 else
    642 {
    643 if ( determinesize )
    644 {
    645 ++(*degrees)[first];
    646 ++(*degrees)[second];
    647 ++(*nedges);
    648 }
    649 else
    650 {
    651 /* add (bidirected) edge */
    652 SG->e[pos[first]++] = second;
    653 SG->e[pos[second]++] = first;
    654
    655 assert( first == *nnodes - 1 || pos[first] <= (int) SG->v[first+1] );
    656 assert( second == *nnodes - 1 || pos[second] <= (int) SG->v[second+1] );
    657 }
    658 }
    659 }
    660 }
    661
    662 /* possibly add groupable edges */
    663 if ( ngroupedges > 0 )
    664 {
    665 int firstidx = 0;
    666 int firstnodeidx;
    667 int naddednodes;
    668 int naddededges;
    669
    670 /* sort edges according to their first nodes */
    671 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
    672 firstnodeidx = groupfirsts[0];
    673
    674 for (j = 1; j < ngroupedges; ++j)
    675 {
    676 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
    677 if ( groupfirsts[j] != firstnodeidx )
    678 {
    679 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, SG, pos, determinesize, &internodeid,
    680 degrees, maxdegrees, colors, ncolors, nnodes, nedges, firstnodeidx, &groupseconds[firstidx],
    681 &groupcolors[firstidx], j - firstidx, &naddednodes, &naddededges) );
    682
    683 firstidx = j;
    684 firstnodeidx = groupfirsts[j];
    685
    686 if ( determinesize )
    687 {
    688 *nnodes += naddednodes;
    689 *nedges += naddededges;
    690 }
    691 }
    692 }
    693
    694 /* process the last group */
    695 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, SG, pos, determinesize, &internodeid,
    696 degrees, maxdegrees, colors, ncolors, nnodes, nedges, firstnodeidx, &groupseconds[firstidx],
    697 &groupcolors[firstidx], ngroupedges - firstidx, &naddednodes, &naddededges) );
    698
    699 if ( determinesize )
    700 {
    701 *nnodes += naddednodes;
    702 *nedges += naddededges;
    703 }
    704 }
    705
    706 SCIPfreeBufferArray(scip, &groupcolors);
    707 SCIPfreeBufferArray(scip, &groupseconds);
    708 SCIPfreeBufferArray(scip, &groupfirsts);
    709
    710 /* for signed permutation, also add edges connecting a variable and its negation */
    711 switch ( SCIPgetSymgraphSymtype(symgraph) )
    712 {
    714 if ( determinesize )
    715 {
    716 for (j = 0; j < nvarnodestoadd; ++j)
    717 ++(*degrees)[j];
    718 *nedges += nsymvars;
    719 }
    720 else
    721 {
    722 for (j = 0; j < nsymvars; ++j)
    723 {
    724 SG->e[pos[j]++] = j + nsymvars;
    725 SG->e[pos[j + nsymvars]++] = j;
    726
    727 assert( pos[j] <= (int) SG->v[j+1] );
    728 assert( j + nsymvars == *nnodes - 1 || pos[j+nsymvars] <= (int) SG->v[j+nsymvars+1] );
    729 }
    730 }
    731 break;
    732 default:
    733 assert( SCIPgetSymgraphSymtype(symgraph) == SYM_SYMTYPE_PERM );
    734 } /*lint !e788*/
    735
    736 if ( determinesize )
    737 {
    738 SCIPdebugMsg(scip, "#nodes: %d\n", *nnodes);
    739 SCIPdebugMsg(scip, "#nodes for variables: %d\n", nvarnodestoadd);
    740 SCIPdebugMsg(scip, "#nodes for rhs: %d\n", SCIPgetSymgraphNConsnodes(symgraph));
    741 SCIPdebugMsg(scip, "#edges: %d\n", *nedges);
    742 }
    743 else
    744 {
    746 }
    747
    748 return SCIP_OKAY;
    749}
    750
    751/** either creates a graph for checking symmetries or determines its size
    752 *
    753 * The input are two graphs and the graph to be constructed consists of copies
    754 * of the two input graphs, in which non-variable nodes are colored according
    755 * to the colors used in symmetry detection. Each variable gets a unique color.
    756 * Finally, a dummy node is introduced that is adjacent with all remaining nodes.
    757 */
    758static
    760 SCIP* scip, /**< SCIP instance */
    761 SYM_GRAPH* graph1, /**< first symmetry detection graph */
    762 SYM_GRAPH* graph2, /**< second symmetry detection graph */
    763 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
    764 sparsegraph* SG, /**< graph to be constructed */
    765 int* nnodes, /**< pointer to store the total number of nodes in graph */
    766 int* nedges, /**< pointer to store the total number of edges in graph */
    767 int** degrees, /**< pointer to store the degrees of the nodes */
    768 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
    769 int** colors, /**< pointer to store the colors of the nodes */
    770 int* ncolors, /**< pointer to store number of different colors in graph */
    771 int* nusedvars, /**< pointer to store number of variables used in one graph */
    772 int* nnodesfromgraph1, /**< pointer to store number of nodes arising from graph1 (or NULL) */
    773 SCIP_Bool* success /**< pointer to store whether the graph could be built */
    774 )
    775{
    777 SYM_NODETYPE comparetype;
    778 SCIP_Bool groupByConstraints;
    779 SYM_GRAPH* symgraph;
    780 int* nvarused1 = NULL;
    781 int* nvarused2 = NULL;
    782 int* varlabel = NULL;
    783 int* groupfirsts = NULL;
    784 int* groupseconds = NULL;
    785 int* groupcolors = NULL;
    786 int* pos = NULL;
    787 int nusdvars = 0;
    788 int edgebegincnt = 0;
    789 int ngroupedges = 0;
    790 int nodeshift;
    791 int curnnodes;
    792 int nvarnodestoadd;
    793 int internodeid;
    794 int nsymvars;
    795 int nsymedges;
    796 int first;
    797 int second;
    798 int color;
    799 int e;
    800 int i;
    801 int j;
    802
    803 assert( scip != NULL );
    804 assert( graph1 != NULL );
    805 assert( graph2 != NULL );
    806 assert( SG != NULL || determinesize );
    807 assert( nnodes != NULL );
    808 assert( nedges != NULL );
    809 assert( degrees != NULL );
    810 assert( maxdegrees != NULL );
    811 assert( colors != NULL );
    812 assert( ncolors != NULL );
    813 assert( nusedvars != NULL );
    814 assert( ! determinesize || nnodesfromgraph1 != NULL );
    815 assert( success != NULL );
    816
    817 *success = FALSE;
    818 if ( determinesize )
    819 {
    820 *degrees = NULL;
    821 *colors = NULL;
    822 *maxdegrees = 0;
    823 *ncolors = 0;
    824 }
    825
    826 /* graphs cannot be symmetric */
    827 if ( SCIPgetSymgraphNEdges(graph1) != SCIPgetSymgraphNEdges(graph2)
    828 || SCIPgetSymgraphNVars(graph1) != SCIPgetSymgraphNVars(graph2) )
    829 return SCIP_OKAY;
    830
    831 /* collect basic information from symmetry detection graph */
    832 nsymvars = SCIPgetSymgraphNVars(graph1);
    833 nsymedges = SCIPgetSymgraphNEdges(graph1);
    835 switch ( symtype )
    836 {
    837 case SYM_SYMTYPE_PERM:
    838 nvarnodestoadd = nsymvars;
    839 break;
    840 default:
    841 assert( symtype == SYM_SYMTYPE_SIGNPERM );
    842 nvarnodestoadd = 2 * nsymvars;
    843 } /*lint !e788*/
    844
    845 /* find the variables that are contained in an edge */
    846 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused1, nvarnodestoadd) );
    847 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused2, nvarnodestoadd) );
    848 SCIP_CALL( SCIPallocBufferArray(scip, &varlabel, nvarnodestoadd) );
    849
    850 for (e = 0; e < nsymedges; ++e)
    851 {
    852 first = SCIPgetSymgraphEdgeFirst(graph1, e);
    853 second = SCIPgetSymgraphEdgeSecond(graph1, e);
    854 if ( first < 0 )
    855 nvarused1[-first - 1] += 1;
    856 if ( second < 0 )
    857 nvarused1[-second - 1] += 1;
    858
    859 first = SCIPgetSymgraphEdgeFirst(graph2, e);
    860 second = SCIPgetSymgraphEdgeSecond(graph2, e);
    861 if ( first < 0 )
    862 nvarused2[-first - 1] += 1;
    863 if ( second < 0 )
    864 nvarused2[-second - 1] += 1;
    865 }
    866
    867 for (j = 0; j < nvarnodestoadd; ++j)
    868 {
    869 /* graphs cannot be identical */
    870 if ( nvarused1[j] != nvarused2[j] )
    871 {
    872 SCIPfreeBufferArray(scip, &varlabel);
    873 SCIPfreeBufferArray(scip, &nvarused2);
    874 SCIPfreeBufferArray(scip, &nvarused1);
    875
    876 return SCIP_OKAY;
    877 }
    878
    879 /* relabel variables by restricting to variables used in constraint (or their negation) */
    880 if ( nvarused1[j] > 0 || nvarused1[j % SCIPgetSymgraphNVars(graph1)] > 0 )
    881 varlabel[j] = nusdvars++;
    882 else
    883 varlabel[j] = -1;
    884 }
    885
    886 /* possibly find number of nodes in sassy graph and allocate memory for dynamic array */
    887 if ( determinesize )
    888 {
    889 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees,
    890 SCIPgetSymgraphNNodes(graph1) + SCIPgetSymgraphNNodes(graph2) + 2 * nusdvars + 100) );
    891 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colors, ncolors,
    892 SCIPgetSymgraphNNodes(graph1) + SCIPgetSymgraphNNodes(graph2) + 2 * nusdvars + 100) );
    893
    894 *nnodes = 0;
    895 *nedges = 0;
    896 }
    897 else
    898 {
    900
    901 /* add nodes for variables and remaining (axuiliary) nodes in graph */
    902 for (j = 0; j < *nnodes; ++j)
    903 {
    904 SG->d[j] = (*degrees)[j];
    905 SG->v[j] = (size_t) (unsigned) edgebegincnt;
    906 pos[j] = edgebegincnt;
    907 edgebegincnt += (*degrees)[j];
    908 }
    909 }
    910
    911 /* determine grouping depending on the number of rhs vs. variables */
    912 groupByConstraints = SCIPgetSymgraphNConsnodes(graph1) < SCIPgetSymgraphNVars(graph1);
    913
    914 /* allocate arrays to collect edges to be grouped */
    915 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
    916 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
    917 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
    918
    919 /* collect information or generate graphs, we shift the node indices of the second graph when adding them to G */
    920 nodeshift = 0;
    921 for (i = 0; i < 2; ++i)
    922 {
    923 curnnodes = 0;
    924 symgraph = i == 0 ? graph1 : graph2;
    925 ngroupedges = 0;
    926
    927 /* possibly add nodes for variables and remaining nodes, each variable gets a unique color */
    928 if ( determinesize )
    929 {
    930 /* add nodes for variables */
    931 for (j = 0; j < nvarnodestoadd; ++j)
    932 {
    933 if ( varlabel[j] >= 0 )
    934 {
    935 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
    936 (*degrees)[nodeshift + varlabel[j]] = 0;
    937 (*colors)[nodeshift + varlabel[j]] = SCIPgetSymgraphVarnodeColor(symgraph, j);
    938 ++(*nnodes);
    939 ++curnnodes;
    940 }
    941 }
    942
    943 /* add nodes for remaining nodes of graph */
    944 for (j = 0; j < SCIPgetSymgraphNNodes(symgraph); ++j)
    945 {
    946 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
    947 (*degrees)[nodeshift + nusdvars + j] = 0;
    948 (*colors)[nodeshift + nusdvars + j] = SCIPgetSymgraphNodeColor(symgraph, j);
    949 ++(*nnodes);
    950 ++curnnodes;
    951 }
    952 }
    953 else
    954 {
    955 /* increase counter of nodes */
    956 for (j = 0; j < nvarnodestoadd; ++j)
    957 {
    958 if ( varlabel[j] >= 0 )
    959 ++curnnodes;
    960 }
    961 curnnodes += SCIPgetSymgraphNNodes(symgraph);
    962 }
    963
    964 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
    965 internodeid = nodeshift + curnnodes;
    966 for (e = 0; e < nsymedges; ++e)
    967 {
    968 first = SCIPgetSymgraphEdgeFirst(symgraph, e);
    969 second = SCIPgetSymgraphEdgeSecond(symgraph, e);
    970
    971 /* get the first and second node in edge (corrected by variable shift) */
    972 if ( first < 0 )
    973 first = varlabel[-first - 1];
    974 else
    975 first = nusdvars + first;
    976 if ( second < 0 )
    977 second = varlabel[-second - 1];
    978 else
    979 second = nusdvars + second;
    980
    981 /* check whether edge is used for grouping */
    982 if ( ! SCIPhasGraphUniqueEdgetype(symgraph) && isEdgeGroupable(symgraph, e, groupByConstraints) )
    983 {
    984 /* store edge, first becomes the cons or var node */
    985 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
    986
    987 if ( SCIPgetSymgraphNodeType(symgraph, SCIPgetSymgraphEdgeFirst(symgraph, e)) == comparetype )
    988 {
    989 groupfirsts[ngroupedges] = nodeshift + first;
    990 groupseconds[ngroupedges] = nodeshift + second;
    991 }
    992 else
    993 {
    994 groupfirsts[ngroupedges] = nodeshift + second;
    995 groupseconds[ngroupedges] = nodeshift + first;
    996 }
    997 groupcolors[ngroupedges++] = nusdvars + SCIPgetSymgraphEdgeColor(symgraph, e);
    998 }
    999 else
    1000 {
    1001 /* immediately add edge or increase degrees */
    1002 assert(0 <= first && first < *nnodes);
    1003 assert(0 <= second && second < *nnodes);
    1004
    1005 /* possibly split edge if it is colored */
    1006 if ( ! SCIPhasGraphUniqueEdgetype(symgraph) && SCIPisSymgraphEdgeColored(symgraph, e) )
    1007 {
    1008 if ( determinesize )
    1009 {
    1010 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, nodeshift + internodeid + 1) );
    1011 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colors, ncolors, nodeshift + internodeid + 1) );
    1012
    1013 ++(*degrees)[nodeshift + first];
    1014 ++(*degrees)[nodeshift + second];
    1015 (*degrees)[internodeid] = 2;
    1016
    1017 color = SCIPgetSymgraphEdgeColor(symgraph, e);
    1018 (*colors)[internodeid] = nusdvars + color;
    1019
    1020 ++(*nnodes);
    1021 *nedges += 2;
    1022 }
    1023 else
    1024 {
    1025 assert( internodeid < *nnodes );
    1026
    1027 SG->e[pos[internodeid]++] = nodeshift + first;
    1028 SG->e[pos[internodeid]++] = nodeshift + second;
    1029 SG->e[pos[nodeshift + first]++] = internodeid;
    1030 SG->e[pos[nodeshift + second]++] = internodeid;
    1031
    1032 assert( internodeid == *nnodes - 1
    1033 || pos[internodeid] <= (int) SG->v[internodeid+1] );
    1034 assert( nodeshift + first == *nnodes - 1
    1035 || pos[nodeshift + first] <= (int) SG->v[nodeshift+first+1] );
    1036 assert( nodeshift + second == *nnodes - 1 ||
    1037 pos[nodeshift + second] <= (int) SG->v[nodeshift+second+1] );
    1038 }
    1039 ++internodeid;
    1040 ++curnnodes;
    1041 }
    1042 else
    1043 {
    1044 if ( determinesize )
    1045 {
    1046 ++(*degrees)[nodeshift + first];
    1047 ++(*degrees)[nodeshift + second];
    1048 ++(*nedges);
    1049 }
    1050 else
    1051 {
    1052 SG->e[pos[nodeshift + first]++] = nodeshift + second;
    1053 SG->e[pos[nodeshift + second]++] = nodeshift + first;
    1054
    1055 assert( nodeshift+first == *nnodes - 1 || pos[nodeshift+first] <= (int) SG->v[nodeshift+first+1] );
    1056 assert( nodeshift+second == *nnodes - 1 || pos[nodeshift+second] <= (int) SG->v[nodeshift+second+1] );
    1057 }
    1058 }
    1059 }
    1060 }
    1061
    1062 /* possibly add groupable edges */
    1063 if ( ngroupedges > 0 )
    1064 {
    1065 int firstidx = 0;
    1066 int firstnodeidx;
    1067 int naddednodes;
    1068 int naddededges;
    1069
    1070 /* sort edges according to their first nodes */
    1071 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
    1072 firstnodeidx = groupfirsts[0];
    1073
    1074 for (j = 1; j < ngroupedges; ++j)
    1075 {
    1076 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
    1077 if ( groupfirsts[j] != firstnodeidx )
    1078 {
    1079 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, SG, pos, determinesize, &internodeid,
    1080 degrees, maxdegrees, colors, ncolors, nnodes, nedges, firstnodeidx,
    1081 &groupseconds[firstidx], &groupcolors[firstidx], j - firstidx, &naddednodes, &naddededges) );
    1082
    1083 firstidx = j;
    1084 firstnodeidx = groupfirsts[j];
    1085
    1086 if ( determinesize )
    1087 {
    1088 *nnodes += naddednodes;
    1089 *nedges += naddededges;
    1090 }
    1091 curnnodes += naddednodes;
    1092 }
    1093 }
    1094
    1095 /* process the last group */
    1096 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, SG, pos, determinesize, &internodeid,
    1097 degrees, maxdegrees, colors, ncolors, nnodes, nedges, firstnodeidx,
    1098 &groupseconds[firstidx], &groupcolors[firstidx], ngroupedges - firstidx, &naddednodes, &naddededges) );
    1099
    1100 if ( determinesize )
    1101 {
    1102 *nnodes += naddednodes;
    1103 *nedges += naddededges;
    1104 }
    1105 curnnodes += naddednodes;
    1106 }
    1107
    1108 /* for signed permutation, also add edges connecting a variable and its negation */
    1110 {
    1111 if ( determinesize )
    1112 {
    1113 for (j = 0; j < nusdvars; ++j)
    1114 ++(*degrees)[nodeshift + j];
    1115 (*nedges) += nusdvars / 2;
    1116 }
    1117 else
    1118 {
    1119 for (j = 0; j < nusdvars/2; ++j)
    1120 {
    1121 SG->e[pos[nodeshift+j]++] = nodeshift + j + nusdvars/2;
    1122 SG->e[pos[nodeshift + j + nusdvars/2]++] = nodeshift + j;
    1123
    1124 assert( pos[nodeshift+j] <= (int) SG->v[nodeshift+j+1] );
    1125 assert( nodeshift+j+nusdvars/2 == *nnodes - 1
    1126 || pos[nodeshift+j+nusdvars/2] <= (int) SG->v[nodeshift+j+nusdvars/2+1] );
    1127 }
    1128 }
    1129 }
    1130 nodeshift = curnnodes;
    1131
    1132 /* possibly store number of nodes arising from first graph */
    1133 if ( determinesize && i == 0 )
    1134 *nnodesfromgraph1 = *nnodes;
    1135 }
    1136
    1137 /* add dummy node */
    1138 if ( determinesize )
    1139 {
    1140 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
    1141 SCIP_CALL( SCIPensureBlockMemoryArray(scip, colors, ncolors, *nnodes + 1) );
    1142
    1143 ++(*nnodes);
    1144 for (j = 0; j < *nnodes - 1; ++j)
    1145 ++(*degrees)[j];
    1146 (*degrees)[*nnodes - 1] = *nnodes - 1;
    1147 (*nedges) += *nnodes - 1;
    1148 (*colors)[*nnodes - 1] = 8;
    1149 }
    1150 else
    1151 {
    1152 for (j = 0; j < *nnodes - 1; ++j)
    1153 {
    1154 SG->e[pos[j]++] = *nnodes - 1;
    1155 SG->e[pos[*nnodes-1]++] = j;
    1156 }
    1158 }
    1159
    1160 SCIPfreeBufferArray(scip, &groupcolors);
    1161 SCIPfreeBufferArray(scip, &groupseconds);
    1162 SCIPfreeBufferArray(scip, &groupfirsts);
    1163
    1164 SCIPfreeBufferArray(scip, &varlabel);
    1165 SCIPfreeBufferArray(scip, &nvarused2);
    1166 SCIPfreeBufferArray(scip, &nvarused1);
    1167
    1168 *success = TRUE;
    1169 if ( determinesize )
    1170 *nusedvars = nusdvars;
    1171
    1172 return SCIP_OKAY;
    1173}
    1174
    1175/** return whether symmetry can be computed */
    1177{
    1178 return TRUE;
    1179}
    1180
    1181/** nauty/traces version string */
    1182#ifdef NAUTY
    1183static const char nautyname[] = {'N', 'a', 'u', 't', 'y', ' ', NAUTYVERSIONID/10000 + '0', '.', (NAUTYVERSIONID%10000)/1000 + '0', '.', (NAUTYVERSIONID%1000)/10 + '0', '\0'};
    1184#else
    1185static const char nautyname[] = {'T', 'r', 'a', 'c', 'e', 's', ' ', NAUTYVERSIONID/10000 + '0', '.', (NAUTYVERSIONID%10000)/1000 + '0', '.', (NAUTYVERSIONID%1000)/10 + '0', '\0'};
    1186#endif
    1187
    1188/** return name of external program used to compute generators */
    1189const char* SYMsymmetryGetName(void)
    1190{
    1191 return nautyname;
    1192}
    1193
    1194/** return description of external program used to compute generators */
    1195const char* SYMsymmetryGetDesc(void)
    1196{
    1197#ifdef NAUTY
    1198 return "Computing Graph Automorphism Groups by Brendan D. McKay (https://users.cecs.anu.edu.au/~bdm/nauty/)";
    1199#else
    1200 return "Computing Graph Automorphism Groups by Adolfo Piperno (https://pallini.di.uniroma1.it/)";
    1201#endif
    1202}
    1203
    1204/** return name of additional external program used for computing symmetries */
    1205const char* SYMsymmetryGetAddName(void)
    1206{
    1207 return NULL;
    1208}
    1209
    1210/** return description of additional external program used to compute symmetries */
    1211const char* SYMsymmetryGetAddDesc(void)
    1212{
    1213 return NULL;
    1214}
    1215
    1216/** compute generators of symmetry group */
    1218 SCIP* scip, /**< SCIP pointer */
    1219 int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
    1220 SYM_GRAPH* symgraph, /**< symmetry detection graph */
    1221 int* nperms, /**< pointer to store number of permutations */
    1222 int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
    1223 int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
    1224 SCIP_Real* log10groupsize, /**< pointer to store size of group */
    1225 SCIP_Real* symcodetime /**< pointer to store the time for symmetry code */
    1226 )
    1227{
    1228 SCIP_Real oldtime;
    1229 int nnodes;
    1230 int nedges;
    1231 int* degrees;
    1232 int maxdegrees;
    1233 int* colors;
    1234 int ncolors;
    1235 SCIP_Bool success;
    1236 int v;
    1237
    1238 /* nauty data structures */
    1239 sparsegraph SG;
    1240 int* lab;
    1241 int* ptn;
    1242 int* orbits;
    1243
    1244#ifdef NAUTY
    1245 DEFAULTOPTIONS_SPARSEGRAPH(options);
    1246 statsblk stats;
    1247#else
    1248 static DEFAULTOPTIONS_TRACES(options);
    1249 TracesStats stats;
    1250#endif
    1251
    1252 /* init */
    1253 *nperms = 0;
    1254 *nmaxperms = 0;
    1255 *perms = NULL;
    1256 *log10groupsize = 0;
    1257 *symcodetime = 0.0;
    1258
    1259 /* init options */
    1260#ifdef NAUTY
    1261 /* init callback functions for nauty (accumulate the group generators found by nauty) */
    1262 options.writeautoms = FALSE;
    1263 options.userautomproc = nautyhook;
    1264 options.defaultptn = FALSE; /* use color classes */
    1265 options.usernodeproc = nautyterminationhook;
    1266#else
    1267 /* init callback functions for traces (accumulate the group generators found by traces) */
    1268 options.writeautoms = FALSE;
    1269 options.userautomproc = traceshook;
    1270 options.defaultptn = FALSE; /* use color classes */
    1271#endif
    1272
    1273 oldtime = SCIPgetSolvingTime(scip);
    1274
    1275 /* determine the number of nodes and edges */
    1276 SCIP_CALL( createOrDetermineSizeGraph(scip, symgraph, TRUE, NULL, &nnodes, &nedges,
    1277 &degrees, &maxdegrees, &colors, &ncolors, &success) );
    1278
    1279 if ( ! success )
    1280 {
    1282 "Stopped symmetry computation: Symmetry graph would become too large.\n");
    1283 return SCIP_OKAY;
    1284 }
    1285
    1286 /* init graph */
    1287 SG_INIT(SG);
    1288
    1289 SG_ALLOC(SG, (size_t) nnodes, 2 * (size_t)(unsigned) nedges, "malloc"); /*lint !e647*//*lint !e774*//*lint !e571*/
    1290
    1291 SG.nv = nnodes; /* number of nodes */
    1292 SG.nde = (size_t) (unsigned) (2 * nedges); /* number of directed edges */
    1293
    1294 /* add the nodes for linear and nonlinear constraints to the graph */
    1295 SCIP_CALL( createOrDetermineSizeGraph(scip, symgraph, FALSE, &SG, &nnodes, &nedges,
    1296 &degrees, &maxdegrees, &colors, &ncolors, &success) );
    1297
    1298 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
    1299
    1300 /* memory allocation for nauty/traces */
    1304
    1305 /* fill in array with colors for variables */
    1306 for (v = 0; v < nnodes; ++v)
    1307 lab[v] = v;
    1308
    1309 /* sort nodes according to colors */
    1310 SCIPsortIntInt(colors, lab, nnodes);
    1311
    1312 /* set up ptn marking new colors */
    1313 for (v = 0; v < nnodes; ++v)
    1314 {
    1315 if ( v < nnodes-1 && colors[v] == colors[v+1] )
    1316 ptn[v] = 1; /* color class does not end */
    1317 else
    1318 ptn[v] = 0; /* color class ends */
    1319 }
    1320
    1321 SCIPdebugMsg(scip, "Symmetry detection graph has %d nodes.\n", nnodes);
    1322
    1323 data_.scip = scip;
    1325 data_.nperms = 0;
    1326 data_.nmaxperms = 0;
    1328 data_.perms = NULL;
    1331 SCIP_CALL( SCIPgetIntParam(scip, "propagating/symmetry/nautymaxlevel", &data_.maxlevel) );
    1332
    1333 /* call nauty/traces */
    1334#ifdef NAUTY
    1335 sparsenauty(&SG, lab, ptn, orbits, &options, &stats, NULL);
    1336#else
    1337 Traces(&SG, lab, ptn, orbits, &options, &stats, NULL);
    1338#endif
    1339 *symcodetime = SCIPgetSolvingTime(scip) - oldtime;
    1340
    1341 SCIPfreeBufferArray(scip, &orbits);
    1344
    1345 SCIPfreeBlockMemoryArray(scip, &colors, ncolors);
    1346
    1347 SG_FREE(SG); /*lint !e774*/
    1348
    1349 /* prepare return values */
    1350 if ( data_.nperms > 0 )
    1351 {
    1352 *perms = data_.perms;
    1353 *nperms = data_.nperms;
    1355 }
    1356 else
    1357 {
    1358 assert( data_.perms == NULL );
    1359 assert( data_.nmaxperms == 0 );
    1360
    1361 *perms = NULL;
    1362 *nperms = 0;
    1363 *nmaxperms = 0;
    1364 }
    1365
    1366 /* determine log10 of symmetry group size */
    1367 *log10groupsize = log10(stats.grpsize1 * pow(10.0, (SCIP_Real) stats.grpsize2));
    1368
    1369 return SCIP_OKAY;
    1370}
    1371
    1372/** returns whether two given graphs are identical */
    1374 SCIP* scip, /**< SCIP pointer */
    1375 SYM_SYMTYPE symtype, /**< type of symmetries to be checked */
    1376 SYM_GRAPH* G1, /**< first graph */
    1377 SYM_GRAPH* G2 /**< second graph */
    1378 )
    1379{
    1380 int nnodes;
    1381 int nedges;
    1382 int* degrees;
    1383 int maxdegrees;
    1384 int* colors;
    1385 int ncolors;
    1386 int nusedvars;
    1387 SCIP_Bool success;
    1388 int v;
    1389 int nnodesfromG1;
    1390
    1391 assert( scip != NULL );
    1392 assert( G1 != NULL );
    1393 assert( G2 != NULL );
    1394
    1395 /* some simple checks */
    1396 if ( G1->nnodes != G2->nnodes || G1->nopnodes != G2->nopnodes || G1->nvalnodes != G2->nvalnodes
    1397 || G1->nconsnodes != G2->nconsnodes || G1->nedges != G2->nedges )
    1398 return FALSE;
    1399
    1400 SCIP_CALL_ABORT( createOrDetermineSizeGraphCheck(scip, G1, G2, TRUE, NULL, &nnodes, &nedges, &degrees, &maxdegrees,
    1401 &colors, &ncolors, &nusedvars, &nnodesfromG1, &success) );
    1402
    1403 if ( ! success )
    1404 {
    1405 SCIPfreeBlockMemoryArrayNull(scip, &degrees, maxdegrees);
    1406 SCIPfreeBlockMemoryArrayNull(scip, &colors, ncolors);
    1407
    1408 return FALSE;
    1409 }
    1410
    1411 /* nauty data structures */
    1412 sparsegraph SG;
    1413 int* lab;
    1414 int* ptn;
    1415 int* orbits;
    1416
    1417#ifdef NAUTY
    1418 DEFAULTOPTIONS_SPARSEGRAPH(options);
    1419 statsblk stats;
    1420#else
    1421 static DEFAULTOPTIONS_TRACES(options);
    1422 TracesStats stats;
    1423#endif
    1424
    1425 /* init options */
    1426#ifdef NAUTY
    1427 /* init callback functions for nauty (accumulate the group generators found by nauty) */
    1428 options.writeautoms = FALSE;
    1429 options.userautomproc = nautyhook;
    1430 options.defaultptn = FALSE; /* use color classes */
    1431#else
    1432 /* init callback functions for traces (accumulate the group generators found by traces) */
    1433 options.writeautoms = FALSE;
    1434 options.userautomproc = traceshook;
    1435 options.defaultptn = FALSE; /* use color classes */
    1436#endif
    1437
    1438 /* init graph */
    1439 SG_INIT(SG);
    1440
    1441 SG_ALLOC(SG, (size_t) nnodes, 2 * (size_t)(unsigned) nedges, "malloc"); /*lint !e647*//*lint !e774*//*lint !e571*/
    1442
    1443 SG.nv = nnodes; /* number of nodes */
    1444 SG.nde = (size_t) (unsigned) (2 * nedges); /* number of directed edges */
    1445
    1446 /* add the nodes for linear and nonlinear constraints to the graph */
    1447 SCIP_CALL_ABORT( createOrDetermineSizeGraphCheck(scip, G1, G2, FALSE, &SG, &nnodes, &nedges, &degrees, &maxdegrees,
    1448 &colors, &ncolors, &nusedvars, NULL, &success) );
    1449 assert( success );
    1450
    1451 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
    1452
    1453#ifdef SCIP_DISABLED_CODE
    1454 /* print information about sparsegraph */
    1455 SCIPinfoMessage(scip, NULL, "number of nodes: %d\n", SG.nv);
    1456 SCIPinfoMessage(scip, NULL, "number of (directed) edges: %lu\n", SG.nde);
    1457 SCIPinfoMessage(scip, NULL, "degrees\n");
    1458 for (v = 0; v < SG.nv; ++v)
    1459 {
    1460 SCIPinfoMessage(scip, NULL, "node %d: %d\n", v, SG.d[v]);
    1461 }
    1462 SCIPinfoMessage(scip, NULL, "colors\n");
    1463 for (v = 0; v < SG.nv; ++v)
    1464 {
    1465 SCIPinfoMessage(scip, NULL, "node %d: %d\n", v, colors[v]);
    1466 }
    1467 SCIPinfoMessage(scip, NULL, "edges\n");
    1468 for (v = 0; v < SG.nv; ++v)
    1469 {
    1470 for (int w = 0; w < SG.d[v]; ++w)
    1471 {
    1472 SCIPinfoMessage(scip, NULL, "(%d,%d)\n", v, SG.e[SG.v[v] + w]);
    1473 }
    1474 }
    1475#endif
    1476
    1477 /* memory allocation for nauty/traces */
    1481
    1482 /* fill in array with colors for variables */
    1483 for (v = 0; v < nnodes; ++v)
    1484 lab[v] = v;
    1485
    1486 /* sort nodes according to colors */
    1487 SCIPsortIntInt(colors, lab, nnodes);
    1488
    1489 /* set up ptn marking new colors */
    1490 for (v = 0; v < nnodes; ++v)
    1491 {
    1492 if ( v < nnodes-1 && colors[v] == colors[v+1] )
    1493 ptn[v] = 1; /* color class does not end */
    1494 else
    1495 ptn[v] = 0; /* color class ends */
    1496 }
    1497
    1498#ifdef SCIP_DISABLED_CODE
    1499 /* print further information about sparsegraph */
    1500 SCIPinfoMessage(scip, NULL, "lab (and ptn):\n");
    1501 for (v = 0; v < SG.nv; ++v)
    1502 {
    1503 SCIPinfoMessage(scip, NULL, "%d (%d)\n", lab[v], ptn[v]);
    1504 }
    1505#endif
    1506
    1507 /* compute automorphisms */
    1508 data_.scip = scip;
    1510 data_.nperms = 0;
    1511 data_.nmaxperms = 0;
    1512 data_.maxgenerators = 0;
    1513 data_.perms = NULL;
    1516 SCIP_CALL( SCIPgetIntParam(scip, "propagating/symmetry/nautymaxlevel", &data_.maxlevel) ); /*lint !e641*//*lint !e732*/
    1517
    1518 /* call nauty/traces */
    1519#ifdef NAUTY
    1520 sparsenauty(&SG, lab, ptn, orbits, &options, &stats, NULL);
    1521#else
    1522 Traces(&SG, lab, ptn, orbits, &options, &stats, NULL);
    1523#endif
    1524
    1525 SCIPfreeBufferArray(scip, &orbits);
    1528
    1529 SCIPfreeBlockMemoryArray(scip, &colors, ncolors);
    1530
    1531 SG_FREE(SG); /*lint !e774*/
    1532
    1533 /* G1 and G2 cannot be isomorphic */
    1534 if ( data_.nperms == 0 )
    1535 return FALSE;
    1536
    1537 success = FALSE;
    1538 for (int p = 0; p < data_.nperms; ++p)
    1539 {
    1540 for (int i = 0; i < nnodesfromG1; ++i)
    1541 {
    1542 if ( data_.perms[p][i] >= nnodesfromG1 )
    1543 {
    1544 success = TRUE;
    1545 break;
    1546 }
    1547 }
    1548 }
    1549
    1550 /* free memory */
    1551 for (int p = 0; p < data_.nperms; ++p)
    1552 {
    1554 }
    1556
    1557 SG_FREE(SG); /*lint !e774*/
    1558
    1559 return success;
    1560}
    SCIP_VAR * w
    Definition: circlepacking.c:67
    interface for symmetry computations
    static _Thread_local NAUTY_DATA data_
    SCIP_Bool SYMcheckGraphsAreIdentical(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH *G1, SYM_GRAPH *G2)
    const char * SYMsymmetryGetName(void)
    static const char nautyname[]
    static void nautyhook(int count, int *p, int *orbits, int numorbits, int stabvertex, int n)
    const char * SYMsymmetryGetAddName(void)
    SCIP_Bool SYMcanComputeSymmetry(void)
    const char * SYMsymmetryGetDesc(void)
    static SCIP_RETCODE createOrDetermineSizeGraphCheck(SCIP *scip, SYM_GRAPH *graph1, SYM_GRAPH *graph2, SCIP_Bool determinesize, sparsegraph *SG, int *nnodes, int *nedges, int **degrees, int *maxdegrees, int **colors, int *ncolors, int *nusedvars, int *nnodesfromgraph1, SCIP_Bool *success)
    static void nautyterminationhook(graph *g, int *lab, int *ptn, int level, int numcells, int tc, int code, int m, int n)
    static SCIP_RETCODE addOrDetermineEffectOfGroupedEdges(SCIP *scip, sparsegraph *SG, int *edgestartpos, SCIP_Bool determinesize, int *internodeid, int **degrees, int *maxdegrees, int **colorstostore, int *ncolorstostore, int *nnodes, int *nedges, int commonnodeidx, int *neighbors, int *colors, int nneighbors, int *naddednodes, int *naddededges)
    static SCIP_RETCODE createOrDetermineSizeGraph(SCIP *scip, SYM_GRAPH *symgraph, SCIP_Bool determinesize, sparsegraph *SG, int *nnodes, int *nedges, int **degrees, int *maxdegrees, int **colors, int *ncolors, SCIP_Bool *success)
    static SCIP_Bool isEdgeGroupable(SYM_GRAPH *symgraph, int edgeidx, SCIP_Bool groupbycons)
    const char * SYMsymmetryGetAddDesc(void)
    SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_GRAPH *symgraph, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Real *symcodetime)
    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 SCIP_Real
    Definition: def.h:156
    #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 SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:208
    void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:225
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    SCIP_RETCODE SCIPgetIntParam(SCIP *scip, const char *name, int *value)
    Definition: scip_param.c:269
    #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
    int SCIPcalcMemGrowSize(SCIP *scip, int num)
    Definition: scip_mem.c:139
    #define SCIPallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:124
    #define SCIPfreeBufferArray(scip, ptr)
    Definition: scip_mem.h:136
    #define SCIPallocBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:93
    #define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
    Definition: scip_mem.h:99
    #define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
    Definition: scip_mem.h:111
    #define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
    Definition: scip_mem.h:105
    SCIP_Real SCIPgetSolvingTime(SCIP *scip)
    Definition: scip_timing.c:378
    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
    SYM_SYMTYPE symtype
    SCIP_Bool restricttovars
    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