SCIP Doxygen Documentation
Loading...
Searching...
No Matches
sepa_subtour.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-2026 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/**@file sepa_subtour.c
25 * @brief If there exists a transition forward along the cycle, then the state that the transition originates from can
26 * be reached only after another ncluster - 1 transitions. Therefore cycles with a number of transitions smaller than
27 * that can be separated.
28 * @author Leon Eifler
29 */
30
31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
32
33#include "sepa_subtour.h"
34
35#include "probdata_cyc.h"
36#include "scip/cons_linear.h"
37#include "scip/pub_misc.h"
38
39#define SEPA_NAME "subtour"
40#define SEPA_DESC "separator that elininates subtours of length smaller than |NCluster|"
41#define SEPA_PRIORITY 1000
42#define SEPA_FREQ 5
43#define SEPA_MAXBOUNDDIST 0.0
44#define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
45#define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
46#define MAXCUTS 2000
47#define MAXROUNDS 15
48
49#ifdef SCIP_DEBUG
50/** Print a cycle to the command line. For debugging purposes */
51static
52void printCycle(
53 SCIP* scip, /**< SCIP data structure */
54 int* cycle, /**< The cycle to be printed */
55 int cyclelength, /**< The length of the cycle */
56 int nstates /**< The number of states */
57 )
58{
59 int i;
60
61 SCIPinfoMessage(scip, NULL, "cycle_l%d_c: %d", cyclelength, cycle[0]);
62 for( i = 0; i < cyclelength; ++i )
63 {
64 SCIPinfoMessage(scip, NULL, " -> %d", cycle[i+1]);
65 }
67}
68#endif
69
70/** get distance of longest path between two states with exactly n arcs from the matrix */
71static
73 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrices of all paths with 1,...,|Clutster| arcs */
74 int n, /**< length */
75 int state1, /**< starting state */
76 int state2 /**< end state */
77 )
78{
79 assert(adjacencymatrix[n] != NULL);
80 assert(adjacencymatrix[n][state1] != NULL);
81
82 return adjacencymatrix[n][state1][state2];
83}
84
85/** After finding a violation, construct and add all violated subtour cuts to scip */
86static
88 SCIP* scip, /**< SCIP data structure. */
89 SCIP_SEPA* sepa, /**< the subtour separator */
90 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrices of all paths with 1,...,|Clutster| arcs */
91 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
92 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
93 int cyclelength, /**< the length of the subtours to add */
94 SCIP_RESULT* result, /**< pointer to store the result of separation */
95 int* ncuts /**< pointer to store number of cuts */
96 )
97{
98 SCIP_VAR**** edgevars;
99 char cutname[SCIP_MAXSTRLEN];
100 SCIP_ROW* cut;
101 int** subtours;
102 int* insubtour;
103 int* successors;
104 int nsuccessors;
105 int nstates;
106 int currentnode;
107 int successor;
108 int intermediate;
109 int anchor;
110 int ncontractions;
111 int liftabley;
112 int liftablez;
113 int greater;
114 int smaller;
115 int c;
116 int k;
117 int l;
118 SCIP_Bool isduplicate;
119
120 edgevars = SCIPcycGetEdgevars(scip);
121 nstates = SCIPdigraphGetNNodes(adjacencygraph);
122
123 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &insubtour, nstates) );
124 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &subtours, nstates) );
125
126 for( k = 0; k < nstates; ++k )
127 {
128 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &subtours[k], cyclelength + 1) ); /*lint !e866, !e776*/
129 insubtour[k] = -1;
130 }
131
132 /* for each state, check if a subtour inequality is violated */
133 for( anchor = 0; anchor < nstates; ++anchor )
134 {
135 /* while reconstructing the subtour, count the number of contractions */
136 ncontractions = 0;
137
138 /* a cycle inequality is violated if the following is true */
139 if( SCIPisGT(scip, getDist(adjacencymatrix, cyclelength - 1, anchor, anchor), cyclelength - 1.0) )
140 {
141 subtours[anchor][0] = anchor;
142 if( insubtour[anchor] == -1 )
143 insubtour[anchor] = anchor;
144
145 /* traverse the cycle */
146 for( k = 0; k < cyclelength -1; ++k )
147 {
148 currentnode = subtours[anchor][k];
149
150 assert(0 <= currentnode && currentnode < nstates);
151
152 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
153 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
154
155 /* find the next state along the subtour */
156 for( l = 0; l < nsuccessors; l++ )
157 {
158 successor = successors[l];
159
160 assert(0 <= successor && successor < nstates);
161
162 /* check if this successor of the current node is the one in the cycle. If so add it. */
163 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
164 + getDist(adjacencymatrix, cyclelength - (k + 2), successor, anchor),
165 getDist(adjacencymatrix, cyclelength - (k + 1), currentnode, anchor)) )
166 {
167 subtours[anchor][k + 1] = successor;
168 insubtour[successor] = anchor;
169
170 if( iscontracted[currentnode][successor] != -1 )
171 ncontractions++;
172
173 break;
174 }
175 }
176 }
177
178 /* start and endnode are always the same in a cycle */
179 subtours[anchor][cyclelength] = anchor;
180
181 /* check last arc for a contraction */
182 if( iscontracted[subtours[anchor][cyclelength - 1]][anchor] != -1 )
183 ncontractions++;
184
185 isduplicate = FALSE;
186
187 /* if this anchor is already in another subtour, we check if the subtour is the same, since we don't want to
188 * add duplicates
189 */
190 if( insubtour[anchor] != anchor )
191 {
192 c = 0;
193 isduplicate = TRUE;
194
195 while( subtours[insubtour[anchor]][c] != anchor )
196 c++;
197
198 for( k = 0; k < cyclelength && isduplicate; ++k )
199 {
200 if( subtours[insubtour[anchor]][(k + c) % cyclelength] != subtours[anchor][k] )
201 isduplicate = FALSE;
202 }
203 }
204
205 if( isduplicate )
206 continue;
207
208 /* set the amount of y and z variables that we can still lift into the inequality */
209 liftabley = cyclelength - 1;
210 liftablez = SCIPcycGetNCluster(scip) - cyclelength - 1;
211
212 /* Now build the cut and add the subtour inequality */
213 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "subtour_%d_length_%d_contracted_%d", anchor,
214 cyclelength, ncontractions );
215 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
216 cyclelength + ncontractions - 1.0, FALSE, FALSE, TRUE) );
217
219
220 for( k = 0; k < cyclelength; ++k )
221 {
222 currentnode = subtours[anchor][k];
223 successor = subtours[anchor][k+1];
224 intermediate = iscontracted[currentnode][successor];
225
226 if( intermediate != -1 )
227 {
228 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
230 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
231
232 greater = intermediate > currentnode ? intermediate : currentnode;
233 smaller = intermediate < currentnode ? intermediate : currentnode;
234
235 if( liftabley > 0 && SCIPvarGetLPSol(getEdgevar(edgevars, greater, smaller, INCLUSTER)) > 0 )
236 {
237 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, greater, smaller, INCLUSTER), 1.0) );
238 liftabley--;
239 }
240 if( liftablez > 0 && SCIPvarGetLPSol(getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER)) > 0 )
241 {
242 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER), 1.0) );
243 liftablez--;
244 }
245 }
246 else
247 {
248 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
249 if( SCIPvarGetLPSol(getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER))
250 > 0 && liftabley > 0 )
251 {
253 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER), 1.0) );
254 liftabley--;
255 }
256 }
257 }
258
261
262 /* print for debugging purposes */
264
265 /* release data and increment cut counter */
266 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
267
269 (*ncuts)++;
270 }
271 }
272
273 for( k = 0; k < nstates; ++k )
274 {
275 SCIPfreeBlockMemoryArray(scip, &(subtours[k]), cyclelength + 1);
276 }
277 SCIPfreeBlockMemoryArray(scip, &subtours, nstates);
278 SCIPfreeBlockMemoryArray(scip, &insubtour, nstates);
279
280 return SCIP_OKAY;
281}
282
283/** Detect if path inequalities are violated and if so, add them to scip */
284static
286 SCIP* scip, /**< SCIP data structure. */
287 SCIP_SEPA* sepa, /**< the subtour separator */
288 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrix of all paths with 1,...,|Clutster| arcs */
289 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
290 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
291 int pathlength, /**< the length of the subtours to add */
292 SCIP_RESULT* result, /**< pointer to store the result of separation */
293 int* ncuts /**< pointer to store number of cuts */
294 )
295{
296 SCIP_VAR**** edgevars;
297 char cutname[SCIP_MAXSTRLEN];
298 SCIP_ROW* cut;
299 int* path;
300 int nstates;
301 int currentnode;
302 int successor;
303 int* successors;
304 int nsuccessors;
305 int intermediate;
306 int start;
307 int end;
308 int ncontractions;
309 int k;
310 int i;
311 int j;
312 int nz;
313 int ny;
314
315 edgevars = SCIPcycGetEdgevars(scip);
316 nstates = SCIPdigraphGetNNodes(adjacencygraph);
317
318 SCIP_CALL( SCIPallocMemoryArray(scip, &path, pathlength + 1) );
319
320 for( start = 0; start < nstates; ++start )
321 {
322 path[0] = start;
323
324 for( j = 0; j < SCIPdigraphGetNSuccessors(adjacencygraph, start); ++j )
325 {
326 ncontractions = 0;
327
328 end = SCIPdigraphGetSuccessors(adjacencygraph, start)[j];
329 path[pathlength] = end;
330
331 /* check if path-inequality is violated */
332 if( SCIPisGT(scip, getDist(adjacencymatrix, pathlength - 1, start, end)
333 + getDist(adjacencymatrix, 0, start, end), (SCIP_Real) pathlength) )
334 {
335 /*reconstruct the path */
336 for( k = 0; k < pathlength - 1; ++k )
337 {
338 currentnode = path[k];
339
340 assert(0 <= currentnode && currentnode < nstates);
341
342 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
343 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
344
345 for( i = 0; i < nsuccessors; ++i )
346 {
347 successor = successors[i];
348
349 assert(0 <= successor && successor < nstates);
350
351 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
352 + getDist(adjacencymatrix, pathlength - (k + 2), successor, end),
353 getDist(adjacencymatrix, pathlength - (k + 1), currentnode, end)) )
354 {
355 path[k + 1] = successor;
356
357 if( iscontracted[currentnode][successor] != -1 )
358 ncontractions++;
359
360 break;
361 }
362 }
363 }
364
365 /* check the last arc along the path and the direct arc from start to end for contractions */
366 if( iscontracted[path[pathlength - 1]][end] != -1 )
367 ncontractions++;
368
369 if( iscontracted[start][end] != -1 )
370 ncontractions++;
371
372 nz = pathlength;
373 ny = 0;
374
375 /* construct the corresponding inequality and add it to scip */
376 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "path_%d_%d_length_%d_contracted_%d",
377 start, end, pathlength, ncontractions );
378 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
379 (SCIP_Real) pathlength + ncontractions, FALSE, FALSE, TRUE) );
380
382
383 for( k = 0; k < pathlength; ++k )
384 {
385 currentnode = path[k];
386 successor = path[k+1];
387 intermediate = iscontracted[currentnode][successor];
388
389 if( intermediate != -1 )
390 {
391 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
393 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
394
395 if( nz < SCIPcycGetNCluster(scip)
396 && SCIPisPositive(scip, SCIPvarGetLPSol(getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER))) )
397 {
398 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER), 1.0) );
399 nz++;
400 }
401
402 if( ny < pathlength - 2 && SCIPisPositive(scip, SCIPvarGetLPSol(
403 getEdgevar(edgevars, MAX(currentnode, intermediate), MIN(currentnode, intermediate), INCLUSTER))) )
404 {
406 getEdgevar(edgevars, MAX(currentnode, intermediate), MIN(currentnode, intermediate), INCLUSTER), 1.0) );
407 ny++;
408 }
409 }
410 else
411 {
412 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
413
414 if( ny < pathlength - 2 && SCIPisPositive(scip, SCIPvarGetLPSol(
415 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER))) )
416 {
418 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER), 1.0) );
419 ny++;
420 }
421 }
422 }
423
424 /* add the direct arc from start to end */
425 intermediate = iscontracted[start][end];
426
427 if( iscontracted[start][end] != -1 )
428 {
429 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, start, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
431 MAX(intermediate, end), MIN(intermediate, end), INCLUSTER), 1.0) );
432 }
433 else
434 {
435 assert( NULL != getEdgevar(edgevars, start, end, CONSECUTIVE_CLUSTER));
436
437 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, start, end, CONSECUTIVE_CLUSTER), 1.0) );
438 }
439
441
442 /* print row if in debug mode */
444
445 /* if an arc appears twice then the path inequality should not be used */
446 if( SCIPisEQ(scip, SCIPgetRowMaxCoef(scip, cut), 1.0) )
447 {
450 (*ncuts)++;
451 }
452
453 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
454 }
455 }
456 }
457
459
460 return SCIP_OKAY;
461}
462
463/** detect if path inequalities are violated and if so, add them to scip */
464static
466 SCIP* scip, /**< SCIP data structure. */
467 SCIP_SEPA* sepa, /**< the subtour separator */
468 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrix of all paths with 1,...,|Clutster| arcs */
469 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
470 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
471 int tourlength, /**< the length of the subtours to add */
472 SCIP_RESULT* result, /**< pointer to store the result of separation */
473 int* ncuts /**< pointer to store number of cuts */
474 )
475{
476 SCIP_VAR**** edgevars;
477 char cutname[SCIP_MAXSTRLEN];
478 SCIP_ROW* cut;
479 int* tour;
480 int* successors;
481 int* succerssorsstart;
482 int nsuccessorsstart;
483 int nsuccessors;
484 int nstates;
485 int currentnode;
486 int successor;
487 int intermediate;
488 int start;
489 int end;
490 int ncontractions;
491 int k;
492 int i;
493 int j;
494
495 edgevars = SCIPcycGetEdgevars(scip);
496 nstates = SCIPdigraphGetNNodes(adjacencygraph);
497
498 SCIP_CALL( SCIPallocMemoryArray(scip, &tour, tourlength + 1) );
499
500 for( start = 0; start < nstates; ++start )
501 {
502 tour[0] = start;
503 succerssorsstart = SCIPdigraphGetSuccessors(adjacencygraph, start);
504 nsuccessorsstart = SCIPdigraphGetNSuccessors(adjacencygraph, start);
505
506 for( j = 0; j < nsuccessorsstart; ++j )
507 {
508 ncontractions = 0;
509
510 end = succerssorsstart[j];
511 tour[tourlength] = end;
512
513 /* check if tour-inequality is violated */
514 if( SCIPisGT(scip, getDist(adjacencymatrix, tourlength - 1, start, end)
515 - getDist(adjacencymatrix, 0, end, start), (SCIP_Real) tourlength - 1) )
516 {
517 /*reconstruct the tour */
518 for( k = 0; k < tourlength - 1; ++k )
519 {
520 currentnode = tour[k];
521 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
522 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
523
524 for( i = 0; i < nsuccessors; ++i )
525 {
526 successor = successors[i];
527
528 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
529 + getDist(adjacencymatrix, tourlength - (k + 2), successor, end)
530 , getDist(adjacencymatrix, tourlength - (k + 1), currentnode, end)) )
531 {
532 tour[k + 1] = successor;
533
534 if( iscontracted[currentnode][successor] != -1 )
535 ncontractions++;
536 break;
537 }
538 }
539 }
540
541 /* check the last arc along the tour and the direct arc from start to end for contractions */
542 if( iscontracted[tour[tourlength - 1]][end] != -1 )
543 ncontractions++;
544 if( iscontracted[end][start] != -1 )
545 ncontractions++;
546
547 /* construct the corresponding inequality and add it to scip */
548 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "tour_%d_%d_length_%d_contracted_%d",
549 start, end, tourlength, ncontractions );
550 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
551 (SCIP_Real) tourlength + ncontractions - 1, FALSE, FALSE, TRUE) );
552
554
555 for( k = 0; k < tourlength; ++k )
556 {
557 currentnode = tour[k];
558 successor = tour[k+1];
559 intermediate = iscontracted[currentnode][successor];
560
561 if( intermediate != -1 )
562 {
563 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
565 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
566 }
567 else
568 {
569 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
570 }
571 }
572
573 /* add the direct arc from start to end */
574 intermediate = iscontracted[end][start];
575 if( iscontracted[end][start] != -1 )
576 {
577 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, end, intermediate, CONSECUTIVE_CLUSTER), -1.0) );
579 getEdgevar(edgevars, MAX(intermediate, start), MIN(intermediate, start), INCLUSTER), 1.0) );
580 }
581 else
582 {
583 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, end, start, CONSECUTIVE_CLUSTER), -1.0) );
584 }
585
587
588 /* print row if in debug mode */
590
591 /* if an arc appears twice then the tour inequality should not be used */
592 if( SCIPisEQ(scip, SCIPgetRowMaxCoef(scip, cut), 1.0) )
593 {
596 (*ncuts)++;
597 }
598
599 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
600 }
601 }
602 }
603
605
606 return SCIP_OKAY;
607}
608
609/** compute the next matrix with the weight off all the longest paths with exactly narcs and store it in
610 * adjacencymatrix[narcs - 1]. For this, simply compute
611 * \f{align*}{ d^{k}(currentnode,successor) = max_{l=1,\ldots,n} \{d^{k-1}(currentnode,l) + d^1(l,successor) \} \f}.
612 */
613static
615(
616 SCIP* scip, /**< SCIP data structure */
617 SCIP_Real*** adjacencymatrix, /**< the max-distance matrices for all number of arcs less than narcs. */
618 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
619 int narcs /**< the current number of arcs in the paths */
620)
621{
622 int* intermediates;
623 int nintermediates;
624 int currentnode;
625 int intermediate;
626 int successor;
627 int l;
628 int nnodes;
629 SCIP_Bool foundviolation;
630
631 foundviolation = FALSE;
632 nnodes = SCIPdigraphGetNNodes(adjacencygraph);
633
634 for( currentnode = 0; currentnode < nnodes; ++currentnode )
635 {
636 intermediates = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
637 nintermediates = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
638
639 for( l = 0; l < nintermediates; ++l )
640 {
641 intermediate = intermediates[l];
642
643 assert(0 <= intermediate && intermediate < nnodes);
644
645 for( successor = 0; successor < nnodes; ++successor )
646 {
647 if( SCIPisPositive(scip, getDist(adjacencymatrix, 0, currentnode, intermediate))
648 && SCIPisPositive(scip, getDist(adjacencymatrix, narcs - 2, intermediate, successor)) )
649 {
650 if( SCIPisGT(scip, getDist(adjacencymatrix, 0, currentnode, intermediate)
651 + getDist(adjacencymatrix, narcs - 2, intermediate, successor),
652 getDist(adjacencymatrix, narcs - 1, currentnode, successor)) )
653 {
654 adjacencymatrix[narcs - 1][currentnode][successor] = getDist(adjacencymatrix, 0, currentnode, intermediate)
655 + getDist(adjacencymatrix, narcs - 2, intermediate, successor);
656 }
657 }
658 }
659 }
660 }
661
662 /* check if we have found a violated subtour constraint */
663 for( currentnode = 0; currentnode < nnodes; ++currentnode )
664 {
665 if( SCIPisGT(scip, getDist(adjacencymatrix, narcs - 1, currentnode, currentnode), narcs - 1.0) )
666 foundviolation = TRUE;
667 }
668 return foundviolation;
669}
670
671/** copy method for separator plugins (called when SCIP copies plugins) */
672static
673SCIP_DECL_SEPACOPY(sepaCopySubtour)
674{ /*lint --e{715}*/
675 assert(scip != NULL);
676 assert(sepa != NULL);
677
679
680 /* call inclusion method of constraint handler */
682
683 return SCIP_OKAY;
684}
685
686/** LP solution separation method of separator */
687static
688SCIP_DECL_SEPAEXECLP(sepaExeclpSubtour)
689{ /*lint --e{715}*/
690 SCIP_VAR**** edgevars;
691 SCIP_Real*** adjacencymatrix;
692 SCIP_DIGRAPH* adjacencygraph;
693 SCIP_DIGRAPH* edgegraph;
694 int** iscontracted;
695 SCIP_Bool violation;
696 int* successors1;
697 int* successors2;
698 int nsuccessors1;
699 int nsuccessors2;
700 int ncuts;
701 int nstates;
702 int ncluster;
703 int cyclelength;
704 int rounds;
705 int i;
706 int j;
707 int k;
708 int state1;
709 int state2;
710 int state3;
711
712 /* get problem information */
713 rounds = SCIPsepaGetNCallsAtNode(sepa);
714 ncluster = SCIPcycGetNCluster(scip);
715 edgevars = SCIPcycGetEdgevars(scip);
716 nstates = SCIPcycGetNBins(scip);
717 edgegraph = SCIPcycGetEdgeGraph(scip);
718 ncuts = 0;
719
720 if( rounds >= MAXROUNDS )
721 {
723 return SCIP_OKAY;
724 }
725
726 assert(nstates > 0);
727 assert(ncluster > 0 && ncluster < nstates);
728 assert(NULL != edgevars);
729 assert(NULL != edgegraph);
730
731 /* allocate memory */
732 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &adjacencymatrix, ncluster) );
733
734 for( k = 0; k < ncluster; ++k )
735 {
736 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &adjacencymatrix[k], nstates) ); /*lint !e866*/
737
738 for( j = 0; j < nstates; ++j )
739 {
740 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &adjacencymatrix[k][j], nstates) ); /*lint !e866*/
741 }
742 }
743
744 /* create Digraph from the current LP-Solution */
745 SCIP_CALL( SCIPcreateDigraph(scip, &adjacencygraph, nstates) );
746 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &iscontracted, nstates) );
747
748
749 /* get the values of the lp-solution */
750 for( i = 0; i < nstates; ++i )
751 {
752 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &iscontracted[i], nstates) );
753
754 for( j = 0; j < nstates; ++j )
755 {
756 iscontracted[i][j] = -1;
757
758 if( edgevars[i] != NULL && edgevars[i][j] != NULL && getEdgevar(edgevars, i, j, CONSECUTIVE_CLUSTER) != NULL )
759 adjacencymatrix[0][i][j] = SCIPvarGetLPSol(getEdgevar(edgevars, i, j, CONSECUTIVE_CLUSTER));
760 }
761 }
762
763 /* contract the adjacency matrix if it is better to take z_{ij} + y_{jk} rather than z_{ik} directly,
764 * this stores j at position (i,k)
765 */
766 for( i = 0; i < nstates; ++i )
767 {
768 state1 = i;
769
770 assert( edgevars[state1] != NULL);
771
772 successors1 = SCIPdigraphGetSuccessors(edgegraph, state1);
773 nsuccessors1 = SCIPdigraphGetNSuccessors(edgegraph, state1);
774
775 for( j = 0; j < nsuccessors1; ++j )
776 {
777 state2 = successors1[j];
778
779 assert( edgevars[state2] != NULL);
780
781 successors2 = SCIPdigraphGetSuccessors(edgegraph, state2);
782 nsuccessors2 = SCIPdigraphGetNSuccessors(edgegraph, state2);
783
784 for( k = 0 ; k < nsuccessors2; ++k )
785 {
786 state3 = successors2[k];
787
788 if( edgevars[state1][state2] == NULL || edgevars[state2][state3] == NULL || edgevars[state1][state3] == NULL )
789 continue;
790
791 if( SCIPisLT( scip, getDist(adjacencymatrix, 0, state1, state3),
792 SCIPvarGetLPSol(getEdgevar(edgevars, state1, state2, CONSECUTIVE_CLUSTER))
793 + SCIPvarGetLPSol(getEdgevar(edgevars, MAX(state2, state3), MIN(state2, state3), INCLUSTER)) - 1) )
794 {
795 adjacencymatrix[0][state1][state3] = SCIPvarGetLPSol(getEdgevar(edgevars, state1, state2, CONSECUTIVE_CLUSTER))
796 + SCIPvarGetLPSol(getEdgevar(edgevars, MAX(state2, state3), MIN(state2, state3), INCLUSTER)) - 1;
797
798 iscontracted[state1][state3] = state2;
799 }
800 }
801 }
802 }
803
804 /* save the contracted matrix as a digraph to be able to reuse it quicker */
805 for( i = 0; i < nstates; ++i )
806 {
807 for( j = 0; j < nstates; ++j )
808 {
809 if( !SCIPisZero(scip, getDist(adjacencymatrix, 0, i, j)) )
810 {
811 SCIP_CALL( SCIPdigraphAddArc(adjacencygraph, i , j, NULL) );
812 }
813 }
814 }
815
816 /* a cyclelength of one does not make sense as there are no loops */
817 cyclelength = 2;
819
820 /* Iterate until we have found a sufficient number of cuts or until we have checked all possible violations */
821 while( cyclelength < ncluster )
822 {
823 /* Compute the next adjacency matrix */
824 violation = computeNextAdjacency(scip, adjacencymatrix, adjacencygraph, cyclelength);
825
826 /* if we found a violation separate it */
827 if( violation )
828 {
829 SCIP_CALL( addSubtourCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength,
830 result, &ncuts) );
831 }
832
833 /* check if any path-inequalities are violated and sepatare them */
834 SCIP_CALL( addPathCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength, result, &ncuts) );
835
836 if( cyclelength == ncluster - 1 )
837 {
838 SCIP_CALL( addTourCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength,
839 result, &ncuts) );
840 }
841
842 /* stop if we added maximal number of cuts */
843 if( ncuts >= MAXCUTS )
844 break;
845
846 cyclelength++;
847 }
848
849 SCIPdigraphFreeComponents(adjacencygraph);
850 SCIPdigraphFree(&adjacencygraph);
851
852 /* free allocated memory */
853 for( i = 0; i < nstates; ++i )
854 {
855 SCIPfreeBlockMemoryArray(scip, &iscontracted[i], nstates);
856 }
857 SCIPfreeBlockMemoryArray(scip, &iscontracted, nstates);
858
859 for( i = 0; i < ncluster; ++i )
860 {
861 for( j = 0; j < nstates; ++j )
862 {
863 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix[i][j], nstates);
864 }
865 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix[i], nstates);
866 }
867 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix, ncluster);
868
869 return SCIP_OKAY;
870}
871
872
873/** creates the Subtour separator and includes it in SCIP */
875 SCIP* scip /**< SCIP data structure */
876)
877{
878 SCIP_SEPA* sepa;
879
880 /* include separator */
881
884 sepaExeclpSubtour, NULL,
885 NULL) );
886
887 assert(sepa != NULL);
888
889 /* set non fundamental callbacks via setter functions */
890 SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopySubtour) );
891
892
893 return SCIP_OKAY;
894}
Constraint handler for linear constraints in their most general form, .
#define NULL
Definition def.h:257
#define SCIP_MAXSTRLEN
Definition def.h:278
#define SCIP_Bool
Definition def.h:100
#define MIN(x, y)
Definition def.h:233
#define SCIP_STRINGEQ(name, reference, retcode)
Definition def.h:454
#define SCIP_Real
Definition def.h:165
#define TRUE
Definition def.h:102
#define FALSE
Definition def.h:103
#define MAX(x, y)
Definition def.h:229
#define SCIP_CALL(x)
Definition def.h:364
#define nnodes
Definition gastrans.c:74
#define narcs
Definition gastrans.c:77
void SCIPdigraphFreeComponents(SCIP_DIGRAPH *digraph)
Definition misc.c:8594
int SCIPdigraphGetNSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition misc.c:7881
int SCIPdigraphGetNNodes(SCIP_DIGRAPH *digraph)
Definition misc.c:7823
SCIP_RETCODE SCIPdigraphAddArc(SCIP_DIGRAPH *digraph, int startnode, int endnode, void *data)
Definition misc.c:7739
void SCIPdigraphFree(SCIP_DIGRAPH **digraph)
Definition misc.c:7645
int * SCIPdigraphGetSuccessors(SCIP_DIGRAPH *digraph, int node)
Definition misc.c:7896
SCIP_RETCODE SCIPcreateDigraph(SCIP *scip, SCIP_DIGRAPH **digraph, int nnodes)
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
Definition scip_cut.c:336
#define SCIPfreeBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:110
#define SCIPallocMemoryArray(scip, ptr, num)
Definition scip_mem.h:64
#define SCIPallocClearBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:97
#define SCIPfreeMemoryArray(scip, ptr)
Definition scip_mem.h:80
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:93
SCIP_Real SCIPgetRowMaxCoef(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1886
SCIP_RETCODE SCIPcacheRowExtensions(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1581
SCIP_RETCODE SCIPflushRowExtensions(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:1604
SCIP_RETCODE SCIPaddVarToRow(SCIP *scip, SCIP_ROW *row, SCIP_VAR *var, SCIP_Real val)
Definition scip_lp.c:1646
SCIP_RETCODE SCIPprintRow(SCIP *scip, SCIP_ROW *row, FILE *file)
Definition scip_lp.c:2176
SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
Definition scip_lp.c:1508
SCIP_RETCODE SCIPcreateEmptyRowSepa(SCIP *scip, SCIP_ROW **row, SCIP_SEPA *sepa, const char *name, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool removable)
Definition scip_lp.c:1429
SCIP_RETCODE SCIPincludeSepaBasic(SCIP *scip, SCIP_SEPA **sepa, const char *name, const char *desc, int priority, int freq, SCIP_Real maxbounddist, SCIP_Bool usessubscip, SCIP_Bool delay, SCIP_DECL_SEPAEXECLP((*sepaexeclp)), SCIP_DECL_SEPAEXECSOL((*sepaexecsol)), SCIP_SEPADATA *sepadata)
Definition scip_sepa.c:115
const char * SCIPsepaGetName(SCIP_SEPA *sepa)
Definition sepa.c:746
int SCIPsepaGetNCallsAtNode(SCIP_SEPA *sepa)
Definition sepa.c:893
SCIP_RETCODE SCIPsetSepaCopy(SCIP *scip, SCIP_SEPA *sepa,)
Definition scip_sepa.c:157
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisGT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Real SCIPvarGetLPSol(SCIP_VAR *var)
Definition var.c:24696
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition misc.c:10827
return SCIP_OKAY
int c
assert(minobj< SCIPgetCutoffbound(scip))
SCIP_VAR **** SCIPcycGetEdgevars(SCIP *scip)
int SCIPcycGetNBins(SCIP *scip)
SCIP_VAR * getEdgevar(SCIP_VAR ****edgevars, int state1, int state2, EDGETYPE edgetype)
int SCIPcycGetNCluster(SCIP *scip)
SCIP_DIGRAPH * SCIPcycGetEdgeGraph(SCIP *scip)
problem data for cycle clustering problem
@ CONSECUTIVE_CLUSTER
@ INCLUSTER
#define SCIPdebug(x)
Definition pub_message.h:93
public data structures and miscellaneous methods
#define SEPA_PRIORITY
#define SEPA_DELAY
#define SEPA_DESC
#define SEPA_USESSUBSCIP
#define SEPA_MAXBOUNDDIST
#define SEPA_FREQ
#define SEPA_NAME
#define MAXROUNDS
Definition sepa_edge.c:46
#define MAXCUTS
Definition sepa_edge.c:44
static SCIP_RETCODE addSubtourCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int cyclelength, SCIP_RESULT *result, int *ncuts)
static SCIP_RETCODE addPathCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int pathlength, SCIP_RESULT *result, int *ncuts)
static SCIP_Real getDist(SCIP_Real ***adjacencymatrix, int n, int state1, int state2)
static SCIP_RETCODE addTourCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int tourlength, SCIP_RESULT *result, int *ncuts)
static SCIP_Bool computeNextAdjacency(SCIP *scip, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int narcs)
SCIP_RETCODE SCIPincludeSepaSubtour(SCIP *scip)
Separate Subtours-Elimination inequalities in Cycle-Clustering Applications.
struct SCIP_Row SCIP_ROW
Definition type_lp.h:105
struct SCIP_Digraph SCIP_DIGRAPH
Definition type_misc.h:145
@ SCIP_DIDNOTRUN
Definition type_result.h:42
@ SCIP_DIDNOTFIND
Definition type_result.h:44
@ SCIP_SEPARATED
Definition type_result.h:49
enum SCIP_Result SCIP_RESULT
Definition type_result.h:61
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
#define SCIP_DECL_SEPAEXECLP(x)
Definition type_sepa.h:136
struct SCIP_Sepa SCIP_SEPA
Definition type_sepa.h:51
#define SCIP_DECL_SEPACOPY(x)
Definition type_sepa.h:61
struct SCIP_Var SCIP_VAR
Definition type_var.h:166