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