CouenneFPSolveMILP.cpp
Go to the documentation of this file.
1 /* $Id: CouenneFPSolveMILP.cpp 1071 2014-03-13 01:35:13Z pbelotti $
2  *
3  * Name: CouenneFPSolveMILP.cpp
4  * Authors: Pietro Belotti
5  * Timo Berthold, ZIB Berlin
6  * Purpose: Solve the MILP within the Feasibility Pump
7  *
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CbcModel.hpp"
12 
13 #include "CouenneConfig.h"
14 #include "CouenneFeasPump.hpp"
16 #include "CouenneProblem.hpp"
17 #include "CouenneProblemElem.hpp"
18 #include "CouenneExprVar.hpp"
19 
20 #include "CouenneFPpool.hpp"
21 
22 #ifdef COIN_HAS_SCIP
23 /* general SCIP includes */
24 #include "scip/scip.h"
25 #include "scip/cons_linear.h"
26 #include "scip/scipdefplugins.h"
27 #endif
28 
29 using namespace Couenne;
30 
31 #define NUMERICS_THRES 1e19
32 
33 #ifdef COIN_HAS_SCIP
34 void CouenneFeasPump::checkInfinity(SCIP *scip, SCIP_Real val, double infinity){
35  if( SCIPisInfinity(scip, val) && val < infinity)
36  problem_ -> Jnlst () -> Printf (Ipopt::J_WARNING, J_NLPHEURISTIC,
37  "Warning: %g will be considered to be Infinity by SCIP.\n", val);
38 }
39 #endif
40 
41 
43 OsiSolverInterface *createCloneMILP (const CouenneFeasPump *fp, CbcModel *model, bool isMILP, int *match);
44 
45 
47 void addDistanceConstraints (const CouenneFeasPump *fp, OsiSolverInterface *lp, double *sol, bool isMILP, int *match);
48 
49 
53 CouNumber CouenneFeasPump::solveMILP (const CouNumber *nSol0, CouNumber *&iSol, int niter, int* nsuciter) {
54 
55  // The problem is of the form
56  //
57  // min f(x)
58  // s.t. Ax >= b
59  // x_i in Z, i in N
60  //
61  // where N is the index set of integer variables and f(x) is one of
62  // the following:
63  //
64  // 1) sum {i in Vars} |x_i - x_i^0|
65  // 2) sum {i in N} |x_i - x_i^0|
66  // 3) sum {i in Vars} |P^i (x - x^0)|
67  // 4) sum {i in N} |P^i (x - x^0)|
68  //
69  // where is x^0 is the optimal solution of a NLP problem. In the
70  // last two, the l-1 norm is multiplied by the i-th row of a matrix
71  // P obtained from the Hessian H of the Lagrangian of the problem. H
72  // is a positive semidefinite matrix only on the null space of the
73  // gradient g, i.e.
74  //
75  // x' H x >= 0 for all x: g'x = 0
76  //
77  // (where x' is x transposed), therefore we add a quadratic term to
78  // make it positive semidefinite everywhere else. In order to do so,
79  // the quadratic term (alpha is a scalar)
80  //
81  // alpha (g'x)^2
82  //
83  // is zero in the null space of g, and it is strictly positive
84  // everywhere else. Hence, we need an alpha so that
85  //
86  // P = (H + alpha g g')
87  //
88  // is PSD. In general, we might have a parameter beta in [0,1] such
89  // that
90  //
91  // P = beta I + (1-beta) (H + alpha g g')
92  //
93  // so that we can balance the Hessian and the distance.
94 
95  bool firstCall = (milp_ == NULL); // true if this is the first call to
96  // solveMILP; initialization will be
97  // necessary
98 
99  // match [i] contains the i-th extra variable associated with the
100  // term in the L1 norm for x_i, and -1 otherwise.
101 
102  if (firstCall) {
103 
104  match_ = new int [problem_ -> nVars ()];
105 
106  for (int i=problem_ -> nVars (); i--;)
107  match_ [i] = -1;
108 
109  // create MILP
110 
111  milp_ = createCloneMILP (this, model_, true, match_);
112 
113  // the bounds might have improved because of FBBT. Unless a new
114  // MINLP solution was found (unlikely ...), this should be done only
115  // at the first call to the FP
116 
117  for (int i=problem_ -> nVars (); i--;) {
118  milp_ -> setColLower (i, problem_ -> Lb (i));
119  milp_ -> setColUpper (i, problem_ -> Ub (i));
120  }
121 
122  // Post-processing LP: persistent if FP_DIST_POST, created on the
123  // fly if FP_DIST_INT and numerics, not created if FP_DIST_ALL
124  //
125  // set up an LP as a copy of the original MILP. Don't do the
126  // same for FP_DIST_INT as it is only necessary upon numerical
127  // problems, which might not happen
128 
129  if ((compDistInt_ == FP_DIST_POST) && !postlp_)
130  postlp_ = createCloneMILP (this, model_, false, NULL);
131  }
132 
133 #if 0
134  printf ("======================================================================================================================================\n");
135  for (int i=0,j; i<problem_->nVars (); ++i)
136  if (match_ [i] >= 0) {
137  printf ("(%d,%d)", i, match_ [i]);
138  if ((++j) % 6 == 0) printf ("\n");
139  }
140  printf ("======================================================================================================================================\n");
141 #endif
142 
143  int nInitRows = milp_ -> getNumRows ();
144 
145  CouNumber * nlpSolExp;
146 
147  if (nSol0) {
148 
149  nlpSolExp = new CouNumber [problem_ -> nVars ()];
150 
151  CoinCopyN (nSol0, problem_ -> nOrigVars (), nlpSolExp);
152  problem_ -> getAuxs (nlpSolExp);
153 
154  } else
155  nlpSolExp = CoinCopyOfArray (milp_ -> getColSolution (),
156  problem_ -> nVars ());
157 
158  // create constraints to define l_1 distance objective function
159  addDistanceConstraints (this, milp_, nlpSolExp, true, match_);
160 
161  //milp_ -> writeLp ("afterAdd");
162 
163  delete [] nlpSolExp;
164 
165  int nFinalRows = milp_ -> getNumRows ();
166 
167  // The MILP is complete. We have several ways of solving it, or
168  // better, to find feasible solutions to it. We have to interface
169  // with each of them once at the very beginning, and later we loop
170  // through them in order to find a feasible solution.
171 
172  if (firstCall)
173  init_MILP ();
174 
175  if (false) { // should always be false upon commit
176  static int cntr = 0;
177  char filename [30];
178  sprintf (filename, "fp-milp%04d", cntr++);
179  milp_ -> writeLp (filename);
180  printf ("saving FP_MILP %d\n", cntr);
181  }
182 
183  double obj = findSolution (nSol0, iSol, niter, nsuciter);
184 
185  if ((nSol0 && iSol) &&
186  (problem_ -> Jnlst () -> ProduceOutput (Ipopt::J_WARNING, J_NLPHEURISTIC))) {
187 
188  double dist = 0.;
189  int nNonint = 0;
190 
191  for (int i = 0; i < problem_ -> nVars (); ++i) {
192 
193  if (problem_ -> Var (i) -> Multiplicity () <= 0)
194  continue;
195 
196  if (problem_ -> Var (i) -> isInteger () &&
197  (fabs (iSol [i] - floor (iSol [i] + .5)) > COUENNE_EPS)) // need stricter tolerance
198  ++nNonint;
199 
200  dist +=
201  (iSol [i] - nSol0 [i]) *
202  (iSol [i] - nSol0 [i]);
203  }
204 
205  problem_ -> Jnlst () -> Printf (Ipopt::J_WARNING, J_NLPHEURISTIC, "FP: after MILP, distance %g, %d nonintegers\n", sqrt (dist), nNonint);
206  }
207 
208  //
209  // POST PROCESSING
210  //
211  // (if we got a solution from MILP, otherwise bail out)
212  //
213 
214  if (iSol &&
215  (compDistInt_ != FP_DIST_ALL)) {
216 
217  // check iSol for numerics (i.e. components whose fabs () is large,
218  // or >= 1e20) or post-process to obtain a second solution by fixing
219  // the integer coordinates and solving the resulting LP
220 
221  bool numerics = false;
222 
223  if (compDistInt_ == FP_DIST_INT) {
224 
225  for (std::vector <exprVar *>::iterator i = problem_ -> Variables (). begin ();
226  i != problem_ -> Variables (). end (); ++i)
227 
228  if (( (*i) -> Multiplicity () > 0) &&
229  ! ((*i) -> isInteger ()) &&
230  (fabs (iSol [(*i) -> Index ()]) > NUMERICS_THRES)) {
231 
232  numerics = true;
233  break;
234  }
235  }
236 
237  if (numerics || (compDistInt_ == FP_DIST_POST)) {
238 
239  // solve LP where integer variables have been fixed:
240  //
241  // a) check if postlp_ exists yet
242  // 0) save integer bounds
243  // 1) fix integer variables
244  // 2) add variables and inequalities
245  // 3) solve LP
246  // 4) if optimal, save solution
247  // 5) restore IP bounds
248  // 6) delete variables
249 
250  if (!postlp_)
251  postlp_ = createCloneMILP (this, model_, false, NULL);
252 
253  int nvars = postlp_ -> getNumCols ();
254 
255  // save integer bounds to restore them later
256  double
257  *saveLB = CoinCopyOfArray (postlp_ -> getColLower (), nvars),
258  *saveUB = CoinCopyOfArray (postlp_ -> getColUpper (), nvars),
259  *newLB = CoinCopyOfArray (postlp_ -> getColLower (), nvars),
260  *newUB = CoinCopyOfArray (postlp_ -> getColUpper (), nvars);
261 
262  // fix integer variables
263 
264  for (int i = problem_ -> nVars (); i--;)
265 
266  if ((problem_ -> Var (i) -> Multiplicity () > 0) &&
267  (milp_ -> isInteger (i)))
268 
269  newLB [i] = newUB [i] = iSol [i] = floor (iSol [i] + .5);
270 
271  postlp_ -> setColLower (newLB);
272  postlp_ -> setColUpper (newUB);
273 
274  // add inequalities
275 
276  int nInitRowsLP = postlp_ -> getNumRows ();
277  addDistanceConstraints (this, postlp_, iSol, false, match_);
278  int nFinalRowsLP = postlp_ -> getNumRows ();
279 
280  // Solve the LP, obtain closest point with integer variables fixed
281 
282  postlp_ -> initialSolve ();
283 
284  // save as new solution
285 
286  if (postlp_ -> isProvenOptimal ())
287  CoinCopyN (postlp_ -> getColSolution (), problem_ -> nVars (), iSol);
288 
289  postlp_ -> setColLower (saveLB);
290  postlp_ -> setColUpper (saveUB);
291 
292  // delete temp data
293 
294  delete [] saveLB;
295  delete [] saveUB;
296  delete [] newLB;
297  delete [] newUB;
298 
299  // delete added rows
300 
301  int
302  nDeleted = nFinalRowsLP - nInitRowsLP,
303  *deleted = new int [nDeleted],
304  nCurRow = nInitRowsLP;
305 
306  for (int i = nDeleted; i--;)
307  deleted [i] = nCurRow++;
308 
309  postlp_ -> deleteRows (nDeleted, deleted);
310 
311  delete [] deleted;
312  }
313  }
314 
315  // delete last rows and add them from scratch (common block below)
316 
317  int
318  nDeleted = nFinalRows - nInitRows,
319  *deleted = new int [nDeleted],
320  nCurRow = nInitRows;
321 
322  for (int i = nDeleted; i--;)
323  deleted [i] = nCurRow++;
324 
325  milp_ -> deleteRows (nDeleted, deleted);
326 
327  delete [] deleted;
328 
329  return obj;
330 }
int nVars() const
Total number of variables.
virtual CouNumber solveMILP(const CouNumber *nSol, CouNumber *&iSol, int niter, int *nsuciter)
find integer (possibly NLP-infeasible) point isol closest (according to the l-1 norm of the hessian) ...
static int match(const char **sp, const char *t)
Definition: OSdtoa.cpp:1604
void addDistanceConstraints(const CouenneFeasPump *fp, OsiSolverInterface *lp, double *sol, bool isMILP, int *match)
modify MILP or LP to implement distance by adding extra rows (extra cols were already added by create...
int * match_
matching between reformulation&#39;s variables and L-1 norm variables
static char * j
Definition: OSdtoa.cpp:3622
#define NUMERICS_THRES
An implementation of the Feasibility pump that uses linearization and Ipopt to find the two sequences...
void fint fint fint real fint real real real real real real real real real fint real fint * lp
double findSolution(const double *nSol, double *&sol, int niter, int *nsuciter)
find feasible solution (called by solveMILP ())
fint end
#define COUENNE_EPS
enum fpCompDistIntType compDistInt_
Compute distance from integer variables only, not all variables.
void init_MILP()
initialize all solvers at the first call, where the initial MILP is built
double CouNumber
main number type in Couenne
OsiSolverInterface * postlp_
LP relaxation of the MINLP used when fixing integer variables (used for compDistInt_ in FP_DIST_POST ...
OsiSolverInterface * createCloneMILP(const CouenneFeasPump *fp, CbcModel *model, bool isMILP, int *match)
create clone of MILP and add variables for special objective
const Ipopt::EJournalCategory J_NLPHEURISTIC(Ipopt::J_USER5)
CouenneProblem * problem_
Couenne representation of the problem.
The in-memory representation of the variables element.
Definition: OSInstance.h:83
OsiSolverInterface * milp_
MILP relaxation of the MINLP (used to find integer, non-NLP-feasible solutions)
bool isInteger(CouNumber x)
is this number integer?