quadCuts.cpp
Go to the documentation of this file.
1 /* $Id: quadCuts.cpp 490 2011-01-14 16:07:12Z pbelotti $
2  *
3  * Name: quadCuts.cpp
4  * Author: Pierre Bonami
5  * Purpose: based on upper and lower convexification, add cuts to convexify
6  *
7  * (C) International Business Machines 2007-10.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CoinHelperFunctions.hpp"
12 
13 #include "CouenneCutGenerator.hpp"
14 
15 #include "CouenneExprQuad.hpp"
16 #include "CouenneProblem.hpp"
17 
18 using namespace Couenne;
19 
20 //#define DEBUG
21 
22 void exprQuad::quadCuts (expression *w, OsiCuts &cs, const CouenneCutGenerator *cg){
23 
24 #ifdef DEBUG
25  std::cout<<"Expression has "<< lcoeff_.size () <<" linear terms and "
26  << nqterms_ <<" quadratic terms." << std::endl;
27 
28  printf ("Q:");
29  for (sparseQ::iterator row = matrix_.begin (); row != matrix_.end (); ++row) {
30  int xind = row -> first -> Index ();
31  for (sparseQcol::iterator col = row -> second.begin (); col != row -> second.end (); ++col)
32  printf (" <%d,%d,%g>", xind, col -> first -> Index (), col -> second);
33  }
34 
35  printf ("\nb:");
36  for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el)
37  //for (int i=0; i < nlterms_; i++)
38  printf (" <%d,%g>", el -> first -> Index (), el -> second);//index_ [i], coeff_ [i]);
39 
40  if (c0_)
41  printf ("; <c0 = %g>", c0_);
42 
43  printf ("\nBounds: var val lb ub eigval scaled\n");
44 
45  int index = 0;
46 
47  for (std::map <exprVar *, std::pair <CouNumber, CouNumber> >::iterator i = bounds_.begin ();
48  i != bounds_.end (); ++i, index++) {
49 
50  printf ("%3d:\t", index);
51  i -> first -> print (); printf ("\t");
52  printf (" %8g [%8g, %8g]",
53  (*(i -> first)) (), i -> second.first, i -> second.second);
54 
55  CouNumber
56  lb = cg -> Problem () -> Lb (i -> first -> Index ()),
57  ub = cg -> Problem () -> Ub (i -> first -> Index ());
58 
59  if ((eigen_.size () > 0) &&
60  (fabs (ub-lb) > COUENNE_EPS))
61  printf (" --> %8g %8g",
62  eigen_.begin () -> first,
63  eigen_.begin () -> first / (ub-lb));
64 
65  printf ("\n");
66  }
67 #endif
68 
69  // Get on which side constraint is violated to get the good lambda
70 
71  CouNumber
72  varVal = (*w) (),
73  exprVal = (*this) (),
74  lambda =
75  (eigen_.size () == 0) ? 0. :
76  (varVal < exprVal) ?
77  CoinMin (0., eigen_.begin () -> first) : // Use under-estimator
78  CoinMax (0., eigen_.rbegin () -> first), // Use over-estimator
79  convVal = 0.;
80 
81  enum auxSign sign = cg -> Problem () -> Var (w -> Index ()) -> sign ();
82 
83  // if this is a "semi"-auxiliary, check if necessary to separate
84  if ((sign == expression::AUX_GEQ && varVal > exprVal) ||
85  (sign == expression::AUX_LEQ && varVal < exprVal))
86  return;
87 
88  const CouenneProblem& problem = *(cg -> Problem ());
89  const int numcols = problem.nVars ();
90 
91  const double
92  *colsol = problem.X (), // current solution
93  *lower = problem.Lb (), // lower bound
94  *upper = problem.Ub (); // upper
95 
96  // compute lower or upper convexification and check if it contains
97  // the current point
98 
99  if (fabs (lambda) > COUENNE_EPS) {
100 
101  convVal = exprVal;
102 
103  // there is a convexification, check if out of current point
104 
105  for (std::map <exprVar *, std::pair <CouNumber, CouNumber> >::iterator i = bounds_.begin ();
106  i != bounds_.end (); ++i) {
107 
108  int ind = i -> first -> Index ();
109 
110  CouNumber
111  xi = colsol [ind],
112  lb = lower [ind],
113  ub = upper [ind],
114  delta = ub-lb;
115 
116  if (fabs (delta) > COUENNE_EPS)
117  convVal += lambda * (xi-lb) * (ub-xi) / (delta * delta);
118  }
119 
120  if (varVal < exprVal) {if (convVal < varVal) return;}
121  else {if (convVal > varVal) return;}
122  }
123 
124 #ifdef DEBUG
125  std::cout << "Point to cut: ";
126  for (int i = 0 ; i < numcols ; i++) std::cout << colsol [i] << ", ";
127  printf (" (w,f(x),c) = (%g,%g,%g) -- lambda = %g\n", (*w) (), exprVal, convVal, lambda);
128 #endif
129 
130  // Initialize by copying $a$ into a dense vector and computing Q x^*
131  double
132  *Qxs = new double [numcols], // sparse coefficient vector, $Qx^*$
133  a0 = -c0_; // constant term
134 
135  CoinFillN (Qxs, numcols, 0.);
136 
137  // Compute 2 * Q x^*.
138  for (sparseQ::iterator row = matrix_.begin (); row != matrix_.end (); ++row) {
139 
140  int qi = row -> first -> Index ();
141 
142  for (sparseQcol::iterator col = row -> second.begin (); col != row -> second.end (); ++col) {
143 
144  int qj = col -> first -> Index ();
145 
146  CouNumber
147  qc = col -> second,
148  xi = colsol [qi],
149  xj = colsol [qj];
150 
151  if (qi != qj) {
152  Qxs [qi] += qc * xj; // contribution of element $q_{ij}$ to (Qx)_i
153  Qxs [qj] += qc * xi; // $q_{ij}$ (Qx)_j
154  a0 += 2 * qc * xi * xj;
155  }
156  else {
157  /*
158  if (fabs (lambda) > COUENNE_EPS) {
159 
160  CouNumber
161  lb = lower [qi],
162  ub = upper [qi],
163  delta = ub-lb;
164 
165  if (fabs (delta) > COUENNE_EPS)
166  qc -= lambda / (delta*delta);
167  }
168  */
169  // elements on the diagonal are not halved upon reading
170  a0 += qc * xi * xi;
171  Qxs [qi] += 2 * qc * xi;
172  }
173  }
174  }
175 
176 #ifdef DEBUG
177  printf ("2Qx = ("); for (int i = 0; i < numcols; i++) printf ("%g ", Qxs [i]); printf (")\n");
178 #endif
179 
180  // Add a to it.
181  for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el)
182  Qxs [el -> first -> Index ()] += el -> second; //coeff_ [i];
183 
184  // multiply Qx^* by x^*^T again and store the result for the lower
185  // bound into constant term
186 
187  /*
188  for (int i=0; i < numcols; i++){
189  a0 -= 0.5 * Qxs [i] * colsol [i];
190  // Qxs [i] *= 2;
191  }
192  */
193 
194  // And myself
195  Qxs [w -> Index ()] -= 1;
196 
197 #ifdef DEBUG
198  printf ("2Qx = ("); for(int i = 0; i < numcols; i++) printf ("%g ", Qxs [i]); printf (")[%g]\n",a0);
199 #endif
200 
201  //a0 -= exprVal;
202 
203  if (fabs (lambda) > COUENNE_EPS) // Now the part which depends on lambda, if there is one
204 
205  for (std::map <exprVar *, std::pair <CouNumber, CouNumber> >::iterator i = bounds_.begin ();
206  i != bounds_.end (); ++i) {
207 
208  int ind = i -> first -> Index ();
209 
210  CouNumber
211  xi = colsol [ind],
212  lb = lower [ind],
213  ub = upper [ind],
214  delta = ub-lb;
215 
216  if (fabs (delta) > COUENNE_EPS) {
217 
218  CouNumber normlambda = lambda / (delta*delta),
219  coeff = normlambda * (lb + ub - 2. * xi);
220 
221  a0 += normlambda * (lb*ub - xi*xi);
222 
223  //a0 += coeff * xi - normlambda * (xi - lb) * (ub - xi);
224  //a0 += normlambda * lb * ub;
225  Qxs [ind] += coeff;
226  //Qxs [ind] += normlambda * (lb + ub);
227  }// else coeff = 0.;
228 
229  // a0 += lambda [k] * lower [ind] * upper [ind];
230  // a0 -= lambda [k] * colsol [ind] * colsol [ind];
231 
232  //Qxs [ind] -= lambda [k] * (colsol [ind]) * 2;
233  }
234 
235  // Count the number of non-zeroes
236  int nnz = 0;
237  for (int i=0; i < numcols ; i++)
238  if (fabs (Qxs [i]) > COUENNE_EPS)
239  nnz++;
240 
241 #ifdef DEBUG
242  printf ("2Qx = (");for(int i=0;i<numcols;i++)printf("%g ",Qxs[i]);printf (")[%g], %d nz\n",a0,nnz);
243 #endif
244 
245  // Pack the vector into a CoinPackedVector and generate the cut.
246  CoinPackedVector a (false);
247  a.reserve (nnz);
248 
249 #ifdef DEBUG
250  CouNumber lhs = 0, lhsc = 0,
251  *optimum = cg -> Problem () -> bestSol (),
252  *current = cg -> Problem () -> X ();
253 #endif
254 
255  for (int i=0; i < numcols; i++)
256 
257  if (fabs (Qxs [i]) > 1.0e-21) { // why 1.0e-21? Look at CoinPackedMatrix.cpp:2188
258 
259  // compute violation
260 #ifdef DEBUG
261  if (optimum) {
262  printf ("%+g * %g ", Qxs [i], optimum [i]);
263  lhs += Qxs [i] * optimum [i];
264  }
265  lhsc += Qxs [i] * current [i];
266 #endif
267  a.insert (i, Qxs [i]);
268  }
269 
270  OsiRowCut cut;
271  cut.setRow (a);
272 
273  delete [] Qxs;
274 
275  if (varVal < exprVal) { //(lambda == dCoeffLo_) {
276 
277  cut.setUb (a0);
278 
279 #ifdef DEBUG
280  if (optimum && (lhs - a0 > COUENNE_EPS)) {
281  printf ("cut violates optimal solution: %g > %g\n", lhs, a0);
282  cut.print ();
283  }
284  if (lhsc < a0 + COUENNE_EPS){
285  printf ("cut (+) is not cutting: ");
286  cut.print ();
287  }
288 #endif
289  // cut.setLb(-COUENNE_INFINITY);
290  }
291  else {
292 
293  cut.setLb (a0);
294 #ifdef DEBUG
295  if (optimum && (lhs - a0 < -COUENNE_EPS)) {
296  printf ("cut violates optimal solution: %g < %g\n", lhs, a0);
297  cut.print ();
298  }
299  if (lhsc > a0 - COUENNE_EPS){
300  printf ("cut (-) is not cutting: ");
301  cut.print ();
302  }
303 #endif
304  // cut.setUb(COUENNE_INFINITY);
305  }
306 
307  cs.insert (cut);
308 }
Cut Generator for linear convexifications.
CouNumber & Ub(int i) const
upper bound on
int nVars() const
Total number of variables.
void fint fint fint real * a
CouNumber c0_
constant term
CouNumber & X(int i) const
CouNumber & Lb(int i) const
lower bound on
int nqterms_
number of non-zeroes in Q
void fint fint fint real fint real real real real real real real real real * e
virtual int Index() const
Return index of variable (only valid for exprVar and exprAux)
void quadCuts(expression *w, OsiCuts &cs, const CouenneCutGenerator *cg)
method exprQuad::quadCuts
Definition: quadCuts.cpp:22
auxSign
&quot;sign&quot; of the constraint defining an auxiliary.
lincoeff lcoeff_
coefficients and indices of the linear term
Class for MINLP problems with symbolic information.
std::vector< std::pair< CouNumber, std::vector< std::pair< exprVar *, CouNumber > > > > eigen_
eigenvalues and eigenvectors
#define COUENNE_EPS
std::map< exprVar *, std::pair< CouNumber, CouNumber > > bounds_
current bounds (checked before re-computing eigenvalues/vectors)
double CouNumber
main number type in Couenne
variable-type operator
Expression base class.
int nnz
ATTENTION: Filter expect the jacobian to be ordered by row.
void fint fint fint real fint real real real real real real real real * w
virtual void print(std::ostream &=std::cout, bool=false) const
Print expression to an iostream.
Definition: exprQuad.cpp:178