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

CglSimpleRounding Class Reference

#include <CglSimpleRounding.hpp>

Inheritance diagram for CglSimpleRounding:

CglCutGenerator List of all members.

Public Member Functions

Generate Cuts
virtual void generateCuts (const OsiSolverInterface &si, OsiCuts &cs) const
Constructors and destructors
 CglSimpleRounding ()
 Default constructor.

 CglSimpleRounding (const CglSimpleRounding &)
 Copy constructor.

CglSimpleRoundingoperator= (const CglSimpleRounding &rhs)
 Assignment operator.

virtual ~CglSimpleRounding ()
 Destructor.


Private Member Functions

Private methods
bool deriveAnIntegerRow (const OsiSolverInterface &si, int rowIndex, const CoinShallowPackedVector &matrixRow, CoinPackedVector &irow, double &b, bool *negative) const
 Derive a <= inequality in integer variables from the rowIndex-th constraint.

int power10ToMakeDoubleAnInt (int size, const double *x, double dataTol) const
Greatest common denominators methods
int gcd (int a, int b) const
 Returns the greatest common denominator of two positive integers, a and b.

int gcdv (int n, const int *const vi) const

Private Attributes

Private member data
double epsilon_
 A value within an epsilon_ neighborhood of 0 is considered to be 0.


Friends

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

Detailed Description

Simple Rounding Cut Generator Class

This class generates simple rounding cuts via the following method: For each contraint, attempt to derive a <= inequality in all integer variables by netting out any continuous variables. Divide the resulting integer inequality through by the greatest common denomimator (gcd) of the lhs coefficients. Round down the rhs.

Warning: Use with careful attention to data precision.

(Reference: Nemhauser and Wolsey, Integer and Combinatorial Optimization, 1988, pg 211.)

Definition at line 26 of file CglSimpleRounding.hpp.


Member Function Documentation

int CglSimpleRounding::gcdv int  n,
const int *const  vi
const [inline, private]
 

Returns the greatest common denominator of a vector of positive integers, vi, of length n.

Definition at line 141 of file CglSimpleRounding.hpp.

References gcd().

Referenced by generateCuts().

00142 {
00143   if (n==0)
00144     abort();
00145 
00146   if (n==1)
00147     return vi[0];
00148 
00149   int retval=gcd(vi[0], vi[1]);
00150   for (int i=2; i<n; i++){
00151      retval=gcd(retval,vi[i]);
00152   }
00153   return retval;
00154 }

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

Generate simple rounding cuts for the model accessed through the solver interface. Insert generated cuts into the cut set cs.

Implements CglCutGenerator.

Definition at line 20 of file CglSimpleRounding.cpp.

References deriveAnIntegerRow(), epsilon_, gcd(), gcdv(), CoinPackedVector::getElements(), CoinPackedVector::getIndices(), CoinPackedVector::getNumElements(), CoinPackedVector::insert(), power10ToMakeDoubleAnInt(), and CoinPackedVector::setVector().

