// We could have implemented this by having each postsolve routine // directly call the next one, but this may make it easier to add debugging checks. void OsiPresolve::postsolve(CoinPostsolveMatrix &prob) { const CoinPresolveAction *paction = paction_; #if PRESOLVE_DEBUG printf("Begin POSTSOLVING\n") ; if (prob.colstat_) { presolve_check_nbasic(&prob); presolve_check_sol(&prob); } presolve_check_duals(&prob); #endif while (paction) { # if PRESOLVE_DEBUG printf("POSTSOLVING %s\n", paction->name()); # endif paction->postsolve(&prob); # if PRESOLVE_DEBUG if (prob.colstat_) { presolve_check_nbasic(&prob); presolve_check_sol(&prob); } # endif paction = paction->next; # if PRESOLVE_DEBUG presolve_check_duals(&prob); # endif } # if PRESOLVE_DEBUG printf("End POSTSOLVING\n") ; # endif #if 0 && PRESOLVE_DEBUG << This block of checks will require some work to get it to compile. >>
/* Undo the substitutions from presolve and reintroduce the target constraint and column. */ void subst_constraint_action::postsolve(CoinPostsolveMatrix *prob) const { # if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0 # if PRESOLVE_DEBUG > 0 std::cout << "Entering subst_constraint_action::postsolve, " << nactions_ << " constraints to process." << std::endl ; # endif int ncols = prob->ncols_ ; char *cdone = prob->cdone_ ; char *rdone = prob->rdone_ ; const double ztolzb = prob->ztolzb_ ; presolve_check_threads(prob) ; presolve_check_free_list(prob) ; presolve_check_reduced_costs(prob) ; presolve_check_duals(prob) ; presolve_check_sol(prob,2,2,2) ; presolve_check_nbasic(prob) ; # endif /* Unpack the column-major representation. */ CoinBigIndex *colStarts = prob->mcstrt_ ; int *colLengths = prob->hincol_ ; int *rowIndices = prob->hrow_ ; double *colCoeffs = prob->colels_ ; /* Rim vectors, solution, reduced costs, duals, row activity. */ double *rlo = prob->rlo_ ; double *rup = prob->rup_ ; double *cost = prob->cost_ ; double *sol = prob->sol_ ; double *rcosts = prob->rcosts_ ; double *acts = prob->acts_ ; double *rowduals = prob->rowduals_ ; CoinBigIndex *link = prob->link_ ; CoinBigIndex &free_list = prob->free_list_ ; const double maxmin = prob->maxmin_ ; const action *const actions = actions_ ; const int nactions = nactions_ ; /* Open the main loop to step through the postsolve objects. First activity is to unpack the postsolve object. We have the target column and row indices, the full target column, and complete copies of all entangled rows (column indices, coefficients, lower and upper bounds). There may be a vector of objective coefficients which we'll get to later. */ for (const action *f = &actions[nactions-1] ; actions <= f ; f--) { const int tgtcol = f->col ; const int tgtrow = f->rowy ; const int tgtcol_len = f->nincol ; const double *tgtcol_coeffs = f->coeffxs ; const int *entngld_rows = f->rows ; const int *entngld_lens = f->ninrowxs ; const int *entngld_colndxs = f->rowcolsxs ; const double *entngld_colcoeffs = f->rowelsxs ; const double *entngld_rlos = f->rlos ; const double *entngld_rups = f->rups ; const double *costs = f->costsx ; # if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0 # if PRESOLVE_DEBUG > 1 std::cout << " reintroducing column x(" << tgtcol << ") and row " << tgtrow ; if (costs) std::cout << ", nonzero costs" ; std::cout << "." << std::endl ; # endif /* We're about to reintroduce the target row and column; empty stubs should be present. All other rows should already be present. */ PRESOLVEASSERT(cdone[tgtcol] == DROP_COL) ; PRESOLVEASSERT(colLengths[tgtcol] == 0) ; PRESOLVEASSERT(rdone[tgtrow] == DROP_ROW) ; for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) { if (entngld_rows[cndx] != tgtrow) PRESOLVEASSERT(rdone[entngld_rows[cndx]]) ; } /* In a postsolve matrix, we can't just check that the length of the row is zero. We need to look at all columns and confirm its absence. */ for (int j = 0 ; j < ncols ; ++j) { if (colLengths[j] > 0 && cdone[j]) { const CoinBigIndex kcs = colStarts[j] ; const int lenj = colLengths[j] ; CoinBigIndex krow = presolve_find_row3(tgtrow,kcs,lenj,rowIndices,link) ; if (krow >= 0) { std::cout << " BAD COEFF! row " << tgtrow << " present in column " << j << " before reintroduction; a(" << tgtrow << "," << j << ") = " << colCoeffs[krow] << "; x(" << j << ") = " << sol[j] << "; cdone " << static_cast<int>(cdone[j]) << "." << std::endl ; } } } # endif /* Find the copy of the target row. Restore the upper and lower bounds of entangled rows while we're looking. Recall that the target row is an equality. */ int tgtrow_len = -1 ; const int *tgtrow_colndxs = NULL ; const double *tgtrow_coeffs = NULL ; double tgtcoeff = 0.0 ; double tgtrhs = 1.0e50 ; int nel = 0 ; for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) { int i = entngld_rows[cndx] ; rlo[i] = entngld_rlos[cndx] ; rup[i] = entngld_rups[cndx] ; if (i == tgtrow) { tgtrow_len = entngld_lens[cndx] ; tgtrow_colndxs = &entngld_colndxs[nel] ; tgtrow_coeffs = &entngld_colcoeffs[nel] ; tgtcoeff = tgtcol_coeffs[cndx] ; tgtrhs = rlo[i] ; } nel += entngld_lens[cndx] ; } /* Solve the target equality to find the solution for the eliminated col. tgtcol is present in tgtrow_colndxs, so initialise sol[tgtcol] to zero to make sure it doesn't contribute. If we're debugging, check that the result is within bounds. */ double tgtexp = tgtrhs ; sol[tgtcol] = 0.0 ; for (int ndx = 0 ; ndx < tgtrow_len ; ++ndx) { int j = tgtrow_colndxs[ndx] ; double coeffj = tgtrow_coeffs[ndx] ; tgtexp -= coeffj*sol[j] ; } sol[tgtcol] = tgtexp/tgtcoeff ; # if PRESOLVE_DEBUG > 0 double *clo = prob->clo_ ; double *cup = prob->cup_ ; if (!(sol[tgtcol] > (clo[tgtcol]-ztolzb) && (cup[tgtcol]+ztolzb) > sol[tgtcol])) { std::cout << "BAD SOL: x(" << tgtcol << ") " << sol[tgtcol] << "; lb " << clo[tgtcol] << "; ub " << cup[tgtcol] << "." << std::endl ; } # endif /* Now restore the original entangled rows. We first delete any columns present in tgtrow. This will remove any fillin, but may also remove columns that were originally present in both the entangled row and the target row. Note that even cancellations (explicit zeros) are present at this point --- in presolve, they were removed after the substition transform completed, hence they're already restored. What isn't present is the target column, which is deleted as part of the transform. */ { # if PRESOLVE_DEBUG > 2 std::cout << " removing coefficients:" ; # endif for (int rndx = 0 ; rndx < tgtrow_len ; ++rndx) { int j = tgtrow_colndxs[rndx] ; if (j != tgtcol) for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) { if (entngld_rows[cndx] != tgtrow) { # if PRESOLVE_DEBUG > 2 std::cout << " a(" << entngld_rows[cndx] << "," << j << ")" ; # endif presolve_delete_from_col2(entngld_rows[cndx],j,colStarts, colLengths,rowIndices,link,&free_list) ; } } } # if PRESOLVE_DEBUG > 2 std::cout << std::endl ; # endif # if PRESOLVE_CONSISTENCY > 0 presolve_check_threads(prob) ; presolve_check_free_list(prob) ; # endif /* Next we restore the original coefficients. The outer loop walks tgtcol; cols_i and coeffs_i are advanced as we go to point to each entangled row. The inner loop walks the entangled row and restores the row's coefficients. Tgtcol is handled as any other column. Skip tgtrow, we'll do it below. Since we don't have a row-major representation, we have to look for a(i,j) from entangled row i in the existing column j. If we find a(i,j), simply update it (and a(tgtrow,j) should not exist). If we don't find a(i,j), introduce it (and a(tgtrow,j) should exist). Recalculate the row activity while we're at it. */ # if PRESOLVE_DEBUG > 2 std::cout << " restoring coefficients:" ; # endif colLengths[tgtcol] = 0 ; const int *cols_i = entngld_colndxs ; const double *coeffs_i = entngld_colcoeffs ; for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) { const int leni = entngld_lens[cndx] ; const int i = entngld_rows[cndx] ; if (i != tgtrow) { double acti = 0.0 ; for (int rndx = 0 ; rndx < leni ; ++rndx) { const int j = cols_i[rndx] ; CoinBigIndex kcoli = presolve_find_row3(i,colStarts[j], colLengths[j],rowIndices,link) ; if (kcoli != -1) { # if PRESOLVE_DEBUG > 2 std::cout << " u a(" << i << "," << j << ")" ; PRESOLVEASSERT(presolve_find_col1(j,0,tgtrow_len, tgtrow_colndxs) == tgtrow_len) ; # endif colCoeffs[kcoli] = coeffs_i[rndx] ; } else { # if PRESOLVE_DEBUG > 2 std::cout << " f a(" << i << "," << j << ")" ; PRESOLVEASSERT(presolve_find_col1(j,0,tgtrow_len, tgtrow_colndxs) < tgtrow_len) ; # endif CoinBigIndex kk = free_list ; assert(kk >= 0 && kk < prob->bulk0_) ; free_list = link[free_list] ; link[kk] = colStarts[j] ; colStarts[j] = kk ; colCoeffs[kk] = coeffs_i[rndx] ; rowIndices[kk] = i ; ++colLengths[j] ; } acti += coeffs_i[rndx]*sol[j] ; } acts[i] = acti ; } cols_i += leni ; coeffs_i += leni ; } # if PRESOLVE_DEBUG > 2 std::cout << std::endl ; # endif # if PRESOLVE_CONSISTENCY > 0 presolve_check_threads(prob) ; presolve_check_free_list(prob) ; # endif /* Restore tgtrow. Arguably we could to this in the previous loop, but we'd do a lot of unnecessary work. By construction, the target row is tight. */ # if PRESOLVE_DEBUG > 2 std::cout << " restoring row " << tgtrow << ":" ; # endif for (int rndx = 0 ; rndx < tgtrow_len ; ++rndx) { int j = tgtrow_colndxs[rndx] ; # if PRESOLVE_DEBUG > 2 std::cout << " a(" << tgtrow << "," << j << ")" ; # endif CoinBigIndex kk = free_list ; assert(kk >= 0 && kk < prob->bulk0_) ; free_list = link[free_list] ; link[kk] = colStarts[j] ; colStarts[j] = kk ; colCoeffs[kk] = tgtrow_coeffs[rndx] ; rowIndices[kk] = tgtrow ; ++colLengths[j] ; } acts[tgtrow] = tgtrhs ; # if PRESOLVE_DEBUG > 2 std::cout << std::endl ; # endif # if PRESOLVE_CONSISTENCY > 0 presolve_check_threads(prob) ; presolve_check_free_list(prob) ; # endif } /* Restore original cost coefficients, if necessary. */ if (costs) { for (int ndx = 0 ; ndx < tgtrow_len ; ++ndx) { cost[tgtrow_colndxs[ndx]] = costs[ndx] ; } } /* Calculate the reduced cost for the column absent any contribution from tgtrow, then set the dual for tgtrow so that the reduced cost of tgtcol is zero. */ double dj = maxmin*cost[tgtcol] ; rowduals[tgtrow] = 0.0 ; for (int cndx = 0 ; cndx < tgtcol_len ; ++cndx) { int i = entngld_rows[cndx] ; double coeff = tgtcol_coeffs[cndx] ; dj -= rowduals[i]*coeff ; } rowduals[tgtrow] = dj/tgtcoeff ; rcosts[tgtcol] = 0.0 ; if (rowduals[tgtrow] > 0) prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atUpperBound) ; else prob->setRowStatus(tgtrow,CoinPrePostsolveMatrix::atLowerBound) ; prob->setColumnStatus(tgtcol,CoinPrePostsolveMatrix::basic) ; # if PRESOLVE_DEBUG > 2 std::cout << " row " << tgtrow << " " << prob->rowStatusString(prob->getRowStatus(tgtrow)) << " dual " << rowduals[tgtrow] << std::endl ; std::cout << " col " << tgtcol << " " << prob->columnStatusString(prob->getColumnStatus(tgtcol)) << " dj " << dj << std::endl ; # endif # if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0 cdone[tgtcol] = SUBST_ROW ; rdone[tgtrow] = SUBST_ROW ; # endif } # if PRESOLVE_DEBUG > 0 || PRESOLVE_CONSISTENCY > 0 presolve_check_threads(prob) ; presolve_check_free_list(prob) ; presolve_check_reduced_costs(prob) ; presolve_check_duals(prob) ; presolve_check_sol(prob,2,2,2) ; presolve_check_nbasic(prob) ; # if PRESOLVE_DEBUG > 0 std::cout << "Leaving subst_constraint_action::postsolve." << std::endl ; # endif # endif return ; }