1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file heur_multistart.c
17 * @ingroup DEFPLUGINS_HEUR
18 * @brief multistart heuristic for convex and nonconvex MINLPs
19 * @author Benjamin Mueller
20 */
21
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23
24 #include "blockmemshell/memory.h"
25 #include "scip/scip_expr.h"
26 #include "scip/pub_expr.h"
27 #include "scip/heur_multistart.h"
28 #include "scip/heur_subnlp.h"
29 #include "scip/pub_heur.h"
30 #include "scip/pub_message.h"
31 #include "scip/pub_misc.h"
32 #include "scip/pub_misc_sort.h"
33 #include "scip/pub_nlp.h"
34 #include "scip/pub_var.h"
35 #include "scip/scip_general.h"
36 #include "scip/scip_heur.h"
37 #include "scip/scip_mem.h"
38 #include "scip/scip_message.h"
39 #include "scip/scip_nlp.h"
40 #include "scip/scip_nlpi.h"
41 #include "scip/scip_numerics.h"
42 #include "scip/scip_param.h"
43 #include "scip/scip_prob.h"
44 #include "scip/scip_randnumgen.h"
45 #include "scip/scip_sol.h"
46 #include "scip/scip_timing.h"
47 #include <string.h>
48
49
50 #define HEUR_NAME "multistart"
51 #define HEUR_DESC "multistart heuristic for convex and nonconvex MINLPs"
52 #define HEUR_DISPCHAR SCIP_HEURDISPCHAR_LNS
53 #define HEUR_PRIORITY -2100000
54 #define HEUR_FREQ 0
55 #define HEUR_FREQOFS 0
56 #define HEUR_MAXDEPTH -1
57 #define HEUR_TIMING SCIP_HEURTIMING_AFTERNODE
58 #define HEUR_USESSUBSCIP TRUE /**< does the heuristic use a secondary SCIP instance? */
59
60 #define DEFAULT_RANDSEED 131 /**< initial random seed */
61 #define DEFAULT_NRNDPOINTS 100 /**< default number of generated random points per call */
62 #define DEFAULT_MAXBOUNDSIZE 2e+4 /**< default maximum variable domain size for unbounded variables */
63 #define DEFAULT_MAXITER 300 /**< default number of iterations to reduce the violation of a point */
64 #define DEFAULT_MINIMPRFAC 0.05 /**< default minimum required improving factor to proceed in improvement of a point */
65 #define DEFAULT_MINIMPRITER 10 /**< default number of iteration when checking the minimum improvement */
66 #define DEFAULT_MAXRELDIST 0.15 /**< default maximum distance between two points in the same cluster */
67 #define DEFAULT_GRADLIMIT 5e+6 /**< default limit for gradient computations for all improvePoint() calls */
68 #define DEFAULT_MAXNCLUSTER 3 /**< default maximum number of considered clusters per heuristic call */
69 #define DEFAULT_ONLYNLPS TRUE /**< should the heuristic run only on continuous problems? */
70
71 #define MINFEAS -1e+4 /**< minimum feasibility for a point; used for filtering and improving
72 * feasibility */
73 #define MINIMPRFAC 0.95 /**< improvement factor used to discard randomly generated points with a
74 * too large objective value */
75 #define GRADCOSTFAC_LINEAR 1.0 /**< gradient cost factor for the number of linear variables */
76 #define GRADCOSTFAC_NONLINEAR 3.0 /**< gradient cost factor for the number of nodes in nonlinear expression */
77
78 /*
79 * Data structures
80 */
81
82 /** primal heuristic data */
83 struct SCIP_HeurData
84 {
85 int nrndpoints; /**< number of random points generated per execution call */
86 SCIP_Real maxboundsize; /**< maximum variable domain size for unbounded variables */
87 SCIP_RANDNUMGEN* randnumgen; /**< random number generator */
88 SCIP_HEUR* heursubnlp; /**< sub-NLP heuristic */
89
90 int maxiter; /**< number of iterations to reduce the maximum violation of a point */
91 SCIP_Real minimprfac; /**< minimum required improving factor to proceed in the improvement of a single point */
92 int minimpriter; /**< number of iteration when checking the minimum improvement */
93
94 SCIP_Real maxreldist; /**< maximum distance between two points in the same cluster */
95 SCIP_Real gradlimit; /**< limit for gradient computations for all improvePoint() calls (0 for no limit) */
96 int maxncluster; /**< maximum number of considered clusters per heuristic call */
97 SCIP_Bool onlynlps; /**< should the heuristic run only on continuous problems? */
98 };
99
100
101 /*
102 * Local methods
103 */
104
105
106 /** returns an unique index of a variable in the range of 0,..,SCIPgetNVars(scip)-1 */
107 #ifndef NDEBUG
108 static
109 int getVarIndex(
110 SCIP_HASHMAP* varindex, /**< maps variables to indicies between 0,..,SCIPgetNVars(scip)-1 */
111 SCIP_VAR* var /**< variable */
112 )
113 {
114 assert(varindex != NULL);
115 assert(var != NULL);
116 assert(SCIPhashmapExists(varindex, (void*)var));
117
118 return SCIPhashmapGetImageInt(varindex, (void*)var);
119 }
120 #else
121 #define getVarIndex(varindex,var) (SCIPhashmapGetImageInt((varindex), (void*)(var)))
122 #endif
123
124 /** samples and stores random points; stores points which have a better objective value than the current incumbent
125 * solution
126 */
127 static
128 SCIP_RETCODE sampleRandomPoints(
129 SCIP* scip, /**< SCIP data structure */
130 SCIP_SOL** rndpoints, /**< array to store all random points */
131 int nmaxrndpoints, /**< maximum number of random points to compute */
132 SCIP_Real maxboundsize, /**< maximum variable domain size for unbounded variables */
133 SCIP_RANDNUMGEN* randnumgen, /**< random number generator */
134 SCIP_Real bestobj, /**< objective value in the transformed space of the current incumbent */
135 int* nstored /**< pointer to store the number of randomly generated points */
136 )
137 {
138 SCIP_VAR** vars;
139 SCIP_SOL* sol;
140 SCIP_Real val;
141 SCIP_Real lb;
142 SCIP_Real ub;
143 int nvars;
144 int niter;
145 int i;
146
147 assert(scip != NULL);
148 assert(rndpoints != NULL);
149 assert(nmaxrndpoints > 0);
150 assert(maxboundsize > 0.0);
151 assert(randnumgen != NULL);
152 assert(nstored != NULL);
153
154 vars = SCIPgetVars(scip);
155 nvars = SCIPgetNVars(scip);
156 *nstored = 0;
157
158 SCIP_CALL( SCIPcreateSol(scip, &sol, NULL) );
159
160 for( niter = 0; niter < 3 * nmaxrndpoints && *nstored < nmaxrndpoints; ++niter )
161 {
162 for( i = 0; i < nvars; ++i )
163 {
164 lb = MIN(SCIPvarGetLbLocal(vars[i]), SCIPvarGetUbLocal(vars[i])); /*lint !e666*/
165 ub = MAX(SCIPvarGetLbLocal(vars[i]), SCIPvarGetUbLocal(vars[i])); /*lint !e666*/
166
167 if( SCIPisFeasEQ(scip, lb, ub) )
168 val = (lb + ub) / 2.0;
169 /* use a smaller domain for unbounded variables */
170 else if( !SCIPisInfinity(scip, -lb) && !SCIPisInfinity(scip, ub) )
171 val = SCIPrandomGetReal(randnumgen, lb, ub);
172 else if( !SCIPisInfinity(scip, -lb) )
173 val = lb + SCIPrandomGetReal(randnumgen, 0.0, maxboundsize);
174 else if( !SCIPisInfinity(scip, ub) )
175 val = ub - SCIPrandomGetReal(randnumgen, 0.0, maxboundsize);
176 else
177 {
178 assert(SCIPisInfinity(scip, -lb) && SCIPisInfinity(scip, ub));
179 val = SCIPrandomGetReal(randnumgen, -0.5*maxboundsize, 0.5*maxboundsize);
180 }
181 assert(SCIPisFeasGE(scip, val, lb) && SCIPisFeasLE(scip, val, ub));
182
183 /* set solution value; round the sampled point for integer variables */
184 if( SCIPvarGetType(vars[i]) < SCIP_VARTYPE_CONTINUOUS )
185 val = SCIPfeasRound(scip, val);
186 SCIP_CALL( SCIPsetSolVal(scip, sol, vars[i], val) );
187 }
188
189 /* add solution if it is good enough */
190 if( SCIPisLE(scip, SCIPgetSolTransObj(scip, sol), bestobj) )
191 {
192 SCIP_CALL( SCIPcreateSolCopy(scip, &rndpoints[*nstored], sol) );
193 ++(*nstored);
194 }
195 }
196 assert(*nstored <= nmaxrndpoints);
197 SCIPdebugMsg(scip, "found %d randomly generated points\n", *nstored);
198
199 SCIP_CALL( SCIPfreeSol(scip, &sol) );
200
201 return SCIP_OKAY;
202 }
203
204 /** computes the minimum feasibility of a given point; a negative value means that there is an infeasibility */
205 static
206 SCIP_RETCODE getMinFeas(
207 SCIP* scip, /**< SCIP data structure */
208 SCIP_NLROW** nlrows, /**< array containing all nlrows */
209 int nnlrows, /**< total number of nlrows */
210 SCIP_SOL* sol, /**< solution */
211 SCIP_Real* minfeas /**< buffer to store the minimum feasibility */
212 )
213 {
214 SCIP_Real tmp;
215 int i;
216
217 assert(scip != NULL);
218 assert(sol != NULL);
219 assert(minfeas != NULL);
220 assert(nlrows != NULL);
221 assert(nnlrows > 0);
222
223 *minfeas = SCIPinfinity(scip);
224
225 for( i = 0; i < nnlrows; ++i )
226 {
227 assert(nlrows[i] != NULL);
228
229 SCIP_CALL( SCIPgetNlRowSolFeasibility(scip, nlrows[i], sol, &tmp) );
230 *minfeas = MIN(*minfeas, tmp);
231 }
232
233 return SCIP_OKAY;
234 }
235
236 /** computes the gradient for a given point and nonlinear row */
237 static
238 SCIP_RETCODE computeGradient(
239 SCIP* scip, /**< SCIP data structure */
240 SCIP_NLROW* nlrow, /**< nonlinear row */
241 SCIP_SOL* sol, /**< solution to compute the gradient for */
242 SCIP_HASHMAP* varindex, /**< maps variables to indicies between 0,..,SCIPgetNVars(scip)-1 uniquely */
243 SCIP_EXPRITER* exprit, /**< expression iterator that can be used */
244 SCIP_Real* grad, /**< buffer to store the gradient; grad[varindex(i)] corresponds to SCIPgetVars(scip)[i] */
245 SCIP_Real* norm /**< buffer to store ||grad||^2 */
246 )
247 {
248 SCIP_EXPR* expr;
249 SCIP_VAR* var;
250 int i;
251
252 assert(scip != NULL);
253 assert(nlrow != NULL);
254 assert(varindex != NULL);
255 assert(sol != NULL);
256 assert(norm != NULL);
257
258 BMSclearMemoryArray(grad, SCIPgetNVars(scip));
259 *norm = 0.0;
260
261 /* linear part */
262 for( i = 0; i < SCIPnlrowGetNLinearVars(nlrow); i++ )
263 {
264 var = SCIPnlrowGetLinearVars(nlrow)[i];
265 assert(var != NULL);
266 assert(getVarIndex(varindex, var) >= 0 && getVarIndex(varindex, var) < SCIPgetNVars(scip));
267
268 grad[getVarIndex(varindex, var)] += SCIPnlrowGetLinearCoefs(nlrow)[i];
269 }
270
271 /* expression part */
272 expr = SCIPnlrowGetExpr(nlrow);
273
274 if( expr != NULL )
275 {
276 assert(exprit != NULL);
277
278 SCIP_CALL( SCIPevalExprGradient(scip, expr, sol, 0L) );
279
280 /* TODO: change this when nlrows store the vars */
281 SCIP_CALL( SCIPexpriterInit(exprit, expr, SCIP_EXPRITER_DFS, FALSE) );
282 for( ; !SCIPexpriterIsEnd(exprit); expr = SCIPexpriterGetNext(exprit) ) /*lint !e441*/ /*lint !e440*/
283 {
284 if( !SCIPisExprVar(scip, expr) )
285 continue;
286
287 var = SCIPgetVarExprVar(expr);
288 assert(var != NULL);
289 assert(getVarIndex(varindex, var) >= 0 && getVarIndex(varindex, var) < SCIPgetNVars(scip));
290
291 grad[getVarIndex(varindex, var)] += SCIPexprGetDerivative(expr);
292 }
293 }
294
295 /* compute ||grad||^2 */
296 for( i = 0; i < SCIPgetNVars(scip); ++i )
297 *norm += SQR(grad[i]);
298
299 return SCIP_OKAY;
300 }
301
302 /** use consensus vectors to improve feasibility for a given starting point */
303 static
304 SCIP_RETCODE improvePoint(
305 SCIP* scip, /**< SCIP data structure */
306 SCIP_NLROW** nlrows, /**< array containing all nlrows */
307 int nnlrows, /**< total number of nlrows */
308 SCIP_HASHMAP* varindex, /**< maps variables to indicies between 0,..,SCIPgetNVars(scip)-1 */
309 SCIP_SOL* point, /**< random generated point */
310 int maxiter, /**< maximum number of iterations */
311 SCIP_Real minimprfac, /**< minimum required improving factor to proceed in the improvement of a single point */
312 int minimpriter, /**< number of iteration when checking the minimum improvement */
313 SCIP_Real* minfeas, /**< pointer to store the minimum feasibility */
314 SCIP_Real* nlrowgradcosts, /**< estimated costs for each gradient computation */
315 SCIP_Real* gradcosts /**< pointer to store the estimated gradient costs */
316 )
317 {
318 SCIP_VAR** vars;
319 SCIP_EXPRITER* exprit;
320 SCIP_Real* grad;
321 SCIP_Real* updatevec;
322 SCIP_Real lastminfeas;
323 int nvars;
324 int r;
325 int i;
326
327 assert(varindex != NULL);
328 assert(point != NULL);
329 assert(maxiter > 0);
330 assert(minfeas != NULL);
331 assert(nlrows != NULL);
332 assert(nnlrows > 0);
333 assert(nlrowgradcosts != NULL);
334 assert(gradcosts != NULL);
335
336 *gradcosts = 0.0;
337
338 SCIP_CALL( getMinFeas(scip, nlrows, nnlrows, point, minfeas) );
339 #ifdef SCIP_DEBUG_IMPROVEPOINT
340 printf("start minfeas = %e\n", *minfeas);
341 #endif
342
343 /* stop since start point is feasible */
344 if( !SCIPisFeasLT(scip, *minfeas, 0.0) )
345 {
346 #ifdef SCIP_DEBUG_IMPROVEPOINT
347 printf("start point is feasible");
348 #endif
349 return SCIP_OKAY;
350 }
351
352 lastminfeas = *minfeas;
353 vars = SCIPgetVars(scip);
354 nvars = SCIPgetNVars(scip);
355
356 SCIP_CALL( SCIPallocBufferArray(scip, &grad, nvars) );
357 SCIP_CALL( SCIPallocBufferArray(scip, &updatevec, nvars) );
358 SCIP_CALL( SCIPcreateExpriter(scip, &exprit) );
359
360 /* main loop */
361 for( r = 0; r < maxiter && SCIPisFeasLT(scip, *minfeas, 0.0); ++r )
362 {
363 SCIP_Real feasibility;
364 SCIP_Real activity;
365 SCIP_Real nlrownorm;
366 SCIP_Real scale;
367 int nviolnlrows;
368
369 BMSclearMemoryArray(updatevec, nvars);
370 nviolnlrows = 0;
371
372 for( i = 0; i < nnlrows; ++i )
373 {
374 int j;
375
376 SCIP_CALL( SCIPgetNlRowSolFeasibility(scip, nlrows[i], point, &feasibility) );
377
378 /* do not consider non-violated constraints */
379 if( SCIPisFeasGE(scip, feasibility, 0.0) )
380 continue;
381
382 /* increase number of violated nlrows */
383 ++nviolnlrows;
384
385 SCIP_CALL( SCIPgetNlRowSolActivity(scip, nlrows[i], point, &activity) );
386 SCIP_CALL( computeGradient(scip, nlrows[i], point, varindex, exprit, grad, &nlrownorm) );
387
388 /* update estimated costs for computing gradients */
389 *gradcosts += nlrowgradcosts[i];
390
391 /* stop if the gradient disappears at the current point */
392 if( SCIPisZero(scip, nlrownorm) )
393 {
394 #ifdef SCIP_DEBUG_IMPROVEPOINT
395 printf("gradient vanished at current point -> stop\n");
396 #endif
397 goto TERMINATE;
398 }
399
400 /* compute -g(x_k) / ||grad(g)(x_k)||^2 for a constraint g(x_k) <= 0 */
401 scale = -feasibility / nlrownorm;
402 if( !SCIPisInfinity(scip, SCIPnlrowGetRhs(nlrows[i])) && SCIPisGT(scip, activity, SCIPnlrowGetRhs(nlrows[i])) )
403 scale *= -1.0;
404
405 /* skip nonliner row if the scaler is too small or too large */
406 if( SCIPisEQ(scip, scale, 0.0) || SCIPisHugeValue(scip, REALABS(scale)) )
407 continue;
408
409 for( j = 0; j < nvars; ++j )
410 updatevec[j] += scale * grad[j];
411 }
412
413 /* if there are no violated rows, stop since start point is feasible */
414 if( nviolnlrows == 0 )
415 {
416 assert(updatevec[i] == 0.0);
417 return SCIP_OKAY;
418 }
419
420 for( i = 0; i < nvars; ++i )
421 {
422 /* adjust point */
423 updatevec[i] = SCIPgetSolVal(scip, point, vars[i]) + updatevec[i] / nviolnlrows;
424 updatevec[i] = MIN(updatevec[i], SCIPvarGetUbLocal(vars[i])); /*lint !e666*/
425 updatevec[i] = MAX(updatevec[i], SCIPvarGetLbLocal(vars[i])); /*lint !e666*/
426
427 SCIP_CALL( SCIPsetSolVal(scip, point, vars[i], updatevec[i]) );
428 }
429
430 /* update feasibility */
431 SCIP_CALL( getMinFeas(scip, nlrows, nnlrows, point, minfeas) );
432
433 /* check stopping criterion */
434 if( r % minimpriter == 0 && r > 0 )
435 {
436 if( *minfeas <= MINFEAS
437 || (*minfeas-lastminfeas) / MAX(REALABS(*minfeas), REALABS(lastminfeas)) < minimprfac ) /*lint !e666*/
438 break;
439 lastminfeas = *minfeas;
440 }
441 }
442
443 TERMINATE:
444 #ifdef SCIP_DEBUG_IMPROVEPOINT
445 printf("niter=%d minfeas=%e\n", r, *minfeas);
446 #endif
447
448 SCIPfreeExpriter(&exprit);
449
450 SCIPfreeBufferArray(scip, &updatevec);
451 SCIPfreeBufferArray(scip, &grad);
452
453 return SCIP_OKAY;
454 }
455
456 /** sorts points w.r.t their feasibilities; points with a feasibility which is too small (w.r.t. the geometric mean of
457 * all feasibilities) will be filtered out
458 */
459 static
460 SCIP_RETCODE filterPoints(
461 SCIP* scip, /**< SCIP data structure */
462 SCIP_SOL** points, /**< array containing improved points */
463 SCIP_Real* feasibilities, /**< array containing feasibility for each point (sorted) */
464 int npoints, /**< total number of points */
465 int* nusefulpoints /**< pointer to store the total number of useful points */
466 )
467 {
468 SCIP_Real minfeas;
469 SCIP_Real meanfeas;
470 int i;
471
472 assert(points != NULL);
473 assert(feasibilities != NULL);
474 assert(npoints > 0);
475 assert(nusefulpoints != NULL);
476
477 /* sort points w.r.t their feasibilities; non-negative feasibility correspond to feasible points for the NLP */
478 SCIPsortDownRealPtr(feasibilities, (void**)points, npoints);
479 minfeas = feasibilities[npoints - 1];
480
481 /* check if all points are feasible */
482 if( SCIPisFeasGE(scip, minfeas, 0.0) )
483 {
484 *nusefulpoints = npoints;
485 return SCIP_OKAY;
486 }
487
488 *nusefulpoints = 0;
489
490 /* compute shifted geometric mean of feasibilities (shift value = 1 - minfeas) */
491 meanfeas = 1.0;
492 for( i = 0; i < npoints; ++i )
493 {
494 assert(feasibilities[i] - minfeas + 1.0 > 0.0);
495 meanfeas *= pow(feasibilities[i] - minfeas + 1.0, 1.0 / npoints);
496 }
497 meanfeas += minfeas - 1.0;
498 SCIPdebugMsg(scip, "meanfeas = %e\n", meanfeas);
499
500 /* keep all points with which have a feasibility not much below the geometric mean of infeasibilities */
501 for( i = 0; i < npoints; ++i )
502 {
503 if( SCIPisFeasLT(scip, feasibilities[i], 0.0)
504 && (feasibilities[i] <= 1.05 * meanfeas || SCIPisLE(scip, feasibilities[i], MINFEAS)) )
505 break;
506
507 ++(*nusefulpoints);
508 }
509
510 return SCIP_OKAY;
511 }
512
513 /** returns the relative distance between two points; considers a smaller bounded domain for unbounded variables */
514 static
515 SCIP_Real getRelDistance(
516 SCIP* scip, /**< SCIP data structure */
517 SCIP_SOL* x, /**< first point */
518 SCIP_SOL* y, /**< second point */
519 SCIP_Real maxboundsize /**< maximum variable domain size for unbounded variables */
520 )
521 {
522 SCIP_VAR** vars;
523 SCIP_Real distance;
524 SCIP_Real solx;
525 SCIP_Real soly;
526 SCIP_Real lb;
527 SCIP_Real ub;
528 int i;
529
530 assert(x != NULL);
531 assert(y != NULL);
532
533 vars = SCIPgetVars(scip);
534 distance = 0.0;
535
(1) Event cond_false: |
Condition "SCIPgetNVars(scip) == 0", taking false branch. |
536 if( SCIPgetNVars(scip) == 0 )
(2) Event if_end: |
End of if statement. |
537 return 0.0;
538
(3) Event cond_true: |
Condition "i < SCIPgetNVars(scip)", taking true branch. |
(16) Event loop_begin: |
Jumped back to beginning of loop. |
(17) Event cond_true: |
Condition "i < SCIPgetNVars(scip)", taking true branch. |
(30) Event loop_begin: |
Jumped back to beginning of loop. |
(31) Event cond_true: |
Condition "i < SCIPgetNVars(scip)", taking true branch. |
(46) Event loop_begin: |
Jumped back to beginning of loop. |
(47) Event zero_return: |
Function call "SCIPgetNVars(scip)" returns 0. [details] |
(48) Event cond_false: |
Condition "i < SCIPgetNVars(scip)", taking false branch. |
Also see events: |
[divide_by_zero] |
539 for( i = 0; i < SCIPgetNVars(scip); ++i )
540 {
541 lb = SCIPvarGetLbLocal(vars[i]);
542 ub = SCIPvarGetUbLocal(vars[i]);
543 solx = SCIPgetSolVal(scip, x, vars[i]);
544 soly = SCIPgetSolVal(scip, y, vars[i]);
545
546 /* adjust lower and upper bounds for unbounded variables*/
(4) Event cond_true: |
Condition "-lb >= scip->set->num_infinity", taking true branch. |
(5) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
(18) Event cond_true: |
Condition "-lb >= scip->set->num_infinity", taking true branch. |
(19) Event cond_true: |
Condition "ub >= scip->set->num_infinity", taking true branch. |
(32) Event cond_true: |
Condition "-lb >= scip->set->num_infinity", taking true branch. |
(33) Event cond_false: |
Condition "ub >= scip->set->num_infinity", taking false branch. |
547 if( SCIPisInfinity(scip, -lb) && SCIPisInfinity(scip, ub) )
548 {
549 lb = -maxboundsize / 2.0;
550 ub = +maxboundsize / 2.0;
(6) Event if_fallthrough: |
Falling through to end of if statement. |
(20) Event if_fallthrough: |
Falling through to end of if statement. |
551 }
(34) Event else_branch: |
Reached else branch. |
(35) Event cond_true: |
Condition "-lb >= scip->set->num_infinity", taking true branch. |
552 else if( SCIPisInfinity(scip, -lb) )
553 {
554 lb = ub - maxboundsize;
(36) Event if_fallthrough: |
Falling through to end of if statement. |
555 }
556 else if( SCIPisInfinity(scip, ub) )
557 {
558 ub = lb + maxboundsize;
(7) Event if_end: |
End of if statement. |
(21) Event if_end: |
End of if statement. |
(37) Event if_end: |
End of if statement. |
559 }
560
561 /* project solution values to the variable domain */
(8) Event cond_true: |
Condition "solx >= lb", taking true branch. |
(9) Event cond_true: |
Condition "((solx >= lb) ? solx : lb) <= ub", taking true branch. |
(10) Event cond_true: |
Condition "solx >= lb", taking true branch. |
(22) Event cond_true: |
Condition "solx >= lb", taking true branch. |
(23) Event cond_true: |
Condition "((solx >= lb) ? solx : lb) <= ub", taking true branch. |
(24) Event cond_true: |
Condition "solx >= lb", taking true branch. |
(38) Event cond_true: |
Condition "solx >= lb", taking true branch. |
(39) Event cond_true: |
Condition "((solx >= lb) ? solx : lb) <= ub", taking true branch. |
(40) Event cond_true: |
Condition "solx >= lb", taking true branch. |
562 solx = MIN(MAX(solx, lb), ub);
(11) Event cond_true: |
Condition "soly >= lb", taking true branch. |
(12) Event cond_true: |
Condition "((soly >= lb) ? soly : lb) <= ub", taking true branch. |
(13) Event cond_true: |
Condition "soly >= lb", taking true branch. |
(25) Event cond_true: |
Condition "soly >= lb", taking true branch. |
(26) Event cond_true: |
Condition "((soly >= lb) ? soly : lb) <= ub", taking true branch. |
(27) Event cond_true: |
Condition "soly >= lb", taking true branch. |
(41) Event cond_true: |
Condition "soly >= lb", taking true branch. |
(42) Event cond_true: |
Condition "((soly >= lb) ? soly : lb) <= ub", taking true branch. |
(43) Event cond_true: |
Condition "soly >= lb", taking true branch. |
563 soly = MIN(MAX(soly, lb), ub);
564
(14) Event cond_true: |
Condition "1. >= ub - lb", taking true branch. |
(28) Event cond_true: |
Condition "1. >= ub - lb", taking true branch. |
(44) Event cond_true: |
Condition "1. >= ub - lb", taking true branch. |
565 distance += REALABS(solx - soly) / MAX(1.0, ub - lb);
(15) Event loop: |
Jumping back to the beginning of the loop. |
(29) Event loop: |
Jumping back to the beginning of the loop. |
(45) Event loop: |
Jumping back to the beginning of the loop. |
(49) Event loop_end: |
Reached end of loop. |
566 }
567
(50) Event divide_by_zero: |
In expression "distance / SCIPgetNVars(scip)", division by expression "SCIPgetNVars(scip)" which may be zero has undefined behavior. |
Also see events: |
[zero_return] |
568 return distance / SCIPgetNVars(scip);
569 }
570
571 /** cluster useful points with a greedy algorithm */
572 static
573 SCIP_RETCODE clusterPointsGreedy(
574 SCIP* scip, /**< SCIP data structure */
575 SCIP_SOL** points, /**< array containing improved points */
576 int npoints, /**< total number of points */
577 int* clusteridx, /**< array to store for each point the index of the cluster */
578 int* ncluster, /**< pointer to store the total number of cluster */
579 SCIP_Real maxboundsize, /**< maximum variable domain size for unbounded variables */
580 SCIP_Real maxreldist, /**< maximum relative distance between any two points of the same cluster */
581 int maxncluster /**< maximum number of clusters to compute */
582 )
583 {
584 int i;
585
586 assert(points != NULL);
587 assert(npoints > 0);
588 assert(clusteridx != NULL);
589 assert(ncluster != NULL);
590 assert(maxreldist >= 0.0);
591 assert(maxncluster >= 0);
592
593 /* initialize cluster indices */
594 for( i = 0; i < npoints; ++i )
595 clusteridx[i] = INT_MAX;
596
597 *ncluster = 0;
598
599 for( i = 0; i < npoints && (*ncluster < maxncluster); ++i )
600 {
601 int j;
602
603 /* point is already assigned to a cluster */
604 if( clusteridx[i] != INT_MAX )
605 continue;
606
607 /* create a new cluster for i */
608 clusteridx[i] = *ncluster;
609
610 for( j = i + 1; j < npoints; ++j )
611 {
612 if( clusteridx[j] == INT_MAX && getRelDistance(scip, points[i], points[j], maxboundsize) <= maxreldist )
613 clusteridx[j] = *ncluster;
614 }
615
616 ++(*ncluster);
617 }
618
619 #ifndef NDEBUG
620 for( i = 0; i < npoints; ++i )
621 {
622 assert(clusteridx[i] >= 0);
623 assert(clusteridx[i] < *ncluster || clusteridx[i] == INT_MAX);
624 }
625 #endif
626
627 return SCIP_OKAY;
628 }
629
630 /** calls the sub-NLP heuristic for a given cluster */
631 static
632 SCIP_RETCODE solveNLP(
633 SCIP* scip, /**< SCIP data structure */
634 SCIP_HEUR* heur, /**< multi-start heuristic */
635 SCIP_HEUR* nlpheur, /**< pointer to NLP local search heuristics */
636 SCIP_SOL** points, /**< array containing improved points */
637 int npoints, /**< total number of points */
638 SCIP_Bool* success /**< pointer to store if we could find a solution */
639 )
640 {
641 SCIP_VAR** vars;
642 SCIP_SOL* refpoint;
643 SCIP_RESULT nlpresult;
644 SCIP_Real val;
645 int nbinvars;
646 int nintvars;
647 int nvars;
648 int i;
649
650 assert(points != NULL);
651 assert(npoints > 0);
652
653 SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, &nintvars, NULL, NULL) );
654 *success = FALSE;
655
656 SCIP_CALL( SCIPcreateSol(scip, &refpoint, heur) );
657
658 /* compute reference point */
659 for( i = 0; i < nvars; ++i )
660 {
661 int p;
662
663 val = 0.0;
664
665 for( p = 0; p < npoints; ++p )
666 {
667 assert(points[p] != NULL);
668 val += SCIPgetSolVal(scip, points[p], vars[i]);
669 }
670
671 SCIP_CALL( SCIPsetSolVal(scip, refpoint, vars[i], val / npoints) );
672 }
673
674 /* round point for sub-NLP heuristic */
675 SCIP_CALL( SCIProundSol(scip, refpoint, success) );
676 SCIPdebugMsg(scip, "rounding of refpoint successfully? %u\n", *success);
677
678 /* round variables manually if the locks did not allow us to round them */
679 if( !(*success) )
680 {
681 for( i = 0; i < nbinvars + nintvars; ++i )
682 {
683 val = SCIPgetSolVal(scip, refpoint, vars[i]);
684
685 if( !SCIPisFeasIntegral(scip, val) )
686 {
687 assert(SCIPisFeasIntegral(scip, SCIPvarGetLbLocal(vars[i])));
688 assert(SCIPisFeasIntegral(scip, SCIPvarGetUbLocal(vars[i])));
689
690 /* round and adjust value */
691 val = SCIPround(scip, val);
692 val = MIN(val, SCIPvarGetUbLocal(vars[i])); /*lint !e666*/
693 val = MAX(val, SCIPvarGetLbLocal(vars[i])); /*lint !e666*/
694 assert(SCIPisFeasIntegral(scip, val));
695
696 SCIP_CALL( SCIPsetSolVal(scip, refpoint, vars[i], val) );
697 }
698 }
699 }
700
701 /* call sub-NLP heuristic */
702 SCIP_CALL( SCIPapplyHeurSubNlp(scip, nlpheur, &nlpresult, refpoint, NULL) );
703 SCIP_CALL( SCIPfreeSol(scip, &refpoint) );
704
705 /* let sub-NLP heuristic decide whether the solution is feasible or not */
706 *success = nlpresult == SCIP_FOUNDSOL;
707
708 return SCIP_OKAY;
709 }
710
711 /** recursive helper function to count the number of nodes in a sub-expr */
712 static
713 int getExprSize(
714 SCIP_EXPR* expr /**< expression */
715 )
716 {
717 int sum;
718 int i;
719
720 assert(expr != NULL);
721
722 sum = 0;
723 for( i = 0; i < SCIPexprGetNChildren(expr); ++i )
724 {
725 SCIP_EXPR* child = SCIPexprGetChildren(expr)[i];
726 sum += getExprSize(child);
727 }
728 return 1 + sum;
729 }
730
731 /** main function of the multi-start heuristic (see @ref heur_multistart.h for more details); it consists of the
732 * following four steps:
733 *
734 * 1. sampling points in the current domain; for unbounded variables we use a bounded box
735 *
736 * 2. reduce infeasibility by using a gradient descent method
737 *
738 * 3. cluster points; filter points with a too large infeasibility
739 *
740 * 4. compute start point for each cluster and use it in the sub-NLP heuristic (@ref heur_subnlp.h)
741 */
742 static
743 SCIP_RETCODE applyHeur(
744 SCIP* scip, /**< SCIP data structure */
745 SCIP_HEUR* heur, /**< heuristic */
746 SCIP_HEURDATA* heurdata, /**< heuristic data */
747 SCIP_RESULT* result /**< pointer to store the result */
748 )
749 {
750 SCIP_NLROW** nlrows;
751 SCIP_SOL** points;
752 SCIP_HASHMAP* varindex;
753 SCIP_Real* feasibilities;
754 SCIP_Real* nlrowgradcosts;
755 int* clusteridx;
756 SCIP_Real gradlimit;
757 SCIP_Real bestobj;
758 int nusefulpoints;
759 int nrndpoints;
760 int ncluster;
761 int nnlrows;
762 int npoints;
763 int start;
764 int i;
765
766 assert(scip != NULL);
767 assert(heur != NULL);
768 assert(result != NULL);
769 assert(heurdata != NULL);
770
771 SCIPdebugMsg(scip, "call applyHeur()\n");
772
773 nlrows = SCIPgetNLPNlRows(scip);
774 nnlrows = SCIPgetNNLPNlRows(scip);
775 bestobj = SCIPgetNSols(scip) > 0 ? MINIMPRFAC * SCIPgetSolTransObj(scip, SCIPgetBestSol(scip)) : SCIPinfinity(scip);
776
777 SCIP_CALL( SCIPallocBufferArray(scip, &points, heurdata->nrndpoints) );
778 SCIP_CALL( SCIPallocBufferArray(scip, &nlrowgradcosts, nnlrows) );
779 SCIP_CALL( SCIPallocBufferArray(scip, &feasibilities, heurdata->nrndpoints) );
780 SCIP_CALL( SCIPallocBufferArray(scip, &clusteridx, heurdata->nrndpoints) );
781 SCIP_CALL( SCIPhashmapCreate(&varindex, SCIPblkmem(scip), SCIPgetNVars(scip)) );
782
783 /* create an unique mapping of all variables to 0,..,SCIPgetNVars(scip)-1 */
784 for( i = 0; i < SCIPgetNVars(scip); ++i )
785 {
786 SCIP_CALL( SCIPhashmapInsertInt(varindex, (void*)SCIPgetVars(scip)[i], i) );
787 }
788
789 /* compute estimated costs of computing a gradient for each nlrow */
790 for( i = 0; i < nnlrows; ++i )
791 {
792 nlrowgradcosts[i] = GRADCOSTFAC_LINEAR * SCIPnlrowGetNLinearVars(nlrows[i]);
793 if( SCIPnlrowGetExpr(nlrows[i]) != NULL )
794 nlrowgradcosts[i] += GRADCOSTFAC_NONLINEAR * getExprSize(SCIPnlrowGetExpr(nlrows[i]));
795 }
796
797 /*
798 * 1. sampling points in the current domain; for unbounded variables we use a bounded box
799 */
800 SCIP_CALL( sampleRandomPoints(scip, points, heurdata->nrndpoints, heurdata->maxboundsize, heurdata->randnumgen,
801 bestobj, &nrndpoints) );
802 assert(nrndpoints >= 0);
803
804 if( nrndpoints == 0 )
805 goto TERMINATE;
806
807 /*
808 * 2. improve points via consensus vectors
809 */
810 gradlimit = heurdata->gradlimit == 0.0 ? SCIPinfinity(scip) : heurdata->gradlimit;
811 for( npoints = 0; npoints < nrndpoints && gradlimit >= 0 && !SCIPisStopped(scip); ++npoints )
812 {
813 SCIP_Real gradcosts;
814
815 SCIP_CALL( improvePoint(scip, nlrows, nnlrows, varindex, points[npoints],
816 heurdata->maxiter, heurdata->minimprfac, heurdata->minimpriter, &feasibilities[npoints], nlrowgradcosts,
817 &gradcosts) );
818
819 gradlimit -= gradcosts;
820 SCIPdebugMsg(scip, "improve point %d / %d gradlimit = %g\n", npoints, nrndpoints, gradlimit);
821 }
822 assert(npoints >= 0 && npoints <= nrndpoints);
823
824 if( npoints == 0 )
825 goto TERMINATE;
826
827 /*
828 * 3. filter and cluster points
829 */
830 SCIP_CALL( filterPoints(scip, points, feasibilities, npoints, &nusefulpoints) );
831 assert(nusefulpoints >= 0);
832 SCIPdebugMsg(scip, "nusefulpoints = %d\n", nusefulpoints);
833
834 if( nusefulpoints == 0 )
835 goto TERMINATE;
836
837 SCIP_CALL( clusterPointsGreedy(scip, points, nusefulpoints, clusteridx, &ncluster, heurdata->maxboundsize,
838 heurdata->maxreldist, heurdata->maxncluster) );
839 assert(ncluster >= 0 && ncluster <= heurdata->maxncluster);
840 SCIPdebugMsg(scip, "ncluster = %d\n", ncluster);
841
842 SCIPsortIntPtr(clusteridx, (void**)points, nusefulpoints);
843
844 /*
845 * 4. compute start point for each cluster and use it in the sub-NLP heuristic (@ref heur_subnlp.h)
846 */
847 start = 0;
848 while( start < nusefulpoints && clusteridx[start] != INT_MAX && !SCIPisStopped(scip) )
849 {
850 SCIP_Bool success;
851 int end;
852
853 end = start;
854 while( end < nusefulpoints && clusteridx[start] == clusteridx[end] )
855 ++end;
856
857 assert(end - start > 0);
858
859 /* call sub-NLP heuristic */
860 SCIP_CALL( solveNLP(scip, heur, heurdata->heursubnlp, &points[start], end - start, &success) );
861 SCIPdebugMsg(scip, "solveNLP result = %u\n", success);
862
863 if( success )
864 *result = SCIP_FOUNDSOL;
865
866 /* go to the next cluster */
867 start = end;
868 }
869
870 TERMINATE:
871 /* free memory */
872 for( i = nrndpoints - 1; i >= 0 ; --i )
873 {
874 assert(points[i] != NULL);
875 SCIP_CALL( SCIPfreeSol(scip, &points[i]) );
876 }
877
878 SCIPhashmapFree(&varindex);
879 SCIPfreeBufferArray(scip, &clusteridx);
880 SCIPfreeBufferArray(scip, &feasibilities);
881 SCIPfreeBufferArray(scip, &nlrowgradcosts);
882 SCIPfreeBufferArray(scip, &points);
883
884 return SCIP_OKAY;
885 }
886
887 /*
888 * Callback methods of primal heuristic
889 */
890
891 /** copy method for primal heuristic plugins (called when SCIP copies plugins) */
892 static
893 SCIP_DECL_HEURCOPY(heurCopyMultistart)
894 { /*lint --e{715}*/
895 assert(strcmp(SCIPheurGetName(heur), HEUR_NAME) == 0);
896
897 /* call inclusion method of primal heuristic */
898 SCIP_CALL( SCIPincludeHeurMultistart(scip) );
899
900 return SCIP_OKAY;
901 }
902
903 /** destructor of primal heuristic to free user data (called when SCIP is exiting) */
904 static
905 SCIP_DECL_HEURFREE(heurFreeMultistart)
906 { /*lint --e{715}*/
907 SCIP_HEURDATA* heurdata;
908
909 /* free heuristic data */
910 heurdata = SCIPheurGetData(heur);
911
912 SCIPfreeBlockMemory(scip, &heurdata);
913 SCIPheurSetData(heur, NULL);
914
915 return SCIP_OKAY;
916 }
917
918 /** initialization method of primal heuristic (called after problem was transformed) */
919 static
920 SCIP_DECL_HEURINIT(heurInitMultistart)
921 { /*lint --e{715}*/
922 SCIP_HEURDATA* heurdata;
923
924 assert( heur != NULL );
925
926 heurdata = SCIPheurGetData(heur);
927 assert(heurdata != NULL);
928
929 SCIP_CALL( SCIPcreateRandom(scip, &heurdata->randnumgen,
930 DEFAULT_RANDSEED, TRUE) );
931
932 /* try to find sub-NLP heuristic */
933 heurdata->heursubnlp = SCIPfindHeur(scip, "subnlp");
934
935 return SCIP_OKAY;
936 }
937
938 /** deinitialization method of primal heuristic (called before transformed problem is freed) */
939 static
940 SCIP_DECL_HEUREXIT(heurExitMultistart)
941 { /*lint --e{715}*/
942 SCIP_HEURDATA* heurdata;
943
944 assert( heur != NULL );
945
946 heurdata = SCIPheurGetData(heur);
947 assert(heurdata != NULL);
948 assert(heurdata->randnumgen != NULL);
949
950 SCIPfreeRandom(scip, &heurdata->randnumgen);
951
952 return SCIP_OKAY;
953 }
954
955 /** execution method of primal heuristic */
956 static
957 SCIP_DECL_HEUREXEC(heurExecMultistart)
958 { /*lint --e{715}*/
959 SCIP_HEURDATA* heurdata;
960
961 assert( heur != NULL );
962
963 heurdata = SCIPheurGetData(heur);
964 assert(heurdata != NULL);
965
966 *result = SCIP_DIDNOTRUN;
967
968 /* check cases for which the heuristic is not applicable */
969 if( !SCIPisNLPConstructed(scip) || heurdata->heursubnlp == NULL || SCIPgetNNlpis(scip) <= 0 )
970 return SCIP_OKAY;
971
972 /* check whether the heuristic should be applied for a problem containing integer variables */
973 if( heurdata->onlynlps && (SCIPgetNBinVars(scip) > 0 || SCIPgetNIntVars(scip) > 0) )
974 return SCIP_OKAY;
975
976 *result = SCIP_DIDNOTFIND;
977
978 SCIP_CALL( applyHeur(scip, heur, heurdata, result) );
979
980 return SCIP_OKAY;
981 }
982
983 /*
984 * primal heuristic specific interface methods
985 */
986
987 /** creates the multistart primal heuristic and includes it in SCIP */
988 SCIP_RETCODE SCIPincludeHeurMultistart(
989 SCIP* scip /**< SCIP data structure */
990 )
991 {
992 SCIP_HEURDATA* heurdata;
993 SCIP_HEUR* heur;
994
995 /* create multistart primal heuristic data */
996 SCIP_CALL( SCIPallocBlockMemory(scip, &heurdata) );
997 BMSclearMemory(heurdata);
998
999 /* include primal heuristic */
1000 SCIP_CALL( SCIPincludeHeurBasic(scip, &heur,
1001 HEUR_NAME, HEUR_DESC, HEUR_DISPCHAR, HEUR_PRIORITY, HEUR_FREQ, HEUR_FREQOFS,
1002 HEUR_MAXDEPTH, HEUR_TIMING, HEUR_USESSUBSCIP, heurExecMultistart, heurdata) );
1003
1004 assert(heur != NULL);
1005
1006 /* set non fundamental callbacks via setter functions */
1007 SCIP_CALL( SCIPsetHeurCopy(scip, heur, heurCopyMultistart) );
1008 SCIP_CALL( SCIPsetHeurFree(scip, heur, heurFreeMultistart) );
1009 SCIP_CALL( SCIPsetHeurInit(scip, heur, heurInitMultistart) );
1010 SCIP_CALL( SCIPsetHeurExit(scip, heur, heurExitMultistart) );
1011
1012 /* add multistart primal heuristic parameters */
1013 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/" HEUR_NAME "/nrndpoints",
1014 "number of random points generated per execution call",
1015 &heurdata->nrndpoints, FALSE, DEFAULT_NRNDPOINTS, 0, INT_MAX, NULL, NULL) );
1016
1017 SCIP_CALL( SCIPaddRealParam(scip, "heuristics/" HEUR_NAME "/maxboundsize",
1018 "maximum variable domain size for unbounded variables",
1019 &heurdata->maxboundsize, FALSE, DEFAULT_MAXBOUNDSIZE, 0.0, SCIPinfinity(scip), NULL, NULL) );
1020
1021 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/" HEUR_NAME "/maxiter",
1022 "number of iterations to reduce the maximum violation of a point",
1023 &heurdata->maxiter, FALSE, DEFAULT_MAXITER, 0, INT_MAX, NULL, NULL) );
1024
1025 SCIP_CALL( SCIPaddRealParam(scip, "heuristics/" HEUR_NAME "/minimprfac",
1026 "minimum required improving factor to proceed in improvement of a single point",
1027 &heurdata->minimprfac, FALSE, DEFAULT_MINIMPRFAC, -SCIPinfinity(scip), SCIPinfinity(scip), NULL, NULL) );
1028
1029 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/" HEUR_NAME "/minimpriter",
1030 "number of iteration when checking the minimum improvement",
1031 &heurdata->minimpriter, FALSE, DEFAULT_MINIMPRITER, 1, INT_MAX, NULL, NULL) );
1032
1033 SCIP_CALL( SCIPaddRealParam(scip, "heuristics/" HEUR_NAME "/maxreldist",
1034 "maximum distance between two points in the same cluster",
1035 &heurdata->maxreldist, FALSE, DEFAULT_MAXRELDIST, 0.0, SCIPinfinity(scip), NULL, NULL) );
1036
1037 SCIP_CALL( SCIPaddRealParam(scip, "heuristics/" HEUR_NAME "/gradlimit",
1038 "limit for gradient computations for all improvePoint() calls (0 for no limit)",
1039 &heurdata->gradlimit, FALSE, DEFAULT_GRADLIMIT, 0.0, SCIPinfinity(scip), NULL, NULL) );
1040
1041 SCIP_CALL( SCIPaddIntParam(scip, "heuristics/" HEUR_NAME "/maxncluster",
1042 "maximum number of considered clusters per heuristic call",
1043 &heurdata->maxncluster, FALSE, DEFAULT_MAXNCLUSTER, 0, INT_MAX, NULL, NULL) );
1044
1045 SCIP_CALL( SCIPaddBoolParam(scip, "heuristics/" HEUR_NAME "/onlynlps",
1046 "should the heuristic run only on continuous problems?",
1047 &heurdata->onlynlps, FALSE, DEFAULT_ONLYNLPS, NULL, NULL) );
1048
1049 return SCIP_OKAY;
1050 }
1051