SCIP Doxygen Documentation
Loading...
Searching...
No Matches
heur_zirounding.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
25/**@file heur_zirounding.c
26 * @ingroup DEFPLUGINS_HEUR
27 * @brief zirounding primal heuristic
28 * @author Gregor Hendel
29 */
30
31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
32
35#include "scip/pub_heur.h"
36#include "scip/pub_lp.h"
37#include "scip/pub_message.h"
38#include "scip/pub_var.h"
39#include "scip/scip_branch.h"
40#include "scip/scip_heur.h"
41#include "scip/scip_lp.h"
42#include "scip/scip_mem.h"
43#include "scip/scip_message.h"
44#include "scip/scip_numerics.h"
45#include "scip/scip_param.h"
46#include "scip/scip_sol.h"
48
49
50#define HEUR_NAME "zirounding"
51#define HEUR_DESC "LP rounding heuristic as suggested by C. Wallace taking row slacks and bounds into account"
52#define HEUR_DISPCHAR SCIP_HEURDISPCHAR_ROUNDING
53#define HEUR_PRIORITY -500
54#define HEUR_FREQ 1
55#define HEUR_FREQOFS 0
56#define HEUR_MAXDEPTH -1
57#define HEUR_TIMING SCIP_HEURTIMING_AFTERLPNODE
58#define HEUR_USESSUBSCIP FALSE /**< does the heuristic use a secondary SCIP instance? */
59
60#define DEFAULT_MAXROUNDINGLOOPS 2 /**< delimits the number of main loops, can be set to -1 for no limit */
61#define DEFAULT_STOPZIROUND TRUE /**< deactivation check is enabled by default */
62#define DEFAULT_STOPPERCENTAGE 0.02 /**< the tolerance percentage after which zirounding will not be executed anymore */
63#define DEFAULT_MINSTOPNCALLS 1000 /**< number of heuristic calls before deactivation check */
64
65#define MINSHIFT 1e-4 /**< minimum shift value for every single step */
66
67/*
68 * Data structures
69 */
70
71/** primal heuristic data */
72struct SCIP_HeurData
73{
74 SCIP_SOL* sol; /**< working solution */
75 SCIP_Longint lastlp; /**< the number of the last LP for which ZIRounding was called */
76 int maxroundingloops; /**< limits rounding loops in execution */
77 SCIP_Bool stopziround; /**< sets deactivation check */
78 SCIP_Real stoppercentage; /**< threshold for deactivation check */
79 int minstopncalls; /**< number of heuristic calls before deactivation check */
80};
81
87typedef enum Direction DIRECTION;
88
89/*
90 * Local methods
91 */
92
93/** returns the fractionality of a value x, which is calculated as zivalue(x) = min(x-floor(x), ceil(x)-x) */
94static
96 SCIP* scip, /**< pointer to current SCIP data structure */
97 SCIP_Real val /**< the value for which the fractionality should be computed */
98 )
99{
100 SCIP_Real upgap; /* the gap between val and ceil(val) */
101 SCIP_Real downgap; /* the gap between val and floor(val) */
102
103 assert(scip != NULL);
104
105 upgap = SCIPfeasCeil(scip, val) - val;
106 downgap = val - SCIPfeasFloor(scip, val);
107
108 return MIN(upgap, downgap);
109}
110
111/** determines shifting bounds for variable */
112static
114 SCIP* scip, /**< pointer to current SCIP data structure */
115 SCIP_VAR* var, /**< the variable for which lb and ub have to be calculated */
116 SCIP_Real currentvalue, /**< the current value of var in the working solution */
117 SCIP_Real* upperbound, /**< pointer to store the calculated upper bound on the variable shift */
118 SCIP_Real* lowerbound, /**< pointer to store the calculated lower bound on the variable shift */
119 SCIP_Real* upslacks, /**< array that contains the slacks between row activities and the right hand sides of the rows */
120 SCIP_Real* downslacks, /**< array that contains lhs slacks */
121 int nslacks, /**< current number of slacks */
122 SCIP_Bool* numericalerror /**< flag to determine whether a numerical error occurred */
123 )
124{
125 SCIP_COL* col;
126 SCIP_ROW** colrows;
127 SCIP_Real* colvals;
128 int ncolvals;
129 int i;
130
131 assert(scip != NULL);
132 assert(var != NULL);
133 assert(upslacks != NULL);
134 assert(downslacks != NULL);
135 assert(upperbound != NULL);
136 assert(lowerbound != NULL);
137
138 /* get the column associated to the variable, the nonzero rows and the nonzero coefficients */
139 col = SCIPvarGetCol(var);
140 colrows = SCIPcolGetRows(col);
141 colvals = SCIPcolGetVals(col);
142 ncolvals = SCIPcolGetNLPNonz(col);
143
144 /* only proceed, when variable has nonzero coefficients */
145 if( ncolvals == 0 )
146 return;
147
148 assert(colvals != NULL);
149 assert(colrows != NULL);
150
151 /* initialize the bounds on the shift to be the gap of the current solution value to the bounds of the variable */
153 *upperbound = SCIPinfinity(scip);
154 else
155 *upperbound = SCIPvarGetUbGlobal(var) - currentvalue;
156
158 *lowerbound = SCIPinfinity(scip);
159 else
160 *lowerbound = currentvalue - SCIPvarGetLbGlobal(var);
161
162 /* go through every nonzero row coefficient corresponding to var to determine bounds for shifting
163 * in such a way that shifting maintains feasibility in every LP row.
164 * a lower or upper bound as it is calculated in zirounding always has to be >= 0.0.
165 * if one of these values is significantly < 0.0, this will cause the abort of execution of the heuristic so that
166 * infeasible solutions are avoided
167 */
168 for( i = 0; i < ncolvals && MAX(*lowerbound, *upperbound) >= MINSHIFT; ++i )
169 {
170 SCIP_ROW* row;
171 int rowpos;
172
173 row = colrows[i];
174 rowpos = SCIProwGetLPPos(row);
175
176 /* the row might currently not be in the LP, ignore it! */
177 if( rowpos == -1 )
178 continue;
179
180 assert(0 <= rowpos && rowpos < nslacks);
181
182 /* all bounds and slacks as they are calculated in zirounding always have to be greater equal zero.
183 * It might however be due to numerical issues, e.g. with scaling, that they are not. Better abort in this case.
184 */
185 if( SCIPisFeasLT(scip, *lowerbound, 0.0) || SCIPisFeasLT(scip, *upperbound, 0.0)
186 || SCIPisFeasLT(scip, upslacks[rowpos], 0.0) || SCIPisFeasLT(scip, downslacks[rowpos] , 0.0) )
187 {
188 *numericalerror = TRUE;
189 return;
190 }
191
192 SCIPdebugMsg(scip, "colval: %15.8g, downslack: %15.8g, upslack: %5.2g, lb: %5.2g, ub: %5.2g\n", colvals[i], downslacks[rowpos], upslacks[rowpos],
193 *lowerbound, *upperbound);
194
195 /* if coefficient > 0, rounding up might violate up slack and rounding down might violate down slack
196 * thus search for the minimum so that no constraint is violated; vice versa for coefficient < 0
197 */
198 if( colvals[i] > 0 )
199 {
200 if( !SCIPisInfinity(scip, upslacks[rowpos]) )
201 {
202 SCIP_Real upslack;
203 upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
204 *upperbound = MIN(*upperbound, upslack/colvals[i]);
205 }
206
207 if( !SCIPisInfinity(scip, downslacks[rowpos]) )
208 {
209 SCIP_Real downslack;
210 downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
211 *lowerbound = MIN(*lowerbound, downslack/colvals[i]);
212 }
213 }
214 else
215 {
216 assert(colvals[i] != 0.0);
217
218 if( !SCIPisInfinity(scip, upslacks[rowpos]) )
219 {
220 SCIP_Real upslack;
221 upslack = MAX(upslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
222 *lowerbound = MIN(*lowerbound, -upslack/colvals[i]);
223 }
224
225 if( !SCIPisInfinity(scip, downslacks[rowpos]) )
226 {
227 SCIP_Real downslack;
228 downslack = MAX(downslacks[rowpos], 0.0); /* avoid errors due to numerically slightly infeasible rows */
229 *upperbound = MIN(*upperbound, -downslack/colvals[i]);
230 }
231 }
232 }
233}
234
235/** when a variable is shifted, the activities and slacks of all rows it appears in have to be updated */
236static
238 SCIP* scip, /**< pointer to current SCIP data structure */
239 SCIP_SOL* sol, /**< working solution */
240 SCIP_VAR* var, /**< pointer to variable to be modified */
241 SCIP_Real shiftvalue, /**< the value by which the variable is shifted */
242 SCIP_Real* upslacks, /**< upslacks of all rows the variable appears in */
243 SCIP_Real* downslacks, /**< downslacks of all rows the variable appears in */
244 SCIP_Real* activities, /**< activities of the LP rows */
245 SCIP_VAR** slackvars, /**< the slack variables for equality rows */
246 SCIP_Real* slackcoeffs, /**< the slack variable coefficients */
247 int nslacks /**< size of the arrays */
248 )
249{
250 SCIP_COL* col; /* the corresponding column of variable var */
251 SCIP_ROW** rows; /* pointer to the nonzero coefficient rows for variable var */
252 int nrows; /* the number of nonzeros */
253 SCIP_Real* colvals; /* array to store the nonzero coefficients */
254 int i;
255
256 assert(scip != NULL);
257 assert(sol != NULL);
258 assert(var != NULL);
259 assert(upslacks != NULL);
260 assert(downslacks != NULL);
262 assert(nslacks >= 0);
263
264 col = SCIPvarGetCol(var);
265 assert(col != NULL);
266
267 rows = SCIPcolGetRows(col);
268 nrows = SCIPcolGetNLPNonz(col);
269 colvals = SCIPcolGetVals(col);
270 assert(nrows == 0 || (rows != NULL && colvals != NULL));
271
272 /* go through all rows the shifted variable appears in */
273 for( i = 0; i < nrows; ++i )
274 {
275 int rowpos;
276
277 rowpos = SCIProwGetLPPos(rows[i]);
278 assert(-1 <= rowpos && rowpos < nslacks);
279
280 /* if the row is in the LP, update its activity, up and down slack */
281 if( rowpos >= 0 )
282 {
283 SCIP_Real val;
284 SCIP_Real lhs;
285 SCIP_Real rhs;
286 SCIP_ROW* row;
287
288 val = colvals[i] * shiftvalue;
289 row = rows[i];
290 lhs = SCIProwGetLhs(row);
291 rhs = SCIProwGetRhs(row);
292
293 /* if the row is an equation, we update its slack variable instead of its activities */
294 if( SCIPisFeasEQ(scip, lhs, rhs) )
295 {
296 SCIP_Real slackvarshiftval;
297 SCIP_Real slackvarsolval;
298
299 assert(slackvars[rowpos] != NULL);
300 assert(!SCIPisZero(scip, slackcoeffs[rowpos]));
301
302 slackvarsolval = SCIPgetSolVal(scip, sol, slackvars[rowpos]);
303 slackvarshiftval = -val / slackcoeffs[rowpos];
304
305 assert(SCIPisFeasGE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetLbGlobal(slackvars[rowpos])));
306 assert(SCIPisFeasLE(scip, slackvarsolval + slackvarshiftval, SCIPvarGetUbGlobal(slackvars[rowpos])));
307
308 SCIP_CALL( SCIPsetSolVal(scip, sol, slackvars[rowpos], slackvarsolval + slackvarshiftval) );
309 }
310 else if( !SCIPisInfinity(scip, REALABS(activities[rowpos])) )
311 activities[rowpos] += val;
312
313 /* the slacks of the row now can be updated independently of its type */
314 if( !SCIPisInfinity(scip, upslacks[rowpos]) )
315 upslacks[rowpos] -= val;
316 if( !SCIPisInfinity(scip, -downslacks[rowpos]) )
317 downslacks[rowpos] += val;
318
319 assert(SCIPisInfinity(scip, -lhs) || SCIPisFeasGE(scip, activities[rowpos], lhs));
320 assert(SCIPisInfinity(scip, rhs) || SCIPisFeasLE(scip, activities[rowpos], rhs));
321 }
322 }
323 return SCIP_OKAY;
324}
325
326/** finds a continuous slack variable for an equation row, NULL if none exists */
327static
329 SCIP* scip, /**< pointer to current SCIP data structure */
330 SCIP_ROW* row, /**< the row for which a slack variable is searched */
331 SCIP_VAR** varpointer, /**< pointer to store the slack variable */
332 SCIP_Real* coeffpointer /**< pointer to store the coefficient of the slack variable */
333 )
334{
335 int v;
336 SCIP_COL** rowcols;
337 SCIP_Real* rowvals;
338 int nrowvals;
339
340 assert(row != NULL);
341 assert(varpointer != NULL);
342 assert(coeffpointer != NULL);
343
344 rowcols = SCIProwGetCols(row);
345 rowvals = SCIProwGetVals(row);
346 nrowvals = SCIProwGetNNonz(row);
347
348 assert(nrowvals == 0 || rowvals != NULL);
349 assert(nrowvals == 0 || rowcols != NULL);
350
351 /* iterate over the row variables. Stop after the first unfixed continuous variable was found. */
352 for( v = nrowvals - 1; v >= 0; --v )
353 {
354 SCIP_VAR* colvar;
355
356 assert(rowcols[v] != NULL);
357 if( SCIPcolGetLPPos(rowcols[v]) == -1 )
358 continue;
359
360 colvar = SCIPcolGetVar(rowcols[v]);
361
362 if( !SCIPvarIsIntegral(colvar)
364 && SCIPcolGetNLPNonz(rowcols[v]) == 1 )
365 {
366 SCIPdebugMsg(scip, " slack variable for row %s found: %s\n", SCIProwGetName(row), SCIPvarGetName(colvar));
367
368 *coeffpointer = rowvals[v];
369 *varpointer = colvar;
370
371 return;
372 }
373 }
374
375 *varpointer = NULL;
376 *coeffpointer = 0.0;
377
378 SCIPdebugMsg(scip, "No slack variable for row %s found. \n", SCIProwGetName(row));
379}
380
381/*
382 * Callback methods of primal heuristic
383 */
384
385/** copy method for primal heuristic plugins (called when SCIP copies plugins) */
386static
387SCIP_DECL_HEURCOPY(heurCopyZirounding)
388{ /*lint --e{715}*/
389 assert(scip != NULL);
390 assert(heur != NULL);
391
393
394 /* call inclusion method of primal heuristic */
396
397 return SCIP_OKAY;
398}
399
400/** destructor of primal heuristic to free user data (called when SCIP is exiting) */
401static
402SCIP_DECL_HEURFREE(heurFreeZirounding)
403{ /*lint --e{715}*/
405
407
409 assert(heurdata != NULL);
410
411 /* free heuristic data */
413 SCIPheurSetData(heur, NULL);
414
415 return SCIP_OKAY;
416}
417
418/** initialization method of primal heuristic (called after problem was transformed) */
419static
420SCIP_DECL_HEURINIT(heurInitZirounding)
421{ /*lint --e{715}*/
423
425
427 assert(heurdata != NULL);
428
429 /* create working solution */
430 SCIP_CALL( SCIPcreateSol(scip, &heurdata->sol, heur) );
431
432 return SCIP_OKAY;
433}
434
435/** deinitialization method of primal heuristic (called before transformed problem is freed) */
436static
437SCIP_DECL_HEUREXIT(heurExitZirounding) /*lint --e{715}*/
438{ /*lint --e{715}*/
440
442
445
446 /* free working solution */
448
449 return SCIP_OKAY;
450}
451
452/** solving process initialization method of primal heuristic (called when branch and bound process is about to begin) */
453static
454SCIP_DECL_HEURINITSOL(heurInitsolZirounding)
455{ /*lint --e{715}*/
457
459
461 assert(heurdata != NULL);
462
463 heurdata->lastlp = -1;
464
465 return SCIP_OKAY;
466}
467
468
469/** execution method of primal heuristic */
470static
471SCIP_DECL_HEUREXEC(heurExecZirounding)
472{ /*lint --e{715}*/
474 SCIP_SOL* sol;
476 SCIP_VAR** zilpcands;
477
478 SCIP_VAR** slackvars;
479 SCIP_Real* upslacks;
480 SCIP_Real* downslacks;
482 SCIP_Real* slackvarcoeffs;
483 SCIP_Bool* rowneedsslackvar;
484
485 SCIP_ROW** rows;
487 SCIP_Real* solarray;
488
490 int currentlpcands;
491 int nlpcands;
492 int nimplfracs;
493 int i;
494 int c;
495 int nslacks;
496 int nroundings;
497
498 SCIP_Bool improvementfound;
499 SCIP_Bool numericalerror;
500
501 assert(result != NULL);
503
505
507
508 /* do not call heuristic of node was already detected to be infeasible */
509 if( nodeinfeasible )
510 return SCIP_OKAY;
511
512 /* only call heuristic if an optimal LP-solution is at hand */
514 return SCIP_OKAY;
515
516 /* only call heuristic, if the LP objective value is smaller than the cutoff bound */
518 return SCIP_OKAY;
519
520 /* get heuristic data */
522 assert(heurdata != NULL);
523
524 /* Do not call heuristic if deactivation check is enabled and percentage of found solutions in relation
525 * to number of calls falls below heurdata->stoppercentage */
526 if( heurdata->stopziround && SCIPheurGetNCalls(heur) >= heurdata->minstopncalls
527 && SCIPheurGetNSolsFound(heur)/(SCIP_Real)SCIPheurGetNCalls(heur) < heurdata->stoppercentage )
528 return SCIP_OKAY;
529
530 /* assure that heuristic has not already been called after the last LP had been solved */
532 if( nlps == heurdata->lastlp )
533 return SCIP_OKAY;
534
535 heurdata->lastlp = nlps;
536
537 /* @todo: check whether rounding continuous implied integral variables is necessary */
538 /* get fractional variables */
540 nlpcands = nlpcands + nimplfracs;
541
542 /* make sure that there is at least one fractional variable that should be integral */
543 if( nlpcands == 0 )
544 return SCIP_OKAY;
545
546 assert(nlpcands > 0);
547 assert(lpcands != NULL);
549
550 /* get LP rows data */
551 rows = SCIPgetLPRows(scip);
552 nslacks = SCIPgetNLPRows(scip);
553
554 /* cannot do anything if LP is empty */
555 if( nslacks == 0 )
556 return SCIP_OKAY;
557
558 assert(rows != NULL);
559 assert(nslacks > 0);
560
561 /* get the working solution from heuristic's local data */
562 sol = heurdata->sol;
563 assert(sol != NULL);
564
566
567 solarray = NULL;
568 zilpcands = NULL;
569
570 /* copy the current LP solution to the working solution and allocate memory for local data */
574
575 /* copy necessary data to local arrays */
578
579 /* allocate buffer data arrays */
580 SCIP_CALL( SCIPallocBufferArray(scip, &slackvars, nslacks) );
581 SCIP_CALL( SCIPallocBufferArray(scip, &upslacks, nslacks) );
582 SCIP_CALL( SCIPallocBufferArray(scip, &downslacks, nslacks) );
583 SCIP_CALL( SCIPallocBufferArray(scip, &slackvarcoeffs, nslacks) );
584 SCIP_CALL( SCIPallocBufferArray(scip, &rowneedsslackvar, nslacks) );
586
587 BMSclearMemoryArray(slackvars, nslacks);
588 BMSclearMemoryArray(slackvarcoeffs, nslacks);
589 BMSclearMemoryArray(rowneedsslackvar, nslacks);
590
591 numericalerror = FALSE;
592 nroundings = 0;
593
594 /* loop over fractional variables and involved LP rows to find all rows which require a slack variable */
595 for( c = 0; c < nlpcands; ++c )
596 {
597 SCIP_VAR* cand;
598 SCIP_ROW** candrows;
599 int r;
600 int ncandrows;
601
602 cand = zilpcands[c];
603 assert(cand != NULL);
605
606 candrows = SCIPcolGetRows(SCIPvarGetCol(cand));
607 ncandrows = SCIPcolGetNLPNonz(SCIPvarGetCol(cand));
608
609 assert(candrows == NULL || ncandrows > 0);
610
611 for( r = 0; r < ncandrows; ++r )
612 {
613 int rowpos;
614
615 assert(candrows != NULL); /* to please flexelint */
616 assert(candrows[r] != NULL);
617 rowpos = SCIProwGetLPPos(candrows[r]);
618
619 if( rowpos >= 0 && SCIPisFeasEQ(scip, SCIProwGetLhs(candrows[r]), SCIProwGetRhs(candrows[r])) )
620 {
621 rowneedsslackvar[rowpos] = TRUE;
622 SCIPdebugMsg(scip, " Row %s needs slack variable for variable %s\n", SCIProwGetName(candrows[r]), SCIPvarGetName(cand));
623 }
624 }
625 }
626
627 /* calculate row slacks for every every row that belongs to the current LP and ensure, that the current solution
628 * has no violated constraint -- if any constraint is violated, i.e. a slack is significantly smaller than zero,
629 * this will cause the termination of the heuristic because Zirounding does not provide feasibility recovering
630 */
631 for( i = 0; i < nslacks; ++i )
632 {
633 SCIP_ROW* row;
634 SCIP_Real lhs;
635 SCIP_Real rhs;
636
637 row = rows[i];
638
639 assert(row != NULL);
640
641 lhs = SCIProwGetLhs(row);
642 rhs = SCIProwGetRhs(row);
643
644 /* get row activity */
647
648 /* in special case if LHS or RHS is (-)infinity slacks have to be initialized as infinity */
649 if( SCIPisInfinity(scip, -lhs) )
650 downslacks[i] = SCIPinfinity(scip);
651 else
652 downslacks[i] = activities[i] - lhs;
653
654 if( SCIPisInfinity(scip, rhs) )
655 upslacks[i] = SCIPinfinity(scip);
656 else
657 upslacks[i] = rhs - activities[i];
658
659 SCIPdebugMsg(scip, "lhs:%5.2f <= act:%5.2g <= rhs:%5.2g --> down: %5.2g, up:%5.2g\n", lhs, activities[i], rhs, downslacks[i], upslacks[i]);
660
661 /* row is an equation. Try to find a slack variable in the row, i.e.,
662 * a continuous variable which occurs only in this row. If no such variable exists,
663 * there is no hope for an IP-feasible solution in this round
664 */
665 if( SCIPisFeasEQ(scip, lhs, rhs) && rowneedsslackvar[i] )
666 {
667 /* @todo: This is only necessary for rows containing fractional variables. */
668 rowFindSlackVar(scip, row, &(slackvars[i]), &(slackvarcoeffs[i]));
669
670 if( slackvars[i] == NULL )
671 {
672 SCIPdebugMsg(scip, "No slack variable found for equation %s, terminating ZI Round heuristic\n", SCIProwGetName(row));
673 goto TERMINATE;
674 }
675 else
676 {
677 SCIP_Real ubslackvar;
678 SCIP_Real lbslackvar;
679 SCIP_Real solvalslackvar;
680 SCIP_Real coeffslackvar;
681 SCIP_Real ubgap;
682 SCIP_Real lbgap;
683
684 assert(!SCIPvarIsIntegral(slackvars[i]));
685 solvalslackvar = SCIPgetSolVal(scip, sol, slackvars[i]);
686 ubslackvar = SCIPvarGetUbGlobal(slackvars[i]);
687 lbslackvar = SCIPvarGetLbGlobal(slackvars[i]);
688
689 coeffslackvar = slackvarcoeffs[i];
690 assert(!SCIPisZero(scip, coeffslackvar));
691
692 ubgap = MAX(0.0, ubslackvar - solvalslackvar);
693 lbgap = MAX(0.0, solvalslackvar - lbslackvar);
694
695 if( SCIPisPositive(scip, coeffslackvar) )
696 {
697 if( !SCIPisInfinity(scip, lbslackvar) )
698 upslacks[i] += coeffslackvar * lbgap;
699 else
700 upslacks[i] = SCIPinfinity(scip);
701 if( !SCIPisInfinity(scip, ubslackvar) )
702 downslacks[i] += coeffslackvar * ubgap;
703 else
704 downslacks[i] = SCIPinfinity(scip);
705 }
706 else
707 {
708 if( !SCIPisInfinity(scip, ubslackvar) )
709 upslacks[i] -= coeffslackvar * ubgap;
710 else
711 upslacks[i] = SCIPinfinity(scip);
712 if( !SCIPisInfinity(scip, lbslackvar) )
713 downslacks[i] -= coeffslackvar * lbgap;
714 else
715 downslacks[i] = SCIPinfinity(scip);
716 }
717 SCIPdebugMsg(scip, " Slack variable for row %s at pos %d: %g <= %s = %g <= %g; Coeff %g, upslack = %g, downslack = %g \n",
718 SCIProwGetName(row), SCIProwGetLPPos(row), lbslackvar, SCIPvarGetName(slackvars[i]), solvalslackvar, ubslackvar, coeffslackvar,
719 upslacks[i], downslacks[i]);
720 }
721 }
722 /* due to numerical inaccuracies, the rows might be feasible, even if the slacks are
723 * significantly smaller than zero -> terminate
724 */
725 if( SCIPisFeasLT(scip, upslacks[i], 0.0) || SCIPisFeasLT(scip, downslacks[i], 0.0) )
726 goto TERMINATE;
727 }
728
729 assert(nslacks == 0 || (upslacks != NULL && downslacks != NULL && activities != NULL));
730
731 /* initialize number of remaining variables and flag to enter the main loop */
732 currentlpcands = nlpcands;
733 improvementfound = TRUE;
734
735 /* iterate over variables as long as there are fractional variables left */
736 while( currentlpcands > 0 && improvementfound && (heurdata->maxroundingloops == -1 || nroundings < heurdata->maxroundingloops) )
737 { /*lint --e{850}*/
738 improvementfound = FALSE;
739 nroundings++;
740 SCIPdebugMsg(scip, "zirounding enters while loop for %d time with %d candidates left. \n", nroundings, currentlpcands);
741
742 /* check for every remaining fractional variable if a shifting decreases ZI-value of the variable */
743 for( c = 0; c < currentlpcands; ++c )
744 {
745 SCIP_VAR* var;
746 SCIP_Real oldsolval;
747 SCIP_Real upperbound;
748 SCIP_Real lowerbound;
749 SCIP_Real up;
750 SCIP_Real down;
751 SCIP_Real ziup;
752 SCIP_Real zidown;
753 SCIP_Real zicurrent;
754 SCIP_Real shiftval;
755
756 DIRECTION direction;
757
758 /* get values from local data */
759 oldsolval = solarray[c];
760 var = zilpcands[c];
761
762 assert(!SCIPisFeasIntegral(scip, oldsolval));
764
765 /* calculate bounds for variable and make sure that there are no numerical inconsistencies */
766 upperbound = SCIPinfinity(scip);
767 lowerbound = SCIPinfinity(scip);
768 calculateBounds(scip, var, oldsolval, &upperbound, &lowerbound, upslacks, downslacks, nslacks, &numericalerror);
769
770 if( numericalerror )
771 goto TERMINATE;
772
773 /* continue if only marginal shifts are possible */
774 if( MAX(upperbound, lowerbound) < MINSHIFT )
775 {
776 /* stop immediately if a variable has not been rounded during the last round */
777 if( nroundings == heurdata->maxroundingloops )
778 break;
779 else
780 continue;
781 }
782
783 /* calculate the possible values after shifting */
784 up = oldsolval + upperbound;
785 down = oldsolval - lowerbound;
786
787 /* if the variable is integer or implied integral, do not shift further than the nearest integer */
789 {
790 SCIP_Real ceilx;
791 SCIP_Real floorx;
792
793 ceilx = SCIPfeasCeil(scip, oldsolval);
794 floorx = SCIPfeasFloor(scip, oldsolval);
795 up = MIN(up, ceilx);
796 down = MAX(down, floorx);
797 }
798
799 /* calculate necessary values */
800 ziup = getZiValue(scip, up);
801 zidown = getZiValue(scip, down);
802 zicurrent = getZiValue(scip, oldsolval);
803
804 /* calculate the shifting direction that reduces ZI-value the most,
805 * if both directions improve ZI-value equally, take the direction which improves the objective
806 */
807 if( SCIPisFeasLT(scip, zidown, zicurrent) || SCIPisFeasLT(scip, ziup, zicurrent) )
808 {
809 if( SCIPisFeasEQ(scip,ziup, zidown) )
811 else if( SCIPisFeasLT(scip, zidown, ziup) )
812 direction = DIRECTION_DOWN;
813 else
814 direction = DIRECTION_UP;
815
816 /* once a possible shifting direction and value have been found, variable value is updated */
817 shiftval = (direction == DIRECTION_UP ? up - oldsolval : down - oldsolval);
818
819 /* this improves numerical stability in some cases */
820 if( direction == DIRECTION_UP )
821 shiftval = MIN(shiftval, upperbound);
822 else
823 shiftval = MIN(shiftval, lowerbound);
824 /* update the solution */
825 solarray[c] = direction == DIRECTION_UP ? up : down;
826 SCIP_CALL( SCIPsetSolVal(scip, sol, var, solarray[c]) );
827
828 /* update the rows activities and slacks */
829 SCIP_CALL( updateSlacks(scip, sol, var, shiftval, upslacks,
830 downslacks, activities, slackvars, slackvarcoeffs, nslacks) );
831
832 SCIPdebugMsg(scip, "zirounding update step : %d var index, oldsolval=%g, shiftval=%g\n",
833 SCIPvarGetIndex(var), oldsolval, shiftval);
834 /* since at least one improvement has been found, heuristic will enter main loop for another time because the improvement
835 * might affect many LP rows and their current slacks and thus make further rounding steps possible */
836 improvementfound = TRUE;
837 }
838
839 /* if solution value of variable has become feasibly integral due to rounding step,
840 * variable is put at the end of remaining candidates array so as not to be considered in future loops
841 */
842 if( SCIPisFeasIntegral(scip, solarray[c]) )
843 {
844 zilpcands[c] = zilpcands[currentlpcands - 1];
845 solarray[c] = solarray[currentlpcands - 1];
846 currentlpcands--;
847
848 /* counter is decreased if end of candidates array has not been reached yet */
849 if( c < currentlpcands )
850 c--;
851 }
852 else if( nroundings == heurdata->maxroundingloops )
853 goto TERMINATE;
854 }
855 }
856
857 /* in case that no candidate is left for rounding after the final main loop
858 * the found solution has to be checked for feasibility in the original problem
859 */
860 if( currentlpcands == 0 )
861 {
862 SCIP_Bool stored;
864 if( stored )
865 {
866#ifdef SCIP_DEBUG
867 SCIPdebugMsg(scip, "found feasible rounded solution:\n");
869#endif
870 SCIPstatisticMessage(" ZI Round solution value: %g \n", SCIPgetSolOrigObj(scip, sol));
871
873 }
874 }
875
876 /* free memory for all locally allocated data */
877 TERMINATE:
879 SCIPfreeBufferArrayNull(scip, &rowneedsslackvar);
880 SCIPfreeBufferArrayNull(scip, &slackvarcoeffs);
881 SCIPfreeBufferArrayNull(scip, &downslacks);
882 SCIPfreeBufferArrayNull(scip, &upslacks);
883 SCIPfreeBufferArrayNull(scip, &slackvars);
884 SCIPfreeBufferArrayNull(scip, &zilpcands);
885 SCIPfreeBufferArrayNull(scip, &solarray);
886
887 return SCIP_OKAY;
888}
889
890/*
891 * primal heuristic specific interface methods
892 */
893
894/** creates the zirounding primal heuristic and includes it in SCIP */
896 SCIP* scip /**< SCIP data structure */
897 )
898{
900 SCIP_HEUR* heur;
901
902 /* create zirounding primal heuristic data */
904
905 /* include primal heuristic */
908 HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecZirounding, heurdata) );
909
910 assert(heur != NULL);
911
912 /* primal heuristic is safe to use in exact solving mode */
913 SCIPheurMarkExact(heur);
914
915 /* set non-NULL pointers to callback methods */
916 SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyZirounding) );
917 SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeZirounding) );
918 SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitZirounding) );
919 SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitZirounding) );
920 SCIP_CALL( SCIPsetHeurInitsol(scip, heur, heurInitsolZirounding) );
921
922 /* add zirounding primal heuristic parameters */
923 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/zirounding/maxroundingloops",
924 "determines maximum number of rounding loops",
925 &heurdata->maxroundingloops, TRUE, DEFAULT_MAXROUNDINGLOOPS, -1, INT_MAX, NULL, NULL) );
926 SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/zirounding/stopziround",
927 "flag to determine if Zirounding is deactivated after a certain percentage of unsuccessful calls",
928 &heurdata->stopziround, TRUE, DEFAULT_STOPZIROUND, NULL, NULL) );
929 SCIP_CALL( SCIPaddRealParam(scip,"heuristics/zirounding/stoppercentage",
930 "if percentage of found solutions falls below this parameter, Zirounding will be deactivated",
931 &heurdata->stoppercentage, TRUE, DEFAULT_STOPPERCENTAGE, 0.0, 1.0, NULL, NULL) );
932 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/zirounding/minstopncalls",
933 "determines the minimum number of calls before percentage-based deactivation of Zirounding is applied",
934 &heurdata->minstopncalls, TRUE, DEFAULT_MINSTOPNCALLS, 1, INT_MAX, NULL, NULL) );
935
936 return SCIP_OKAY;
937}
#define NULL
Definition def.h:257
#define SCIP_Longint
Definition def.h:150
#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 REALABS(x)
Definition def.h:191
#define SCIP_CALL(x)
Definition def.h:364
#define SCIPdebugMsg
SCIP_RETCODE SCIPaddIntParam(SCIP *scip, const char *name, const char *desc, int *valueptr, SCIP_Bool isadvanced, int defaultvalue, int minvalue, int maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:83
SCIP_RETCODE SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:139
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition scip_param.c:57
SCIP_RETCODE SCIPincludeHeurZirounding(SCIP *scip)
SCIP_RETCODE SCIPgetLPBranchCands(SCIP *scip, SCIP_VAR ***lpcands, SCIP_Real **lpcandssol, SCIP_Real **lpcandsfrac, int *nlpcands, int *npriolpcands, int *nfracimplvars)
int SCIPcolGetLPPos(SCIP_COL *col)
Definition lp.c:17487
SCIP_VAR * SCIPcolGetVar(SCIP_COL *col)
Definition lp.c:17425
SCIP_Real * SCIPcolGetVals(SCIP_COL *col)
Definition lp.c:17555
SCIP_ROW ** SCIPcolGetRows(SCIP_COL *col)
Definition lp.c:17545
int SCIPcolGetNLPNonz(SCIP_COL *col)
Definition lp.c:17534
SCIP_RETCODE SCIPsetHeurFree(SCIP *scip, SCIP_HEUR *heur,)
Definition scip_heur.c:183
SCIP_HEURDATA * SCIPheurGetData(SCIP_HEUR *heur)
Definition heur.c:1368
SCIP_RETCODE SCIPincludeHeurBasic(SCIP *scip, SCIP_HEUR **heur, const char *name, const char *desc, char dispchar, int priority, int freq, int freqofs, int maxdepth, SCIP_HEURTIMING timingmask, SCIP_Bool usessubscip, SCIP_DECL_HEUREXEC((*heurexec)), SCIP_HEURDATA *heurdata)
Definition scip_heur.c:122
SCIP_Longint SCIPheurGetNSolsFound(SCIP_HEUR *heur)
Definition heur.c:1603
SCIP_RETCODE SCIPsetHeurInitsol(SCIP *scip, SCIP_HEUR *heur,)
Definition scip_heur.c:231
SCIP_RETCODE SCIPsetHeurCopy(SCIP *scip, SCIP_HEUR *heur,)
Definition scip_heur.c:167
SCIP_Longint SCIPheurGetNCalls(SCIP_HEUR *heur)
Definition heur.c:1593
void SCIPheurMarkExact(SCIP_HEUR *heur)
Definition heur.c:1457
SCIP_RETCODE SCIPsetHeurExit(SCIP *scip, SCIP_HEUR *heur,)
Definition scip_heur.c:215
SCIP_RETCODE SCIPsetHeurInit(SCIP *scip, SCIP_HEUR *heur,)
Definition scip_heur.c:199
const char * SCIPheurGetName(SCIP_HEUR *heur)
Definition heur.c:1467
void SCIPheurSetData(SCIP_HEUR *heur, SCIP_HEURDATA *heurdata)
Definition heur.c:1378
SCIP_Bool SCIPhasCurrentNodeLP(SCIP *scip)
Definition scip_lp.c:87
SCIP_ROW ** SCIPgetLPRows(SCIP *scip)
Definition scip_lp.c:611
int SCIPgetNLPRows(SCIP *scip)
Definition scip_lp.c:632
SCIP_LPSOLSTAT SCIPgetLPSolstat(SCIP *scip)
Definition scip_lp.c:174
SCIP_Real SCIPgetLPObjval(SCIP *scip)
Definition scip_lp.c:253
#define SCIPallocBufferArray(scip, ptr, num)
Definition scip_mem.h:124
#define SCIPfreeBlockMemory(scip, ptr)
Definition scip_mem.h:108
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition scip_mem.h:137
#define SCIPallocBlockMemory(scip, ptr)
Definition scip_mem.h:89
SCIP_Real SCIProwGetLhs(SCIP_ROW *row)
Definition lp.c:17686
int SCIProwGetNNonz(SCIP_ROW *row)
Definition lp.c:17607
SCIP_COL ** SCIProwGetCols(SCIP_ROW *row)
Definition lp.c:17632
SCIP_Real SCIProwGetRhs(SCIP_ROW *row)
Definition lp.c:17696
int SCIProwGetLPPos(SCIP_ROW *row)
Definition lp.c:17895
const char * SCIProwGetName(SCIP_ROW *row)
Definition lp.c:17745
SCIP_Real SCIPgetRowActivity(SCIP *scip, SCIP_ROW *row)
Definition scip_lp.c:2068
SCIP_Real * SCIProwGetVals(SCIP_ROW *row)
Definition lp.c:17642
SCIP_RETCODE SCIPprintSol(SCIP *scip, SCIP_SOL *sol, FILE *file, SCIP_Bool printzeros)
Definition scip_sol.c:2351
SCIP_RETCODE SCIPtrySol(SCIP *scip, SCIP_SOL *sol, SCIP_Bool printreason, SCIP_Bool completely, SCIP_Bool checkbounds, SCIP_Bool checkintegrality, SCIP_Bool checklprows, SCIP_Bool *stored)
Definition scip_sol.c:4017
SCIP_Real SCIPgetSolOrigObj(SCIP *scip, SCIP_SOL *sol)
Definition scip_sol.c:1890
SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
Definition scip_sol.c:1569
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition scip_sol.c:1763
SCIP_Longint SCIPgetNLPs(SCIP *scip)
SCIP_Real SCIPgetCutoffbound(SCIP *scip)
SCIP_Bool SCIPisFeasGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisFeasEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPfeasCeil(SCIP *scip, SCIP_Real val)
SCIP_Real SCIPfeasFloor(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisFeasLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisFeasLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisFeasIntegral(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisZero(SCIP *scip, SCIP_Real val)
SCIP_COL * SCIPvarGetCol(SCIP_VAR *var)
Definition var.c:23715
SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR *var)
Definition var.c:23418
SCIP_Bool SCIPvarIsImpliedIntegral(SCIP_VAR *var)
Definition var.c:23530
SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
Definition var.c:23932
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition var.c:23485
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition var.c:24174
int SCIPvarGetIndex(SCIP_VAR *var)
Definition var.c:23684
const char * SCIPvarGetName(SCIP_VAR *var)
Definition var.c:23299
SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
Definition var.c:23522
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition var.c:24152
#define HEUR_TIMING
return SCIP_OKAY
#define HEUR_FREQOFS
#define HEUR_DESC
#define HEUR_DISPCHAR
#define HEUR_MAXDEPTH
#define HEUR_PRIORITY
#define HEUR_NAME
#define HEUR_FREQ
#define HEUR_USESSUBSCIP
SCIPcreateSol(scip, &heurdata->sol, heur))
int c
heurdata lastlp
static SCIP_SOL * sol
SCIP_Real * lpcandssol
int nlpcands
SCIP_Longint nlps
int r
SCIP_VAR ** lpcands
assert(minobj< SCIPgetCutoffbound(scip))
SCIPlinkLPSol(scip, sol))
SCIP_VAR * var
SCIP_Real * activities
Direction
@ DIRECTION_DOWN
@ DIRECTION_UP
enum Direction DIRECTION
static void calculateBounds(SCIP *scip, SCIP_VAR *var, SCIP_Real currentvalue, SCIP_Real *upperbound, SCIP_Real *lowerbound, SCIP_Real *upslacks, SCIP_Real *downslacks, int nslacks, SCIP_Bool *numericalerror)
#define DEFAULT_MAXROUNDINGLOOPS
static SCIP_RETCODE updateSlacks(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real shiftvalue, SCIP_Real *upslacks, SCIP_Real *downslacks, SCIP_Real *activities, SCIP_VAR **slackvars, SCIP_Real *slackcoeffs, int nslacks)
static void rowFindSlackVar(SCIP *scip, SCIP_ROW *row, SCIP_VAR **varpointer, SCIP_Real *coeffpointer)
#define DEFAULT_STOPPERCENTAGE
#define DEFAULT_STOPZIROUND
SCIPfreeSol(scip, &heurdata->sol))
#define DEFAULT_MINSTOPNCALLS
#define MINSHIFT
static SCIP_Real getZiValue(SCIP *scip, SCIP_Real val)
ZI Round primal heuristic.
memory allocation routines
#define BMScopyMemoryArray(ptr, source, num)
Definition memory.h:134
#define BMSclearMemoryArray(ptr, num)
Definition memory.h:130
public methods for primal heuristics
public methods for LP management
public methods for message output
#define SCIPstatisticMessage
public methods for problem variables
public methods for branching rule plugins and branching
public methods for primal heuristic plugins and divesets
public methods for the LP relaxation, rows and columns
public methods for memory management
public methods for message handling
public methods for numerical tolerances
public methods for SCIP parameter handling
public methods for solutions
public methods for querying solving statistics
#define SCIP_DECL_HEURINITSOL(x)
Definition type_heur.h:132
#define SCIP_DECL_HEURCOPY(x)
Definition type_heur.h:97
struct SCIP_HeurData SCIP_HEURDATA
Definition type_heur.h:77
struct SCIP_Heur SCIP_HEUR
Definition type_heur.h:76
#define SCIP_DECL_HEURINIT(x)
Definition type_heur.h:113
#define SCIP_DECL_HEUREXIT(x)
Definition type_heur.h:121
#define SCIP_DECL_HEURFREE(x)
Definition type_heur.h:105
#define SCIP_DECL_HEUREXEC(x)
Definition type_heur.h:163
struct SCIP_Row SCIP_ROW
Definition type_lp.h:105
struct SCIP_Col SCIP_COL
Definition type_lp.h:99
@ SCIP_LPSOLSTAT_OPTIMAL
Definition type_lp.h:44
@ SCIP_DIDNOTRUN
Definition type_result.h:42
@ SCIP_DIDNOTFIND
Definition type_result.h:44
@ SCIP_FOUNDSOL
Definition type_result.h:56
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Sol SCIP_SOL
Definition type_sol.h:57
struct SCIP_Var SCIP_VAR
Definition type_var.h:166
@ SCIP_VARTYPE_BINARY
Definition type_var.h:64
@ SCIP_VARSTATUS_COLUMN
Definition type_var.h:53