FixPointGenCuts.cpp
Go to the documentation of this file.
1 /* $Id: FixPointGenCuts.cpp 1272 2019-02-23 17:27:43Z stefan $
2  *
3  * Name: FixPointGenCuts.cpp
4  * Author: Pietro Belotti
5  * Purpose: Fix point bound tightener -- separator
6  *
7  * (C) Pietro Belotti, 2010.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CoinHelperFunctions.hpp"
12 #include "OsiClpSolverInterface.hpp"
13 #include "OsiCuts.hpp"
14 #include "CoinTime.hpp"
15 
16 #include "CouenneFixPoint.hpp"
17 
18 #include "CouenneProblem.hpp"
19 #include "CouennePrecisions.hpp"
20 #include "CouenneExprVar.hpp"
21 #include "CouenneInfeasCut.hpp"
22 
23 using namespace Ipopt;
24 using namespace Couenne;
25 
27 
28 void CouenneFixPoint::generateCuts (const OsiSolverInterface &si,
29  OsiCuts &cs,
30  const CglTreeInfo treeInfo)
31 #if CGL_VERSION_MAJOR == 0 && CGL_VERSION_MINOR <= 57
32  const
33 #endif
34  {
35 
44 
45  if (firstCall_)
46  firstCall_ = false;
47  //else
48  //if (!(problem_ -> fbbtReachedIterLimit ()))
49  //return;
50 
51  if (levelStop_ == 0) // turned off; should never happen, can actually assert != 0
52  return;
53 
54  if (isWiped (cs))
55  return;
56 
57  // do it at most once per node
58 
59  if (treeInfo.inTree &&
60  treeInfo.level > 0 &&
61  treeInfo.pass > 1)
62  return;
63 
64  // Avoid if excessive BB tree depth. Run run it at every node until
65  // depth -levelStop_, then every other level until -2 levelStop_,
66  // then stop.
67  if ((levelStop_ < 0) && // If this cut generator has negative frequency option
68  ((treeInfo.level > -2*levelStop_) || // AND either we are at a low depth
69  ((treeInfo.level > -levelStop_) && // OR the depth is low enough
70  (treeInfo.level & 1)))) // AND it's even,
71  return; // skip
72 
73  double startTime = CoinCpuTime ();
74 
75  int nInitTightened = nTightened_;
76 
77  if (treeInfo.inTree &&
78  treeInfo.level <= 0) {
79 
80  problem_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "Fixed Point FBBT: ");
81  fflush (stdout);
82  }
83 
84  //++nRuns_;
85 
86  double now = CoinCpuTime ();
87 
88  problem_ -> domain () -> push (&si, &cs);
89 
90  double
91  *oldLB = CoinCopyOfArray (problem_ -> Lb (), problem_ -> nVars ()),
92  *oldUB = CoinCopyOfArray (problem_ -> Ub (), problem_ -> nVars ());
93 
94  perfIndicator_. setOldBounds (problem_ -> Lb (), problem_ -> Ub ());
95 
114 
115  OsiSolverInterface *fplp = NULL;
116 
117  if (true) { // placeholder for later selection of LP solver among
118  // those available
119 
120  fplp = new OsiClpSolverInterface;
121  }
122 
139 
141 
142  const CoinPackedMatrix *A = si. getMatrixByRow ();
143 
144  const int
145  n = si. getNumCols (),
146  m = si. getNumRows (),
147  nCuts = cs.sizeRowCuts (),
148  *ind = A -> getIndices ();
149 
150  const double
151  *lb = problem_ -> Lb (), //si. getColLower (),
152  *ub = problem_ -> Ub (), //si. getColUpper (),
153  *rlb = si. getRowLower (),
154  *rub = si. getRowUpper (),
155  *coe = A -> getElements ();
156 
157  if (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_BOUNDTIGHTENING))
158  for (int i=0; i<n; i++)
159  printf ("----------- x_%d in [%g,%g]\n", i, lb [i], ub [i]);
160 
161  // turn off logging
162  fplp -> messageHandler () -> setLogLevel (0);
163 
164  // add lvars and uvars to the new problem
165  for (int i=0; i<n; i++) {
166  bool isActive = problem_ -> Var (i) -> Multiplicity () > 0;
167  fplp -> addCol (0, NULL, NULL, lb [i], ub [i], isActive ? -1. : 0.); // xL_i
168  }
169 
170  for (int i=0; i<n; i++) {
171  bool isActive = problem_ -> Var (i) -> Multiplicity () > 0;
172  fplp -> addCol (0, NULL, NULL, lb [i], ub [i], isActive ? +1. : 0.); // xU_i
173  }
174 
175  if (extendedModel_) {
176 
177  for (int j=0; j<m; j++) fplp -> addCol (0, NULL, NULL, rlb [j], COIN_DBL_MAX, 0.); // bL_j
178  for (int j=0; j<m; j++) fplp -> addCol (0, NULL, NULL, -COIN_DBL_MAX, rub [j], 0.); // bU_j
179  }
180 
181  // Scan each row of the matrix
182 
183  for (int j=0; j<m; j++) { // for each row
184 
185  const int nEl = A->getVectorLast(j) - A->getVectorFirst(j), // A -> getVectorSize (j); // # elements in each row
186  *rind = &ind [A->getVectorFirst (j)];
187  const double *rcoe = &coe [A->getVectorFirst (j)];
188 
189  if (!nEl)
190  continue;
191 
192  if (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_BOUNDTIGHTENING)) {
193 
194  printf ("row %4d, %4d elements: ", j, nEl);
195 
196  for (int i=0; i<nEl; i++) {
197  printf ("%+g x%d ", rcoe [i], rind [i]);
198  fflush (stdout);
199  }
200 
201  printf ("\n");
202  }
203 
204  // create cuts for the xL and xU elements //////////////////////
205 
206  if (extendedModel_ || rlb [j] > -COUENNE_INFINITY)
207  for (int i = A->getVectorFirst(j); i < A->getVectorLast(j); i++)
208  createRow (-1, ind [i], n, fplp, rind, rcoe, rlb [j], nEl, extendedModel_, j, m + nCuts); // downward constraints -- on x_i
209 
210  if (extendedModel_ || rub [j] < COUENNE_INFINITY)
211  for (int i = A->getVectorFirst(j); i < A->getVectorLast(j); i++)
212  createRow (+1, ind [i], n, fplp, rind, rcoe, rub [j], nEl, extendedModel_, j, m + nCuts); // downward constraints -- on x_i
213 
214  // create (at most 2) cuts for the bL and bU elements //////////////////////
215 
216  if (extendedModel_) {
217  createRow (-1, 2*n + j, n, fplp, rind, rcoe, rlb [j], nEl, extendedModel_, j, m + nCuts); // upward constraints -- on bL_i
218  createRow (+1, 2*n + m + j, n, fplp, rind, rcoe, rub [j], nEl, extendedModel_, j, m + nCuts); // upward constraints -- on bU_i
219  }
220  }
221 
222  // similarly, scan previous cuts in cs //////////////////////////////////////
223 
224  for (int j = 0, jj = nCuts; jj--; j++) {
225 
226  // create cuts for the xL and xU elements //////////////////////
227 
228  OsiRowCut *cut = cs.rowCutPtr (j);
229 
230  const CoinPackedVector &row = cut -> row ();
231 
232  const int
233  nEl = row.getNumElements (),
234  *ind = row.getIndices ();
235 
236  const double *coe = row.getElements ();
237 
238  if (extendedModel_) {
239  fplp -> addCol (0, NULL, NULL, cut -> lb (), COIN_DBL_MAX, 0.); // bL_j
240  fplp -> addCol (0, NULL, NULL, -COIN_DBL_MAX, cut -> ub (), 0.); // bU_j
241  }
242 
243  if (extendedModel_ || cut -> lb () > -COUENNE_INFINITY)
244  for (int i=0; i<nEl; i++)
245  createRow (-1, ind [i], n, fplp, ind, coe, cut -> lb (), nEl, extendedModel_, m + j, m + nCuts); // downward constraints -- on x_i
246 
247  if (extendedModel_ || cut -> ub () < COUENNE_INFINITY)
248  for (int i=0; i<nEl; i++)
249  createRow (+1, ind [i], n, fplp, ind, coe, cut -> ub (), nEl, extendedModel_, m + j, m + nCuts); // downward constraints -- on x_i
250 
251  // create (at most 2) cuts for the bL and bU elements
252 
253  if (extendedModel_) {
254  createRow (-1, 2*n + j, n, fplp, ind, coe, cut -> lb (), nEl, extendedModel_, m + j, m + nCuts); // upward constraints -- on bL_i
255  createRow (+1, 2*n + m + nCuts + j, n, fplp, ind, coe, cut -> ub (), nEl, extendedModel_, m + j, m + nCuts); // upward constraints -- on bU_i
256  }
257  }
258 
259  // Add zL <= zU constraints
260 
261  for (int j=0; j<n; j++) { // for each row
262 
263  int ind [2] = {j, n + j};
264  double coe [2] = {1., -1.};
265 
266  CoinPackedVector row (2, ind, coe);
267  fplp -> addRow (row, -COIN_DBL_MAX, 0.);
268  }
269 
270  // Finally, add consistency cuts, bL <= bU
271 
272  if (extendedModel_)
273 
274  for (int j=0; j<m; j++) { // for each row
275 
276  int ind [2] = {2*n + j, 2*n + m + j};
277  double coe [2] = {1., -1.};
278 
279  CoinPackedVector row (2, ind, coe);
280  fplp -> addRow (row, -COIN_DBL_MAX, 0.);
281  }
282 
285 
286  fplp -> setObjSense (-1.); // we want to maximize
287 
288  fplp -> initialSolve ();
289 
290  // Extra pre-solve: if the initial LP is unbounded (that is,
291  // dual-infeasible but not primal-infeasible), then repeat after a
292  // call to BT. This is a cheap way not to throw
293 
294  if (fplp -> isProvenDualInfeasible ()) {
295 
296  problem_ -> Jnlst () -> Printf (J_WARNING, J_BOUNDTIGHTENING, "FPLP unbounded: extra BT\n");
297 
298  if (!(problem_ -> doFBBT ()) && // if FBBT was not applied before (because it was excluded,
299  !(problem_ -> btCore (NULL))) // do a round of FBBT. If infeasible,
300  WipeMakeInfeas (cs); // well, this is infeasible though FPLP will take the merit
301 
302  // Otherwise, reset bounds on auxiliaries based on new bounds in problem_ -> domain ();
303 
304  for (int i=0; i<n; i++) {
305  fplp -> setColLower ( i, problem_ -> Lb (i)); // set lower bound for xL [i]
306  fplp -> setColLower (n+i, problem_ -> Lb (i)); // xU
307  fplp -> setColUpper ( i, problem_ -> Ub (i)); // upper xL
308  fplp -> setColUpper (n+i, problem_ -> Ub (i)); // xU
309  }
310 
311  fplp -> resolve ();
312  }
313 
314  const double
315  *newLB = fplp -> getColSolution (),
316  *newUB = newLB + n;
317 
318  double infeasBounds [] = {1,-1};
319 
320  if (fplp -> isProvenOptimal ()) {
321 
322  // if problem not solved to optimality, bounds are useless
323 
324  // check old and new bounds
325 
326  int
327  *indLB = new int [n],
328  *indUB = new int [n],
329  ntightenedL = 0,
330  ntightenedU = 0;
331 
332  double
333  *valLB = new double [n],
334  *valUB = new double [n];
335 
336  for (int i=0; i<n; i++) {
337 
338  if (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_BOUNDTIGHTENING))
339  printf ("x%d: [%g,%g] --> [%g,%g]\n", i,
340  oldLB [i], oldUB [i],
341  newLB [i], newUB [i]);
342 
343  if (newLB [i] > oldLB [i] + COUENNE_EPS) {
344 
345  indLB [ntightenedL] = i;
346  valLB [ntightenedL++] = newLB [i];
347 
348  ++nTightened_;
349  }
350 
351  if (newUB [i] < oldUB [i] - COUENNE_EPS) {
352 
353  indUB [ntightenedU] = i;
354  valUB [ntightenedU++] = newUB [i];
355 
356  ++nTightened_;
357  }
358  }
359 
360  if (ntightenedL || ntightenedU) {
361 
362  OsiColCut newBound;
363 
364  newBound.setLbs (ntightenedL, indLB, valLB);
365  newBound.setUbs (ntightenedU, indUB, valUB);
366 
367  cs.insert (newBound);
368  }
369 
370  delete [] indLB;
371  delete [] indUB;
372  delete [] valLB;
373  delete [] valUB;
374 
375  CPUtime_ += CoinCpuTime () - now;
376 
377  if (treeInfo.inTree &&
378  treeInfo.level <= 0)
379  problem_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "%d bounds tightened (%g seconds)\n",
380  nTightened_ - nInitTightened, CoinCpuTime () - now);
381 
382  } else if (fplp -> isProvenPrimalInfeasible ()) {
383 
384  if (treeInfo.inTree &&
385  treeInfo.level <= 0)
386  problem_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, " FPLP infeasible.\n");
387 
388  WipeMakeInfeas (cs);
389 
390  newLB = infeasBounds;
391  newUB = infeasBounds + 1;
392 
393  } else {
394 
395  // we won't use the solution from FPLP, and should tell the BT
396  // performance indicator that nothing should change.
397 
398  if (treeInfo.inTree &&
399  treeInfo.level <= 0)
400  problem_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, " FPLP inconclusive, won't be used.\n");
401 
402  newLB = oldLB;
403  newUB = oldUB;
404  }
405 
406  // problem_ -> Jnlst () -> Printf (J_ERROR, J_BOUNDTIGHTENING, "END\n");
407 
408  perfIndicator_. update (newLB, newUB, treeInfo.level);
409  perfIndicator_. addToTimer (CoinCpuTime () - startTime);
410 
411  delete fplp;
412 
413  problem_ -> domain () -> pop ();
414 
415  delete [] oldLB;
416  delete [] oldUB;
417 }
418 
419 
420 // single cut creation. Parameters:
421 //
422 // 1) sign: tells us whether this is a <= or a >= (part of a) constraint.
423 // 2) indexVar: index of variable we want to do upward or downward bt
424 // 3) nVars: number of variables in the original problems (original +
425 // auxiliaries). Used to understand if we are adding an
426 // up or a down constraint
427 // 4) p: solver interface to which we are adding constraints
428 // 5) indices: vector containing indices of the linearization constraint (the i's)
429 // 6) coe: coeffs a_ji's
430 // 7) rhs: right-hand side of constraint
431 // 8) nEl: number of elements of this linearization cut
432 // 9) extMod: extendedModel_
433 // 10) indCon: index of constraint being treated (and corresponding bL, bU)
434 // 11) nCon: number of constraints
435 
436 void CouenneFixPoint::createRow (int sign,
437  int indexVar,
438  int nVars,
439  OsiSolverInterface *p,
440  const int *indices,
441  const double *coe,
442  const double rhs,
443  const int nEl,
444  bool extMod,
445  int indCon,
446  int nCon) const {
447 
549 
550 
551  if (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_BOUNDTIGHTENING)) {
552 
553  printf ("creating constraint from: ");
554 
555  for (int i=0; i<nEl; i++)
556  printf ("%+g x%d ", coe [i], indices [i]);
557 
558  printf ("%c= %g for variable index %d: ", sign > 0 ? '<' : '>', rhs, indexVar);
559  }
560 
561  int nTerms = nEl;
562 
563  if (extMod)
564  nTerms++; // always add one element when using extended model
565 
566  int *iInd = new int [nTerms];
567  double *elem = new double [nTerms];
568 
569  // coefficients are really easy
570 
571  CoinCopyN (coe, nEl, elem);
572 
573  if (extMod) {
574  elem [nEl] = -1.; // extended model, coefficient for bL or bU
575  iInd [nEl] = 2*nVars + indCon + ((sign > 0) ? nCon : 0);
576  }
577 
578  // indices are not so easy...
579 
580  for (int k=0; k<nEl; k++) {
581 
582  int curInd = indices [k];
583 
584  iInd [k] = curInd; // Begin with xL_i, same index as x_i in the
585  // original model. Should add n depending on a
586  // few things...
587 
588  if (curInd == indexVar) { // x_k is x_i itself
589  if (((sign > 0) && (coe [k] > 0.)) ||
590  ((sign < 0) && (coe [k] < 0.)))
591 
592  iInd [k] += nVars;
593 
594  } else if (((coe [k] > 0.) && (sign < 0)) ||
595  ((coe [k] < 0.) && (sign > 0)))
596 
597  iInd [k] += nVars;
598  }
599 
600  CoinPackedVector vec (nTerms, iInd, elem);
601 
602  double
603  lb = sign > 0 ? -COIN_DBL_MAX : extMod ? 0. : rhs,
604  ub = sign < 0 ? +COIN_DBL_MAX : extMod ? 0. : rhs;
605 
606  p -> addRow (vec, lb, ub);
607 
608  // Update time spent doing this
609 
610  if (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_BOUNDTIGHTENING)) {
611 
612  for (int i=0; i<nEl; i++)
613  printf ("%+g x%d ", elem [i], iInd [i]);
614 
615  printf ("in [%g,%g]\n", lb, ub);
616  }
617 
618  // OsiRowCut *cut = new OsiRowCut (lb, ub, nTerms, nTerms, iInd, elem);
619  // cut -> print ();
620  // delete cut;
621 
622  delete [] iInd;
623  delete [] elem;
624 }
bool isWiped(OsiCuts &cs)
Check whether the previous cut generators have added an infeasible cut.
static char * j
Definition: OSdtoa.cpp:3622
const Ipopt::EJournalCategory J_BOUNDTIGHTENING(Ipopt::J_USER2)
void fint fint * k
#define COUENNE_EPS
#define COUENNE_INFINITY
void fint * m
const Ipopt::EJournalCategory J_COUENNE(Ipopt::J_USER8)
void WipeMakeInfeas(OsiCuts &cs)
Add a fictitious cut 1&lt;= x_0 &lt;= -1 as a signal to the node solver that this node is deemed infeasible...
void fint * n