Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CglLiftAndProject Class Reference

#include <CglLiftAndProject.hpp>

Inheritance diagram for CglLiftAndProject:

CglCutGenerator List of all members.

Public Member Functions

Generate Cuts
virtual void generateCuts (const OsiSolverInterface &si, OsiCuts &cs) const
double getBeta () const
void setBeta (int oneOrMinusOne)
Constructors and destructors
 CglLiftAndProject ()
 Default constructor.

 CglLiftAndProject (const CglLiftAndProject &)
 Copy constructor.

CglLiftAndProjectoperator= (const CglLiftAndProject &rhs)
 Assignment operator.

virtual ~CglLiftAndProject ()
 Destructor.


Private Attributes

Private member data
double beta_
 The normalization is beta_=1 or beta_=-1.

double epsilon_
 epsilon

double onetol_
 1-epsilon


Friends

void CglLiftAndProjectUnitTest (const OsiSolverInterface *siP, const std::string mpdDir)

Detailed Description

Lift And Project Cut Generator Class

Definition at line 11 of file CglLiftAndProject.hpp.


Member Function Documentation

void CglLiftAndProject::generateCuts const OsiSolverInterface &  si,
OsiCuts &  cs
const [virtual]
 

Generate lift-and-project cuts for the model of the solver interface, si. Insert the generated cuts into OsiCut, cs.

Implements CglCutGenerator.

Definition at line 23 of file CglLiftAndProject.cpp.

References beta_.

