CouenneFPphaseMILP.cpp
Go to the documentation of this file.
1 /* $Id$
2  *
3  * Name: CouenneFPphaseMILP.cpp
4  * Authors: Pietro Belotti
5  * Purpose: MILP part of the loop
6  * Created: February 1, 2014
7  *
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CbcModel.hpp"
12 #include "CoinTime.hpp"
13 #include "CoinHelperFunctions.hpp"
14 
15 #include "CouenneExprAux.hpp"
16 #include "CouenneFeasPump.hpp"
17 #include "CouenneProblem.hpp"
18 #include "CouenneProblemElem.hpp"
19 #include "CouenneCutGenerator.hpp"
20 #include "CouenneTNLP.hpp"
21 #include "CouenneFPpool.hpp"
22 #include "CouenneRecordBestSol.hpp"
23 
24 #ifdef COIN_HAS_SCIP
25 /* general SCIP includes */
26 #include "scip/scip.h"
27 #include "scip/cons_linear.h"
28 #include "scip/scipdefplugins.h"
29 #endif
30 
31 using namespace Ipopt;
32 using namespace Couenne;
33 
34 void printDist (CouenneProblem *p, const double *iSol, double *nSol);
35 void printCmpSol (CouenneProblem *p, const double *iSol, double *nSol, int direction);
36 
37 // Solve
38 int CouenneFeasPump::milpPhase (double *nSol, double *iSol, int niter, int *nsuciter) {
39 
40  const int depth = (model_ && (model_ -> currentNode ())) ? model_ -> currentNode () -> depth () : 0;
41 
42  double time0 = CoinCpuTime();
43 
44  // INTEGER PART /////////////////////////////////////////////////////////
45 
46  // Solve IP using nSol as the initial point to minimize weighted
47  // l-1 distance from. If nSol==NULL, the MILP is created using the
48  // original milp's LP solution.
49 
50  double z = solveMILP (nSol, iSol, niter, nsuciter);
51 
52  // if no MILP solution was found, bail out
53 
54  bool try_again = false;
55 
56  if (!iSol || z >= COIN_DBL_MAX/2) {
57 
58  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: could not find IP solution\n");
59 
60  // find non-tabu solution in the solution pool
61  while (!pool_ -> Set (). empty ()) {
62 
63  // EXTRACT the closest (to nSol) IP solution from the pool
64  pool_ -> findClosestAndReplace (iSol, iSol, problem_ -> nVars ());
65 
66  CouenneFPsolution newSol (problem_, iSol);
67 
68  // we found a solution that is not in the tabu list
69  if (tabuPool_ . find (newSol) == tabuPool_ . end ()) {
70 
71  try_again = true;
72  break;
73  }
74  }
75 
76  if (!try_again) { // try moving around current solution
77 
78  // SCIP could not find a MILP solution and we're somewhat
79  // locked. Round current solution and feed it to the NLP. This
80  // is better than just bailing out.
81 
82  int n = problem_ -> nVars ();
83 
84  if (!iSol)
85  iSol = new double [n];
86 
87  for (int i=0; i<n; i++)
88  iSol [i] = (problem_ -> Var (i) -> isInteger ()) ?
89  COUENNE_round (nSol [i]) :
90  nSol [i];
91  }
92 
93  // if (!try_again) { // nothing to do, bail out
94  // problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: could not find from pool either, bailing out\n");
95  // break;
96  // }
97  }
98 
99  bool isChecked = false;
100 
101  // If a solution was found, but is in the tabu list, two choices:
102  //
103  // 1) the pool is empty: do a round of cuts and repeat;
104  //
105  // 2) the pool is nonempty: extract the best solution from the
106  // pool and use it instead
107 
108  CouenneFPsolution checkedSol (problem_, iSol, false); // false is for not allocating space for this
109 
110  if (tabuPool_. find (checkedSol) == tabuPool_ . end ())
111 
112  tabuPool_. insert (CouenneFPsolution (problem_, iSol)); // only insertion to tabu pool: we check its feasibility now
113 
114  else {
115 
116  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: found solution is tabu\n");
117 
118  // Current solution was visited before. Replace it with another
119  // MILP solution from the pool, if any.
120 
121  if (tabuMgt_ == FP_TABU_NONE) break;
122 
123  else if ((tabuMgt_ == FP_TABU_POOL) && !(pool_ -> Set (). empty ())) {
124 
125  // try to find non-tabu solution in the solution pool
126  do {
127 
128  // retrieve the top solution from the pool
129  pool_ -> findClosestAndReplace (iSol, nSol, problem_ -> nVars ());
130 
131  CouenneFPsolution newSol (problem_, iSol);
132 
133  // we found a solution that is not in the tabu list
134  if (tabuPool_. find (newSol) == tabuPool_ . end ())
135  break;
136 
137  // the pool is empty -> bail out
138  if (pool_ -> Set ().empty ())
139  {
140  delete[] iSol;
141  iSol = NULL;
142  }
143 
144  } while( !pool_ -> Set ().empty() );
145 
146  } else if (((tabuMgt_ == FP_TABU_CUT) ||
147  ((pool_ -> Set (). empty ()) && iSol))) {
148 
149  OsiCuts cs;
150 
151  problem_ -> domain () -> push (problem_ -> nVars (), iSol, milp_ -> getColLower (), milp_ -> getColUpper (), false); // don't copy vectors
152  couenneCG_ -> genRowCuts (*milp_, cs, 0, NULL); // remaining three arguments NULL by default
153  problem_ -> domain () -> pop ();
154 
155  if (cs.sizeRowCuts ()) {
156 
157  milp_ -> applyCuts (cs);
158 
159  if (nSep++ < nSepRounds_)
160  continue;
161 
162  } else break; // nothing left to do, just bail out
163 
164  } else if ((tabuMgt_ == FP_TABU_PERTURB) && iSol) {
165 
166  // perturb solution
167 
168  const CouNumber
169  *lb = milp_ -> getColLower (),
170  *ub = milp_ -> getColUpper ();
171 
172  double
173  downMoves = 0.,
174  upMoves = 0.;
175 
176  int startIndex = (int) floor (CoinDrand48 () * problem_ -> nOrigVars ());
177 
178  for (int ii = problem_ -> nOrigVars (); ii--; lb++, ub++) {
179 
180  if (problem_ -> Var (ii) -> Multiplicity () <= 0)
181  continue;
182 
183  // make perturbation start from pseudo-random points
184 
185  int i = (startIndex + ii) % problem_ -> nOrigVars ();
186 
187  if (problem_ -> Var (i) -> isInteger ()) {
188 
189  double
190  rnd = CoinDrand48 (),
191  down = 0.,
192  up = 1.;
193 
194  // if there is room on the left (resp. right) of the
195  // solution, consider moving down (resp. up). Make moves
196  // less likely as we don't want to perturb too many
197  // variables
198 
199 #define RND_DECR_EXPONENT .5
200 
201  if (iSol [i] >= lb [i] + 1.) down = 1. / pow (1. + (downMoves += 1.), RND_DECR_EXPONENT);
202  if (iSol [i] <= ub [i] - 1.) up = 1. - 1. / pow (1. + (upMoves += 1.), RND_DECR_EXPONENT);
203 
204  if (rnd < down) iSol [i] -= 1.;
205  else if (rnd > up) iSol [i] += 1.;
206  }
207  }
208  }
209  }
210 
211  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: checking IP solution for feasibility\n");
212 
213  isChecked = problem_ -> checkNLP0 (iSol, z, true,
214  false, // don't care about obj
215  true, // stop at first violation
216  true); // checkAll
217 
218  // Possible improvement: if IP-infeasible or if z does not improve
219  // the best solution found so far, then try the following:
220  //
221  // 1) Fix integer variables to IP solution's corresponding components
222  // 2) Solve restriction with original obj
223  // 3) While not found (MI)NLP solution or
224  // solution MINLP infeasible or
225  // z not better
226  // 4) Get solution x* from pool
227  // 5) Solve with original objective
228  // 6) Done
229  // 7) If found solution, set it, otherwise keep previously found (IP-feasible) one
230 
231  if ((!isChecked || // not MINLP feasible
232  z > problem_ -> getCutOff ())) { // not improving
233 
234  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: infeasible/non-improving (feas==%d, z=%g, cutoff=%g), looping on pool\n", isChecked, z, problem_->getCutOff ());
235 
236  bool try_again;
237 
238  do {
239 
240  try_again = false;
241 
242  if (CoinCpuTime () > problem_ -> getMaxCpuTime ())
243  break;
244 
245  // Check if fixing integers to those in iSol yields an
246  // infeasible problem. If so, don't optimize
247  if (fixIntVariables (iSol)) {
248 
249  nlp_ -> setInitSol (iSol);
250 
251  if (problem_ -> Jnlst () -> ProduceOutput (J_ALL, J_NLPHEURISTIC)) {
252  printf ("----------------------- Solving NLP:\n");
253  problem_ -> print ();
254  printf ("-----------------------\n");
255  }
256 
257  status = app_ -> OptimizeTNLP (nlp_);
258 
259  if (nlp_ -> getSolution ()) { // check if non-NULL
260  if (nSol) CoinCopyN (nlp_ -> getSolution (), problem_ -> nVars (), nSol);
261  else nSol = CoinCopyOfArray (nlp_ -> getSolution (), problem_ -> nVars ());
262  }
263 
264  if (nlp_ -> getSolution () && (problem_ -> Jnlst () -> ProduceOutput (J_ALL, J_NLPHEURISTIC))) { // check if non-NULL
265  printf ("######################## NLP solution (loop through pool):\n");
266  for (int i=0; i< problem_ -> nVars ();) {
267  printf ("%+e ", nlp_ -> getSolution () [i]);
268  if (!(++i % 15)) printf ("\n");
269  }
270  }
271 
272  z = nlp_ -> getSolValue ();
273 
274  if (z < problem_ -> getCutOff ()) // don't waste time if not improving
275 
276  isChecked = problem_ -> checkNLP0 (nSol, z, true,
277  false, // don't care about obj
278  true, // stop at first violation
279  true); // checkAll
280 
281  if (isChecked && (z < problem_ -> getCutOff ()))
282  break;
283  }
284 
285  // find non-tabu solution in the solution pool
286  while (!pool_ -> Set (). empty ()) {
287 
288  // EXTRACT the closest (to nSol) IP solution from the pool
289  pool_ -> findClosestAndReplace (iSol, nSol, problem_ -> nVars ());
290 
291  CouenneFPsolution newSol (problem_, iSol);
292 
293  // we found a solution that is not in the tabu list
294  if (tabuPool_ . find (newSol) == tabuPool_ . end ()) {
295  try_again = true;
296  break;
297  }
298  }
299 
300  } while (try_again);
301  }
302 
303  // Whatever the previous block yielded, we are now going to check
304  // if we have a new solution, and if so we save it.
305 
306  if (isChecked) {
307 
308  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: IP solution is MINLP feasible\n");
309 
310  // solution is MINLP feasible! Save it.
311 
312  retval = 1;
313  objVal = z;
314 
315  // Found a MINLP-feasible solution, but to keep diversity do NOT
316  // use the best available. Just use this.
317  //
318  // #ifdef FM_CHECKNLP2
319  // # ifdef FM_TRACE_OPTSOL
320  // problem_->getRecordBestSol()->update();
321  // best = problem_->getRecordBestSol()->getSol();
322  // objVal = problem_->getRecordBestSol()->getVal();
323  // # else /* not FM_TRACE_OPTSOL */
324  // best = problem_->getRecordBestSol()->getModSol(problem_->nVars());
325  // objVal = z;
326  // # endif /* not FM_TRACE_OPTSOL */
327  // #else /* not FM_CHECKNLP2 */
328  // # ifdef FM_TRACE_OPTSOL
329  // problem_->getRecordBestSol()->update(iSol, problem_->nVars(),
330  // z, problem_->getFeasTol());
331  // best = problem_->getRecordBestSol()->getSol();
332  // objVal = problem_->getRecordBestSol()->getVal();
333  // # else /* not FM_TRACE_OPTSOL */
334 
335  best = iSol;
336 
337  // objVal = z;
338  // # ifdef FM_TRACE_OPTSOL
339  // problem_->getRecordBestSol()->update();
340  // best = problem_->getRecordBestSol()->getSol();
341  // objVal = problem_->getRecordBestSol()->getVal();
342  // # endif /* not FM_TRACE_OPTSOL */
343  // #endif /* not FM_CHECKNLP2 */
344 
345  if (z < problem_ -> getCutOff ()) {
346 
347  problem_ -> setCutOff (objVal);
348 
349  t_chg_bounds *chg_bds = NULL;
350 
351  if (objInd >= 0) {
352  chg_bds = new t_chg_bounds [problem_ -> nVars ()];
353  chg_bds [objInd].setUpper (t_chg_bounds::CHANGED);
354  }
355 
356  // If, with new cutoff, bound tightening clears the whole
357  // feasible set, stop
358  bool is_still_feas = problem_ -> btCore (chg_bds);
359 
360  if (chg_bds)
361  delete [] chg_bds;
362 
363  // don't tighten MILP if BT says it's infeasible
364 
365  if (!is_still_feas)
366  break;
367 
368  // Update lb/ub on milp and nlp here
369  const CouNumber
370  *plb = problem_ -> Lb (),
371  *pub = problem_ -> Ub (),
372  *mlb = milp_ -> getColLower (),
373  *mub = milp_ -> getColUpper ();
374 
375  for (int i=problem_ -> nVars (), j=0; i--; ++j, ++plb, ++pub) {
376 
377  bool neglect = problem_ -> Var (j) -> Multiplicity () <= 0;
378 
379  if (*plb > *mlb++) milp_ -> setColLower (j, neglect ? 0. : *plb);
380  if (*pub < *mub++) milp_ -> setColUpper (j, neglect ? 0. : *pub);
381  }
382  }
383 
384  break;
385 
386  } else {
387 
388  problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: IP solution NOT MINLP feasible\n");
389 
390  if (milpCuttingPlane_ == FP_CUT_EXTERNAL ||
391  milpCuttingPlane_ == FP_CUT_POST) {
392 
393  // Solution is IP- but not MINLP feasible: it might get cut by
394  // linearization cuts. If so, add a round of cuts and repeat.
395 
396  OsiCuts cs;
397 
398  problem_ -> domain () -> push (milp_);
399  couenneCG_ -> genRowCuts (*milp_, cs, 0, NULL); // remaining three arguments NULL by default
400  problem_ -> domain () -> pop ();
401 
402  if (cs.sizeRowCuts ()) {
403 
404  // the (integer, NLP infeasible) solution could be separated
405 
406  milp_ -> applyCuts (cs);
407 
408  // found linearization cut, now re-solve MILP (not quite a FP)
409  if (milpCuttingPlane_ == FP_CUT_EXTERNAL &&
410  nSep++ < nSepRounds_)
411  continue;
412  }
413  }
414  }
415 }
#define COUENNE_round(x)
status of lower/upper bound of a variable, to be checked/modified in bound tightening ...
static char * j
Definition: OSdtoa.cpp:3622
int up
Definition: OSdtoa.cpp:1817
Class containing a solution with infeasibility evaluation.
void setUpper(ChangeStatus upper)
fint end
Class for MINLP problems with symbolic information.
static int
Definition: OSdtoa.cpp:2173
void printCmpSol(CouenneProblem *p, const double *iSol, double *nSol, int direction)
double CouNumber
main number type in Couenne
const Ipopt::EJournalCategory J_NLPHEURISTIC(Ipopt::J_USER5)
#define RND_DECR_EXPONENT
void fint * n
void printDist(CouenneProblem *p, const double *iSol, double *nSol)
bool isInteger(CouNumber x)
is this number integer?