//------------------------------------------------------------------------------- // Generate three cycle cuts //------------------------------------------------------------------- void CglOddHole::generateCuts(const OsiSolverInterface & si, OsiCuts & cs, const CglTreeInfo info) { // Get basic problem information int nRows=si.getNumRows(); int nCols=si.getNumCols(); const CoinPackedMatrix * rowCopy = si.getMatrixByRow(); // Could do cliques and extra OSL cliques // For moment just easy ones // If no information exists then get a list of suitable rows // If it does then suitable rows are subset of information CglOddHole temp; int * checkRow = new int[nRows]; int i; if (!suitableRows_) { for (i=0;i<nRows;i++) { checkRow[i]=1; } } else { // initialize and extend rows to current size memset(checkRow,0,nRows*sizeof(int)); memcpy(checkRow,suitableRows_,CoinMin(nRows,numberRows_)*sizeof(int)); } temp.createRowList(si,checkRow); // now cut down further by only allowing rows with fractional solution double * solution = new double[nCols]; memcpy(solution,si.getColSolution(),nCols*sizeof(double)); const int * column = rowCopy->getIndices(); const CoinBigIndex * rowStart = rowCopy->getVectorStarts(); const int * rowLength = rowCopy->getVectorLengths(); const double * collower = si.getColLower(); const double * colupper = si.getColUpper(); int * suitable = temp.suitableRows_; // At present I am using new and delete as easier to see arrays in debugger int * fixed = new int[nCols]; // mark fixed columns for (i=0;i<nCols;i++) { if (si.isBinary(i) ) { fixed[i]=0; if (colupper[i]-collower[i]<epsilon_) { solution[i]=0.0; fixed[i]=2; } else if (solution[i]<epsilon_) { solution[i]=0.0; fixed[i]=-1; } else if (solution[i]>onetol_) { solution[i]=1.0; fixed[i]=+1; } } else { //mark as fixed even if not (can not intersect any interesting rows) solution[i]=0.0; fixed[i]=3; } } // first do packed const double * rowlower = si.getRowLower(); const double * rowupper = si.getRowUpper(); for (i=0;i<nRows;i++) { if (suitable[i]) { int k; double sum=0.0; if (rowupper[i]>1.001) suitable[i]=-1; for (k=rowStart[i]; k<rowStart[i]+rowLength[i];k++) { int icol=column[k]; if (!fixed[icol]) sum += solution[icol]; } if (sum<0.9) suitable[i]=-1; //say no good } } #ifdef CGL_DEBUG const OsiRowCutDebugger * debugger = si.getRowCutDebugger(); if (debugger&&!debugger->onOptimalPath(si)) debugger = NULL; #else const OsiRowCutDebugger * debugger = NULL; #endif temp.generateCuts(debugger, *rowCopy,solution, si.getReducedCost(),cs,suitable,fixed,info,true); // now cover //if no >= then skip bool doCover=false; int nsuitable=0; for (i=0;i<nRows;i++) { suitable[i]=abs(suitable[i]); if (suitable[i]) { int k; double sum=0.0; if (rowlower[i]<0.999) sum=2.0; if (rowupper[i]>1.001) doCover=true; for (k=rowStart[i]; k<rowStart[i]+rowLength[i];k++) { int icol=column[k]; if (!fixed[icol]) sum += solution[icol]; if (fixed[icol]==1) sum=2.0; //don't use if any at 1 } if (sum>1.1) { suitable[i]=-1; //say no good } else { nsuitable++; } } } if (doCover&&nsuitable) temp.generateCuts(debugger, *rowCopy,solution,si.getReducedCost(), cs,suitable,fixed,info,false); delete [] checkRow; delete [] solution; delete [] fixed; }
//-------------------------------------------------------------------------- // test cut debugger methods. void OsiRowCutDebuggerUnitTest(const OsiSolverInterface * baseSiP, const std::string & mpsDir) { CoinRelFltEq eq; // Test default constructor { OsiRowCutDebugger r; OSIUNITTEST_ASSERT_ERROR(r.integerVariable_ == NULL, {}, "osirowcutdebugger", "default constructor"); OSIUNITTEST_ASSERT_ERROR(r.knownSolution_ == NULL, {}, "osirowcutdebugger", "default constructor"); OSIUNITTEST_ASSERT_ERROR(r.numberColumns_ == 0, {}, "osirowcutdebugger", "default constructor"); } { // Get non trivial instance OsiSolverInterface * imP = baseSiP->clone(); std::string fn = mpsDir+"exmip1"; imP->readMps(fn.c_str(),"mps"); OSIUNITTEST_ASSERT_ERROR(imP->getNumRows() == 5, {}, "osirowcutdebugger", "read exmip1"); /* Activate the debugger. The garbled name here is deliberate; the debugger should isolate the portion of the string between '/' and '.' (in normal use, this would be the base file name, stripped of the prefix and extension). */ imP->activateRowCutDebugger("ab cd /x/ /exmip1.asc"); int i; // return debugger const OsiRowCutDebugger * debugger = imP->getRowCutDebugger(); OSIUNITTEST_ASSERT_ERROR(debugger != NULL, {}, "osirowcutdebugger", "return debugger"); OSIUNITTEST_ASSERT_ERROR(debugger->numberColumns_ == 8, {}, "osirowcutdebugger", "return debugger"); const bool type[]={0,0,1,1,0,0,0,0}; const double values[]= {2.5, 0, 1, 1, 0.5, 3, 0, 0.26315789473684253}; CoinPackedVector objCoefs(8,imP->getObjCoefficients()); bool type_ok = true; #if 0 for (i=0;i<8;i++) type_ok &= type[i] == debugger->integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "???"); #endif double objValue = objCoefs.dotProduct(values); double debuggerObjValue = objCoefs.dotProduct(debugger->knownSolution_); OSIUNITTEST_ASSERT_ERROR(eq(objValue, debuggerObjValue), {}, "osirowcutdebugger", "objective value"); OsiRowCutDebugger rhs; { OsiRowCutDebugger rC1(*debugger); OSIUNITTEST_ASSERT_ERROR(rC1.numberColumns_ == 8, {}, "osirowcutdebugger", "copy constructor"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rC1.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rC1.knownSolution_)), {}, "osirowcutdebugger", "copy constructor"); rhs = rC1; OSIUNITTEST_ASSERT_ERROR(rhs.numberColumns_ == 8, {}, "osirowcutdebugger", "assignment operator"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rhs.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "assignment operator"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rhs.knownSolution_)), {}, "osirowcutdebugger", "assignment operator"); } // Test that rhs has correct values even though lhs has gone out of scope OSIUNITTEST_ASSERT_ERROR(rhs.numberColumns_ == 8, {}, "osirowcutdebugger", "assignment operator"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rhs.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "assignment operator"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rhs.knownSolution_)), {}, "osirowcutdebugger", "assignment operator"); OsiRowCut cut[2]; const int ne = 3; int inx[ne] = { 0, 2, 3 }; double el[ne] = { 1., 1., 1. }; cut[0].setRow(ne,inx,el); cut[0].setUb(5.); el[1]=5; cut[1].setRow(ne,inx,el); cut[1].setUb(5); OsiCuts cs; cs.insert(cut[0]); cs.insert(cut[1]); OSIUNITTEST_ASSERT_ERROR(!debugger->invalidCut(cut[0]), {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR( debugger->invalidCut(cut[1]), {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR(debugger->validateCuts(cs,0,2) == 1, {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR(debugger->validateCuts(cs,0,1) == 0, {}, "osirowcutdebugger", "recognize (in)valid cut"); delete imP; } }
//------------------------------------------------------------------- // Generate cuts //------------------------------------------------------------------- void CglAllDifferent::generateCuts(const OsiSolverInterface & si, OsiCuts & cs, const CglTreeInfo ) const { #ifndef NDEBUG int nCols=si.getNumCols(); #endif int i; const double * lower = si.getColLower(); const double * upper = si.getColUpper(); #ifdef CGL_DEBUG const OsiRowCutDebugger * debugger = si.getRowCutDebugger(); if (debugger&&debugger->onOptimalPath(si)) { printf("On optimal path %d\n",nPath); nPath++; int nCols=si.getNumCols(); const double * solution = si.getColSolution(); const double * optimal = debugger->optimalSolution(); const double * objective = si.getObjCoefficients(); double objval1=0.0,objval2=0.0; for (i=0;i<nCols;i++) { #if CGL_DEBUG>1 printf("%d %g %g %g %g\n",i,lower[i],solution[i],upper[i],optimal[i]); #endif objval1 += solution[i]*objective[i]; objval2 += optimal[i]*objective[i]; assert(optimal[i]>=lower[i]&&optimal[i]<=upper[i]); } printf("current obj %g, integer %g\n",objval1,objval2); } #endif int * lo = new int[numberDifferent_]; int * up = new int[numberDifferent_]; for (i=0;i<numberDifferent_;i++) { int iColumn = originalWhich_[i]; assert (iColumn<nCols); lo[i] = static_cast<int> (lower[iColumn]); assert (floor(lower[iColumn]+0.5)==lower[iColumn]); up[i] = static_cast<int> (upper[iColumn]); assert (floor(upper[iColumn]+0.5)==upper[iColumn]); assert (up[i]>=lo[i]); } // We are going to assume we can just have one big 2d array! // Could save by going to bits // also could skip sets where all are fixed // could do some of above by separate first pass // once a variable fixed - can take out of list // so need to redo complete stuff (including temp which_) every big pass int offset = COIN_INT_MAX; int maxValue = -COIN_INT_MAX; int numberLook=0; // copies //int * which = new int [numberTotal]; //int * start = new int [numberSets_+1]; for (i=0;i<numberSets_;i++) { for (int j=start_[i];j<start_[i+1];j++) { int k=which_[j]; offset = CoinMin(offset,lo[k]); maxValue = CoinMax(maxValue,up[k]); } numberLook++; int gap = maxValue-offset+1; double size = static_cast<double> (gap) * numberDifferent_; if (size>1.0e7) { if (logLevel_) printf("Only looking at %d sets\n",numberLook); break; } } // Which sets a variable is in int * back = new int [start_[numberSets_]]; int * backStart = new int[numberDifferent_+1]; memset(backStart,0,(numberDifferent_+1)*sizeof(int)); int numberTotal = start_[numberLook]; for (i=0;i<numberTotal;i++) { int k=which_[i]; // note +1 backStart[k+1]++; } int n=0; for (i=0;i<numberDifferent_;i++) { int nThis = backStart[i+1]; backStart[i+1]=n; n+= nThis; } // at end all backStart correct! for (i=0;i<numberLook;i++) { for (int j=start_[i];j<start_[i+1];j++) { int k=which_[j]; // note +1 int iPut = backStart[k+1]; back[iPut]=i; backStart[k+1]=iPut+1; } } // value is possible for variable k if possible[k*gap+value] is nonzero int gap = maxValue-offset+1; char * possible = new char[gap*numberDifferent_]; memset(possible,0,gap*numberDifferent_); // initialize int numberFixed=0; int * alreadyFixed = new int[numberDifferent_]; for (i=0;i<numberDifferent_;i++) { alreadyFixed[i]=-1; int startV = i*gap + lo[i] - offset; int n = up[i]-lo[i]+1; memset(possible+startV,1,n); } for (i=0;i<numberDifferent_;i++) { int n = up[i]-lo[i]+1; if (n==1) { int fixedAt = lo[i]-offset; numberFixed++; alreadyFixed[i]=fixedAt; // take out of all others for (int j=backStart[i];j<backStart[i+1];j++) { int iSet = back[j]; for (int jj=start_[iSet];jj<start_[iSet+1];jj++) { int k=which_[jj]; if (k!=i) { // impossible possible[k*gap+fixedAt]=0; } } } } } bool finished=false; //int numberTightened=0; bool infeasible=false; // space to see which values possible int * check = new int[gap]; unsigned int * bitmap = new unsigned int[numberDifferent_]; int * stack = new int[numberDifferent_+1]; int * first = new int[numberDifferent_+1]; // just for valgrind etc memset(stack,0,(numberDifferent_+1)*sizeof(int)); memset(first,0,(numberDifferent_+1)*sizeof(int)); // do one set at a time while (!finished) { finished=true; int fixed=numberFixed; for (i=0;i<numberLook;i++) { memset(check,0,gap*sizeof(int)); for (int j=start_[i];j<start_[i+1];j++) { int k=which_[j]; if (alreadyFixed[k]>=0) { if (check[alreadyFixed[k]]==0) { check[alreadyFixed[k]]=1; continue; } else { // infeasible infeasible=true; i=numberLook; break; } } char * allowed = possible + k*gap; int n=0; for (int jj=0;jj<gap;jj++) { if (allowed[jj]) { n++; check[jj]++; } } if (n<2) { if (n==1) { // fix int fixedAt = -1; for (int jj=0;jj<gap;jj++) { if (allowed[jj]) { fixedAt=jj; break; } } numberFixed++; alreadyFixed[k]=fixedAt; check[fixedAt]=1; // take out of all others for (int j=backStart[k];j<backStart[k+1];j++) { int iSet = back[j]; for (int jj=start_[iSet];jj<start_[iSet+1];jj++) { int kk=which_[jj]; if (kk!=k) { // impossible possible[kk*gap+fixedAt]=0; } } } } else { // infeasible infeasible=true; j=numberTotal; i=numberLook; break; } } } // now check set // If number covered < number in set infeasible if (gap<30&&!infeasible) { int n=start_[i+1]-start_[i]; memset(bitmap,0,n*sizeof(unsigned int)); int j; int * which = which_+start_[i]; unsigned int covered=0; bool good=true; for (j=0;j<n;j++) { int k=which[j]; char * allowed = possible + k*gap; int jj; for (jj=0;jj<gap;jj++) if (allowed[jj]) break; assert (jj<gap); first[j]=jj; unsigned int iBit = 1<<jj; if ((covered&iBit)==0) { stack[j]=jj; covered |= iBit; } else { // can't jj++; for (;jj<gap;jj++) { iBit = iBit << 1; if (allowed[jj]&&(covered&iBit)==0) break; } if (jj<gap) { stack[j]=jj; covered |= iBit; } else { good = false; break; } } } int nStack=j; // just do first for rest for (;j<n;j++) { int k=which[j]; char * allowed = possible + k*gap; int jj; for (jj=0;jj<gap;jj++) if (allowed[jj]) break; assert (jj<gap); first[j]=jj; } int kLook=0; while (nStack) { nStack--; if (good) { #if 0 printf("con %d = ",i); for (j=0;j<n;j++) printf("%d ",stack[j]+1); printf("\n"); #endif // bug - kLook >= 0 kLook=0; for (j=kLook;j<n;j++) { int iBit = 1 << stack[j]; bitmap[j] |= iBit; } } kLook=nStack; int jj=stack[nStack]; unsigned int iBit = 1<<jj; covered &= ~iBit; { unsigned int kBit=0; for (int k=0;k<nStack;k++) { int kk=stack[k]; kBit |= 1<<kk; } assert (covered==kBit); } jj++; stack[nStack]=jj; while (nStack<n) { int k=which[nStack]; char * allowed = possible + k*gap; for (;jj<gap;jj++) { iBit = 1 << jj; if (allowed[jj]&&(covered&iBit)==0) break; } if (jj<gap) { stack[nStack]=jj; covered |= iBit; nStack++; stack[nStack]=first[nStack]; jj = first[nStack]; good=true; } else { good = false; break; } } } int nnFix=0; // Now see if we can fix any for (j=0;j<n;j++) { int k=which[j]; unsigned int mapped = bitmap[j]; char * allowed = possible + k*gap; unsigned int iBit=1; for (int jj=0;jj<gap;jj++) { if ((mapped&iBit)==0) { if (allowed[jj]) { if (!nnFix) printf("for con %d x ",i); nnFix++; printf("%d not %d ",j,jj+1); allowed[jj]=0; finished=false; } } iBit = iBit << 1; } } if (nnFix) printf("\n"); } } if (numberFixed>fixed) finished=false; // try again } // Could try two sets if (infeasible) { // create infeasible cut OsiRowCut rc; rc.setLb(COIN_DBL_MAX); rc.setUb(0.0); cs.insert(rc); } else { // check to see if can tighten bounds CoinPackedVector lbs; CoinPackedVector ubs; int nTightened=0; for (i=0;i<numberDifferent_;i++) { int iColumn = originalWhich_[i]; char * allowed = possible+i*gap; int firstLo=-1; int lastUp=-1; for (int jj=0;jj<gap;jj++) { if (allowed[jj]) { if (firstLo<0) firstLo=jj; lastUp = jj; } } if (firstLo+offset>lo[i]) { lbs.insert(iColumn,static_cast<double> (firstLo+offset)); nTightened++; } if (lastUp+offset<up[i]) { ubs.insert(iColumn,static_cast<double> (lastUp+offset)); nTightened++; } } if (nTightened) { OsiColCut cc; cc.setUbs(ubs); cc.setLbs(lbs); cc.setEffectiveness(100.0); cs.insert(cc); } } //delete [] which; //delete [] start; delete [] first; delete [] stack; delete [] bitmap; delete [] check; delete [] alreadyFixed; delete [] back; delete [] backStart; delete [] possible; delete [] lo; delete [] up; }