Scippy

    SCIP

    Solving Constraint Integer Programs

    compute_symmetry_sassy_bliss.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 compute_symmetry_sassy_bliss.cpp
    26 * @brief interface for symmetry computations to sassy as a preprocessor to bliss
    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/* include bliss */
    35#include <bliss/defs.hh>
    36#include <bliss/graph.hh>
    37
    38/* include sassy (as part of dejavu) */
    39#include "build_dejavu_graph.h"
    40#include <dejavu/tools/bliss_converter.h>
    41
    42#include "scip/expr_var.h"
    43#include "scip/expr_sum.h"
    44#include "scip/expr_pow.h"
    45#include "scip/expr.h"
    46#include "scip/cons_nonlinear.h"
    47#include "scip/cons_linear.h"
    48#include "scip/scip_mem.h"
    49#include "scip/symmetry_graph.h"
    50
    51
    52/** struct for symmetry callback */
    53struct SYMMETRY_Data
    54{
    55 SCIP* scip; /**< SCIP pointer */
    56 SYM_SYMTYPE symtype; /**< type of symmetries that need to be computed */
    57 int npermvars; /**< number of variables for permutations */
    58 int nperms; /**< number of permutations */
    59 int** perms; /**< permutation generators as (nperms x npermvars) matrix */
    60 int nmaxperms; /**< maximal number of permutations */
    61 int maxgenerators; /**< maximal number of generators constructed (= 0 if unlimited) */
    62 SCIP_Bool restricttovars; /**< whether permutations shall be restricted to variables */
    63};
    64
    65
    66/* ------------------- hook functions ------------------- */
    67
    68/** callback function for sassy */ /*lint -e{715}*/
    69static
    71 void* user_param, /**< parameter supplied at call to sassy */
    72 int n, /**< dimension of permutations */
    73 const int* aut, /**< permutation */
    74 int nsupp, /**< support size */
    75 const int* suppa /**< support list */
    76 )
    77{
    78 assert( aut != NULL );
    79 assert( user_param != NULL );
    80
    81 SYMMETRY_Data* data = static_cast<SYMMETRY_Data*>(user_param);
    82 assert( data->scip != NULL );
    83 assert( data->maxgenerators >= 0 );
    84
    85 /* make sure we do not generate more that maxgenerators many permutations */
    86 if ( data->maxgenerators != 0 && data->nperms >= data->maxgenerators )
    87 return;
    88
    89 /* copy first part of automorphism */
    90 bool isIdentity = true;
    91 int* p = 0;
    92 int permlen;
    93 if ( data->restricttovars )
    94 {
    95 switch ( data->symtype )
    96 {
    98 permlen = data->npermvars;
    99 break;
    100 default:
    101 assert( data->symtype == SYM_SYMTYPE_SIGNPERM );
    102 permlen = 2 * data->npermvars;
    103 }
    104 }
    105 else
    106 permlen = n;
    107
    108 /* check whether permutation is identity */
    109 for (int j = 0; j < permlen; ++j)
    110 {
    111 if ( (int) aut[j] != j )
    112 isIdentity = false;
    113 }
    114
    115 /* don't store identity permutations */
    116 if ( isIdentity )
    117 return;
    118
    119 if ( SCIPallocBlockMemoryArray(data->scip, &p, permlen) != SCIP_OKAY )
    120 return;
    121
    122 /* store symmetry */
    123 for (int j = 0; j < permlen; ++j)
    124 p[j] = (int) aut[j];
    125
    126 /* check whether we should allocate space for perms */
    127 if ( data->nmaxperms <= 0 )
    128 {
    129 if ( data->maxgenerators == 0 )
    130 data->nmaxperms = 100; /* seems to cover many cases */
    131 else
    132 data->nmaxperms = data->maxgenerators;
    133
    134 if ( SCIPallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms) != SCIP_OKAY )
    135 return;
    136 }
    137 else if ( data->nperms >= data->nmaxperms ) /* check whether we need to resize */
    138 {
    139 int newsize = SCIPcalcMemGrowSize(data->scip, data->nperms + 1);
    140 assert( newsize >= data->nperms );
    141 assert( data->maxgenerators == 0 );
    142
    143 if ( SCIPreallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms, newsize) != SCIP_OKAY )
    144 return;
    145
    146 data->nmaxperms = newsize;
    147 }
    148
    149 data->perms[data->nperms++] = p;
    150}
    151
    152
    153/** return whether symmetry can be computed */
    155{
    156 return TRUE;
    157}
    158
    159/** return name of external program used to compute generators */
    160const char* SYMsymmetryGetName(void)
    161{
    162#ifdef BLISS_PATCH_PRESENT
    163 return "bliss " BLISS_VERSION "p";
    164#else
    165 return "bliss " BLISS_VERSION;
    166#endif
    167}
    168
    169/** return description of external program used to compute generators */
    170const char* SYMsymmetryGetDesc(void)
    171{
    172 return "Computing Graph Automorphisms by T. Junttila and P. Kaski (users.aalto.fi/~tjunttil/bliss)";
    173}
    174
    175#define STR(x) #x
    176#define XSTR(x) STR(x)
    177
    178/** return name of additional external program used for computing symmetries */
    179const char* SYMsymmetryGetAddName(void)
    180{
    181 return "sassy " XSTR(SASSY_VERSION_MAJOR) "." XSTR(SASSY_VERSION_MINOR);
    182}
    183
    184/** return description of additional external program used to compute symmetries */
    185const char* SYMsymmetryGetAddDesc(void)
    186{
    187 return "Symmetry preprocessor by Markus Anders (github.com/markusa4/sassy)";
    188}
    189
    190/** computes autormorphisms of a graph */
    191static
    193 SCIP* scip, /**< SCIP pointer */
    194 SYM_SYMTYPE symtype, /**< type of symmetries that need to be computed */
    195 dejavu::static_graph* G, /**< pointer to graph for that automorphisms are computed */
    196 int nsymvars, /**< number of variables encoded in graph */
    197 int maxgenerators, /**< maximum number of generators to be constructed (=0 if unlimited) */
    198 int*** perms, /**< pointer to store generators as (nperms x npermvars) matrix */
    199 int* nperms, /**< pointer to store number of permutations */
    200 int* nmaxperms, /**< pointer to store maximal number of permutations
    201 * (needed for freeing storage) */
    202 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
    203 SCIP_Bool restricttovars, /**< whether permutations shall be restricted to variables */
    204 SCIP_Real* symcodetime /**< pointer to store the time for symmetry code */
    205 )
    206{
    207 SCIP_Real oldtime;
    208
    209 assert( scip != NULL );
    210 assert( G != NULL );
    211 assert( maxgenerators >= 0 );
    212 assert( perms != NULL );
    213 assert( nperms != NULL );
    214 assert( nmaxperms != NULL );
    215 assert( log10groupsize != NULL );
    216 assert( symcodetime != NULL );
    217
    218 /* init */
    219 *nperms = 0;
    220 *nmaxperms = 0;
    221 *perms = NULL;
    222 *log10groupsize = 0;
    223 *symcodetime = 0.0;
    224
    225 /* init data */
    226 struct SYMMETRY_Data data;
    227 data.scip = scip;
    228 data.symtype = symtype;
    229 data.npermvars = nsymvars;
    230 data.nperms = 0;
    231 data.nmaxperms = 0;
    233 data.perms = NULL;
    235
    236 oldtime = SCIPgetSolvingTime(scip);
    237
    238 /* set up sassy preprocessor */
    239 dejavu::preprocessor sassy;
    240
    241 /* lambda function to have access to data and pass it to sassyhook above */
    242 dejavu_hook sassyglue = [&](int n, const int* p, int nsupp, const int* suppa) {
    243 sassyhook((void*)&data, n, p, nsupp, suppa);
    244 };
    245
    246 /* call sassy to reduce graph */
    247 sassy.reduce(G, &sassyglue);
    248
    249 if( G->get_sgraph()->v_size == 0 )
    250 {
    251 dejavu::big_number grp_sz = sassy.grp_sz;
    252 *log10groupsize = (SCIP_Real) log10l(grp_sz.mantissa * powl(10.0, (SCIP_Real) grp_sz.exponent));
    253 }
    254 else
    255 {
    256 /* create bliss graph */
    257 bliss::Graph blissgraph(0);
    258
    259 /* convert sassy to bliss graph */
    260 convert_dejavu_to_bliss(G, &blissgraph);
    261
    262#ifdef SCIP_OUTPUT
    263 blissgraph.write_dot("debug.dot");
    264#endif
    265
    266#ifdef SCIP_DISABLED_CODE
    267 char filename[SCIP_MAXSTRLEN];
    268 (void) SCIPsnprintf(filename, SCIP_MAXSTRLEN, "%s.dimacs", SCIPgetProbName(scip));
    269 FILE* fp = fopen(filename, "w");
    270 if ( fp )
    271 {
    272 blissgraph.write_dimacs(fp);
    273 fclose(fp);
    274 }
    275#endif
    276
    277 /* compute automorphisms */
    278 bliss::Stats stats;
    279
    280 /* Prefer splitting partition cells corresponding to variables over those corresponding
    281 * to inequalities. This is because we are only interested in the action
    282 * of the automorphism group on the variables, and we need a base for this action */
    283 blissgraph.set_splitting_heuristic(bliss::Graph::shs_f);
    284 /* disable component recursion as advised by Tommi Junttila from bliss */
    285 blissgraph.set_component_recursion(false);
    286
    287#if BLISS_VERSION_MAJOR >= 1 || BLISS_VERSION_MINOR >= 76
    288 /* lambda function to have access to data and terminate the search if maxgenerators are reached */
    289 auto term = [&]() {
    290 return (maxgenerators != 0 && data.nperms >= maxgenerators); /* check the number of generators that we have created so far */
    291 };
    292
    293 auto hook = [&](unsigned int n, const unsigned int* aut) {
    294 sassy.bliss_hook(n, aut);
    295 };
    296
    297 /* start search */
    298 blissgraph.find_automorphisms(stats, hook, term);
    299#else
    300
    301 /* Older bliss versions do not allow to terminate with a limit on the number of generators unless patched. */
    302#ifdef BLISS_PATCH_PRESENT
    303 /* If patch is present, do not use a node limit, but set generator limit. This approach is not very accurate, since
    304 * it limits the generators considered in bliss and not the ones that we generate (the ones that work on the variable
    305 * set). */
    306 blissgraph.set_search_limits(0, (unsigned) maxgenerators);
    307#endif
    308
    309 /* start search */
    310 blissgraph.find_automorphisms(stats, dejavu::preprocessor::bliss_hook, (void*) &sassy);
    311#endif
    312
    313#ifdef SCIP_OUTPUT
    314 (void) stats.print(stdout);
    315#endif
    316 /* determine log10 of symmetry group size */
    317 dejavu::big_number grp_sz = sassy.grp_sz;
    318 *log10groupsize = (SCIP_Real) log10l(stats.get_group_size_approx() * grp_sz.mantissa * powl(10.0, (SCIP_Real) grp_sz.exponent));
    319 }
    320
    321 *symcodetime = SCIPgetSolvingTime(scip) - oldtime;
    322
    323 /* prepare return values */
    324 if ( data.nperms > 0 )
    325 {
    326 *perms = data.perms;
    327 *nperms = data.nperms;
    328 *nmaxperms = data.nmaxperms;
    329 }
    330 else
    331 {
    332 assert( data.perms == NULL );
    333 assert( data.nmaxperms == 0 );
    334
    335 *perms = NULL;
    336 *nperms = 0;
    337 *nmaxperms = 0;
    338 }
    339
    340 return SCIP_OKAY;
    341}
    342
    343/** compute generators of symmetry group */
    345 SCIP* scip, /**< SCIP pointer */
    346 int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
    347 SYM_GRAPH* graph, /**< symmetry detection graph */
    348 int* nperms, /**< pointer to store number of permutations */
    349 int* nmaxperms, /**< pointer to store maximal number of permutations (needed for freeing storage) */
    350 int*** perms, /**< pointer to store permutation generators as (nperms x npermvars) matrix */
    351 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
    352 SCIP_Real* symcodetime /**< pointer to store the time for symmetry code */
    353 )
    354{
    355 SCIP_Bool success = FALSE;
    356
    357 assert( scip != NULL );
    358 assert( maxgenerators >= 0 );
    359 assert( graph != NULL );
    360 assert( nperms != NULL );
    361 assert( nmaxperms != NULL );
    362 assert( perms != NULL );
    363 assert( log10groupsize != NULL );
    364 assert( symcodetime != NULL );
    365
    366 /* init */
    367 *nperms = 0;
    368 *nmaxperms = 0;
    369 *perms = NULL;
    370 *log10groupsize = 0;
    371 *symcodetime = 0.0;
    372
    373 /* create sassy graph */
    374 dejavu::static_graph sassygraph;
    375
    376 SCIP_CALL( SYMbuildDejavuGraph(scip, &sassygraph, graph, &success) );
    377
    378#ifdef WRITE_GRAPH
    379 std::string filename = std::string(SCIPgetProbName(scip)) + std::string(".dimacs");
    380 sassygraph.dump_dimacs(filename);
    381#endif
    382
    383 /* compute symmetries */
    385 maxgenerators, perms, nperms, nmaxperms, log10groupsize, TRUE, symcodetime) );
    386
    387 return SCIP_OKAY;
    388}
    389
    390/** returns whether two given graphs are identical */
    392 SCIP* scip, /**< SCIP pointer */
    393 SYM_SYMTYPE symtype, /**< type of symmetries to be checked */
    394 SYM_GRAPH* G1, /**< first graph */
    395 SYM_GRAPH* G2 /**< second graph */
    396 )
    397{
    398 int** perms;
    399 int nnodes;
    400 int nperms;
    401 int nmaxperms;
    402 int nnodesfromG1;
    403 SCIP_Real symcodetime = 0.0;
    404 SCIP_Real log10groupsize;
    405 SCIP_Bool success;
    406
    407 /* create sassy graph */
    408 dejavu::static_graph sassygraph;
    409
    410 SCIP_CALL( SYMbuildDejavuGraphCheck(scip, &sassygraph, G1, G2, &nnodes, &nnodesfromG1, &success) );
    411
    412 if ( ! success )
    413 return FALSE;
    414
    415 /* compute symmetries */
    417 &perms, &nperms, &nmaxperms, &log10groupsize, FALSE, &symcodetime) );
    418
    419 /* since G1 and G2 are connected and disjoint, they are isomorphic iff there is a permutation
    420 * mapping a node from G1 to a node of G2
    421 */
    422 success = FALSE;
    423 for (int p = 0; p < nperms && ! success; ++p)
    424 {
    425 for (int i = 0; i < nnodesfromG1; ++i)
    426 {
    427 if ( perms[p][i] >= nnodesfromG1 )
    428 {
    429 success = TRUE;
    430 break;
    431 }
    432 }
    433 }
    434
    435 for (int p = 0; p < nperms; ++p)
    436 {
    438 }
    440
    441 return success;
    442}
    SCIP_RETCODE SYMbuildDejavuGraphCheck(SCIP *scip, dejavu::static_graph *dejavugraph, SYM_GRAPH *G1, SYM_GRAPH *G2, int *nnodes, int *nnodesfromG1, 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
    interface for symmetry computations
    SCIP_Bool SYMcheckGraphsAreIdentical(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH *G1, SYM_GRAPH *G2)
    const char * SYMsymmetryGetName(void)
    const char * SYMsymmetryGetAddName(void)
    SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_GRAPH *graph, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Real *symcodetime)
    SCIP_Bool SYMcanComputeSymmetry(void)
    const char * SYMsymmetryGetDesc(void)
    static void sassyhook(void *user_param, int n, const int *aut, int nsupp, const int *suppa)
    #define XSTR(x)
    const char * SYMsymmetryGetAddDesc(void)
    static SCIP_RETCODE computeAutomorphisms(SCIP *scip, SYM_SYMTYPE symtype, dejavu::static_graph *G, int nsymvars, int maxgenerators, int ***perms, int *nperms, int *nmaxperms, SCIP_Real *log10groupsize, SCIP_Bool restricttovars, 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_MAXSTRLEN
    Definition: def.h:269
    #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
    const char * SCIPgetProbName(SCIP *scip)
    Definition: scip_prob.c:1242
    #define SCIPfreeBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:110
    int SCIPcalcMemGrowSize(SCIP *scip, int num)
    Definition: scip_mem.c:139
    #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
    SCIP_Real SCIPgetSolvingTime(SCIP *scip)
    Definition: scip_timing.c:378
    int SCIPsnprintf(char *t, int len, const char *s,...)
    Definition: misc.c:10827
    SYM_SYMTYPE SCIPgetSymgraphSymtype(SYM_GRAPH *graph)
    int SCIPgetSymgraphNVars(SYM_GRAPH *graph)
    public methods for memory management
    methods for dealing with symmetry detection graphs
    @ 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
    @ SYM_SYMTYPE_SIGNPERM
    Definition: type_symmetry.h:62
    @ SYM_SYMTYPE_PERM
    Definition: type_symmetry.h:61