Scippy

SCIP

Solving Constraint Integer Programs

compute_symmetry_bliss.cpp
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-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_bliss.cpp
26 * @brief interface for symmetry computations to bliss
27 * @author Marc Pfetsch
28 * @author Thomas Rehn
29 * @author Fabian Wegscheider
30 */
31
32/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
33
34#include "compute_symmetry.h"
35
36/* include bliss graph */
37#include <bliss/defs.hh>
38#include <bliss/graph.hh>
39
40#include <string.h>
41#include <vector>
42#include <list>
43#include <math.h>
44
45#include "scip/expr_var.h"
46#include "scip/expr_sum.h"
47#include "scip/expr_pow.h"
48#include "scip/expr.h"
49#include "scip/cons_nonlinear.h"
50#include "scip/cons_linear.h"
51#include "scip/symmetry.h"
52#include "scip/symmetry_graph.h"
53
54using std::vector;
55
56
57/** struct for bliss callback */
59{
60 SCIP* scip; /**< SCIP pointer */
61 SYM_SYMTYPE symtype; /**< type of symmetries that need to be computed */
62 int npermvars; /**< number of variables for permutations */
63 int nperms; /**< number of permutations */
64 int** perms; /**< permutation generators as (nperms x npermvars) matrix */
65 int nmaxperms; /**< maximal number of permutations */
66 int maxgenerators; /**< maximal number of generators constructed (= 0 if unlimited) */
67 SCIP_Bool restricttovars; /**< whether permutations shall be restricted to variables */
68};
69
70/** callback function for bliss */
71static
73 void* user_param, /**< parameter supplied at call to bliss */
74 unsigned int n, /**< size of aut vector */
75 const unsigned int* aut /**< automorphism */
76 )
77{
78 assert( aut != NULL );
79 assert( user_param != NULL );
80
81 BLISS_Data* data = static_cast<BLISS_Data*>(user_param);
82 assert( data->scip != NULL );
83 assert( data->maxgenerators >= 0);
84
85 /* make sure we do not generate more that maxgenerators many permutations, if the limit in bliss is not available */
86 if ( data->maxgenerators != 0 && data->nperms >= data->maxgenerators )
87 return;
88
89 /* copy first part of automorphism */
90 bool isIdentity = true;
91 int* p = 0;
92 int permlen;
93 if ( data->restricttovars )
94 {
95 if ( data->symtype == SYM_SYMTYPE_PERM )
96 permlen = data->npermvars;
97 else
98 permlen = 2 * data->npermvars;
99 }
100 else
101 permlen = n;
102
103 /* check whether permutation is identity */
104 for (int j = 0; j < permlen; ++j)
105 {
106 if ( (int) aut[j] != j )
107 isIdentity = false;
108 }
109
110 /* don't store identity permutations */
111 if ( isIdentity )
112 return;
113
114 if ( SCIPallocBlockMemoryArray(data->scip, &p, permlen) != SCIP_OKAY )
115 return;
116
117 /* store symmetry */
118 for (int j = 0; j < permlen; ++j)
119 p[j] = (int) aut[j];
120
121 /* check whether we should allocate space for perms */
122 if ( data->nmaxperms <= 0 )
123 {
124 if ( data->maxgenerators == 0 )
125 data->nmaxperms = 100; /* seems to cover many cases */
126 else
127 data->nmaxperms = data->maxgenerators;
128
129 if ( SCIPallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms) != SCIP_OKAY )
130 return;
131 }
132 else if ( data->nperms >= data->nmaxperms ) /* check whether we need to resize */
133 {
134 int newsize = SCIPcalcMemGrowSize(data->scip, data->nperms + 1);
135 assert( newsize >= data->nperms );
136 assert( data->maxgenerators == 0 );
137
138 if ( SCIPreallocBlockMemoryArray(data->scip, &data->perms, data->nmaxperms, newsize) != SCIP_OKAY )
139 return;
140
141 data->nmaxperms = newsize;
142 }
143
144 data->perms[data->nperms++] = p;
145}
146
147/** return whether symmetry can be computed */
149{
150 return TRUE;
151}
152
153/** return name of external program used to compute generators */
154const char* SYMsymmetryGetName(void)
155{
156#ifdef BLISS_PATCH_PRESENT
157 return "bliss " BLISS_VERSION "p";
158#else
159 return "bliss " BLISS_VERSION;
160#endif
161}
162
163/** return description of external program used to compute generators */
164const char* SYMsymmetryGetDesc(void)
165{
166 return "Computing Graph Automorphism Groups by T. Junttila and P. Kaski (users.aalto.fi/~tjunttil/bliss)";
167}
168
169/** return name of additional external program used for computing symmetries */
170const char* SYMsymmetryGetAddName(void)
171{
172 return NULL;
173}
174
175/** return description of additional external program used to compute symmetries */
176const char* SYMsymmetryGetAddDesc(void)
177{
178 return NULL;
179}
180
181/** returns whether an edge is considered in grouping process */
183 SYM_GRAPH* graph, /**< symmetry detection graph */
184 int edgeidx, /**< index of edge to be checked */
185 SCIP_Bool groupbycons /**< whether edges are grouped by constraints */
186 )
187{
188 assert(graph != NULL);
189
190 int first = SCIPgetSymgraphEdgeFirst(graph, edgeidx);
191 int second = SCIPgetSymgraphEdgeSecond(graph, edgeidx);
192
193 /* uncolored edges are not grouped */
194 if ( ! SCIPisSymgraphEdgeColored(graph, edgeidx) )
195 return FALSE;
196
197 /* two variable nodes are connected */
198 if ( first < 0 && second < 0 )
199 return FALSE;
200
201 if ( ! groupbycons )
202 {
203 /* grouping by variables requires one variable node */
204 if ( first < 0 || second < 0 )
205 return TRUE;
206 }
207 else
208 {
209 /* check whether there is exactly one constraint node in edge */
210 if ( first >= 0 && second >= 0 )
211 {
212 if ( (SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS
213 && SCIPgetSymgraphNodeType(graph, second) != SYM_NODETYPE_CONS)
214 || (SCIPgetSymgraphNodeType(graph, first) != SYM_NODETYPE_CONS
215 && SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS) )
216 return TRUE;
217 }
218 else if ( first >= 0 )
219 {
220 if ( SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS )
221 return TRUE;
222 }
223 else
224 {
225 if ( SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS )
226 return TRUE;
227 }
228 }
229
230 return FALSE;
231}
232
233/** adds grouped edges all of which have one common endpoint to a graph
234 *
235 * The grouping mechanism works by sorting the edges according to their color. If two
236 * edges have the same color, they share the same intermediate node, which is connected
237 * to the common node and the other endpoints of equivalent edges.
238 */
239static
241 bliss::Graph* G, /**< pointer to graph which gets extended */
242 int commonnodeidx, /**< index of common node in G */
243 int* neighbors, /**< neighbors of common node */
244 int* colors, /**< colors of edges to neighbors */
245 int nneighbors, /**< number of neighbors */
246 int* naddednodes, /**< pointer to store number of nodes added to G */
247 int* naddededges /**< pointer to store number of edges added to G */
248 )
249{
250 assert( G != NULL );
251 assert( neighbors != NULL );
252 assert( colors != NULL );
253 assert( naddednodes != NULL );
254 assert( naddededges != NULL );
255
256 *naddednodes = 0;
257 *naddededges = 0;
258
259 /* sort edges according to color */
260 SCIPsortIntInt(colors, neighbors, nneighbors);
261
262 /* iterate over groups of identical edges and group them, ignoring the last group */
263 int curcolor = colors[0];
264 int curstart = 0;
265 for (int e = 1; e < nneighbors; ++e)
266 {
267 /* if a new group started, add edges for previous group */
268 if ( colors[e] != curcolor )
269 {
270 int internode = (*G).add_vertex(curcolor);
271 (*G).add_edge(commonnodeidx, internode);
272 *naddednodes += 1;
273
274 for (int f = curstart; f < e; ++f)
275 (*G).add_edge(internode, neighbors[f]);
276 *naddededges += e - curstart + 1;
277
278 curcolor = colors[e];
279 curstart = e;
280 }
281 }
282
283 /* add edges of last group */
284 int internode = (*G).add_vertex(curcolor);
285 (*G).add_edge(commonnodeidx, internode);
286 *naddednodes += 1;
287
288 for (int f = curstart; f < nneighbors; ++f)
289 (*G).add_edge(internode, neighbors[f]);
290 *naddededges += nneighbors - curstart + 1;
291
292 return SCIP_OKAY;
293}
294
295/** computes autormorphisms of a graph */
296static
298 SCIP* scip, /**< SCIP pointer */
299 SYM_SYMTYPE symtype, /**< type of symmetries that need to be computed */
300 bliss::Graph* G, /**< pointer to graph for that automorphisms are computed */
301 int nsymvars, /**< number of variables encoded in graph */
302 int maxgenerators, /**< maximum number of generators to be constructed (=0 if unlimited) */
303 int*** perms, /**< pointer to store generators as (nperms x npermvars) matrix */
304 int* nperms, /**< pointer to store number of permutations */
305 int* nmaxperms, /**< pointer to store maximal number of permutations
306 * (needed for freeing storage) */
307 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
308 SCIP_Bool restricttovars, /**< whether permutations shall be restricted to variables */
309 SCIP_Real* symcodetime /**< pointer to store the time for symmetry code */
310 )
311{
312 SCIP_Real oldtime;
313
314 assert( scip != NULL );
315 assert( G != NULL );
316 assert( perms != NULL );
317 assert( nperms != NULL );
318 assert( nmaxperms != NULL );
319 assert( log10groupsize != NULL );
320 assert( maxgenerators >= 0 );
321 assert( symcodetime != NULL );
322
323 /* init */
324 *nperms = 0;
325 *nmaxperms = 0;
326 *perms = NULL;
327 *log10groupsize = 0;
328 *symcodetime = 0.0;
329
330 bliss::Stats stats;
331 BLISS_Data data;
332 data.scip = scip;
333 data.symtype = symtype;
334 data.npermvars = nsymvars;
335 data.nperms = 0;
336 data.nmaxperms = 0;
337 data.maxgenerators = maxgenerators;
338 data.perms = NULL;
339 data.restricttovars = restricttovars;
340
341 /* Prefer splitting partition cells corresponding to variables over those corresponding
342 * to inequalities. This is because we are only interested in the action
343 * of the automorphism group on the variables, and we need a base for this action */
344 G->set_splitting_heuristic(bliss::Graph::shs_f);
345 /* disable component recursion as advised by Tommi Junttila from bliss */
346 G->set_component_recursion(false);
347
348 oldtime = SCIPgetSolvingTime(scip);
349#if BLISS_VERSION_MAJOR >= 1 || BLISS_VERSION_MINOR >= 76
350 /* lambda function to have access to data and pass it to the blisshook above */
351 auto reportglue = [&](unsigned int n, const unsigned int* aut) {
352 blisshook((void*)&data, n, aut);
353 };
354
355 /* lambda function to have access to data and terminate the search if maxgenerators are reached */
356 auto term = [&]() {
357 return (maxgenerators != 0 && data.nperms >= maxgenerators); /* check the number of generators that we have created so far */
358 };
359
360 /* start search */
361 G->find_automorphisms(stats, reportglue, term);
362#else
363
364 /* Older bliss versions do not allow to terminate with a limit on the number of generators unless patched. */
365#ifdef BLISS_PATCH_PRESENT
366 /* If patch is present, do not use a node limit, but set generator limit. This approach is not very accurate, since
367 * it limits the generators considered in bliss and not the ones that we generate (the ones that work on the variable
368 * set). */
369 G->set_search_limits(0, (unsigned) maxgenerators);
370#endif
371
372 /* start search */
373 G->find_automorphisms(stats, blisshook, (void*) &data);
374#endif
375 *symcodetime = SCIPgetSolvingTime(scip) - oldtime;
376
377#ifdef SCIP_OUTPUT
378 (void) stats.print(stdout);
379#endif
380
381 /* prepare return values */
382 if ( data.nperms > 0 )
383 {
384 *perms = data.perms;
385 *nperms = data.nperms;
386 *nmaxperms = data.nmaxperms;
387 }
388 else
389 {
390 assert( data.perms == NULL );
391 assert( data.nmaxperms == 0 );
392
393 *perms = NULL;
394 *nperms = 0;
395 *nmaxperms = 0;
396 }
397
398 /* determine log10 of symmetry group size */
399 *log10groupsize = (SCIP_Real) log10l(stats.get_group_size_approx());
400
401 return SCIP_OKAY;
402}
403
404/** compute generators of symmetry group */
406 SCIP* scip, /**< SCIP pointer */
407 int maxgenerators, /**< maximal number of generators constructed (= 0 if unlimited) */
408 SYM_GRAPH* graph, /**< symmetry detection graph */
409 int* nperms, /**< pointer to store number of permutations */
410 int* nmaxperms, /**< pointer to store maximal number of permutations
411 * (needed for freeing storage) */
412 int*** perms, /**< pointer to store generators as (nperms x npermvars) matrix */
413 SCIP_Real* log10groupsize, /**< pointer to store log10 of size of group */
414 SCIP_Real* symcodetime /**< pointer to store the time for symmetry code */
415 )
416{
417 int nvarnodestoadd;
418 int first;
419 int second;
420 int nnodes = 0;
421 int nedges = 0;
422
423 assert( scip != NULL );
424 assert( graph != NULL );
425 assert( nperms != NULL );
426 assert( nmaxperms != NULL );
427 assert( perms != NULL );
428 assert( log10groupsize != NULL );
429 assert( symcodetime != NULL );
430
431 /* init */
432 *nperms = 0;
433 *nmaxperms = 0;
434 *perms = NULL;
435 *log10groupsize = 0;
436 *symcodetime = 0.0;
437
438 /* create bliss graph */
439 bliss::Graph G(0);
440
441 /* add nodes for variables
442 *
443 * Variable nodes come first to easily extract the variable permutation.
444 * For signed permutations, the first nsymvars nodes correspond to original
445 * variables, and the second nsymvars nodes to their negations.
446 */
447 int nsymvars = SCIPgetSymgraphNVars(graph);
448 SYM_SYMTYPE symtype = SCIPgetSymgraphSymtype(graph);
449 switch ( symtype )
450 {
451 case SYM_SYMTYPE_PERM:
452 nvarnodestoadd = nsymvars;
453 break;
454 default:
455 assert( symtype == SYM_SYMTYPE_SIGNPERM );
456 nvarnodestoadd = 2 * nsymvars;
457 }
458
459 for (int v = 0; v < nvarnodestoadd; ++v)
460 {
461 const int color = SCIPgetSymgraphVarnodeColor(graph, v);
462
463#ifndef NDEBUG
464 int node = (int) G.add_vertex((unsigned) color);
465 assert( node == v );
466#else
467 (void) G.add_vertex((unsigned) color);
468#endif
469
470 ++nnodes;
471 }
472
473 /* add nodes for non-variable nodes */
474 int nsymnodes = SCIPgetSymgraphNNodes(graph);
475 for (int v = 0; v < nsymnodes; ++v)
476 {
477 const int color = SCIPgetSymgraphNodeColor(graph, v);
478
479#ifndef NDEBUG
480 int node = (int) G.add_vertex((unsigned) color);
481 assert( node == nvarnodestoadd + v );
482#else
483 (void) G.add_vertex((unsigned) color);
484#endif
485
486 ++nnodes;
487 }
488
489 /* add edges to bliss graph
490 *
491 * Edges containing neither a variable or constraint node are added immediately.
492 * Remaining edges are collected and we group these edges based on their weight.
493 */
494 const bool groupByConstraints = SCIPgetSymgraphNConsnodes(graph) < SCIPgetSymgraphNVars(graph);
495 int nsymedges = SCIPgetSymgraphNEdges(graph);
496 int* groupfirsts = NULL;
497 int* groupseconds = NULL;
498 int* groupcolors = NULL;
499 int ngroupedges = 0;
500
501 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
502 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
503 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
504
505 for (int e = 0; e < nsymedges; ++e)
506 {
507 first = SCIPgetSymgraphEdgeFirst(graph, e);
508 second = SCIPgetSymgraphEdgeSecond(graph, e);
509
510 if ( first < 0 )
511 first = -first - 1;
512 else
513 first += nvarnodestoadd;
514 if ( second < 0 )
515 second = -second - 1;
516 else
517 second += nvarnodestoadd;
518 assert(first >= 0);
519 assert(second >= 0);
520
521 /* check whether edge is used for grouping */
522 if ( ! SCIPhasGraphUniqueEdgetype(graph) && isEdgeGroupable(graph, e, groupByConstraints) )
523 {
524 /* store edge, first becomes the cons or var node */
525 SYM_NODETYPE comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
526
527 if ( SCIPgetSymgraphNodeType(graph, SCIPgetSymgraphEdgeFirst(graph, e)) == comparetype )
528 {
529 groupfirsts[ngroupedges] = first;
530 groupseconds[ngroupedges] = second;
531 }
532 else
533 {
534 groupfirsts[ngroupedges] = second;
535 groupseconds[ngroupedges] = first;
536 }
537 groupcolors[ngroupedges++] = SCIPgetSymgraphEdgeColor(graph, e);
538 }
539 else
540 {
541 /* immediately add edge */
542 assert(0 <= first && first < nnodes);
543 assert(0 <= second && second < nnodes);
544
545 /* possibly split edge if it is colored */
546 if ( ! SCIPhasGraphUniqueEdgetype(graph) && SCIPisSymgraphEdgeColored(graph, e) )
547 {
548 const int color = SCIPgetSymgraphEdgeColor(graph, e);
549
550 int inter = G.add_vertex((unsigned) color);
551
552 G.add_edge(first, inter);
553 G.add_edge(second, inter);
554
555 ++nnodes;
556 ++nedges;
557 }
558 else
559 G.add_edge(first, second);
560 ++nedges;
561 }
562 }
563
564 /* possibly add groupable edges */
565 if ( ngroupedges > 0 )
566 {
567 /* sort edges according to their first nodes */
568 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
569
570 int firstidx = 0;
571 int firstnodeidx = groupfirsts[0];
572 int naddednodes;
573 int naddededges;
574
575 for (int i = 1; i < ngroupedges; ++i)
576 {
577 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
578 if ( groupfirsts[i] != firstnodeidx )
579 {
580 SCIP_CALL( addGroupedEdges(&G, firstnodeidx, &groupseconds[firstidx],
581 &groupcolors[firstidx], i - firstidx, &naddednodes, &naddededges) );
582
583 firstidx = i;
584 firstnodeidx = groupfirsts[i];
585
586 nnodes += naddednodes;
587 nedges += naddededges;
588 }
589 }
590
591 /* process the last group */
592 SCIP_CALL( addGroupedEdges(&G, firstnodeidx, &groupseconds[firstidx],
593 &groupcolors[firstidx], ngroupedges - firstidx, &naddednodes, &naddededges) );
594
595 nnodes += naddednodes;
596 nedges += naddededges;
597 }
598
599 SCIPfreeBufferArray(scip, &groupcolors);
600 SCIPfreeBufferArray(scip, &groupseconds);
601 SCIPfreeBufferArray(scip, &groupfirsts);
602
603 /* for signed permutation, also add edges connecting a variable and its negation */
604 switch ( SCIPgetSymgraphSymtype(graph) )
605 {
607 for (int v = 0; v < nsymvars; ++v)
608 G.add_edge(v, v + nsymvars);
609 nedges += nsymvars;
610 break;
611 default:
612 assert( SCIPgetSymgraphSymtype(graph) == SYM_SYMTYPE_PERM );
613 }
614
615 assert( (int) G.get_nof_vertices() == nnodes );
616 assert( nedges >= SCIPgetSymgraphNEdges(graph) );
617 SCIPdebugMsg(scip, "Symmetry detection graph has %d nodes and %d edges.\n", nnodes, nedges);
618
619 /* compute automorphisms */
620 SCIP_CALL( computeAutomorphisms(scip, symtype, &G, nsymvars, maxgenerators,
621 perms, nperms, nmaxperms, log10groupsize, TRUE, symcodetime) );
622
623 return SCIP_OKAY;
624}
625
626/** returns whether two given graphs are identical */
628 SCIP* scip, /**< SCIP pointer */
629 SYM_SYMTYPE symtype, /**< type of symmetries to be checked */
630 SYM_GRAPH* G1, /**< first graph */
631 SYM_GRAPH* G2 /**< second graph */
632 )
633{
634 int* nvarused1 = NULL;
635 int* nvarused2 = NULL;
636 int* varlabel = NULL;
637 int nusedvars = 0;
638 int nvars;
639 int i;
640
641 assert( scip != NULL );
642 assert( G1 != NULL );
643 assert( G2 != NULL );
644
645 /* some simple checks */
646 if ( G1->nnodes != G2->nnodes || G1->nopnodes != G2->nopnodes || G1->nvalnodes != G2->nvalnodes
647 || G1->nconsnodes != G2->nconsnodes || G1->nedges != G2->nedges )
648 return FALSE;
649
650 /* check whether the variables used in G1 are the same as in G2 */
651 switch ( symtype )
652 {
653 case SYM_SYMTYPE_PERM:
654 nvars = G1->nsymvars;
655 break;
656 default:
657 assert( symtype == SYM_SYMTYPE_SIGNPERM );
658 nvars = 2 * G1->nsymvars;
659 }
660 SCIP_CALL_ABORT( SCIPallocClearBufferArray(scip, &nvarused1, nvars) );
661 SCIP_CALL_ABORT( SCIPallocClearBufferArray(scip, &nvarused2, nvars) );
662 SCIP_CALL_ABORT( SCIPallocBufferArray(scip, &varlabel, nvars) );
663
664 for (i = 0; i < G1->nedges; ++i)
665 {
666 if ( G1->edgefirst[i] < 0 )
667 nvarused1[-G1->edgefirst[i] - 1] += 1;
668 if ( G2->edgefirst[i] < 0 )
669 nvarused2[-G2->edgefirst[i] - 1] += 1;
670 if ( G1->edgesecond[i] < 0 )
671 nvarused1[-G1->edgesecond[i] - 1] += 1;
672 if ( G2->edgesecond[i] < 0 )
673 nvarused2[-G2->edgesecond[i] - 1] += 1;
674 }
675
676 for (i = 0; i < nvars; ++i)
677 {
678 if ( nvarused1[i] != nvarused2[i] )
679 {
680 SCIPfreeBufferArray(scip, &varlabel);
681 SCIPfreeBufferArray(scip, &nvarused2);
682 SCIPfreeBufferArray(scip, &nvarused1);
683
684 return FALSE;
685 }
686
687 /* relabel variables by restricting to variables used in constraint (or their negation) */
688 if ( nvarused1[i] > 0 || nvarused1[i % G1->nsymvars] > 0 )
689 varlabel[i] = nusedvars++;
690 else
691 varlabel[i] = -1;
692 }
693
694 /* construct bliss graph containing the (disjoint) union of the two graphs */
695 bliss::Graph G(0);
696
697 /* copy of G1 */
698 for (i = 0; i < nusedvars; ++i)
699 G.add_vertex(i);
700
701 for (i = 0; i < G1->nnodes; ++i)
702 G.add_vertex(nusedvars + SCIPgetSymgraphNodeColor(G1, i));
703
704 for (i = 0; i < G1->nedges; ++i)
705 {
706 int first = G1->edgefirst[i];
707 int second = G1->edgesecond[i];
708
709 if ( first < 0 )
710 first = varlabel[-first - 1];
711 else
712 first = nusedvars + first;
713 assert( first >= 0 );
714
715 if ( second < 0 )
716 second = varlabel[-second - 1];
717 else
718 second = nusedvars + second;
719 assert( second >= 0 );
720
721 if ( SCIPisSymgraphEdgeColored(G1, i) )
722 {
723 int inter = G.add_vertex(nusedvars + SCIPgetSymgraphEdgeColor(G1, i));
724 G.add_edge(first, inter);
725 G.add_edge(second, inter);
726 }
727 else
728 G.add_edge(first, second);
729 }
730
731 /* in case of signed permutations, also connect variables with their negation */
732 switch ( symtype )
733 {
735 for (i = 0; i < G1->nsymvars; ++i)
736 {
737 if ( nvarused1[i] > 0 || nvarused1[i + G1->nsymvars])
738 G.add_edge(varlabel[i], varlabel[i + G1->nsymvars]);
739 }
740 break;
741 default:
742 assert( symtype == SYM_SYMTYPE_PERM );
743 }
744
745 /* copy of G2 */
746 int nodeshift = G.get_nof_vertices();
747 for (i = 0; i < nusedvars; ++i)
748 G.add_vertex(i);
749
750 for (i = 0; i < G2->nnodes; ++i)
751 G.add_vertex(nusedvars + SCIPgetSymgraphNodeColor(G2, i));
752
753 for (i = 0; i < G2->nedges; ++i)
754 {
755 int first = G2->edgefirst[i];
756 int second = G2->edgesecond[i];
757
758 if ( first < 0 )
759 first = nodeshift + varlabel[-first - 1];
760 else
761 first = nodeshift + nusedvars + first;
762 assert( first >= 0 );
763
764 if ( second < 0 )
765 second = nodeshift + varlabel[-second - 1];
766 else
767 second = nodeshift + nusedvars + second;
768 assert( second >= 0 );
769
770 if ( SCIPisSymgraphEdgeColored(G2, i) )
771 {
772 int inter = G.add_vertex(nusedvars + SCIPgetSymgraphEdgeColor(G2, i));
773 G.add_edge(first, inter);
774 G.add_edge(second, inter);
775 }
776 else
777 G.add_edge(first, second);
778 }
779
780 /* in case of signed permutations, also connect variables with their negation */
781 switch ( symtype )
782 {
784 for (i = 0; i < G2->nsymvars; ++i)
785 {
786 if ( nvarused2[i] > 0 || nvarused2[i + G2->nsymvars])
787 G.add_edge(nodeshift + varlabel[i], nodeshift + varlabel[i + G2->nsymvars]);
788 }
789 break;
790 default:
791 assert( symtype == SYM_SYMTYPE_PERM );
792 }
793
794 /* compute automorphisms */
795 int** perms;
796 int nperms;
797 int nmaxperms;
798 SCIP_Real log10groupsize;
799 int n = G.get_nof_vertices();
800 int nnodesfromG1 = nusedvars + G1->nnodes;
801 SCIP_Real symcodetime = 0.0;
802
803 SCIP_CALL_ABORT( computeAutomorphisms(scip, symtype, &G, n, 0,
804 &perms, &nperms, &nmaxperms, &log10groupsize, FALSE, &symcodetime) );
805
806 /* since G1 and G2 are connected and disjoint, they are isomorphic iff there is a permutation
807 * mapping a node from G1 to a node of G2
808 */
809 SCIP_Bool success = FALSE;
810 for (int p = 0; p < nperms && ! success; ++p)
811 {
812 for (i = 0; i < nnodesfromG1; ++i)
813 {
814 if ( perms[p][i] >= nnodesfromG1 )
815 {
816 success = TRUE;
817 break;
818 }
819 }
820 }
821
822 for (int p = 0; p < nperms; ++p)
823 {
824 SCIPfreeBlockMemoryArray(scip, &perms[p], n);
825 }
826 SCIPfreeBlockMemoryArrayNull(scip, &perms, nmaxperms);
827
828 SCIPfreeBufferArray(scip, &varlabel);
829 SCIPfreeBufferArray(scip, &nvarused2);
830 SCIPfreeBufferArray(scip, &nvarused1);
831
832 return success;
833}
interface for symmetry computations
SCIP_Bool SYMcheckGraphsAreIdentical(SCIP *scip, SYM_SYMTYPE symtype, SYM_GRAPH *G1, SYM_GRAPH *G2)
const char * SYMsymmetryGetName(void)
const char * SYMsymmetryGetAddName(void)
SCIP_RETCODE SYMcomputeSymmetryGenerators(SCIP *scip, int maxgenerators, SYM_GRAPH *graph, int *nperms, int *nmaxperms, int ***perms, SCIP_Real *log10groupsize, SCIP_Real *symcodetime)
static SCIP_RETCODE computeAutomorphisms(SCIP *scip, SYM_SYMTYPE symtype, bliss::Graph *G, int nsymvars, int maxgenerators, int ***perms, int *nperms, int *nmaxperms, SCIP_Real *log10groupsize, SCIP_Bool restricttovars, SCIP_Real *symcodetime)
SCIP_Bool SYMcanComputeSymmetry(void)
static void blisshook(void *user_param, unsigned int n, const unsigned int *aut)
static SCIP_RETCODE addGroupedEdges(bliss::Graph *G, int commonnodeidx, int *neighbors, int *colors, int nneighbors, int *naddednodes, int *naddededges)
const char * SYMsymmetryGetDesc(void)
SCIP_Bool isEdgeGroupable(SYM_GRAPH *graph, int edgeidx, SCIP_Bool groupbycons)
const char * SYMsymmetryGetAddDesc(void)
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
#define SCIPdebugMsg
Definition: scip_message.h:78
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition: scip_mem.h:110
#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
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)
int * edgefirst
int * edgesecond
methods for handling symmetries
methods for dealing with symmetry detection graphs
@ SCIP_OKAY
Definition: type_retcode.h:42
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63
enum SYM_Symtype SYM_SYMTYPE
Definition: type_symmetry.h:64
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