00022 {
00023   int nRows=si.getNumRows(); // number of rows in the coefficient matrix
00024   int nCols=si.getNumCols(); // number of columns in the coefficient matrix
00025   int rowIndex;             // index into the constraint matrix stored in row
00026                             // order 
00027   CoinPackedVector irow;     // "integer row": working space to hold the integer
00028                             // <= inequality derived from the rowIndex-th
00029                             // constraint 
00030   double b=0;             // working space for the rhs of integer <= inequality
00031   bool * negative= new bool[nCols]; // negative[i]= true if coefficient of the 
00032                                     // ith variable is negative and false
00033                                     // otherwise 
00034   int k;                  // dummy iterator variable 
00035   for ( k=0; k<nCols; k++ ) negative[k] = false;
00036   
00037   const CoinPackedMatrix * rowCopy = 
00038     si.getMatrixByRow(); // row copy: matrix stored in row order
00039 
00041   // Main loop:                                                              //
00042   // For every row in the matrix,                                            //
00043   //     if we can derive a valid <= inequality in integer variables, then   //
00044   //     try to construct a simple rounding cut from the integer inequality. //
00045   //     Add the resulting cut to the set of cuts.                           //
00047 
00048   for (rowIndex=0; rowIndex<nRows; rowIndex++){
00049 
00050     // Only look at tight rows
00051     // double * pi=ekk_rowduals(model); 
00052     // if (fabs(pi[row]) < epsilon_){
00053    //  continue;
00054     // }
00055 
00056     // Try to derive an <= inequality in integer variables from the row 
00057     // by netting out the continuous variables.
00058     // Store the value and the sign of the coefficients separately:
00059     // irow.getElements() contains the absolute values of the coefficients.
00060     // negative is a boolean vector indicating the sign of the coeffcients.
00061     // b is the rhs of the <= integer inequality
00062 
00063     if (!deriveAnIntegerRow( si, 
00064                              rowIndex, 
00065                              rowCopy->getVector(rowIndex),
00066                              irow, b, negative))
00067     {
00068 
00069       // Reset local data for the next iteration of the rowIndex-loop
00070       for(k=0; k<irow.getNumElements(); k++) negative[irow.getIndices()[k]]=false;
00071       irow.setVector(0,NULL,NULL);
00072       continue;
00073     } 
00074  
00075     // Euclid's greatest common divisor (gcd) algorithm applies to positive
00076     // INTEGERS. 
00077     // Determine the power of 10 needed, so that multipylying the integer
00078     // inequality through by 10**power makes all coefficients essentially
00079     // integral. 
00080     int power = power10ToMakeDoubleAnInt(irow.getNumElements(),irow.getElements(),epsilon_*1.0e-4);
00081 
00082     // Now a vector to store the integer-ized values. For instance, 
00083     // if x[i] is .66 and power is 1000 then xInt[i] will be 660
00084     int * xInt = NULL;
00085     if (power >=0) {
00086 
00087       xInt = new int[irow.getNumElements()]; 
00088       double dxInt; // a double version of xInt for error trapping
00089       
00090       
00091 #ifdef CGL_DEBUG      
00092       printf("The (double) coefficients and their integer-ized counterparts:\n");
00093 #endif
00094 
00095       for (k=0; k<irow.getNumElements(); k++){
00096         dxInt = irow.getElements()[k]*pow(10.0,power);
00097         xInt[k]= (int) (dxInt+0.5); // Need to add the 0.5 
00098         // so that a dxInt=9.999 will give a xInt=1
00099 
00100 #ifdef CGL_DEBUG
00101         printf("%g     %g   \n",irow.getElements()[k],dxInt);
00102 #endif
00103 
00104       }
00105 
00106     } else {
00107 
00108       // If overflow is detected, one warning message is printed and 
00109       // the row is skipped.
00110 #ifdef CGL_DEBUG
00111       printf("SimpleRounding: Warning: Overflow detected \n");
00112       printf("      on %i of vars in processing row %i. Row skipped.\n",
00113              -power, rowIndex);
00114 #endif
00115       // reset local data for next iteration
00116       for(k=0; k<irow.getNumElements(); k++) negative[irow.getIndices()[k]]=false;
00117       irow.setVector(0,NULL,NULL);
00118       continue;
00119     }
00120 
00121     // find greatest common divisor of the irow.elements
00122     int gcd = gcdv(irow.getNumElements(), xInt);
00123 
00124 #ifdef CGL_DEBUG
00125     printf("The gcd of xInt is %i\n",gcd);    
00126 #endif
00127 
00128     // construct new cut by dividing through by gcd and 
00129     // rounding down rhs and accounting for negatives
00130     CoinPackedVector cut;
00131     for (k=0; k<irow.getNumElements(); k++){
00132         cut.insert(irow.getIndices()[k],xInt[k]/gcd);
00133     }
00134     double cutRhs = floor((b*pow(10.0,power))/gcd);
00135 
00136     // un-negate the negated variables in the cut
00137     {
00138        const int s = cut.getNumElements();
00139        const int * indices = cut.getIndices();
00140        double* elements = cut.getElements();
00141        for (k=0; k<s; k++){
00142          int column=indices[k];
00143           if (negative[column]) {
00144              elements[k] *= -1;
00145           }
00146        }
00147     }
00148 
00149     // Create the row cut and add it to the set of cuts
00150     // It may not be violated
00151     if (fabs(cutRhs*gcd-b)> epsilon_){ // if the cut and row are different. 
00152       OsiRowCut rc;
00153       rc.setRow(cut.getNumElements(),cut.getIndices(),cut.getElements());
00154       rc.setLb(-DBL_MAX);
00155       rc.setUb(cutRhs);   
00156       cs.insert(rc);
00157 
00158 #ifdef CGL_DEBUG
00159       printf("Row %i had a simple rounding cut:\n",rowIndex);
00160       printf("Cut size: %i Cut rhs: %g  Index       Element \n",
00161              cut.getNumElements(), cutRhs);
00162       for (k=0; k<cut.getNumElements(); k++){
00163         printf("%i      %g\n",cut.getIndices()[k], cut.getElements()[k]);
00164       }
00165       printf("\n");
00166 #endif
00167     }
00168 
00169     // Reset local data for the next iteration of the rowIndex-loop
00170     for(k=0; k<irow.getNumElements(); k++) negative[irow.getIndices()[k]]=false;
00171     irow.setVector(0,NULL,NULL);
00172     delete [] xInt;
00173 
00174 
00175   }
00176 
00177   delete [] negative;
00178 }

