|
13 | 13 | #include <numeric> |
14 | 14 |
|
15 | 15 | #include "lp_data/HConst.h" |
| 16 | +#include "lp_data/HighsModelUtils.h" // For debugging #2001 |
16 | 17 | #include "lp_data/HighsOptions.h" |
17 | 18 | #include "util/HighsCDouble.h" |
18 | 19 | #include "util/HighsUtils.h" |
@@ -1351,4 +1352,72 @@ void HighsPostsolveStack::DuplicateColumn::transformToPresolvedSpace( |
1351 | 1352 | primalSol[col] = primalSol[col] + colScale * primalSol[duplicateCol]; |
1352 | 1353 | } |
1353 | 1354 |
|
| 1355 | +void HighsPostsolveStack::SlackColSubstitution::undo( |
| 1356 | + const HighsOptions& options, const std::vector<Nonzero>& rowValues, |
| 1357 | + HighsSolution& solution, HighsBasis& basis) { |
| 1358 | + bool debug_print = false; |
| 1359 | + // May have to determine row dual and basis status unless doing |
| 1360 | + // primal-only transformation in MIP solver, in which case row may |
| 1361 | + // no longer exist if it corresponds to a removed cut, so have to |
| 1362 | + // avoid exceeding array bounds of solution.row_value |
| 1363 | + bool isModelRow = static_cast<size_t>(row) < solution.row_value.size(); |
| 1364 | + |
| 1365 | + // compute primal values |
| 1366 | + double colCoef = 0; |
| 1367 | + HighsCDouble rowValue = 0; |
| 1368 | + for (const auto& rowVal : rowValues) { |
| 1369 | + if (rowVal.index == col) |
| 1370 | + colCoef = rowVal.value; |
| 1371 | + else |
| 1372 | + rowValue += rowVal.value * solution.col_value[rowVal.index]; |
| 1373 | + } |
| 1374 | + |
| 1375 | + assert(colCoef != 0); |
| 1376 | + // Row values aren't fully postsolved, so why do this? |
| 1377 | + if (isModelRow) |
| 1378 | + solution.row_value[row] = |
| 1379 | + double(rowValue + colCoef * solution.col_value[col]); |
| 1380 | + |
| 1381 | + solution.col_value[col] = double((rhs - rowValue) / colCoef); |
| 1382 | + |
| 1383 | + // If no dual values requested, return here |
| 1384 | + if (!solution.dual_valid) return; |
| 1385 | + |
| 1386 | + // Row retains its dual value, and column has this dual value scaled by coeff |
| 1387 | + if (isModelRow) solution.col_dual[col] = -solution.row_dual[row] / colCoef; |
| 1388 | + |
| 1389 | + // Set basis status if necessary |
| 1390 | + if (!basis.valid) return; |
| 1391 | + |
| 1392 | + // If row is basic, then slack is basic, otherwise row retains its status |
| 1393 | + if (isModelRow) { |
| 1394 | + HighsBasisStatus save_row_basis_status = basis.row_status[row]; |
| 1395 | + if (basis.row_status[row] == HighsBasisStatus::kBasic) { |
| 1396 | + basis.col_status[col] = HighsBasisStatus::kBasic; |
| 1397 | + basis.row_status[row] = |
| 1398 | + computeRowStatus(solution.row_dual[row], RowType::kEq); |
| 1399 | + } else if (basis.row_status[row] == HighsBasisStatus::kLower) { |
| 1400 | + basis.col_status[col] = |
| 1401 | + colCoef > 0 ? HighsBasisStatus::kUpper : HighsBasisStatus::kLower; |
| 1402 | + } else { |
| 1403 | + basis.col_status[col] = |
| 1404 | + colCoef > 0 ? HighsBasisStatus::kLower : HighsBasisStatus::kUpper; |
| 1405 | + } |
| 1406 | + if (debug_print) |
| 1407 | + printf( |
| 1408 | + "HighsPostsolveStack::SlackColSubstitution::undo OgRowStatus = %s; " |
| 1409 | + "RowStatus = %s; ColStatus = %s\n", |
| 1410 | + utilBasisStatusToString(save_row_basis_status).c_str(), |
| 1411 | + utilBasisStatusToString(basis.row_status[row]).c_str(), |
| 1412 | + utilBasisStatusToString(basis.col_status[col]).c_str()); |
| 1413 | + if (basis.col_status[col] == HighsBasisStatus::kLower) { |
| 1414 | + assert(solution.col_dual[col] > -options.dual_feasibility_tolerance); |
| 1415 | + } else if (basis.col_status[col] == HighsBasisStatus::kUpper) { |
| 1416 | + assert(solution.col_dual[col] < options.dual_feasibility_tolerance); |
| 1417 | + } |
| 1418 | + } else { |
| 1419 | + basis.col_status[col] = HighsBasisStatus::kNonbasic; |
| 1420 | + } |
| 1421 | +} |
| 1422 | + |
1354 | 1423 | } // namespace presolve |
0 commit comments