/* * Substituting y away: * * y = (c - a x - d z) / b * * so adjust bounds by: c/b * and x by: -a/b * and z by: -d/b * * This affects both the row and col representations. * * mcstrt only modified if the column must be moved. * * for every row in icoly * if icolx is also has an entry for row * modify the icolx entry for row * drop the icoly entry from row and modify the icolx entry * else * add a new entry to icolx column * create a new icolx entry * (this may require moving the column in memory) * replace icoly entry from row and replace with icolx entry * * same for icolz * The row and column reps are inconsistent during the routine, * because icolx in the column rep is updated, and the entries corresponding * to icolx in the row rep are updated, but nothing concerning icoly * in the col rep is changed. icoly entries in the row rep are deleted, * and icolx entries in both reps are consistent. * At the end, we set the length of icoly to be zero, so the reps would * be consistent if the row were deleted from the row rep. * Both the row and icoly must be removed from both reps. * In the col rep, icoly will be eliminated entirely, at the end of the routine; * irow occurs in just two columns, one of which (icoly) is eliminated * entirely, the other is icolx, which is not deleted here. * In the row rep, irow will be eliminated entirely, but not here; * icoly is removed from the rows it occurs in. */ static bool elim_tripleton(const char * #ifdef PRESOLVE_DEBUG msg #endif , CoinBigIndex *mcstrt, double *rlo, double * acts, double *rup, double *colels, int *hrow, int *hcol, int *hinrow, int *hincol, presolvehlink *clink, int ncols, presolvehlink *rlink, int nrows, CoinBigIndex *mrstrt, double *rowels, //double a, double b, double c, double coeff_factorx,double coeff_factorz, double bounds_factor, int row0, int icolx, int icoly, int icolz) { CoinBigIndex kcs = mcstrt[icoly]; CoinBigIndex kce = kcs + hincol[icoly]; CoinBigIndex kcsx = mcstrt[icolx]; CoinBigIndex kcex = kcsx + hincol[icolx]; CoinBigIndex kcsz = mcstrt[icolz]; CoinBigIndex kcez = kcsz + hincol[icolz]; # if PRESOLVE_DEBUG printf("%s %d x=%d y=%d z=%d cfx=%g cfz=%g nx=%d yrows=(", msg, row0,icolx,icoly,icolz,coeff_factorx,coeff_factorz,hincol[icolx]) ; # endif for (CoinBigIndex kcoly=kcs; kcoly<kce; kcoly++) { int row = hrow[kcoly]; // even though these values are updated, they remain consistent PRESOLVEASSERT(kcex == kcsx + hincol[icolx]); PRESOLVEASSERT(kcez == kcsz + hincol[icolz]); // we don't need to update the row being eliminated if (row != row0/* && hinrow[row] > 0*/) { if (bounds_factor != 0.0) { // (1) if (-PRESOLVE_INF < rlo[row]) rlo[row] -= colels[kcoly] * bounds_factor; // (2) if (rup[row] < PRESOLVE_INF) rup[row] -= colels[kcoly] * bounds_factor; // and solution if (acts) { acts[row] -= colels[kcoly] * bounds_factor; } } // see if row appears in colx CoinBigIndex kcolx = presolve_find_row1(row, kcsx, kcex, hrow); # if PRESOLVE_DEBUG printf("%d%s ",row,(kcolx<kcex)?"x+":"") ; # endif // see if row appears in colz CoinBigIndex kcolz = presolve_find_row1(row, kcsz, kcez, hrow); # if PRESOLVE_DEBUG printf("%d%s ",row,(kcolz<kcez)?"x+":"") ; # endif if (kcolx>=kcex&&kcolz<kcez) { // swap int iTemp; iTemp=kcolx; kcolx=kcolz; kcolz=iTemp; iTemp=kcsx; kcsx=kcsz; kcsz=iTemp; iTemp=kcex; kcex=kcez; kcez=iTemp; iTemp=icolx; icolx=icolz; icolz=iTemp; double dTemp=coeff_factorx; coeff_factorx=coeff_factorz; coeff_factorz=dTemp; } if (kcolx<kcex) { // before: both x and y are in the row // after: only x is in the row // so: number of elems in col x unchanged, and num elems in row is one less // update col rep - just modify coefficent // column y is deleted as a whole at the end of the loop colels[kcolx] += colels[kcoly] * coeff_factorx; // update row rep // first, copy new value for col x into proper place in rowels CoinBigIndex k2 = presolve_find_col(icolx, mrstrt[row], mrstrt[row]+hinrow[row], hcol); rowels[k2] = colels[kcolx]; if (kcolz<kcez) { // before: both z and y are in the row // after: only z is in the row // so: number of elems in col z unchanged, and num elems in row is one less // update col rep - just modify coefficent // column y is deleted as a whole at the end of the loop colels[kcolz] += colels[kcoly] * coeff_factorz; // update row rep // first, copy new value for col z into proper place in rowels CoinBigIndex k2 = presolve_find_col(icolz, mrstrt[row], mrstrt[row]+hinrow[row], hcol); rowels[k2] = colels[kcolz]; // now delete col y from the row; this changes hinrow[row] presolve_delete_from_row(row, icoly, mrstrt, hinrow, hcol, rowels); } else { // before: only y is in the row // after: only z is in the row // so: number of elems in col z is one greater, but num elems in row remains same // update entry corresponding to icolz in row rep // by just overwriting the icoly entry { CoinBigIndex k2 = presolve_find_col(icoly, mrstrt[row], mrstrt[row]+hinrow[row], hcol); hcol[k2] = icolz; rowels[k2] = colels[kcoly] * coeff_factorz; } { bool no_mem = presolve_expand_col(mcstrt,colels,hrow,hincol, clink,ncols,icolz); if (no_mem) return (true); // have to adjust various induction variables kcolx = mcstrt[icolx] + (kcolx - kcsx); kcsx = mcstrt[icolx]; kcex = mcstrt[icolx] + hincol[icolx]; kcoly = mcstrt[icoly] + (kcoly - kcs); kcs = mcstrt[icoly]; // do this for ease of debugging kce = mcstrt[icoly] + hincol[icoly]; kcolz = mcstrt[icolz] + (kcolz - kcs); // don't really need to do this kcsz = mcstrt[icolz]; kcez = mcstrt[icolz] + hincol[icolz]; } // there is now an unused entry in the memory after the column - use it // mcstrt[ncols] == penultimate index of arrays hrow/colels hrow[kcez] = row; colels[kcez] = colels[kcoly] * coeff_factorz; // y factor is 0.0 hincol[icolz]++, kcez++; // expand the col } } else { // before: only y is in the row // after: only x and z are in the row // update entry corresponding to icolx in row rep // by just overwriting the icoly entry { CoinBigIndex k2 = presolve_find_col(icoly, mrstrt[row], mrstrt[row]+hinrow[row], hcol); hcol[k2] = icolx; rowels[k2] = colels[kcoly] * coeff_factorx; } presolve_expand_row(mrstrt,rowels,hcol,hinrow,rlink,nrows,row) ; // there is now an unused entry in the memory after the column - use it int krez = mrstrt[row]+hinrow[row]; hcol[krez] = icolz; rowels[krez] = colels[kcoly] * coeff_factorz; hinrow[row]++; { bool no_mem = presolve_expand_col(mcstrt,colels,hrow,hincol, clink,ncols,icolx) ; if (no_mem) return (true); // have to adjust various induction variables kcoly = mcstrt[icoly] + (kcoly - kcs); kcs = mcstrt[icoly]; // do this for ease of debugging kce = mcstrt[icoly] + hincol[icoly]; kcolx = mcstrt[icolx] + (kcolx - kcs); // don't really need to do this kcsx = mcstrt[icolx]; kcex = mcstrt[icolx] + hincol[icolx]; kcolz = mcstrt[icolz] + (kcolz - kcs); // don't really need to do this kcsz = mcstrt[icolz]; kcez = mcstrt[icolz] + hincol[icolz]; } // there is now an unused entry in the memory after the column - use it hrow[kcex] = row; colels[kcex] = colels[kcoly] * coeff_factorx; // y factor is 0.0 hincol[icolx]++, kcex++; // expand the col { bool no_mem = presolve_expand_col(mcstrt,colels,hrow,hincol,clink, ncols,icolz); if (no_mem) return (true); // have to adjust various induction variables kcoly = mcstrt[icoly] + (kcoly - kcs); kcs = mcstrt[icoly]; // do this for ease of debugging kce = mcstrt[icoly] + hincol[icoly]; kcsx = mcstrt[icolx]; kcex = mcstrt[icolx] + hincol[icolx]; kcsz = mcstrt[icolz]; kcez = mcstrt[icolz] + hincol[icolz]; } // there is now an unused entry in the memory after the column - use it hrow[kcez] = row; colels[kcez] = colels[kcoly] * coeff_factorz; // y factor is 0.0 hincol[icolz]++, kcez++; // expand the col } } } # if PRESOLVE_DEBUG printf(")\n") ; # endif // delete the whole column hincol[icoly] = 0; return (false); }
const CoinPresolveAction *subst_constraint_action::presolve ( CoinPresolveMatrix *prob, const int *implied_free, const int *whichFree, int numberFree, const CoinPresolveAction *next, int maxLook) { # if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0 # if PRESOLVE_DEBUG > 0 std::cout << "Entering subst_constraint_action::presolve, fill level " << maxLook << ", " << numberFree << " candidates." << std::endl ; # endif presolve_consistent(prob) ; presolve_links_ok(prob) ; presolve_check_sol(prob) ; presolve_check_nbasic(prob) ; # endif # if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0 int startEmptyRows = 0 ; int startEmptyColumns = 0 ; startEmptyRows = prob->countEmptyRows() ; startEmptyColumns = prob->countEmptyCols() ; # if COIN_PRESOLVE_TUNING > 0 double startTime = 0.0 ; if (prob->tuning_) startTime = CoinCpuTime() ; # endif # endif /* Unpack the row- and column-major representations. */ const int ncols = prob->ncols_ ; const int nrows = prob->nrows_ ; CoinBigIndex *rowStarts = prob->mrstrt_ ; int *rowLengths = prob->hinrow_ ; double *rowCoeffs = prob->rowels_ ; int *colIndices = prob->hcol_ ; presolvehlink *rlink = prob->rlink_ ; CoinBigIndex *colStarts = prob->mcstrt_ ; int *colLengths = prob->hincol_ ; double *colCoeffs = prob->colels_ ; int *rowIndices = prob->hrow_ ; presolvehlink *clink = prob->clink_ ; /* Row bounds and activity, objective. */ double *rlo = prob->rlo_ ; double *rup = prob->rup_ ; double *acts = prob->acts_ ; double *cost = prob->cost_ ; const double tol = prob->feasibilityTolerance_ ; action *actions = new action [ncols] ; # ifdef ZEROFAULT CoinZeroN(reinterpret_cast<char *>(actions),ncols*sizeof(action)) ; # endif int nactions = 0 ; /* This array is used to hold the indices of columns involved in substitutions, where we have the potential for cancellation. At the end they'll be checked to eliminate any actual zeros that may result. At the end of processing of each target row, the column indices of the target row are copied into zerocols. NOTE that usefulColumnInt_ is already in use for parameters implied_free and whichFree when this routine is called from implied_free. */ int *zerocols = new int[ncols] ; int nzerocols = 0 ; int *x_to_y = new int[ncols] ; int *rowsUsed = &prob->usefulRowInt_[0] ; int nRowsUsed = 0 ; /* Open a loop to process the (equality r, implied free variable t) pairs in whichFree and implied_free. It can happen that removal of (row, natural singleton) pairs back in implied_free will reduce the length of column t. It can also happen that previous processing here has resulted in fillin or cancellation. So check again for column length and exclude natural singletons and overly dense columns. */ for (int iLook = 0 ; iLook < numberFree ; iLook++) { const int tgtcol = whichFree[iLook] ; const int tgtcol_len = colLengths[tgtcol] ; const int tgtrow = implied_free[iLook] ; const int tgtrow_len = rowLengths[tgtrow] ; assert(fabs(rlo[tgtrow]-rup[tgtrow]) < tol) ; if (colLengths[tgtcol] < 2 || colLengths[tgtcol] > maxLook) { # if PRESOLVE_DEBUG > 3 std::cout << " skipping eqn " << tgtrow << " x(" << tgtcol << "); length now " << colLengths[tgtcol] << "." << std::endl ; # endif continue ; } CoinBigIndex tgtcs = colStarts[tgtcol] ; CoinBigIndex tgtce = tgtcs+colLengths[tgtcol] ; /* A few checks to make sure that the candidate pair is still suitable. Processing candidates earlier in the list can eliminate coefficients. * Don't use this pair if any involved row i has become a row singleton or empty. * Don't use this pair if any involved row has been modified as part of the processing for a previous candidate pair on this call. * Don't use this pair if a(i,tgtcol) has become zero. The checks on a(i,tgtcol) seem superfluous but it's possible that implied_free identified two candidate pairs to eliminate the same column. If we've already processed one of them, we could be in trouble. */ double tgtcoeff = 0.0 ; bool dealBreaker = false ; for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) { const int i = rowIndices[kcol] ; if (rowLengths[i] < 2 || prob->rowUsed(i)) { dealBreaker = true ; break ; } const double aij = colCoeffs[kcol] ; if (fabs(aij) <= ZTOLDP2) { dealBreaker = true ; break ; } if (i == tgtrow) tgtcoeff = aij ; } if (dealBreaker == true) { # if PRESOLVE_DEBUG > 3 std::cout << " skipping eqn " << tgtrow << " x(" << tgtcol << "); deal breaker (1)." << std::endl ; # endif continue ; } /* Check for numerical stability.A large coeff_factor will inflate the coefficients in the substitution formula. */ dealBreaker = false ; for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) { const double coeff_factor = fabs(colCoeffs[kcol]/tgtcoeff) ; if (coeff_factor > 10.0) dealBreaker = true ; } /* Given enough target rows with sufficient overlap, there's an outside chance we could overflow zerocols. Unlikely to ever happen. */ if (!dealBreaker && nzerocols+rowLengths[tgtrow] >= ncols) dealBreaker = true ; if (dealBreaker == true) { # if PRESOLVE_DEBUG > 3 std::cout << " skipping eqn " << tgtrow << " x(" << tgtcol << "); deal breaker (2)." << std::endl ; # endif continue ; } /* If c(t) != 0, we will need to modify the objective coefficients and remember the original objective. */ const bool nonzero_cost = (fabs(cost[tgtcol]) > tol) ; double *costsx = (nonzero_cost?new double[rowLengths[tgtrow]]:0) ; # if PRESOLVE_DEBUG > 1 std::cout << " Eliminating row " << tgtrow << ", col " << tgtcol ; if (nonzero_cost) std::cout << ", cost " << cost[tgtcol] ; std::cout << "." << std::endl ; # endif /* Count up the total number of coefficients in entangled rows and mark them as contaminated. */ int ntotels = 0 ; for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) { const int i = rowIndices[kcol] ; ntotels += rowLengths[i] ; PRESOLVEASSERT(!prob->rowUsed(i)) ; prob->setRowUsed(i) ; rowsUsed[nRowsUsed++] = i ; } /* Create the postsolve object. Copy in all the affected rows. Take the opportunity to mark the entangled rows as changed and put them on the list of rows to process in the next round. coeffxs in particular holds the coefficients of the target column. */ action *ap = &actions[nactions++] ; ap->col = tgtcol ; ap->rowy = tgtrow ; PRESOLVE_DETAIL_PRINT(printf("pre_subst %dC %dR E\n",tgtcol,tgtrow)) ; ap->nincol = tgtcol_len ; ap->rows = new int[tgtcol_len] ; ap->rlos = new double[tgtcol_len] ; ap->rups = new double[tgtcol_len] ; ap->costsx = costsx ; ap->coeffxs = new double[tgtcol_len] ; ap->ninrowxs = new int[tgtcol_len] ; ap->rowcolsxs = new int[ntotels] ; ap->rowelsxs = new double[ntotels] ; ntotels = 0 ; for (CoinBigIndex kcol = tgtcs ; kcol < tgtce ; ++kcol) { const int ndx = kcol-tgtcs ; const int i = rowIndices[kcol] ; const CoinBigIndex krs = rowStarts[i] ; prob->addRow(i) ; ap->rows[ndx] = i ; ap->ninrowxs[ndx] = rowLengths[i] ; ap->rlos[ndx] = rlo[i] ; ap->rups[ndx] = rup[i] ; ap->coeffxs[ndx] = colCoeffs[kcol] ; CoinMemcpyN(&colIndices[krs],rowLengths[i],&ap->rowcolsxs[ntotels]) ; CoinMemcpyN(&rowCoeffs[krs],rowLengths[i],&ap->rowelsxs[ntotels]) ; ntotels += rowLengths[i] ; } CoinBigIndex tgtrs = rowStarts[tgtrow] ; CoinBigIndex tgtre = tgtrs+rowLengths[tgtrow] ; /* Adjust the objective coefficients based on the substitution formula c'(j) = c(j) - a(rj)c(t)/a(rt) */ if (nonzero_cost) { const double tgtcost = cost[tgtcol] ; for (CoinBigIndex krow = tgtrs ; krow < tgtre ; krow ++) { const int j = colIndices[krow] ; prob->addCol(j) ; costsx[krow-tgtrs] = cost[j] ; double coeff = rowCoeffs[krow] ; cost[j] -= (tgtcost*coeff)/tgtcoeff ; } prob->change_bias(tgtcost*rlo[tgtrow]/tgtcoeff) ; cost[tgtcol] = 0.0 ; } # if PRESOLVE_DEBUG > 1 std::cout << " tgt (" << tgtrow << ") (" << tgtrow_len << "): " ; for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) { const int j = colIndices[krow] ; const double arj = rowCoeffs[krow] ; std::cout << "x(" << j << ") = " << arj << " (" << colLengths[j] << ") " ; } std::cout << std::endl ; # endif // kill small if wanted int relax= (prob->presolveOptions()&0x60000)>>17; double tolerance = 1.0e-12; for (int i=0;i<relax;i++) tolerance *= 10.0; /* Sort the target row for efficiency when doing elimination. */ CoinSort_2(colIndices+tgtrs,colIndices+tgtre,rowCoeffs+tgtrs) ; /* Get down to the business of substituting for tgtcol in the entangled rows. Open a loop to walk the target column. We walk the saved column because the bulk store can change as we work. We don't want to repeat or miss a row. */ for (int colndx = 0 ; colndx < tgtcol_len ; ++colndx) { int i = ap->rows[colndx] ; if (i == tgtrow) continue ; double ait = ap->coeffxs[colndx] ; double coeff_factor = -ait/tgtcoeff ; CoinBigIndex krs = rowStarts[i] ; CoinBigIndex kre = krs+rowLengths[i] ; # if PRESOLVE_DEBUG > 1 std::cout << " subst pre (" << i << ") (" << rowLengths[i] << "): " ; for (CoinBigIndex krow = krs ; krow < kre ; ++krow) { const int j = colIndices[krow] ; const double aij = rowCoeffs[krow] ; std::cout << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ; } std::cout << std::endl ; # endif /* Sort the row for efficiency and call add_row to do the actual business of changing coefficients due to substitution. This has the potential to trigger compaction of the row-major bulk store, so update bulk store indices. */ CoinSort_2(colIndices+krs,colIndices+kre,rowCoeffs+krs) ; bool outOfSpace = add_row(rowStarts,rlo,acts,rup,rowCoeffs,colIndices, rowLengths,rlink,nrows,coeff_factor,tolerance,i,tgtrow, x_to_y) ; if (outOfSpace) throwCoinError("out of memory","CoinImpliedFree::presolve") ; krs = rowStarts[i] ; kre = krs+rowLengths[i] ; tgtrs = rowStarts[tgtrow] ; tgtre = tgtrs+rowLengths[tgtrow] ; # if PRESOLVE_DEBUG > 1 std::cout << " subst aft (" << i << ") (" << rowLengths[i] << "): " ; for (CoinBigIndex krow = krs ; krow < kre ; ++krow) { const int j = colIndices[krow] ; const double aij = rowCoeffs[krow] ; std::cout << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ; } std::cout << std::endl ; # endif /* Now update the column-major representation from the row-major representation. This is easy if the coefficient already exists, but painful if there's fillin. presolve_find_row1 will return the index of the row in the column vector, or one past the end if it's missing. If the coefficient is fill, presolve_expand_col will make sure that there's room in the column for one more coefficient. This may require that the column be moved in the bulk store, so we need to update kcs and kce. Once we're done, a(it) = 0 (i.e., we've eliminated x(t) from row i). Physically remove the explicit zero from the row-major representation with presolve_delete_from_row. */ for (CoinBigIndex rowndx = 0 ; rowndx < tgtrow_len ; ++rowndx) { const CoinBigIndex ktgt = tgtrs+rowndx ; const int j = colIndices[ktgt] ; CoinBigIndex kcs = colStarts[j] ; CoinBigIndex kce = kcs+colLengths[j] ; assert(colIndices[krs+x_to_y[rowndx]] == j) ; const double coeff = rowCoeffs[krs+x_to_y[rowndx]] ; CoinBigIndex kcol = presolve_find_row1(i,kcs,kce,rowIndices) ; if (kcol < kce) { colCoeffs[kcol] = coeff ; } else { outOfSpace = presolve_expand_col(colStarts,colCoeffs,rowIndices, colLengths,clink,ncols,j) ; if (outOfSpace) throwCoinError("out of memory","CoinImpliedFree::presolve") ; kcs = colStarts[j] ; kce = kcs+colLengths[j] ; rowIndices[kce] = i ; colCoeffs[kce] = coeff ; colLengths[j]++ ; } } presolve_delete_from_row(i,tgtcol, rowStarts,rowLengths,colIndices,rowCoeffs) ; # if PRESOLVE_DEBUG > 1 kre-- ; std::cout << " subst fin (" << i << ") (" << rowLengths[i] << "): " ; for (CoinBigIndex krow = krs ; krow < kre ; ++krow) { const int j = colIndices[krow] ; const double aij = rowCoeffs[krow] ; std::cout << "x(" << j << ") = " << aij << " (" << colLengths[j] << ") " ; } std::cout << std::endl ; # endif } /* End of the substitution loop. Record the column indices of the target row so we can groom these columns later to remove possible explicit zeros. */ CoinMemcpyN(&colIndices[rowStarts[tgtrow]],rowLengths[tgtrow], &zerocols[nzerocols]) ; nzerocols += rowLengths[tgtrow] ; /* Remove the target equality from the column- and row-major representations Somewhat painful in the colum-major representation. We have to walk the target row in the row-major representation and look up each coefficient in the column-major representation. */ for (CoinBigIndex krow = tgtrs ; krow < tgtre ; ++krow) { const int j = colIndices[krow] ; # if PRESOLVE_DEBUG > 1 std::cout << " removing row " << tgtrow << " from col " << j << std::endl ; # endif presolve_delete_from_col(tgtrow,j, colStarts,colLengths,rowIndices,colCoeffs) ; if (colLengths[j] == 0) { PRESOLVE_REMOVE_LINK(clink,j) ; } } /* Finally, physically remove the column from the column-major representation and the row from the row-major representation. */ PRESOLVE_REMOVE_LINK(clink, tgtcol) ; colLengths[tgtcol] = 0 ; PRESOLVE_REMOVE_LINK(rlink, tgtrow) ; rowLengths[tgtrow] = 0 ; rlo[tgtrow] = 0.0 ; rup[tgtrow] = 0.0 ; # if PRESOLVE_CONSISTENCY > 0 presolve_links_ok(prob) ; presolve_consistent(prob) ; # endif } /* That's it, we've processed all the candidate pairs. Clear the row used flags. */ for (int i = 0 ; i < nRowsUsed ; i++) prob->unsetRowUsed(rowsUsed[i]) ; /* Trim the array of substitution transforms and queue up objects for postsolve. Also groom the problem representation to remove explicit zeros. */ if (nactions) { # if PRESOLVE_SUMMARY > 0 std::cout << "NSUBSTS: " << nactions << std::endl ; # endif next = new subst_constraint_action(nactions, CoinCopyOfArray(actions,nactions),next) ; next = drop_zero_coefficients_action::presolve(prob,zerocols, nzerocols, next) ; # if PRESOLVE_CONSISTENCY > 0 presolve_links_ok(prob) ; presolve_consistent(prob) ; # endif } deleteAction(actions,action*) ; delete [] x_to_y ; delete [] zerocols ; # if COIN_PRESOLVE_TUNING > 0 if (prob->tuning_) double thisTime = CoinCpuTime() ; # endif # if PRESOLVE_CONSISTENCY > 0 || PRESOLVE_DEBUG > 0 presolve_check_sol(prob) ; # endif # if PRESOLVE_DEBUG > 0 || COIN_PRESOLVE_TUNING > 0 int droppedRows = prob->countEmptyRows()-startEmptyRows ; int droppedColumns = prob->countEmptyCols()-startEmptyColumns ; std::cout << "Leaving subst_constraint_action::presolve, " << droppedRows << " rows, " << droppedColumns << " columns dropped" ; # if COIN_PRESOLVE_TUNING > 0 std::cout << " in " << thisTime-startTime << "s" ; # endif std::cout << "." << std::endl ; # endif return (next) ; }