int CglSimpleRounding::power10ToMakeDoubleAnInt int  size,
const double *  x,
double  dataTol
const [private]
 

Given a vector of doubles, x, with size elements and a positive tolerance, dataTol, this method returns the smallest power of 10 needed so that x[i]*10**power "is integer" for all i=0,...,size-1.

change of definition of dataTol so that it refers to original data, not to scaled data as that seems to lead to problems.

So if xScaled is x[i]*10**power and xInt is rounded(xScaled) then fabs(xScaled-xInt) <= dataTol*10**power. This means that dataTol should be smaller - say 1.0e-12 rather tahn 1.0e-8

Returns -number of times overflowed if the power is so big that it will cause overflow (i.e. integer stored will be bigger than 2**31). Test in cut generator.

Definition at line 330 of file CglSimpleRounding.cpp.

Referenced by generateCuts().

00335 {
00336   // Assumption: data precision is positive
00337   assert( dataTol > 0 );
00338 
00339 
00340   int i;           // loop iterator 
00341   int maxPower=0;  // maximum power of 10 used to convert any x[i] to an
00342                    // integer 
00343                    // this is the number we are after.
00344   int power = 0;   // power of 10 used to convert a particular x[i] to an
00345                    // integer 
00346 
00347 #ifdef OLD_MULT
00348   double intPart;  // the integer part of the number
00349 #endif
00350   double fracPart; // the fractional part of the number
00351                    // we keep multiplying by 10 until the fractional part is 0
00352                    // (well, really just until the factional part is less than
00353                    // dataTol) 
00354 
00355   // JJF - code seems to fail sometimes as multiplying by 10 - so
00356   // definition of dataTol changed - see header file
00357 
00358   const double multiplier[16]={1.0,1.0e1,1.0e2,1.0e3,1.0e4,1.0e5,
00359                                1.0e6,1.0e7,1.0e8,1.0e9,1.0e10,1.0e11,
00360                                1.0e12,1.0e13,1.0e14,1.0e15};
00361 
00362   // Loop through every element in the array in x
00363   for (i=0; i<size; i++){
00364     power = 0;
00365 
00366 #ifdef OLD_MULT 
00367     // look at the fractional part of x[i]
00368     // FYI: if you want to modify this member function to take an input
00369     // vector x of arbitary sign, change this line below to 
00370     // fracPart = modf(fabs(x[i]),&intPart);
00371     fracPart = modf(x[i],&intPart);
00372 
00373     // if the fractional part is close enough to 0 or 1, we're done with this
00374     // value
00375     while(!(fracPart < dataTol || 1-fracPart < dataTol )) {
00376        // otherwise, multiply by 10 and look at the fractional part of the
00377        // result. 
00378        ++power;
00379        fracPart = fracPart*10.0;
00380        fracPart = modf(fracPart,&intPart);     
00381     }
00382 #else
00383     // use fabs as safer and does no harm
00384     double value = fabs(x[i]);
00385     double scaledValue;
00386     // Do loop - always using original value to stop round off error.
00387     // If we don't find in 15 goes give up
00388     for (power=0;power<16;power++) {
00389       double tolerance = dataTol*multiplier[power];
00390       scaledValue = value*multiplier[power];
00391       fracPart = scaledValue-floor(scaledValue);
00392       if(fracPart < tolerance || 1.0-fracPart < tolerance ) {
00393         break;
00394       }
00395     }
00396     if (power==16||scaledValue>2147483647) {
00397 #ifdef CGL_DEBUG
00398       printf("Overflow %g => %g, power %d\n",x[i],scaledValue,power);
00399 #endif
00400       return -1;
00401     }
00402 #endif    
00403 #ifdef CGL_DEBUG
00404     printf("The smallest power of 10 to make %g  integral = %i\n",x[i],power);
00405 #endif
00406 
00407     
00408     // keep track of the largest power needed so that at the end of the for
00409     // loop
00410     // x[i]*10**maxPower will be integral for all i
00411     if (maxPower < power) maxPower=power;
00412   }
00413 
00414   return maxPower;
00415 }


Friends And Related Function Documentation

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

A function that tests the methods in the CglSimpleRounding 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.

Definition at line 20 of file CglSimpleRoundingTest.cpp.