00025 {
00026   // Assumes the mixed 0-1 problem 
00027   //
00028   //   min {cx: <Atilde,x> >= btilde} 
00029   //
00030   // is in cannonical form with all bounds,
00031   // including x_t>=0, -x_t>=-1 for x_t binary,
00032   // explicitly stated in the constraint matrix. 
00033   // See ~/COIN/Examples/Cgl2/cgl2.cpp 
00034   // for a general purpose "convert" function. 
00035 
00036   // Reference [BCC]: Balas, Ceria, and Corneujols,
00037   // "A lift-and-project cutting plane algorithm
00038   // for mixed 0-1 program", Math Prog 58, (1993) 
00039   // 295-324.
00040 
00041   // This implementation uses Normalization 1.
00042 
00043   // Given cannonical problem and
00044   // the lp-relaxation solution, x,
00045   // the LAP cut generator attempts to construct
00046   // a cut for every x_j such that 0<x_j<1
00047   // [BCC:307]
00048  
00049 
00050   // x_j is the strictly fractional binary variable
00051   // the cut is generated from
00052   int j = 0; 
00053 
00054   // Get basic problem information
00055   // let Atilde be an m by n matrix
00056   const int m = si.getNumRows(); 
00057   const int n = si.getNumCols(); 
00058   const double * x = si.getColSolution();
00059 
00060   // Remember - Atildes may have gaps..
00061   const CoinPackedMatrix * Atilde = si.getMatrixByRow();
00062   const double * AtildeElements =  Atilde->getElements();
00063   const int * AtildeIndices =  Atilde->getIndices();
00064   const int * AtildeStarts = Atilde->getVectorStarts();
00065   const int * AtildeLengths = Atilde->getVectorLengths();  
00066   const int AtildeFullSize = AtildeStarts[m];
00067   const double * btilde = si.getRowLower();
00068 
00069   // Set up memory for system (10) [BCC:307]
00070   // (the problem over the norm intersected 
00071   //  with the polar cone)
00072   // 
00073   // min <<x^T,Atilde^T>,u> + x_ju_0
00074   // s.t.
00075   //     <B,w> = (0,...,0,beta_,beta)^T
00076   //        w  is nonneg for all but the
00077   //           last two entries, which are free.
00078   // where 
00079   // w = (u,v,v_0,u_0)in BCC notation 
00080   //      u and v are m-vectors; u,v >=0
00081   //      v_0 and u_0 are free-scalars, and
00082   //  
00083   // B = Atilde^T  -Atilde^T  -e_j e_j
00084   //     btilde^T   e_0^T      0   0
00085   //     e_0^T      btilde^T   1   0
00086 
00087   // ^T indicates Transpose
00088   // e_0 is a (AtildeNCols x 1) vector of all zeros 
00089   // e_j is e_0 with a 1 in the jth position
00090 
00091   // Storing B in column order. B is a (n+2 x 2m+2) matrix 
00092   // But need to allow for possible gaps in Atilde.
00093   // At each iteration, only need to change 2 cols and objfunc
00094   // Sane design of OsiSolverInterface does not permit mucking
00095   // with matrix.
00096   // Because we must delete and add cols to alter matrix,
00097   // and we can only add columns on the end of the matrix
00098   // put the v_0 and u_0 columns on the end.
00099   // rather than as described in [BCC]
00100  
00101   // Initially allocating B with space for v_0 and u_O cols
00102   // but not populating, for efficiency.
00103 
00104   // B without u_0 and v_0 is a (n+2 x 2m) size matrix.
00105 
00106   int twoM = 2*m;
00107   int BNumRows = n+2;
00108   int BNumCols = twoM+2;
00109   int BFullSize = 2*AtildeFullSize+twoM+3;
00110   double * BElements = new double[BFullSize];
00111   int * BIndices = new int[BFullSize];
00112   int * BStarts = new int[BNumCols+1];
00113   int * BLengths = new int[BNumCols];
00114 
00115 
00116   int i, ij, k=0;
00117   int nPlus1=n+1;
00118   int offset = AtildeStarts[m]+m;
00119   for (i=0; i<m; i++){
00120     for (ij=AtildeStarts[i];ij<AtildeStarts[i]+AtildeLengths[i];ij++){
00121       BElements[k]=AtildeElements[ij];
00122       BElements[k+offset]=-AtildeElements[ij];
00123       BIndices[k]= AtildeIndices[ij];
00124       BIndices[k+offset]= AtildeIndices[ij];
00125 
00126       k++;
00127     }
00128     BElements[k]=btilde[i];
00129     BElements[k+offset]=btilde[i];
00130     BIndices[k]=n;
00131     BIndices[k+offset]=nPlus1;
00132     BStarts[i]= AtildeStarts[i]+i;
00133     BStarts[i+m]=offset+BStarts[i];// = AtildeStarts[m]+m+AtildeStarts[i]+i
00134     BLengths[i]= AtildeLengths[i]+1;
00135     BLengths[i+m]= AtildeLengths[i]+1;
00136     k++;
00137   }
00138 
00139   BStarts[twoM]=BStarts[twoM-1]+BLengths[twoM-1];
00140 
00141   // Cols that will be deleted each iteration
00142   int BNumColsLessOne=BNumCols-1;
00143   int BNumColsLessTwo=BNumCols-2;
00144   const int delCols[2] = {BNumColsLessOne, BNumColsLessTwo};
00145 
00146   // Set lower bound on u and v
00147   // u_0, v_0 will be reset as free
00148   const double solverINFINITY = si.getInfinity();
00149   double * BColLowers = new double[BNumCols];
00150   double * BColUppers = new double[BNumCols];
00151   CoinFillN(BColLowers,BNumCols,0.0);  
00152   CoinFillN(BColUppers,BNumCols,solverINFINITY); 
00153 
00154   // Set row lowers and uppers.
00155   // The rhs is zero, for but the last two rows.
00156   // For these the rhs is beta_
00157   double * BRowLowers = new double[BNumRows];
00158   double * BRowUppers = new double[BNumRows];
00159   CoinFillN(BRowLowers,BNumRows,0.0);  
00160   CoinFillN(BRowUppers,BNumRows,0.0);
00161   BRowLowers[BNumRows-2]=beta_;
00162   BRowUppers[BNumRows-2]=beta_;
00163   BRowLowers[BNumRows-1]=beta_;
00164   BRowUppers[BNumRows-1]=beta_;
00165 
00166 
00167   // Calculate base objective <<x^T,Atilde^T>,u>
00168   // Note: at each iteration coefficient u_0
00169   //       changes to <x^T,e_j>
00170   //       w=(u,v,beta,v_0,u_0) size 2m+3
00171   //       So, BOjective[2m+2]=x[j]
00172   double * BObjective= new double[BNumCols];
00173   double * Atildex = new double[m];
00174   CoinFillN(BObjective,BNumCols,0.0);
00175   Atilde->times(x,Atildex); // Atildex is size m, x is size n
00176   CoinDisjointCopyN(Atildex,m,BObjective); 
00177 
00178   // Number of cols and size of Elements vector
00179   // in B without the v_0 and u_0 cols
00180   int BFullSizeLessThree = BFullSize-3;
00181 
00182   // Load B matrix into a column orders CoinPackedMatrix
00183   CoinPackedMatrix * BMatrix = new CoinPackedMatrix(true, BNumRows,
00184                                                   BNumColsLessTwo, 
00185                                                   BFullSizeLessThree,
00186                                                   BElements,BIndices, 
00187                                                   BStarts,BLengths);
00188   // Assign problem into a solver interface 
00189   // Note: coneSi will cleanup the memory itself
00190   OsiSolverInterface * coneSi = si.clone(false);
00191   coneSi->assignProblem (BMatrix, BColLowers, BColUppers, 
00192                       BObjective,
00193                       BRowLowers, BRowUppers);
00194 
00195   // Problem sense should defalut to "min" by default, 
00196   // but just to be virtuous...
00197   coneSi->setObjSense(1.0);
00198 
00199   // The plot outline from here on down:
00200   // coneSi has been assigned B without the u_0 and v_0 columns
00201   // Calculate base objective <<x^T,Atilde^T>,u>
00202   // bool haveWarmStart = false;
00203   // For (j=0; j<n, j++)
00204   //   if (!isBinary(x_j) || x_j<=0 || x_j>=1) continue;
00205   //   // IMPROVEME: if(haveWarmStart) check if j attractive
00206   //   add {-e_j,0,-1} matrix column for v_0
00207   //   add {e_j,0,0} matrix column for u_0
00208   //   objective coefficient for u_0 is  x_j 
00209   //   if (haveWarmStart) 
00210   //      set warmstart info
00211   //   solve min{objw:Bw=0; w>=0,except v_0, u_0 free}
00212   //   if (bounded)
00213   //      get warmstart info
00214   //      haveWarmStart=true;
00215   //      ustar = optimal u solution
00216   //      ustar_0 = optimal u_0 solution
00217   //      alpha^T= <ustar^T,Atilde> -ustar_0e_j^T
00218   //      (double check <alpha^T,x> >= beta_ should be violated)
00219   //      add <alpha^T,x> >= beta_ to cutset 
00220   //   endif
00221   //   delete column for u_0 // this deletes all column info.
00222   //   delete column for v_0
00223   // endFor
00224   // clean up memory
00225   // return 0;
00226 
00227   int * nVectorIndices = new int[n];
00228   CoinIotaN(nVectorIndices, n, 0);
00229 
00230   bool haveWarmStart = false;
00231   bool equalObj1, equalObj2;
00232   CoinRelFltEq eq;
00233 
00234   double v_0Elements[2] = {-1,1};
00235   double u_0Elements[1] = {1};
00236 
00237   CoinWarmStart * warmStart = 0;
00238 
00239   double * ustar = new double[m];
00240   CoinFillN(ustar, m, 0.0);
00241 
00242   double* alpha = new double[n];
00243   CoinFillN(alpha, n, 0.0);
00244 
00245   for (j=0;j<n;j++){
00246     if (!si.isBinary(j)) continue; // Better to ask coneSi? No! 
00247                                    // coneSi has no binInfo.
00248     equalObj1=eq(x[j],0);
00249     equalObj2=eq(x[j],1);
00250     if (equalObj1 || equalObj2) continue;
00251     // IMPROVEME: if (haveWarmStart) check if j attractive;
00252 
00253     // AskLL:wanted to declare u_0 and v_0 packedVec outside loop
00254     // and setIndices, but didn't see a method to do that(?)
00255     // (Could "insert". Seems inefficient)
00256     int v_0Indices[2]={j,nPlus1};
00257     int u_0Indices[1]={j};
00258     // 
00259     CoinPackedVector  v_0(2,v_0Indices,v_0Elements,false);
00260     CoinPackedVector  u_0(1,u_0Indices,u_0Elements,false);
00261 
00262 #if CGL_DEBUG
00263     const CoinPackedMatrix *see1 = coneSi->getMatrixByRow();
00264 #endif
00265 
00266     coneSi->addCol(v_0,-solverINFINITY,solverINFINITY,0);
00267     coneSi->addCol(u_0,-solverINFINITY,solverINFINITY,x[j]);
00268     if(haveWarmStart) {
00269       coneSi->setWarmStart(warmStart);
00270       coneSi->resolve();
00271     }
00272     else {
00273 
00274 #if CGL_DEBUG
00275       const CoinPackedMatrix *see2 = coneSi->getMatrixByRow();
00276 #endif
00277 
00278       coneSi->initialSolve();
00279     }
00280     if(coneSi->isProvenOptimal()){
00281       warmStart = coneSi->getWarmStart();
00282       haveWarmStart=true;
00283       const double * wstar = coneSi->getColSolution();
00284       CoinDisjointCopyN(wstar, m, ustar);
00285       Atilde->transposeTimes(ustar,alpha);
00286       alpha[j]+=wstar[BNumCols-1]; 
00287       
00288 #if debug
00289       int p;
00290       double sum;
00291       for(p=0;p<n;p++)sum+=alpha[p]*x[p];
00292       if (sum<=beta_){
00293         throw CoinError("Cut not violated",
00294                         "cutGeneration",
00295                         "CglLiftAndProject");
00296       }
00297 #endif
00298 
00299       // add <alpha^T,x> >= beta_ to cutset
00300       OsiRowCut rc;
00301       rc.setRow(n,nVectorIndices,alpha);
00302       rc.setLb(beta_);
00303       rc.setUb(solverINFINITY);
00304       cs.insert(rc);
00305     }
00306     // delete col for u_o and v_0
00307     coneSi->deleteCols(2,delCols);
00308 
00309     // clean up memory
00310   }
00311   // clean up
00312   delete [] alpha;
00313   delete [] ustar;
00314   delete [] nVectorIndices;
00315   // BMatrix, BColLowers,BColUppers, BObjective, BRowLowers, BRowUppers
00316   // are all freed by OsiSolverInterface destructor (?)
00317   delete [] BLengths;
00318   delete [] BStarts;
00319   delete [] BIndices;
00320   delete [] BElements;
00321 }

