1 /* $Id$
2  *
3  * Name:    generateCuts.cpp
4  * Author:  Pietro Belotti
5  * Purpose: the generateCuts() method of the convexification class CouenneCutGenerator
6  *
7  * (C) Carnegie-Mellon University, 2006-11.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "BonCbc.hpp"
12 #include "BonBabInfos.hpp"
13 #include "CglCutGenerator.hpp"
14 
15 #include "CouenneCutGenerator.hpp"
16 #include "CouenneProblem.hpp"
17 #include "CouenneProblemElem.hpp"
18 #include "CouenneExprVar.hpp"
19 #include "CouenneInfeasCut.hpp"
20 
21 #include "CouenneRecordBestSol.hpp"
22 
23 //#define FM_PRINT_INFO
24 
25 #ifdef COIN_HAS_NTY
26 #include "Nauty.h"
27 #endif
28 
29 using namespace Ipopt;
30 
31 namespace Couenne {
32 
33 #define Couenne_large_bound2 9.99e12
34 
35 // checks bad cuts against known optimum
36 bool isOptimumCut (const CouNumber *opt, OsiCuts &cs, CouenneProblem *p);
37 
38 // set and lift bound for auxiliary variable associated with objective
39 // function
fictitiousBound(OsiCuts & cs,CouenneProblem * p,bool action)40 void fictitiousBound (OsiCuts &cs,
41 		      CouenneProblem *p,
42 		      bool action) {     // true before convexifying, false afterwards
43 
44   // fictitious bound for initial unbounded lp relaxations
45   const CouNumber large_tol = (Couenne_large_bound2 / 1e6);
46 
47   // set trivial dual bound to objective function, if there is none
48 
49   int ind_obj = p -> Obj (0) -> Body () -> Index ();
50 
51   if (ind_obj < 0) return;
52 
53   // we have a single variable objective function
54 
55   //int sense = -1; //(p -> Obj (0) -> Sense () == MINIMIZE) ? -1 : 1;
56 
57   if (action)
58     //if (sense<0)
59       {if (p -> Lb (ind_obj) < - Couenne_large_bound2) p -> Lb (ind_obj) = - Couenne_large_bound2;}
60   //else         {if (p -> Ub (ind_obj) >   large_bound2) p -> Ub (ind_obj) =   large_bound2;}
61   else
62     //if (sense>0) {if (fabs (p->Ub(ind_obj)-large_bound2)<large_tol) p->Ub(ind_obj)=COUENNE_INFINITY;}
63     //else
64       {if (fabs (p->Lb(ind_obj)+Couenne_large_bound2)<large_tol) p->Lb(ind_obj) =-COUENNE_INFINITY;}
65 }
66 
67 
68 // translate changed bound sparse array into a dense one
sparse2dense(int ncols,t_chg_bounds * chg_bds,int * & changed,int & nchanged)69 void sparse2dense (int ncols, t_chg_bounds *chg_bds, int *&changed, int &nchanged) {
70 
71   // convert sparse chg_bds in something handier
72 
73   changed  = (int *) realloc (changed, ncols * sizeof (int));
74   nchanged = 0;
75 
76   for (register int i=ncols, j=0; i--; j++, chg_bds++)
77     if (chg_bds -> lower() != t_chg_bounds::UNCHANGED ||
78 	chg_bds -> upper() != t_chg_bounds::UNCHANGED ) {
79       *changed++ = j;
80       nchanged++;
81     }
82 
83   changed -= nchanged;
84   //changed = (int *) realloc (changed, nchanged * sizeof (int));
85 }
86 
87 
88 /// get new bounds from parents' bounds + branching rules
89 void updateBranchInfo (const OsiSolverInterface &si, CouenneProblem *p,
90 		       t_chg_bounds *chg, const CglTreeInfo &info);
91 
92 /// a convexifier cut generator
93 
generateCuts(const OsiSolverInterface & si,OsiCuts & cs,const CglTreeInfo info) const94 void CouenneCutGenerator::generateCuts (const OsiSolverInterface &si,
95 					OsiCuts &cs,
96 					const CglTreeInfo info)
97 #if CGL_VERSION_MAJOR == 0 && CGL_VERSION_MINOR <= 57
98   const
99 #endif
100   {
101 
102   // if si.lb(objInd) > cutoff,
103   //   return infeasCut
104 
105   int indObj = problem_ -> Obj (0) -> Body () -> Index ();
106 
107   if ((indObj >= 0) &&
108       (si.getColLower () [indObj] > problem_ -> getCutOff () + COUENNE_EPS)) {
109 
110     WipeMakeInfeas (cs);
111     return;
112   }
113 
114   // check if out of time or if an infeasibility cut (iis of type 0)
115   // was added as a result of, e.g., pruning on BT. If so, no need to
116   // run this.
117 
118   if (isWiped (cs) ||
119      (CoinCpuTime () > problem_ -> getMaxCpuTime ()))
120     return;
121 
122 #ifdef FM_TRACE_OPTSOL
123   double currCutOff = problem_->getCutOff();
124   double bestVal = 1e50;
125   CouenneRecordBestSol *rs = problem_->getRecordBestSol();
126   if(rs->getHasSol()) {
127     bestVal = rs->getVal();
128   }
129   if(currCutOff > bestVal) {
130     //problem_ -> setCutOff (bestVal - 1e-6); // FIXME: don't add numerical constants
131     problem_ -> setCutOff (bestVal);
132 
133     if ((indObj >= 0) && (si. getColUpper () [indObj] > bestVal)) {
134       OsiColCut *objCut = new OsiColCut;
135       objCut->setUbs(1, &indObj, &bestVal);
136       cs.insert(objCut);
137       delete objCut;
138     }
139   }
140 #endif
141 
142 #ifdef FM_PRINT_INFO
143   if((BabPtr_ != NULL) && (info.level >= 0) && (info.pass == 0) &&
144      (BabPtr_->model().getNodeCount() > lastPrintLine)) {
145     printLineInfo();
146     lastPrintLine += 1;
147   }
148 #endif
149 
150   const int infeasible = 1;
151 
152   int nInitCuts = cs.sizeRowCuts ();
153 
154   CouNumber
155     *&realOpt = problem_ -> bestSol (),
156     *saveOptimum = realOpt;
157 
158   if (!firstcall_ && realOpt) {
159 
160     // have a debug optimal solution. Check if current bounds
161     // contain it, otherwise pretend it does not exist
162 
163     CouNumber *opt = realOpt;
164 
165     const CouNumber
166       *sol = si.getColSolution (),
167       *lb  = si.getColLower (),
168       *ub  = si.getColUpper ();
169 
170     int objind = problem_ -> Obj (0) -> Body () -> Index ();
171 
172     for (int j=0, i=problem_ -> nVars (); i--; j++, opt++, lb++, ub++)
173       if ((j != objind) &&
174 	  ((*opt < *lb - COUENNE_EPS * (1 + CoinMin (fabs (*opt), fabs (*lb)))) ||
175 	   (*opt > *ub + COUENNE_EPS * (1 + CoinMin (fabs (*opt), fabs (*ub)))))) {
176 
177 	jnlst_ -> Printf (J_VECTOR, J_CONVEXIFYING,
178 			  "out of bounds, ignore x%d = %g [%g,%g] opt = %g\n",
179 			  problem_ -> nVars () - i - 1, *sol, *lb, *ub, *opt);
180 
181 	// optimal point is not in current bounding box,
182 	// pretend realOpt is NULL until we return from this procedure
183 	realOpt = NULL;
184 	break;
185       }
186   }
187 
188   /*static int count = 0;
189   char fname [20];
190   sprintf (fname, "relax_%d", count++);
191   si.writeLp (fname);
192   printf ("writing %s\n", fname);*/
193 
194   jnlst_ -> Printf (J_DETAILED, J_CONVEXIFYING,
195 		    "generateCuts: level = %d, pass = %d, intree = %d\n",
196 		    info.level, info.pass, info.inTree);
197 
198   Bonmin::BabInfo * babInfo = dynamic_cast <Bonmin::BabInfo *> (si.getAuxiliaryInfo ());
199 
200   if (babInfo)
201     babInfo -> setFeasibleNode ();
202 
203   double now   = CoinCpuTime ();
204   int    ncols = problem_ -> nVars ();
205 
206   // This vector contains variables whose bounds have changed due to
207   // branching, reduced cost fixing, or bound tightening below. To be
208   // used with malloc/realloc/free
209 
210   t_chg_bounds *chg_bds = new t_chg_bounds [ncols];
211 
212   /*for (int i=0; i < ncols; i++)
213     if (problem_ -> Var (i) -> Multiplicity () <= 0) {
214       chg_bds [i].setLower (t_chg_bounds::UNCHANGED);
215       chg_bds [i].setUpper (t_chg_bounds::UNCHANGED);
216       }*/
217 
218   problem_ -> installCutOff (); // install upper bound
219 
220   if (firstcall_) {
221 
222     // First convexification //////////////////////////////////////
223 
224     // OsiSolverInterface is empty yet, no information can be obtained
225     // on variables or bounds -- and none is needed since our
226     // constructor populated *problem_ with variables and bounds. We
227     // only need to update the auxiliary variables and bounds with
228     // their current value.
229 
230     for (int i=0; i < ncols; i++)
231       if (problem_ -> Var (i) -> Multiplicity () > 0) {
232 	chg_bds [i].setLower (t_chg_bounds::CHANGED);
233 	chg_bds [i].setUpper (t_chg_bounds::CHANGED);
234       }
235 
236     // start with FBBT, should take advantage of cutoff found by NLP
237     // run AFTER initial FBBT...
238     if (problem_ -> doFBBT () &&
239 	(! (problem_ -> boundTightening (chg_bds, info, babInfo))))
240           jnlst_ -> Printf (J_STRONGWARNING, J_CONVEXIFYING,
241             "Couenne: WARNING, first convexification is infeasible\n");
242 
243     // For each auxiliary variable replacing the original (nonlinear)
244     // constraints, check if corresponding bounds are violated, and
245     // add cut to cs
246 
247     int nnlc = problem_ -> nCons ();
248 
249     for (int i=0; i<nnlc; i++) {
250 
251       if (CoinCpuTime () > problem_ -> getMaxCpuTime ())
252 	break;
253 
254       // for each constraint
255       CouenneConstraint *con = problem_ -> Con (i);
256 
257       // (which has an aux as its body)
258       int objindex = con -> Body () -> Index ();
259 
260       if ((objindex >= 0) &&
261 	  ((con -> Body () -> Type () == AUX) ||
262 	   (con -> Body () -> Type () == VAR))) {
263 
264 	// get the auxiliary that is at the lhs
265 	exprVar *conaux = problem_ -> Var (objindex);
266 
267 	if (conaux &&
268 	    (conaux -> Type () == AUX) &&
269 	    (conaux -> Image ()) &&
270 	    (conaux -> Image () -> Linearity () <= LINEAR)) {
271 
272 	  // reduce density of problem by adding w >= l rather than
273 	  // ax + b >= l for any linear auxiliary defined as w := ax+b
274 
275 	  double
276 	    lb = (*(con -> Lb ())) (),
277 	    ub = (*(con -> Ub ())) ();
278 
279 	  OsiColCut newBound;
280 	  if (lb > -COUENNE_INFINITY) newBound.setLbs (1, &objindex, &lb);
281 	  if (ub <  COUENNE_INFINITY) newBound.setUbs (1, &objindex, &ub);
282 
283 	  cs.insert (newBound);
284 
285 	  // the auxiliary w of constraint w <= b is associated with a
286 	  // linear expression w = ax: add constraint ax <= b
287 	  /*conaux -> Image () -> generateCuts (conaux, si, cs, this, chg_bds,
288 					      conaux -> Index (),
289 					      (*(con -> Lb ())) (),
290 					      (*(con -> Ub ())) ());*/
291 
292 	  // take it from the list of the variables to be linearized
293 	  //
294 	  // DO NOT decrease multiplicity. Even if it is a linear
295 	  // term, its bounds can still be used in implied bounds
296 	  //
297 	  // Are we sure? That will happen only if its multiplicity is
298 	  // nonzero, for otherwise this aux is only used here, and is
299 	  // useless elsewhere
300 	  //
301 	  //conaux -> decreaseMult (); // !!!
302 	}
303 
304 	// also, add constraint w <= b
305 
306 	// not now, do it later
307 
308 // 	// if there exists violation, add constraint
309 // 	CouNumber l = con -> Lb () -> Value (),
310 // 	          u = con -> Ub () -> Value ();
311 
312 // 	// tighten bounds in Couenne's problem representation
313 // 	problem_ -> Lb (index) = CoinMax (l, problem_ -> Lb (index));
314 // 	problem_ -> Ub (index) = CoinMin (u, problem_ -> Ub (index));
315 
316       } else { // body is more than just a variable, but it should be
317 	       // linear. If so, generate equivalent linear cut
318 
319 	assert (false);	// TODO
320       }
321     }
322 
323     if (jnlst_ -> ProduceOutput (J_ITERSUMMARY, J_CONVEXIFYING)) {
324       if (cs.sizeRowCuts ()) {
325 	jnlst_ -> Printf (J_ITERSUMMARY, J_CONVEXIFYING,"Couenne: %d constraint row cuts\n",
326 			  cs.sizeRowCuts ());
327 	for (int i=0; i<cs.sizeRowCuts (); i++)
328 	  cs.rowCutPtr (i) -> print ();
329       }
330       if (cs.sizeColCuts ()) {
331 	jnlst_ -> Printf (J_ITERSUMMARY, J_CONVEXIFYING,"Couenne: %d constraint col cuts\n",
332 			  cs.sizeColCuts ());
333 	for (int i=0; i<cs.sizeColCuts (); i++)
334 	  cs.colCutPtr (i) -> print ();
335       }
336     }
337   } else {
338 
339     // use new optimum as lower bound for variable associated w/objective
340 
341     // transmit solution from OsiSolverInterface to problem
342     problem_ -> domain () -> push (&si, &cs);
343 
344     if (indObj >= 0) {
345 
346       // Use current value of objvalue's x as a lower bound for bound
347       // tightening
348       double lp_bound = problem_ -> domain () -> x (indObj);
349 
350       //if (problem_ -> Obj (0) -> Sense () == MINIMIZE)
351       {if (lp_bound > problem_ -> Lb (indObj)) problem_ -> Lb (indObj) = lp_bound;}
352 	   //else {if (lp_bound < problem_ -> Ub (indObj)) problem_ -> Ub (indObj) = lp_bound;}
353     }
354 
355     updateBranchInfo (si, problem_, chg_bds, info); // info.depth >= 0 || info.pass >= 0
356   }
357 
358   // restore constraint bounds before tightening and cut generation
359   for (int i = problem_ -> nCons (); i--;) {
360 
361     // for each constraint
362     CouenneConstraint *con = problem_ -> Con (i);
363 
364     // (which has an aux as its body)
365     int objindex = con -> Body () -> Index ();
366 
367     if ((objindex >= 0) &&
368 	((con -> Body () -> Type () == AUX) ||
369 	 (con -> Body () -> Type () == VAR))) {
370 
371       // if there exists violation, add constraint
372       CouNumber
373 	l = con -> Lb () -> Value (),
374 	u = con -> Ub () -> Value ();
375 
376       // tighten bounds in Couenne's problem representation
377       problem_ -> Lb (objindex) = CoinMax (l, problem_ -> Lb (objindex));
378       problem_ -> Ub (objindex) = CoinMin (u, problem_ -> Ub (objindex));
379     }
380   }
381 
382   problem_ -> installCutOff (); // install upper bound
383 
384   fictitiousBound (cs, problem_, false); // install finite lower bound, if currently -inf
385 
386   int *changed = NULL, nchanged;
387 
388   // Bound tightening ///////////////////////////////////////////
389 
390   // do bound tightening only at first pass of cutting plane in a node
391   // of BB tree (info.pass == 0) or if first call (creation of RLT,
392   // info.pass == -1)
393 
394   try {
395 
396     // Before bound tightening, compute symmetry group. After bound
397     // tightening is done, we can apply further tightening using orbit
398     // information.
399 
400 // #ifdef COIN_HAS_NTY
401 //     //    ChangeBounds (psi -> getColLower (),
402 //     //		  psi -> getColUpper (),
403 //     //		  psi -> getNumCols ());
404 
405 //     if (problem_ -> orbitalBranching ()){
406 
407 //       problem_ -> ChangeBounds (problem_ -> Lb (),
408 // 				problem_ -> Ub (),
409 // 				problem_ -> nVars ());
410 
411 //       problem_ -> Compute_Symmetry ();
412 //     }
413 // #endif
414 
415     // Bound tightening ////////////////////////////////////
416 
417     //bool is_feas = p -> btCore (chg_bds);
418 
419     // Reduced Cost BT -- to be done first to use rcost correctly
420     if (!firstcall_  &&                         // have a linearization already
421 	problem_ -> doRCBT () &&                // authorized to do reduced cost tightening
422 	problem_ -> redCostBT (&si, chg_bds) && // some variables were tightened with reduced cost
423 	!(problem_ -> btCore (chg_bds)))        // in this case, do another round of FBBT
424       throw infeasible;
425 
426     // FBBT
427     if (problem_ -> doFBBT () &&
428 	//(info.pass <= 0) && // do it in subsequent rounds too
429 	(! (problem_ -> boundTightening (chg_bds, info, babInfo))))
430       throw infeasible;
431 
432     // OBBT
433     if (!firstcall_ && // no obbt if first call (there is no LP to work with)
434 	problem_ -> obbt (this, si, cs, info, babInfo, chg_bds) < 0)
435       throw infeasible;
436 
437     // Bound tightening done /////////////////////////////
438 
439     if ((problem_ -> doFBBT () ||
440 	 problem_ -> doOBBT () ||
441 	 problem_ -> doABT  ()) &&
442 	(jnlst_ -> ProduceOutput (J_VECTOR, J_CONVEXIFYING))) {
443 
444       jnlst_ -> Printf(J_VECTOR, J_CONVEXIFYING,"== after bt =============\n");
445       for (int i = 0; i < problem_ -> nVars (); i++)
446 	if (problem_ -> Var (i) -> Multiplicity () > 0)
447 	  jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"%4d %+20.8g [%+20.8g,%+20.8g]\n", i,
448 			 problem_ -> X  (i), problem_ -> Lb (i), problem_ -> Ub (i));
449       jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"=============================\n");
450     }
451 
452     // Use orbit info to tighten bounds
453 
454 #ifdef COIN_HAS_NTY
455 
456     // TODO: when independent bound tightener, can get original bounds
457     // through si.getCol{Low,Upp}er()
458 
459     if (problem_ -> orbitalBranching () && !firstcall_) {
460 
461       CouNumber
462 	*lb = problem_ -> Lb (),
463 	*ub = problem_ -> Ub ();
464 
465       std::vector<std::vector<int> > *new_orbits = problem_ -> getNtyInfo () -> getOrbits();
466 
467       for (int i=0, ii = problem_ -> getNtyInfo () -> getNumOrbits (); ii--; i++){
468 
469 	CouNumber
470 	  ll = -COUENNE_INFINITY,
471 	  uu =  COUENNE_INFINITY;
472 
473 	std::vector <int> orbit = (*new_orbits)[i];
474 
475 	if (orbit.size () <= 1)
476 	  continue; // not much to do when only one variable in this orbit
477 
478 	if (jnlst_ -> ProduceOutput (J_VECTOR, J_BOUNDTIGHTENING)) {
479 	  printf ("orbit bounds: "); fflush (stdout);
480 	  for(int j = 0; j < orbit.size (); j++) {
481 	    printf ("x_%d [%g,%g] ", orbit[j], lb [orbit [j]], ub [orbit [j]]);
482 	    fflush (stdout);
483 	  }
484 	}
485 
486 	for (int j = 0; j < orbit.size (); j++) {
487 
488 	  int indOrb = orbit [j];
489 
490 	  if (indOrb < problem_ -> nVars ()) {
491 
492 	    if (lb [indOrb] > ll) ll = lb [indOrb];
493 	    if (ub [indOrb] < uu) uu = ub [indOrb];
494 	  }
495 	}
496 
497 	jnlst_ -> Printf (J_VECTOR, J_BOUNDTIGHTENING,
498 			  " --> new common bounds: [%g,%g]\n", ll, uu);
499 
500 	for(int j = 0; j < orbit.size (); j++) {
501 
502 	  int indOrb = orbit [j];
503 
504 	  if (indOrb < problem_ -> nVars ()){
505 
506 	    lb [indOrb] = ll;
507 	    ub [indOrb] = uu;
508 	  }
509 	}
510       }
511 
512       delete new_orbits;
513     }
514 
515 #endif
516 
517     // Generate convexification cuts //////////////////////////////
518 
519     sparse2dense (ncols, chg_bds, changed, nchanged);
520 
521     double *nlpSol = NULL;
522 
523     //--------------------------------------------
524 
525     if (true) {
526 
527       if (babInfo)
528 	nlpSol = const_cast <double *> (babInfo -> nlpSolution ());
529 
530       // Aggressive Bound Tightening ////////////////////////////////
531 
532       int logAbtLev = problem_ -> logAbtLev ();
533 
534       if (problem_ -> doABT () &&             // flag is checked, AND
535 	  ((logAbtLev != 0) ||                // (parameter is nonzero OR
536 	   (info.level == 0)) &&              //  we are at root node), AND
537 	  (info.pass == 0) &&                 // at first round of cuts, AND
538 	  ((logAbtLev < 0) ||                 // (logAbtLev = -1, OR
539 	   (info.level <= logAbtLev) ||       //  depth is lower than COU_OBBT_CUTOFF_LEVEL, OR
540 	   (CoinDrand48 () <                  //  probability inversely proportional to the level)
541 	    pow (2., (double) logAbtLev - (info.level + 1))))) {
542 
543 	jnlst_ -> Printf(J_VECTOR, J_BOUNDTIGHTENING,"  performing ABT\n");
544 	if (! (problem_ -> aggressiveBT (nlp_, chg_bds, info, babInfo)))
545 	  throw infeasible;
546 
547 	sparse2dense (ncols, chg_bds, changed, nchanged);
548       }
549 
550       // obtain solution just found by nlp solver
551 
552       // Auxiliaries should be correct. solution should be the one found
553       // at the node even if not as good as the best known.
554 
555       // save violation flag and disregard it while adding cut at NLP
556       // point (which are not violated by the current, NLP, solution)
557       bool save_av = addviolated_;
558       addviolated_ = false;
559 
560       // save values
561       problem_ -> domain () -> push
562 	(problem_ -> nVars (),
563 	 problem_ -> domain () -> x  (),
564 	 problem_ -> domain () -> lb (),
565 	 problem_ -> domain () -> ub (), false);
566 
567       // fill originals with nlp values
568       if (nlpSol) {
569 	CoinCopyN (nlpSol, problem_ -> nOrigVars (), problem_ -> domain () -> x ());
570       //problem_ -> initAuxs ();
571 
572       problem_ -> getAuxs (problem_ -> domain () -> x ());
573       }
574 
575       if (jnlst_ -> ProduceOutput (J_VECTOR, J_CONVEXIFYING)) {
576 	jnlst_ -> Printf(J_VECTOR, J_CONVEXIFYING,"== genrowcuts on NLP =============\n");
577 	for (int i = 0; i < problem_ -> nVars (); i++)
578 	  if (problem_ -> Var (i) -> Multiplicity () > 0)
579 	    jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"%4d %+20.8g [%+20.8g,%+20.8g]\n", i,
580 			   problem_ -> X  (i),
581 			   problem_ -> Lb (i),
582 			   problem_ -> Ub (i));
583 	jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"=============================\n");
584       }
585 
586       problem_ -> domain () -> current () -> isNlp () = true;
587       genRowCuts (si, cs, nchanged, changed, chg_bds);  // add cuts
588 
589       problem_ -> domain () -> pop (); // restore point
590 
591       addviolated_ = save_av;     // restore previous value
592 
593       //    if (!firstcall_) // keep solution if called from extractLinearRelaxation()
594       if (babInfo)
595 	babInfo -> setHasNlpSolution (false); // reset it after use //AW HERE
596 
597     } else {
598 
599       if (jnlst_ -> ProduceOutput (J_VECTOR, J_CONVEXIFYING)) {
600 	jnlst_ -> Printf(J_VECTOR, J_CONVEXIFYING,"== genrowcuts on LP =============\n");
601 	for (int i = 0; i < problem_ -> nVars (); i++)
602 	  if (problem_ -> Var (i) -> Multiplicity () > 0)
603 	    jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"%4d %+20.8g [%+20.8g,%+20.8g]\n", i,
604 			   problem_ -> X  (i),
605 			   problem_ -> Lb (i),
606 			   problem_ -> Ub (i));
607 	jnlst_->Printf(J_VECTOR, J_CONVEXIFYING,"=============================\n");
608       }
609 
610       genRowCuts (si, cs, nchanged, changed, chg_bds);
611     }
612 
613     // change tightened bounds through OsiCuts
614     if (nchanged)
615       genColCuts (si, cs, nchanged, changed);
616 
617     if (firstcall_ && (cs.sizeRowCuts () >= 1))
618       jnlst_->Printf(J_ITERSUMMARY, J_CONVEXIFYING,
619 		     "Couenne: %d initial row cuts\n", cs.sizeRowCuts ());
620 
621     if (realOpt && // this is a good time to check if we have cut the optimal solution
622 	isOptimumCut (realOpt, cs, problem_))
623       jnlst_->Printf(J_ITERSUMMARY, J_CONVEXIFYING,
624 		     "Warning: Optimal solution was cut\n");
625   }
626 
627   catch (int exception) {
628 
629     if ((exception == infeasible) && (!firstcall_)) {
630 
631       jnlst_ -> Printf (J_ITERSUMMARY, J_CONVEXIFYING,
632 			"Couenne: Infeasible node\n");
633 
634       WipeMakeInfeas (cs);
635     }
636 
637     if (babInfo) // set infeasibility to true in order to skip NLP heuristic
638       babInfo -> setInfeasibleNode ();
639   }
640 
641   delete [] chg_bds;
642 
643   if (changed)
644     free (changed);
645 
646   if (firstcall_) {
647 
648     jnlst_ -> Printf (J_SUMMARY, J_CONVEXIFYING,
649 		      "Couenne: %d cuts (%d row, %d col) for linearization\n",
650 		      cs.sizeRowCuts () + cs.sizeColCuts (),
651 		      cs.sizeRowCuts (),  cs.sizeColCuts ());
652 
653     fictitiousBound (cs, problem_, true);
654     firstcall_  = false;
655     ntotalcuts_ = nrootcuts_ = cs.sizeRowCuts ();
656 
657   } else {
658 
659     problem_ -> domain () -> pop ();
660 
661     ntotalcuts_ += (cs.sizeRowCuts () - nInitCuts);
662 
663     if (saveOptimum)
664       realOpt = saveOptimum; // restore debug optimum
665   }
666 
667   septime_ += CoinCpuTime () - now;
668 
669   if (jnlst_ -> ProduceOutput (J_ITERSUMMARY, J_CONVEXIFYING)) {
670 
671     if (cs.sizeColCuts ()) {
672       jnlst_ -> Printf (J_ITERSUMMARY, J_CONVEXIFYING,"Couenne col cuts:\n");
673       for (int i=0; i<cs.sizeColCuts (); i++)
674 	cs.colCutPtr (i) -> print ();
675     }
676   }
677 
678   if (!(info.inTree))
679     rootTime_ = CoinCpuTime ();
680 }
681 
682 }
683