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