double CglLiftAndProject::getBeta  )  const [inline]
 

Get the normalization : Either beta=+1 or beta=-1.

Definition at line 27 of file CglLiftAndProject.hpp.

References beta_.

00027                          {
00028     return beta_;
00029   }

void CglLiftAndProject::setBeta int  oneOrMinusOne  )  [inline]
 

Set the normalization : Either beta=+1 or beta=-1. Default value is 1.

Definition at line 34 of file CglLiftAndProject.hpp.

References beta_.

00034                                  {
00035     if (oneOrMinusOne==1 || oneOrMinusOne==-1){
00036       beta_= (double)oneOrMinusOne;
00037     }
00038     else {
00039       throw CoinError("Unallowable value. Beta must be 1 or -1",
00040                       "cutGeneration","CglLiftAndProject");
00041     }
00042   }


Friends And Related Function Documentation

void CglLiftAndProjectUnitTest const OsiSolverInterface *  siP,
const std::string  mpdDir
[friend]
 

A function that tests the methods in the CglLiftAndProject class. The only reason for it not to be a member method is that this way it doesn't have to be compiled into the library. And that's a gain, because the library should be compiled with optimization on, but this method should be compiled with debugging.


The documentation for this class was generated from the following files:
Generated on Wed Dec 3 14:34:57 2003 for Cgl by doxygen 1.3.5