00023 {
00024 
00025   // Test default constructor
00026   {
00027     CglSimpleRounding cg;
00028   }
00029 
00030   // Test copy & assignment
00031   {
00032     CglSimpleRounding rhs;
00033     {
00034       CglSimpleRounding cg;
00035       CglSimpleRounding cgC(cg);
00036       rhs=cg;
00037     }
00038   }
00039 
00040   // Test gcd and gcdn
00041   {
00042     CglSimpleRounding cg;
00043     int v = cg.gcd(122,356);
00044     assert(v==2);
00045     v=cg.gcd(356,122);
00046     assert(v==2);
00047     v=cg.gcd(54,67);
00048     assert(v==1);
00049     v=cg.gcd(67,54);
00050     assert(v==1);
00051     v=cg.gcd(485,485);
00052     assert(v==485);
00053     v=cg.gcd(17*13,17*23);
00054     assert( v==17);
00055     v=cg.gcd(17*13*5,17*23);
00056     assert( v==17);
00057     v=cg.gcd(17*13*23,17*23);
00058     assert(v==17*23);
00059 
00060     int a[4] = {12, 20, 32, 400};
00061     v= cg.gcdv(4,a);
00062     assert(v== 4);
00063     int b[4] = {782, 4692, 51, 2754};
00064     v= cg.gcdv(4,b);
00065     assert(v== 17);
00066     int c[4] = {50, 40, 30, 10};
00067     v= cg.gcdv(4,c);
00068     assert(v== 10);
00069   }
00070 
00071 
00072   // Test generate cuts method on exmip1.5.mps
00073   {
00074     CglSimpleRounding cg;
00075     
00076     OsiSolverInterface * siP = baseSiP->clone();
00077     std::string fn = mpsDir+"exmip1.5";
00078     siP->readMps(fn.c_str(),"mps");
00079     OsiCuts cuts;
00080     cg.generateCuts(*siP,cuts);
00081 
00082     // there should be 3 cuts
00083     int nRowCuts = cuts.sizeRowCuts();
00084     assert(nRowCuts==3);
00085 
00086     // get the last "sr"=simple rounding cut that was derived
00087     OsiRowCut srRowCut2 = cuts.rowCut(2); 
00088     CoinPackedVector srRowCutPV2 = srRowCut2.row();
00089 
00090     // this is what the last cut should look like: i.e. the "solution"
00091     const int solSize = 2;
00092     int solCols[solSize]={2,3};
00093     double solCoefs[solSize]={5.0, 4.0};
00094     OsiRowCut solRowCut;
00095     solRowCut.setRow(solSize,solCols,solCoefs);
00096     solRowCut.setLb(-DBL_MAX);
00097     solRowCut.setUb(2.0);
00098 
00099     // Test for equality between the derived cut and the solution cut
00100 
00101     // Note: testing two OsiRowCuts are equal invokes testing two
00102     // CoinPackedVectors are equal which invokes testing two doubles
00103     // are equal.  Usually not a good idea to test that two doubles are equal, 
00104     // but in this cut the "doubles" represent integer values.
00105     assert(srRowCut2 == solRowCut);
00106 
00107     delete siP;
00108   }
00109 
00110   // Test generate cuts method on p0033
00111   {
00112     CglSimpleRounding cg;
00113     
00114     OsiSolverInterface * siP = baseSiP->clone();
00115     std::string fn = mpsDir+"p0033";
00116     siP->readMps(fn.c_str(),"mps");
00117     OsiCuts cuts;
00118     cg.generateCuts(*siP,cuts);
00119 
00120     // p0033 is the optimal solution to p0033
00121     int objIndices[14] = { 
00122        0,  6,  7,  9, 13, 17, 18,
00123       22, 24, 25, 26, 27, 28, 29 };
00124     CoinPackedVector p0033(14,objIndices,1.0);
00125 
00126     // test that none of the generated cuts
00127     // chops off the optimal solution
00128     int nRowCuts = cuts.sizeRowCuts();
00129     OsiRowCut rcut;
00130     CoinPackedVector rpv;
00131     int i;
00132     for (i=0; i<nRowCuts; i++){
00133       rcut = cuts.rowCut(i);
00134       rpv = rcut.row();
00135       double p0033Sum = (rpv*p0033).sum();
00136       double rcutub = rcut.ub();
00137       assert (p0033Sum <= rcutub);
00138     }
00139 
00140     // test that the cuts improve the 
00141     // lp objective function value
00142     siP->initialSolve();
00143     double lpRelaxBefore=siP->getObjValue();
00144     OsiSolverInterface::ApplyCutsReturnCode rc = siP->applyCuts(cuts);
00145     siP->resolve();
00146     double lpRelaxAfter=siP->getObjValue(); 
00147 #ifdef CGL_DEBUG
00148     printf("\n\nOrig LP min=%f\n",lpRelaxBefore);
00149     printf("Final LP min=%f\n\n",lpRelaxAfter);
00150 #endif
00151     assert( lpRelaxBefore < lpRelaxAfter );
00152 
00153     delete siP;
00154 
00155   }
00156 
00157 
00158 }


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