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