CouenneFPcreateMILP.cpp
Go to the documentation of this file.
1 /* $Id: CouenneFPcreateMILP.cpp 1128 2015-03-10 15:05:11Z tkr $
2  *
3  * Name: CouenneFPcreateMILP.cpp
4  * Authors: Pietro Belotti
5  * Timo Berthold, ZIB Berlin
6  * Purpose: create 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 "IpLapack.hpp"
14 
15 #include "CouenneSparseMatrix.hpp"
16 #include "CouenneTNLP.hpp"
17 #include "CouenneFeasPump.hpp"
18 #include "CouenneProblem.hpp"
19 #include "CouenneProblemElem.hpp"
20 #include "CouenneExprVar.hpp"
21 
22 #define COUENNE_EIG_RATIO .1 // how much smaller than the largest eigenvalue should the minimum be set at?
23 
24 using namespace Couenne;
25 
27 void ComputeSquareRoot (const CouenneFeasPump *fp, CouenneSparseMatrix *hessian, CoinPackedVector *P);
28 
32 int PSDize (int n, double *A, double *B, bool doSqrRoot);
33 
35 OsiSolverInterface *createCloneMILP (const CouenneFeasPump *fp, CbcModel *model, bool isMILP, int *match) {
36 
37  OsiSolverInterface *lp = model -> solver () -> clone ();
38 
39  // no data is available so far, retrieve it from the MILP solver
40  // used as the linearization
41 
42  // Add q variables, each with coefficient 1 in the objective
43 
44  CoinPackedVector vec;
45 
46  for (int i = fp -> Problem () -> nVars (), j = 0; i--; ++j) {
47 
48  // column has to be added if:
49  //
50  // creating MIP AND (integer variable OR FP_DIST_ALL)
51  // creating LP AND fractional variable
52 
53  // TODO: should this really happen? I bet no
54 
55  if (fp -> Problem () -> Var (j) -> Multiplicity () <= 0)
56  continue;
57 
58  bool intVar = lp -> isInteger (j);
59 
60  if ((isMILP && (intVar || (fp -> compDistInt () == CouenneFeasPump::FP_DIST_ALL)))
61  ||
62  (!isMILP && !intVar)) {
63 
64  // (empty) coeff col vector, lb = 0, ub = inf, obj coeff
65  lp -> addCol (vec, 0., COIN_DBL_MAX, 1.);
66 
67  if (match)
68  match [j] = lp -> getNumCols () - 1;
69  }
70  }
71 
72  // Set to zero all other variables' obj coefficient. This means we
73  // just do it for the single variable in the reformulated
74  // problem's linear relaxation (all other variables do not appear
75  // in the objective)
76 
77  int objInd = fp -> Problem () -> Obj (0) -> Body () -> Index ();
78 
79  if (objInd >= 0)
80  lp -> setObjCoeff (objInd, 0.);
81 
82  return lp;
83 }
84 
86 void addDistanceConstraints (const CouenneFeasPump *fp, OsiSolverInterface *lp, double *sol, bool isMILP, int *match) {
87 
88  // Construct an (empty) Hessian. It will be modified later, but
89  // the changes should be relatively easy for the case when
90  // multHessMILP_ > 0 and there are no changes if multHessMILP_ == 0
91 
92  int n = fp -> Problem () -> nVars ();
93 
94  CoinPackedVector *P = new CoinPackedVector [n];
95 
96  // The MILP has to be changed the first time it is used.
97  //
98  // Suppose Ax >= b has m inequalities. In order to solve the
99  // problem above, we need q new variables z_i and 2q inequalities
100  //
101  // z_i >= P^i (x - x^0) or P^i x - z_i <= P^i x^0 (*)
102  // z_i >= - P^i (x - x^0) (**)
103  //
104  // (the latter being equivalent to
105  //
106  // - z_i <= P^i (x - x^0) or P^i x + z_i >= P^i x^0 (***))
107  //
108  // so we'll use this instead as most coefficients don't change)
109  // for each i, where q is the number of variables involved (either
110  // q=|N|, the number of integer variables, or q=n, the number of
111  // variables).
112  //
113  // We need to memorize the number of initial inequalities and of
114  // variables, so that we know what (coefficients and rhs) to
115  // change at every iteration.
116 
117  CoinPackedVector x0 (n, sol);
118 
119  // set objective coefficient if we are using a little Objective FP
120 
121  if (isMILP && (fp -> multObjFMILP () > 0.)) {
122 
123  int objInd = fp -> Problem () -> Obj (0) -> Body () -> Index ();
124 
125  if (objInd >= 0)
126  lp -> setObjCoeff (objInd, fp -> multObjFMILP ());
127  }
128 
129  if (isMILP &&
130  (fp -> multHessMILP () > 0.) &&
131  (fp -> nlp () -> optHessian ())) {
132 
133  // P is a convex combination, with weights multDistMILP_ and
134  // multHessMILP_, of the distance and the Hessian respectively
135 
136  // obtain optHessian and compute its square root
137 
138  CouenneSparseMatrix *hessian = fp -> nlp () -> optHessian ();
139 
140  ComputeSquareRoot (fp, hessian, P);
141 
142  } else {
143 
144  // simply set P = I
145 
146  for (int i=0; i<n; i++)
147  if (fp -> Problem () -> Var (i) -> Multiplicity () > 0)
148  P[i].insert (i, 1. / sqrt ((double) n));
149  }
150 
151  // Add 2q inequalities
152 
153  for (int i = 0, j = n, k = n; k--; ++i) {
154 
155  if (match && match [i] < 0)
156  continue;
157 
158  if (fp -> Problem () -> Var (i) -> Multiplicity () <= 0)
159  continue;
160 
161  // two rows have to be added if:
162  //
163  // amending MIP AND (integer variable OR FP_DIST_ALL)
164  // amending LP AND fractional variable
165 
166  bool intVar = lp -> isInteger (i);
167 
168  if ((isMILP && (intVar || (fp -> compDistInt () == CouenneFeasPump::FP_DIST_ALL)))
169  ||
170  (!isMILP && !intVar)) {
171 
172  // create vector with single entry of 1 at i-th position
173  CoinPackedVector &vec = P [i];
174 
175  if (vec.getNumElements () == 0)
176  continue;
177 
178  // right-hand side equals <P^i,x^0>
179  double PiX0 = sparseDotProduct (vec, x0);
180 
181  assert (!match || match [i] >= 0);
182 
183  //printf ("adding row %d with %d elements\n", j, vec.getNumElements());
184 
185  // j is the index of the j-th extra variable z_j, used for z_j >= P (x - x0) ===> z_j - Px >= - Px_0 ==> -z_j + Px <= Px_0
186  // Second inequality is z_j >= -P (x - x0) ==> z_j + Px >= Px_0
187  vec.insert (j, -1.); lp -> addRow (vec, -COIN_DBL_MAX, PiX0); // (*)
188  vec.setElement (vec.getNumElements () - 1, +1.); lp -> addRow (vec, PiX0, COIN_DBL_MAX); // (***)
189 
190  ++j; // index of variable within problem (plus nVars_)
191 
192  } else if (intVar) { // implies (!isMILP)
193 
194  // fix integer variable to its value in iSol
195 
196 #define INT_LP_BRACKET 0
197 
198  lp -> setColLower (i, sol [i] - INT_LP_BRACKET);
199  lp -> setColUpper (i, sol [i] + INT_LP_BRACKET);
200  }
201  }
202 
203  delete [] P;
204 }
205 
206 
207 #define GRADIENT_WEIGHT 1
208 
210  CouenneSparseMatrix *hessian,
211  CoinPackedVector *P) {
212 
213  int
214  objInd = fp -> Problem () -> Obj (0) -> Body () -> Index (),
215  n = fp -> Problem () -> nVars ();
216 
217  //assert (objInd >= 0);
218 
219  double *val = hessian -> val ();
220  int *row = hessian -> row ();
221  int *col = hessian -> col ();
222  int num = hessian -> num ();
223 
224  //printf ("compute square root:\n");
225 
226  // Remove objective's row and column (equivalent to taking the
227  // Lagrangian's Hessian, setting f(x) = x_z = c, and recomputing the
228  // hessian).
229 
230  double maxElem = 0.; // used in adding diagonal element of x_z
231 
232  for (int i=num; i--; ++row, ++col, ++val) {
233 
234  //printf ("elem: %d, %d --> %g\n", *row, *col, *val);
235 
236  if ((*row == objInd) ||
237  (*col == objInd))
238 
239  *val = 0;
240 
241  else if (fabs (*val) > maxElem)
242  maxElem = fabs (*val);
243  }
244 
245  val -= num;
246  row -= num;
247  col -= num;
248 
249  // fill an input to Lapack/Blas procedures using hessian
250 
251  double *A = (double *) malloc (n*n * sizeof (double));
252  double *B = (double *) malloc (n*n * sizeof (double));
253 
254  CoinZeroN (A, n*n);
255 
256  double sqrt_trace = 0;
257 
258  // Add Hessian part -- only lower triangular part
259  for (int i=0; i<num; ++i, ++row, ++col, ++val)
260  if (*col <= *row) {
261  A [*col * n + *row] = fp -> multHessMILP () * *val;
262  if (*col == *row)
263  sqrt_trace += *val * *val;
264  }
265 
266  val -= num;
267  row -= num;
268  col -= num;
269 
270  sqrt_trace = sqrt (sqrt_trace);
271 
272  // Add Hessian part -- only lower triangular part
273  if (sqrt_trace > COUENNE_EPS)
274  for (int i=0; i<num; ++i, ++row, ++col)
275  A [*col * n + *row] /= sqrt_trace;
276 
277  row -= num;
278  col -= num;
279 
280  // Add distance part
281  for (int i=0; i<n; ++i)
282  if (fp -> Problem () -> Var (i) -> Multiplicity () > 0)
283  A [i * (n+1)] += fp -> multDistMILP () / sqrt (static_cast<double>(n));
284 
285  // Add gradient-parallel term to the hessian, (x_z - x_z_0)^2. This
286  // amounts to setting the diagonal element to GRADIENT_WEIGHT. Don't
287  // do it directly on hessian
288 
289  if (objInd >= 0)
290  A [objInd * (n+1)] = maxElem * GRADIENT_WEIGHT * n;
291 
292  PSDize (n, A, B, true); // use squareroot
293 
294  double *eigenvec = A; // as overwritten by dsyev;
295 
296  for (int i=0; i<n; ++i)
297  for (int j=0; j<n; ++j) {
298 
299  // multiply i-th row of B by j-th column of E
300 
301  double elem = 0.;
302 
303  for (int k=0; k<n; ++k)
304  elem += B [i + k * n] * eigenvec [j * n + k];
305 
306  if (fabs (elem) > COUENNE_EPS)
307  P [i]. insert (j, elem);
308  }
309 
310  // if (fp -> Problem () -> Jnlst () -> ProduceOutput (Ipopt::J_STRONGWARNING, J_NLPHEURISTIC)) {
311  // printf ("P:\n");
312  // printf ("P^{1/2}:\n");
313  // }
314 
315  free (A);
316  free (B);
317 }
318 
319 
324 //
325 
326 int PSDize (int n, double *A, double *B, bool doSqrRoot) {
327 
328  // call Lapack/Blas routines
329  double *eigenval = (double *) malloc (n * sizeof (double));
330  int status;
331 
332  // compute eigenvalues and eigenvectors
333  Ipopt::IpLapackDsyev (true, n, A, n, eigenval, status);
334 
335  if (status < 0) printf ("Couenne: warning, argument %d illegal\n", -status);
336  else if (status > 0) printf ("Couenne: warning, dsyev did not converge (error code: %d)\n", status);
337 
338  // define a new matrix B = E' * D, where E' is E transposed,
339  //
340  // E = eigenvector matrix
341  // D = diagonal with square roots of eigenvalues
342  //
343  // Eventually, the square root is given by E' D E
344 
345  double *eigenvec = A; // as overwritten by dsyev;
346 
347  //
348  // eigenvec is column major, hence post-multiplying it by D equals
349  // multiplying each column i by the i-th eigenvalue
350  //
351 
352  // if all eigenvalues are nonpositive, set them all to one
353 
354  double
355  MinEigVal = eigenval [0],
356  MaxEigVal = eigenval [n-1];
357 
358  for (int j=1; j<n; j++)
359  assert (eigenval [j-1] <= eigenval [j]);
360 
361  if (MaxEigVal <= 0.)
362 
363  // In this case, we are interested in minimizing a concave
364  // function. It makes sense to invert each eigenvalue (i.e. take
365  // its inverse) and change its sign, as the steepest descent
366  // should correspond to the thinnest direction
367 
368  for (int j=0; j<n; j++)
369  eigenval [j] = 1. / (.1 - eigenval [j]);
370 
371  else {
372 
373  // set all not-too-positive ones to a fraction of the maximum
374  // ("un-thins" the level curves defined by the HL)
375 
376  MinEigVal = MaxEigVal * COUENNE_EIG_RATIO;
377 
378  if (eigenval [0] <= MinEigVal)
379  for (int j=0; eigenval [j] <= MinEigVal; j++)
380  eigenval [j] = MinEigVal;
381  }
382 
383  // Now obtain sqrt (A)
384 
385  int nnz = 0;
386 
387  for (int j=0; j<n; ++j) {
388 
389  register double multEig = doSqrRoot ? sqrt (eigenval [j]) :
390  eigenval [j];
391 
392  for (int i=0; i<n; ++i)
393  if (fabs (*B++ = multEig * eigenvec [i*n+j]) > COUENNE_EPS)
394  ++nnz;
395  }
396 
397  B -= n*n;
398 
399  // TODO: set up B as row-major sparse matrix and
400  // E as column-major sparse matrix
401  //
402  // Otherwise this multiplication is O(n^3)
403 
404  // Now compute B * E
405 
406  free (eigenval);
407 
408  return nnz;
409 }
#define COUENNE_EIG_RATIO
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...
Class for sparse Matrixs (used in modifying distances in FP)
static char * j
Definition: OSdtoa.cpp:3622
#define GRADIENT_WEIGHT
An implementation of the Feasibility pump that uses linearization and Ipopt to find the two sequences...
ULong * x0
Definition: OSdtoa.cpp:1776
#define INT_LP_BRACKET
void fint fint fint real fint real real real real real real real real real fint real fint * lp
void fint fint * k
#define COUENNE_EPS
void ComputeSquareRoot(const CouenneFeasPump *fp, CouenneSparseMatrix *hessian, CoinPackedVector *P)
computes square root of a CouenneSparseMatrix
#define P
Definition: OSdtoa.cpp:399
OsiSolverInterface * createCloneMILP(const CouenneFeasPump *fp, CbcModel *model, bool isMILP, int *match)
create clone of MILP and add variables for special objective
int nnz
ATTENTION: Filter expect the jacobian to be ordered by row.
int PSDize(int n, double *A, double *B, bool doSqrRoot)
project matrix onto the cone of positive semidefinite matrices (possibly take square root of eigenval...
void fint * n
bool isInteger(CouNumber x)
is this number integer?