Scippy

SCIP

Solving Constraint Integer Programs

build_dejavu_graph.cpp
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file build_dejavu_graph.cpp
26 * @brief methods to build dejavu graph for symmetry detection
27 * @author Christopher Hojny
28 * @author Marc Pfetsch
29 */
30
31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
32
33#include <string> /* for dejvu */
34#include "build_dejavu_graph.h"
35#include "scip/expr_var.h"
36#include "scip/expr_sum.h"
37#include "scip/expr_pow.h"
38#include "scip/expr.h"
39#include "scip/cons_nonlinear.h"
40#include "scip/cons_linear.h"
41#include "scip/scip_mem.h"
42#include "scip/symmetry_graph.h"
43
44
45/* ------------------- auxiliary functions ------------------- */
46
47/** returns whether an edge is considered in grouping process */
48static
50 SYM_GRAPH* graph, /**< symmetry detection graph */
51 int edgeidx, /**< index of edge to be checked */
52 SCIP_Bool groupbycons /**< whether edges are grouped by constraints */
53 )
54{
55 int first;
56 int second;
57
58 assert(graph != NULL);
59
60 first = SCIPgetSymgraphEdgeFirst(graph, edgeidx);
61 second = SCIPgetSymgraphEdgeSecond(graph, edgeidx);
62
63 /* uncolored edges are not grouped */
64 if ( ! SCIPisSymgraphEdgeColored(graph, edgeidx) )
65 return FALSE;
66
67 /* two variable nodes are connected */
68 if ( first < 0 && second < 0 )
69 return FALSE;
70
71 if ( ! groupbycons )
72 {
73 /* grouping by variables requires one variable node */
74 if ( first < 0 || second < 0 )
75 return TRUE;
76 }
77 else
78 {
79 /* check whether there is exactly one constraint node in edge */
80 if ( first >= 0 && second >= 0 )
81 {
82 if ( (SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS
83 && SCIPgetSymgraphNodeType(graph, second) != SYM_NODETYPE_CONS)
85 && SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS) )
86 return TRUE;
87 }
88 else if ( first >= 0 )
89 {
90 if ( SCIPgetSymgraphNodeType(graph, first) == SYM_NODETYPE_CONS )
91 return TRUE;
92 }
93 else
94 {
95 if ( SCIPgetSymgraphNodeType(graph, second) == SYM_NODETYPE_CONS )
96 return TRUE;
97 }
98 }
99
100 return FALSE;
101}
102
103/** adds grouped edges all of which have one common endpoint to a graph
104 *
105 * The grouping mechanism works by sorting the edges according to their color. If two
106 * edges have the same color, they share the same intermediate node, which is connected
107 * to the common node and the other endpoints of equivalent edges.
108 */
109static
111 SCIP* scip, /**< SCIP pointer */
112 dejavu::static_graph* G, /**< graph which gets extended */
113 SCIP_Bool determinesize, /**< whether only the effect of grouping on the graph shall be checked */
114 int* internodeid, /**< (initialized) pointer to store the ID of the next intermediate node */
115 int** degrees, /**< pointer to array of degrees for nodes in G */
116 int* maxdegrees, /**< (initialized) pointer to maximum number of entries degrees can hold */
117 int* nnodes, /**< (initialized) pointer to store the number of */
118 int* nedges, /**< (initialized) pointer to store the number of */
119 int commonnodeidx, /**< index of common node in G */
120 int* neighbors, /**< neighbors of common node */
121 int* colors, /**< colors of edges to neighbors */
122 int nneighbors, /**< number of neighbors */
123 int* naddednodes, /**< pointer to store number of nodes added to G */
124 int* naddededges /**< pointer to store number of edges added to G */
125 )
126{
127 int curcolor;
128 int curstart;
129 int e;
130 int f;
131
132 assert( G != NULL || determinesize );
133 assert( internodeid != NULL );
134 assert( *internodeid >= 0 );
135 assert( degrees != NULL );
136 assert( maxdegrees != NULL );
137 assert( *maxdegrees > 0 );
138 assert( nnodes != NULL );
139 assert( nedges != NULL );
140 assert( neighbors != NULL );
141 assert( colors != NULL );
142 assert( naddednodes != NULL );
143 assert( naddededges != NULL );
144 assert( commonnodeidx <= *internodeid );
145
146 *naddednodes = 0;
147 *naddededges = 0;
148
149 /* sort edges according to color */
150 SCIPsortIntInt(colors, neighbors, nneighbors);
151
152 /* iterate over groups of identical edges and group them, ignoring the last group */
153 curcolor = colors[0];
154 curstart = 0;
155 for (e = 1; e < nneighbors; ++e)
156 {
157 /* if a new group started, add edges for previous group */
158 if ( colors[e] != curcolor )
159 {
160 if ( determinesize )
161 {
162 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
163 (*degrees)[*internodeid] = 1;
164 ++(*degrees)[commonnodeidx];
165
166 for (f = curstart; f < e; ++f)
167 {
168 assert( neighbors[f] <= *internodeid );
169 ++(*degrees)[*internodeid];
170 ++(*degrees)[neighbors[f]];
171 }
172 }
173 else
174 {
175 (void) G->add_vertex(curcolor, (*degrees)[*internodeid]);
176 G->add_edge((unsigned) commonnodeidx, (unsigned) *internodeid);
177
178 for (f = curstart; f < e; ++f)
179 (*G).add_edge((unsigned) neighbors[f], (unsigned) *internodeid);
180 }
181 *naddednodes += 1;
182 *naddededges += e - curstart + 1;
183 ++(*internodeid);
184
185 curcolor = colors[e];
186 curstart = e;
187 }
188 }
189
190 /* add edges of last group */
191 if ( determinesize )
192 {
193 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *internodeid + 1) );
194
195 (*degrees)[*internodeid] = 1;
196 ++(*degrees)[commonnodeidx];
197
198 for (f = curstart; f < nneighbors; ++f)
199 {
200 assert( neighbors[f] <= *internodeid );
201 ++(*degrees)[*internodeid];
202 ++(*degrees)[neighbors[f]];
203 }
204 }
205 else
206 {
207 (void) G->add_vertex(curcolor, (*degrees)[*internodeid]);
208 G->add_edge((unsigned) commonnodeidx, (unsigned) *internodeid);
209
210 for (f = curstart; f < nneighbors; ++f)
211 G->add_edge((unsigned) neighbors[f], (unsigned) *internodeid);
212 }
213 *naddednodes += 1;
214 *naddededges += nneighbors - curstart + 1;
215 ++(*internodeid);
216
217 return SCIP_OKAY;
218}
219
220/** either creates a graph or determines its size */
221static
223 SCIP* scip, /**< SCIP instance */
224 SYM_GRAPH* graph, /**< symmetry detection graph */
225 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
226 dejavu::static_graph* G, /**< graph to be constructed */
227 int* nnodes, /**< pointer to store the total number of nodes in graph */
228 int* nedges, /**< pointer to store the total number of edges in graph */
229 int** degrees, /**< pointer to store the degrees of the nodes */
230 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
231 SCIP_Bool* success /**< pointer to store whether the construction was successful */
232 )
233{
234 SYM_SYMTYPE symtype;
235 SYM_NODETYPE comparetype;
236 SCIP_Bool groupByConstraints;
237 int* groupfirsts = NULL;
238 int* groupseconds = NULL;
239 int* groupcolors = NULL;
240 int ngroupedges = 0;
241 int nvarnodestoadd;
242 int internodeid;
243 int nsymvars;
244 int nsymedges;
245 int first;
246 int second;
247 int color;
248 int e;
249 int j;
250
251 assert( scip != NULL );
252 assert( graph != NULL );
253 assert( G != NULL || determinesize );
254 assert( nnodes != NULL );
255 assert( nedges != NULL );
256 assert( degrees != NULL );
257 assert( maxdegrees != NULL );
258 assert( success != NULL );
259
260 *success = TRUE;
261
262 /* collect basic information from symmetry detection graph */
263 nsymvars = SCIPgetSymgraphNVars(graph);
264 symtype = SCIPgetSymgraphSymtype(graph);
265 switch ( symtype )
266 {
267 case SYM_SYMTYPE_PERM:
268 nvarnodestoadd = nsymvars;
269 break;
270 default:
271 assert( symtype == SYM_SYMTYPE_SIGNPERM );
272 nvarnodestoadd = 2 * nsymvars;
273 }
274
275 /* possibly find number of nodes in dejavu graph */
276 if ( determinesize )
277 {
278 /* initialize counters */
279 *nnodes = SCIPgetSymgraphNNodes(graph) + nvarnodestoadd;
280 *nedges = 0;
281
282 /* possibly allocate memory for degrees (will grow dynamically) */
283 *degrees = NULL;
284 *maxdegrees = 0;
285 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 100) );
286 for (j = 0; j < *nnodes; ++j)
287 (*degrees)[j] = 0;
288 }
289 else
290 {
291 assert( G != NULL );
292
293 /* add nodes for variables */
294 for (j = 0; j < nvarnodestoadd; ++j)
295 (void) G->add_vertex(SCIPgetSymgraphVarnodeColor(graph, j), (*degrees)[j]);
296
297 /* add nodes for remaining nodes of graph */
298 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
299 (void) G->add_vertex(SCIPgetSymgraphNodeColor(graph, j), (*degrees)[nvarnodestoadd + j]);
300 }
301
302 /* determine grouping depending on the number of rhs vs. variables */
304 groupByConstraints = TRUE;
305 else
306 groupByConstraints = FALSE;
307
308 /* allocate arrays to collect edges to be grouped */
309 nsymedges = SCIPgetSymgraphNEdges(graph);
310 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
311 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
312 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
313
314 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
315 internodeid = SCIPgetSymgraphNNodes(graph) + nvarnodestoadd;
316 for (e = 0; e < SCIPgetSymgraphNEdges(graph); ++e)
317 {
318 first = SCIPgetSymgraphEdgeFirst(graph, e);
319 second = SCIPgetSymgraphEdgeSecond(graph, e);
320
321 /* get the first and second node in edge (corrected by variable shift) */
322 if ( first < 0 )
323 first = -first - 1;
324 else
325 first += nvarnodestoadd;
326 if ( second < 0 )
327 second = -second - 1;
328 else
329 second += nvarnodestoadd;
330 assert(first >= 0);
331 assert(second >= 0);
332
333 /* check whether edge is used for grouping */
334 if ( ! SCIPhasGraphUniqueEdgetype(graph) && isEdgeGroupable(graph, e, groupByConstraints) )
335 {
336 /* store edge, first becomes the cons or var node */
337 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
338
339 if ( SCIPgetSymgraphNodeType(graph, SCIPgetSymgraphEdgeFirst(graph, e)) == comparetype )
340 {
341 groupfirsts[ngroupedges] = first;
342 groupseconds[ngroupedges] = second;
343 }
344 else
345 {
346 groupfirsts[ngroupedges] = second;
347 groupseconds[ngroupedges] = first;
348 }
349 groupcolors[ngroupedges++] = SCIPgetSymgraphEdgeColor(graph, e);
350 }
351 else
352 {
353 /* immediately add edge or increase degrees */
354 assert(0 <= first && first < *nnodes);
355 assert(0 <= second && second < *nnodes);
356
357 /* possibly split edge if it is colored */
358 if ( ! SCIPhasGraphUniqueEdgetype(graph) && SCIPisSymgraphEdgeColored(graph, e) )
359 {
360 if ( determinesize )
361 {
362 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, internodeid + 1) );
363
364 ++(*degrees)[first];
365 ++(*degrees)[second];
366 (*degrees)[internodeid] = 2;
367 ++(*nnodes);
368 *nedges += 2;
369 ++internodeid;
370 }
371 else
372 {
373 assert( internodeid < *nnodes );
374 assert( G != NULL );
375
376 color = SCIPgetSymgraphEdgeColor(graph, e);
377 (void) G->add_vertex(color, (*degrees)[internodeid]);
378
379 G->add_edge((unsigned) first, (unsigned) internodeid);
380 G->add_edge((unsigned) second, (unsigned) internodeid++);
381 }
382 }
383 else
384 {
385 if ( determinesize )
386 {
387 ++(*degrees)[first];
388 ++(*degrees)[second];
389 ++(*nedges);
390 }
391 else
392 {
393 assert( G != NULL );
394 if ( first < second )
395 G->add_edge((unsigned) first, (unsigned) second);
396 else
397 G->add_edge((unsigned) second, (unsigned) first);
398 }
399 }
400 }
401 }
402
403 /* possibly add groupable edges */
404 if ( ngroupedges > 0 )
405 {
406 int firstidx = 0;
407 int firstnodeidx;
408 int naddednodes;
409 int naddededges;
410
411 /* sort edges according to their first nodes */
412 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
413 firstnodeidx = groupfirsts[0];
414
415 for (j = 1; j < ngroupedges; ++j)
416 {
417 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
418 if ( groupfirsts[j] != firstnodeidx )
419 {
420 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
421 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], j - firstidx,
422 &naddednodes, &naddededges) );
423
424 firstidx = j;
425 firstnodeidx = groupfirsts[j];
426
427 if ( determinesize )
428 {
429 *nnodes += naddednodes;
430 *nedges += naddededges;
431 }
432 }
433 }
434
435 /* process the last group */
436 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
437 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], ngroupedges - firstidx,
438 &naddednodes, &naddededges) );
439
440 if ( determinesize )
441 {
442 *nnodes += naddednodes;
443 *nedges += naddededges;
444 }
445 }
446
447 SCIPfreeBufferArray(scip, &groupcolors);
448 SCIPfreeBufferArray(scip, &groupseconds);
449 SCIPfreeBufferArray(scip, &groupfirsts);
450
451 /* for signed permutation, also add edges connecting a variable and its negation */
452 switch ( SCIPgetSymgraphSymtype(graph) )
453 {
455 if ( determinesize )
456 {
457 for (j = 0; j < nvarnodestoadd; ++j)
458 ++(*degrees)[j];
459 *nedges += nsymvars;
460 }
461 else
462 {
463 assert( G != NULL );
464 for (j = 0; j < nsymvars; ++j)
465 G->add_edge((unsigned) j, (unsigned) (j + nsymvars));
466 }
467 break;
468 default:
469 assert( SCIPgetSymgraphSymtype(graph) == SYM_SYMTYPE_PERM );
470 }
471
472 if ( determinesize )
473 {
474 SCIPdebugMsg(scip, "#nodes: %d\n", *nnodes);
475 SCIPdebugMsg(scip, "#nodes for variables: %d\n", nvarnodestoadd);
476 SCIPdebugMsg(scip, "#nodes for rhs: %d\n", SCIPgetSymgraphNConsnodes(graph));
477 SCIPdebugMsg(scip, "#edges: %d\n", *nedges);
478 }
479
480 return SCIP_OKAY;
481}
482
483/** either creates a graph for checking symmetries or determines its size
484 *
485 * The input are two graphs and the graph to be constructed consists of copies
486 * of the two input graphs, in which non-variable nodes are colored according
487 * to the colors used in symmetry detection. Each variable gets a unique color.
488 */
489static
491 SCIP* scip, /**< SCIP instance */
492 SYM_GRAPH* graph1, /**< first symmetry detection graph */
493 SYM_GRAPH* graph2, /**< second symmetry detection graph */
494 SCIP_Bool determinesize, /**< whether only the size of the graph shall be determined */
495 dejavu::static_graph* G, /**< graph to be constructed */
496 int* nnodes, /**< pointer to store the total number of nodes in graph */
497 int* nedges, /**< pointer to store the total number of edges in graph */
498 int** degrees, /**< pointer to store the degrees of the nodes */
499 int* maxdegrees, /**< pointer to store the maximal size of the degree array */
500 int* nnodesfromG1, /**< pointer to store number of nodes in dejavu graph arising from G1 (or NULL) */
501 SCIP_Bool* success /**< pointer to store whether the graph could be built */
502 )
503{
504 SYM_SYMTYPE symtype;
505 SYM_NODETYPE comparetype;
506 SCIP_Bool groupByConstraints;
507 SYM_GRAPH* graph;
508 int* nvarused1 = NULL;
509 int* nvarused2 = NULL;
510 int* varlabel = NULL;
511 int* groupfirsts = NULL;
512 int* groupseconds = NULL;
513 int* groupcolors = NULL;
514 int ngroupedges = 0;
515 int nusedvars = 0;
516 int nodeshift;
517 int curnnodes;
518 int nvarnodestoadd;
519 int internodeid;
520 int nsymvars;
521 int nsymedges;
522 int first;
523 int second;
524 int color;
525 int e;
526 int i;
527 int j;
528
529 assert( scip != NULL );
530 assert( graph1 != NULL );
531 assert( graph2 != NULL );
532 assert( G != NULL || determinesize );
533 assert( nnodes != NULL );
534 assert( nedges != NULL );
535 assert( degrees != NULL );
536 assert( maxdegrees != NULL );
537 assert( success != NULL );
538
539 *success = FALSE;
540
541 /* graphs cannot be symmetric */
542 if ( SCIPgetSymgraphNEdges(graph1) != SCIPgetSymgraphNEdges(graph2)
543 || SCIPgetSymgraphNVars(graph1) != SCIPgetSymgraphNVars(graph2) )
544 return SCIP_OKAY;
545
546 /* collect basic information from symmetry detection graph */
547 nsymvars = SCIPgetSymgraphNVars(graph1);
548 nsymedges = SCIPgetSymgraphNEdges(graph1);
549 symtype = SCIPgetSymgraphSymtype(graph1);
550 switch ( symtype )
551 {
552 case SYM_SYMTYPE_PERM:
553 nvarnodestoadd = nsymvars;
554 break;
555 default:
556 assert( symtype == SYM_SYMTYPE_SIGNPERM );
557 nvarnodestoadd = 2 * nsymvars;
558 }
559
560 /* find the variables that are contained in an edge */
561 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused1, nvarnodestoadd) );
562 SCIP_CALL( SCIPallocClearBufferArray(scip, &nvarused2, nvarnodestoadd) );
563 SCIP_CALL( SCIPallocBufferArray(scip, &varlabel, nvarnodestoadd) );
564
565 for (e = 0; e < nsymedges; ++e)
566 {
567 first = SCIPgetSymgraphEdgeFirst(graph1, e);
568 second = SCIPgetSymgraphEdgeSecond(graph1, e);
569 if ( first < 0 )
570 nvarused1[-first - 1] += 1;
571 if ( second < 0 )
572 nvarused1[-second - 1] += 1;
573
574 first = SCIPgetSymgraphEdgeFirst(graph2, e);
575 second = SCIPgetSymgraphEdgeSecond(graph2, e);
576 if ( first < 0 )
577 nvarused2[-first - 1] += 1;
578 if ( second < 0 )
579 nvarused2[-second - 1] += 1;
580 }
581
582 for (j = 0; j < nvarnodestoadd; ++j)
583 {
584 /* graphs cannot be identical */
585 if ( nvarused1[j] != nvarused2[j] )
586 {
587 SCIPfreeBufferArray(scip, &varlabel);
588 SCIPfreeBufferArray(scip, &nvarused2);
589 SCIPfreeBufferArray(scip, &nvarused1);
590
591 return SCIP_OKAY;
592 }
593
594 /* relabel variables by restricting to variables used in constraint (or their negation) */
595 if ( nvarused1[j] > 0 || nvarused1[j % SCIPgetSymgraphNVars(graph1)] > 0 )
596 varlabel[j] = nusedvars++;
597 else
598 varlabel[j] = -1;
599 }
600
601 /* possibly find number of nodes in dejavu graph and allocate memory for dynamic array */
602 if ( determinesize )
603 {
604 *degrees = NULL;
605 *maxdegrees = 0;
606 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees,
607 SCIPgetSymgraphNNodes(graph1) + SCIPgetSymgraphNNodes(graph2) + 2 * nusedvars + 100) );
608
609 *nnodes = 0;
610 *nedges = 0;
611 }
612
613 /* determine grouping depending on the number of rhs vs. variables */
614 if ( SCIPgetSymgraphNConsnodes(graph1) < SCIPgetSymgraphNVars(graph1) )
615 groupByConstraints = TRUE;
616 else
617 groupByConstraints = FALSE;
618
619 /* allocate arrays to collect edges to be grouped */
620 SCIP_CALL( SCIPallocBufferArray(scip, &groupfirsts, nsymedges) );
621 SCIP_CALL( SCIPallocBufferArray(scip, &groupseconds, nsymedges) );
622 SCIP_CALL( SCIPallocBufferArray(scip, &groupcolors, nsymedges) );
623
624 /* collect information or generate graphs, we shift the node indices of the second graph when adding them to G */
625 nodeshift = 0;
626 for (i = 0; i < 2; ++i)
627 {
628 curnnodes = 0;
629 graph = i == 0 ? graph1 : graph2;
630 ngroupedges = 0;
631
632 /* possibly add nodes for variables and remaining nodes, each variable gets a unique color */
633 if ( determinesize )
634 {
635 /* add nodes for variables */
636 for (j = 0; j < nvarnodestoadd; ++j)
637 {
638 if ( varlabel[j] >= 0 )
639 {
640 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
641 (*degrees)[nodeshift + varlabel[j]] = 0;
642 ++(*nnodes);
643 ++curnnodes;
644 }
645 }
646
647 /* add nodes for remaining nodes of graph */
648 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
649 {
650 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, *nnodes + 1) );
651 (*degrees)[nodeshift + nusedvars + j] = 0;
652 ++(*nnodes);
653 ++curnnodes;
654 }
655 }
656 else
657 {
658 assert( G != NULL );
659
660 /* add nodes for variables, each variable gets a unique color */
661 for (j = 0; j < nvarnodestoadd; ++j)
662 {
663 if ( varlabel[j] >= 0 )
664 {
665 (void) G->add_vertex(j, (*degrees)[nodeshift + varlabel[j]]);
666 ++curnnodes;
667 }
668 }
669
670 /* add nodes for remaining nodes of graph, ensure that colors do not conflict with variable colors */
671 for (j = 0; j < SCIPgetSymgraphNNodes(graph); ++j)
672 {
673 (void) G->add_vertex(nusedvars + SCIPgetSymgraphNodeColor(graph, j),
674 (*degrees)[nodeshift + nusedvars + j]);
675 ++curnnodes;
676 }
677 }
678
679 /* loop through all edges of the symmetry detection graph and either get degrees of nodes or add edges */
680 internodeid = nodeshift + curnnodes;
681 for (e = 0; e < nsymedges; ++e)
682 {
683 first = SCIPgetSymgraphEdgeFirst(graph, e);
684 second = SCIPgetSymgraphEdgeSecond(graph, e);
685
686 /* get the first and second node in edge (corrected by variable shift) */
687 if ( first < 0 )
688 first = varlabel[-first - 1];
689 else
690 first = nusedvars + first;
691 if ( second < 0 )
692 second = varlabel[-second - 1];
693 else
694 second = nusedvars + second;
695
696 /* check whether edge is used for grouping */
697 if ( ! SCIPhasGraphUniqueEdgetype(graph) && isEdgeGroupable(graph, e, groupByConstraints) )
698 {
699 /* store edge, first becomes the cons or var node */
700 comparetype = groupByConstraints ? SYM_NODETYPE_CONS : SYM_NODETYPE_VAR;
701
702 if ( SCIPgetSymgraphNodeType(graph, SCIPgetSymgraphEdgeFirst(graph, e)) == comparetype )
703 {
704 groupfirsts[ngroupedges] = nodeshift + first;
705 groupseconds[ngroupedges] = nodeshift + second;
706 }
707 else
708 {
709 groupfirsts[ngroupedges] = nodeshift + second;
710 groupseconds[ngroupedges] = nodeshift + first;
711 }
712 groupcolors[ngroupedges++] = nusedvars + SCIPgetSymgraphEdgeColor(graph, e);
713 }
714 else
715 {
716 /* immediately add edge or increase degrees */
717 assert(0 <= first && first < *nnodes);
718 assert(0 <= second && second < *nnodes);
719
720 /* possibly split edge if it is colored */
721 if ( ! SCIPhasGraphUniqueEdgetype(graph) && SCIPisSymgraphEdgeColored(graph, e) )
722 {
723 if ( determinesize )
724 {
725 SCIP_CALL( SCIPensureBlockMemoryArray(scip, degrees, maxdegrees, internodeid + 1) );
726
727 ++(*degrees)[nodeshift + first];
728 ++(*degrees)[nodeshift + second];
729 (*degrees)[internodeid] = 2;
730 ++(*nnodes);
731 *nedges += 2;
732 }
733 else
734 {
735 assert( internodeid < *nnodes );
736 assert( G != NULL );
737
738 color = SCIPgetSymgraphEdgeColor(graph, e);
739 (void) G->add_vertex(nusedvars + color, (*degrees)[internodeid]);
740 G->add_edge((unsigned) (nodeshift + first), (unsigned) internodeid);
741 G->add_edge((unsigned) (nodeshift + second), (unsigned) internodeid);
742 }
743 ++internodeid;
744 ++curnnodes;
745 }
746 else
747 {
748 if ( determinesize )
749 {
750 ++(*degrees)[nodeshift + first];
751 ++(*degrees)[nodeshift + second];
752 ++(*nedges);
753 }
754 else
755 {
756 assert( G != NULL );
757
758 if ( first < second )
759 G->add_edge((unsigned) (nodeshift + first), (unsigned) (nodeshift + second));
760 else
761 G->add_edge((unsigned) (nodeshift + second), (unsigned) (nodeshift + first));
762 }
763 }
764 }
765 }
766
767 /* possibly add groupable edges */
768 if ( ngroupedges > 0 )
769 {
770 int firstidx = 0;
771 int firstnodeidx;
772 int naddednodes;
773 int naddededges;
774
775 /* sort edges according to their first nodes */
776 SCIPsortIntIntInt(groupfirsts, groupseconds, groupcolors, ngroupedges);
777 firstnodeidx = groupfirsts[0];
778
779 for (j = 1; j < ngroupedges; ++j)
780 {
781 /* if a new first node has been found, group the edges of the previous first node; ignoring the last group */
782 if ( groupfirsts[j] != firstnodeidx )
783 {
784 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
785 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], j - firstidx,
786 &naddednodes, &naddededges) );
787
788 firstidx = j;
789 firstnodeidx = groupfirsts[j];
790
791 if ( determinesize )
792 {
793 *nnodes += naddednodes;
794 *nedges += naddededges;
795 }
796 curnnodes += naddednodes;
797 }
798 }
799
800 /* process the last group */
801 SCIP_CALL( addOrDetermineEffectOfGroupedEdges(scip, G, determinesize, &internodeid, degrees, maxdegrees,
802 nnodes, nedges, firstnodeidx, &groupseconds[firstidx], &groupcolors[firstidx], ngroupedges - firstidx,
803 &naddednodes, &naddededges) );
804
805 if ( determinesize )
806 {
807 *nnodes += naddednodes;
808 *nedges += naddededges;
809 }
810 curnnodes += naddednodes;
811 }
812
813 /* for signed permutation, also add edges connecting a variable and its negation */
815 {
816 if ( determinesize )
817 {
818 for (j = 0; j < nusedvars; ++j)
819 ++(*degrees)[nodeshift + j];
820 (*nedges) += nusedvars / 2;
821 }
822 else
823 {
824 assert( G != NULL );
825
826 for (j = 0; j < nusedvars/2; ++j)
827 G->add_edge((unsigned) (nodeshift + j), (unsigned) (nodeshift + j + nusedvars / 2));
828 }
829 }
830 nodeshift = curnnodes;
831
832 if ( i == 0 && nnodesfromG1 != NULL )
833 *nnodesfromG1 = curnnodes;
834 }
835
836 SCIPfreeBufferArray(scip, &groupcolors);
837 SCIPfreeBufferArray(scip, &groupseconds);
838 SCIPfreeBufferArray(scip, &groupfirsts);
839
840 SCIPfreeBufferArray(scip, &varlabel);
841 SCIPfreeBufferArray(scip, &nvarused2);
842 SCIPfreeBufferArray(scip, &nvarused1);
843
844 *success = TRUE;
845
846 return SCIP_OKAY;
847}
848
849
850/** compute generators of symmetry group */
852 SCIP* scip, /**< SCIP pointer */
853 dejavu::static_graph* dejavugraph, /**< pointer to hold dejavu graph being created */
854 SYM_GRAPH* graph, /**< symmetry detection graph */
855 SCIP_Bool* success /**< pointer to store whether dejavugraph could be built */
856 )
857{
858 int* degrees;
859 int maxdegrees;
860 int nnodes;
861 int nedges;
862
863 assert( scip != NULL );
864 assert( dejavugraph != NULL );
865 assert( graph != NULL );
866
867 *success = FALSE;
868
869 /* determine number of nodes and edges */
870 SCIP_CALL( createOrDetermineSizeGraph(scip, graph, TRUE, NULL, &nnodes, &nedges, &degrees, &maxdegrees, success) );
871
872 if ( ! *success )
873 {
875 "Stopped symmetry computation: Symmetry graph would become too large.\n");
876 return SCIP_OKAY;
877 }
878
879 /* init graph */
880 (*dejavugraph).initialize_graph(nnodes, nedges);
881
882 /* add the nodes for linear and nonlinear constraints to the graph */
883 SCIP_CALL( createOrDetermineSizeGraph(scip, graph, FALSE, dejavugraph,
884 &nnodes, &nedges, &degrees, &maxdegrees, success) );
885
886 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
887
888 SCIPdebugMsg(scip, "Symmetry detection graph has %d nodes.\n", nnodes);
889
890 return SCIP_OKAY;
891}
892
893/** returns whether two given graphs are identical */
895 SCIP* scip, /**< SCIP pointer */
896 dejavu::static_graph* dejavugraph, /**< pointer to hold dejavu graph being created */
897 SYM_GRAPH* G1, /**< first graph */
898 SYM_GRAPH* G2, /**< second graph */
899 int* nnodes, /**< pointer to store number of nodes in dejavu graph */
900 int* nnodesfromG1, /**< pointer to store number of nodes in dejavu graph arising from G1 */
901 SCIP_Bool* success /**< pointer to store whether dejavugraph could be built */
902 )
903{
904 int* degrees = NULL;
905 int maxdegrees = 0;
906 int nedges;
907
908 assert( scip != NULL );
909 assert( dejavugraph != NULL );
910 assert( G1 != NULL );
911 assert( G2 != NULL );
912 assert( nnodes != NULL );
913 assert( nnodesfromG1 != NULL );
914 assert( success != NULL );
915
916 *success = FALSE;
917 *nnodes = 0;
918 *nnodesfromG1 = 0;
919
920 /* some simple checks */
921 if ( G1->nnodes != G2->nnodes || G1->nopnodes != G2->nopnodes || G1->nvalnodes != G2->nvalnodes
922 || G1->nconsnodes != G2->nconsnodes || G1->nedges != G2->nedges )
923 return SCIP_OKAY;
924
925 /* determine number of nodes and edges */
927 nnodes, &nedges, &degrees, &maxdegrees, nnodesfromG1, success) );
928
929 if ( ! *success )
930 {
931 assert( degrees == NULL );
932 assert( maxdegrees == 0 );
933 return SCIP_OKAY;
934 }
935 if ( *nnodes % 2 != 0 )
936 {
937 assert( degrees != NULL );
938 assert( maxdegrees > 0 );
939
940 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
941 return SCIP_OKAY;
942 }
943
944 /* init graph */
945 (*dejavugraph).initialize_graph(*nnodes, nedges);
946
947 /* add the nodes for linear and nonlinear constraints to the graph */
949 nnodes, &nedges, &degrees, &maxdegrees, NULL, success) );
950 assert( *success );
951
952 SCIPfreeBlockMemoryArray(scip, &degrees, maxdegrees);
953
954 return SCIP_OKAY;
955}
SCIP_RETCODE SYMbuildDejavuGraphCheck(SCIP *scip, dejavu::static_graph *dejavugraph, SYM_GRAPH *G1, SYM_GRAPH *G2, int *nnodes, int *nnodesfromG1, SCIP_Bool *success)
static SCIP_RETCODE addOrDetermineEffectOfGroupedEdges(SCIP *scip, dejavu::static_graph *G, SCIP_Bool determinesize, int *internodeid, int **degrees, int *maxdegrees, int *nnodes, int *nedges, int commonnodeidx, int *neighbors, int *colors, int nneighbors, int *naddednodes, int *naddededges)
static SCIP_Bool isEdgeGroupable(SYM_GRAPH *graph, int edgeidx, SCIP_Bool groupbycons)
static SCIP_RETCODE createOrDetermineSizeGraphCheck(SCIP *scip, SYM_GRAPH *graph1, SYM_GRAPH *graph2, SCIP_Bool determinesize, dejavu::static_graph *G, int *nnodes, int *nedges, int **degrees, int *maxdegrees, int *nnodesfromG1, SCIP_Bool *success)
static SCIP_RETCODE createOrDetermineSizeGraph(SCIP *scip, SYM_GRAPH *graph, SCIP_Bool determinesize, dejavu::static_graph *G, int *nnodes, int *nedges, int **degrees, int *maxdegrees, SCIP_Bool *success)
SCIP_RETCODE SYMbuildDejavuGraph(SCIP *scip, dejavu::static_graph *dejavugraph, SYM_GRAPH *graph, SCIP_Bool *success)
methods to build dejavu graph for symmetry detection
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 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 SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:225
#define SCIPdebugMsg
Definition: scip_message.h:78
#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
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:136
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
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