From 8b7f707829bec5ecb383ee12f097a97e9e0cc17b Mon Sep 17 00:00:00 2001 From: fwesselm Date: Fri, 22 Nov 2024 11:15:35 +0100 Subject: [PATCH 01/10] WIP --- src/mip/HighsCutGeneration.cpp | 7 +++ src/mip/HighsPathSeparator.cpp | 5 +- src/mip/HighsTransformedLp.cpp | 96 +++++++++++++++++----------------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/mip/HighsCutGeneration.cpp b/src/mip/HighsCutGeneration.cpp index 1417c777b7..0db97b9e52 100644 --- a/src/mip/HighsCutGeneration.cpp +++ b/src/mip/HighsCutGeneration.cpp @@ -1184,6 +1184,11 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, } } while (false); + // save data that might otherwise be overwritten when calling the cmir + // separator + bool saveIntegalSupport = integralSupport; + bool saveIntegralCoefficients = integralCoefficients; + double minMirEfficacy = minEfficacy; if (success) { double violation = -double(rhs); @@ -1239,6 +1244,8 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, complementation.clear(); inds = inds_.data(); vals = vals_.data(); + integralSupport = saveIntegalSupport; + integralCoefficients = saveIntegralCoefficients; } else // neither cmir nor lifted cut successful return false; diff --git a/src/mip/HighsPathSeparator.cpp b/src/mip/HighsPathSeparator.cpp index 0989bd098c..4bbc0e94f8 100644 --- a/src/mip/HighsPathSeparator.cpp +++ b/src/mip/HighsPathSeparator.cpp @@ -351,16 +351,17 @@ void HighsPathSeparator::separateLpSolution(HighsLpRelaxation& lpRelaxation, if (addedSubstitutionRows) continue; + // generate cut double rhs = 0; - success = cutGen.generateCut(transLp, baseRowInds, baseRowVals, rhs); lpAggregator.getCurrentAggregation(baseRowInds, baseRowVals, true); if (!aggregatedPath.empty() || bestOutArcCol != -1 || bestInArcCol != -1) aggregatedPath.emplace_back(baseRowInds, baseRowVals); - rhs = 0; + // generate reverse cut + rhs = 0; success |= cutGen.generateCut(transLp, baseRowInds, baseRowVals, rhs); if (success || (bestOutArcCol == -1 && bestInArcCol == -1)) break; diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 16b529a4cb..284a13069a 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -180,31 +180,24 @@ bool HighsTransformedLp::transform(std::vector& vals, if (lprelaxation.isColIntegral(col)) { if (lb == -kHighsInf || ub == kHighsInf) integersPositive = false; - bool useVbd = false; - if (ub - lb > 1.5 && boundDist[col] == 0.0 && simpleLbDist[col] != 0 && - simpleUbDist[col] != 0) { - if (bestVlb[col].first == -1 || - ubDist[col] < lbDist[col] - mip.mipdata_->feastol) { - assert(bestVub[col].first != -1); - boundTypes[col] = BoundType::kVariableUb; - useVbd = true; - } else if (bestVub[col].first == -1 || - lbDist[col] < ubDist[col] - mip.mipdata_->feastol) { - assert(bestVlb[col].first != -1); - boundTypes[col] = BoundType::kVariableLb; - useVbd = true; - } else if (vals[i] > 0) { - assert(bestVub[col].first != -1); - boundTypes[col] = BoundType::kVariableUb; - useVbd = true; - } else { - assert(bestVlb[col].first != -1); - boundTypes[col] = BoundType::kVariableLb; - useVbd = true; - } + if (ub - lb <= 1.5 || boundDist[col] != 0.0 || simpleLbDist[col] == 0 || + simpleUbDist[col] == 0) + continue; + if (bestVlb[col].first == -1 || + ubDist[col] < lbDist[col] - mip.mipdata_->feastol) { + assert(bestVub[col].first != -1); + boundTypes[col] = BoundType::kVariableUb; + } else if (bestVub[col].first == -1 || + lbDist[col] < ubDist[col] - mip.mipdata_->feastol) { + assert(bestVlb[col].first != -1); + boundTypes[col] = BoundType::kVariableLb; + } else if (vals[i] > 0) { + assert(bestVub[col].first != -1); + boundTypes[col] = BoundType::kVariableUb; + } else { + assert(bestVlb[col].first != -1); + boundTypes[col] = BoundType::kVariableLb; } - - if (!useVbd) continue; } else { if (lbDist[col] < ubDist[col] - mip.mipdata_->feastol) { if (bestVlb[col].first == -1) @@ -242,6 +235,7 @@ bool HighsTransformedLp::transform(std::vector& vals, switch (boundTypes[col]) { case BoundType::kSimpleLb: if (vals[i] > 0) { + // relax away using lower bound tmpRhs -= lb * vals[i]; vals[i] = 0.0; removeZeros = true; @@ -250,6 +244,7 @@ bool HighsTransformedLp::transform(std::vector& vals, break; case BoundType::kSimpleUb: if (vals[i] < 0) { + // relax away using upper bound tmpRhs -= ub * vals[i]; vals[i] = 0.0; removeZeros = true; @@ -259,6 +254,10 @@ bool HighsTransformedLp::transform(std::vector& vals, case BoundType::kVariableLb: tmpRhs -= bestVlb[col].second.constant * vals[i]; vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); + // arbitrarily initialize bound type for vlb variable in order to + // distinguish from variable 'col'. the bound type will be set properly + // in subsequently. + boundTypes[bestVlb[col].first] = BoundType::kSimpleLb; if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; @@ -267,8 +266,11 @@ bool HighsTransformedLp::transform(std::vector& vals, case BoundType::kVariableUb: tmpRhs -= bestVub[col].second.constant * vals[i]; vectorsum.add(bestVub[col].first, vals[i] * bestVub[col].second.coef); - vals[i] = -vals[i]; - if (vals[i] > 0) { + // arbitrarily initialize bound type for vub variable in order to + // distinguish from variable 'col'. the bound type will be set properly + // in subsequently. + boundTypes[bestVub[col].first] = BoundType::kSimpleLb; + if (vals[i] < 0) { boundTypes[col] = oldBoundType; vals[i] = 0; } @@ -316,28 +318,25 @@ bool HighsTransformedLp::transform(std::vector& vals, inds.resize(numNz); } - if (integersPositive) { - // complement integers to make coefficients positive - for (HighsInt j = 0; j != numNz; ++j) { - HighsInt col = inds[j]; - if (!lprelaxation.isColIntegral(inds[j])) continue; + // integersPositive == true: complement integers to make coefficients positive + // integersPositive == false: complement integers with closest bound + for (HighsInt j = 0; j != numNz; ++j) { + HighsInt col = inds[j]; - if (vals[j] > 0) - boundTypes[col] = BoundType::kSimpleLb; - else - boundTypes[col] = BoundType::kSimpleUb; - } - } else { - // complement integers with closest bound - for (HighsInt j = 0; j != numNz; ++j) { - HighsInt col = inds[j]; - if (!lprelaxation.isColIntegral(inds[j])) continue; - - if (lbDist[col] < ubDist[col]) - boundTypes[col] = BoundType::kSimpleLb; - else - boundTypes[col] = BoundType::kSimpleUb; - } + // skip non-integer variables as their bound types were set above + if (!lprelaxation.isColIntegral(col)) continue; + + // skip integral vlb / vub variables since their status was also already set + // (and it would otherwise be overwritten) + if (boundTypes[col] == BoundType::kVariableLb || + boundTypes[col] == BoundType::kVariableUb) + continue; + + if ((integersPositive && vals[j] > 0) || + (!integersPositive && lbDist[col] < ubDist[col])) + boundTypes[col] = BoundType::kSimpleLb; + else + boundTypes[col] = BoundType::kSimpleUb; } upper.resize(numNz); @@ -362,12 +361,14 @@ bool HighsTransformedLp::transform(std::vector& vals, switch (boundTypes[col]) { case BoundType::kSimpleLb: { + // shift (lower bound) assert(lb != -kHighsInf); tmpRhs -= lb * vals[j]; solval[j] = lbDist[col]; break; } case BoundType::kSimpleUb: { + // complement (upper bound) assert(ub != kHighsInf); tmpRhs -= ub * vals[j]; vals[j] = -vals[j]; @@ -379,6 +380,7 @@ bool HighsTransformedLp::transform(std::vector& vals, break; } case BoundType::kVariableUb: { + vals[j] = -vals[j]; solval[j] = ubDist[col]; continue; } From 60aec89147018ad5d57c3141d117c0b0f676bbf7 Mon Sep 17 00:00:00 2001 From: fwesselm Date: Mon, 25 Nov 2024 10:08:06 +0100 Subject: [PATCH 02/10] Fix transformation --- src/mip/HighsTransformedLp.cpp | 74 ++++++++++++++++------------------ 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 284a13069a..a6b06fc9af 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -140,20 +140,21 @@ bool HighsTransformedLp::transform(std::vector& vals, HighsInt numNz = inds.size(); bool removeZeros = false; + auto getLb = [&](HighsInt col) { + return (col < slackOffset ? mip.mipdata_->domain.col_lower_[col] + : lprelaxation.slackLower(col - slackOffset)); + }; + + auto getUb = [&](HighsInt col) { + return (col < slackOffset ? mip.mipdata_->domain.col_upper_[col] + : lprelaxation.slackUpper(col - slackOffset)); + }; + for (HighsInt i = 0; i != numNz; ++i) { HighsInt col = inds[i]; - double lb; - double ub; - - if (col < slackOffset) { - lb = mip.mipdata_->domain.col_lower_[col]; - ub = mip.mipdata_->domain.col_upper_[col]; - } else { - HighsInt row = col - slackOffset; - lb = lprelaxation.slackLower(row); - ub = lprelaxation.slackUpper(row); - } + double lb = getLb(col); + double ub = getUb(col); if (ub - lb < mip.options_mip_->small_matrix_value) { tmpRhs -= std::min(lb, ub) * vals[i]; @@ -179,7 +180,6 @@ bool HighsTransformedLp::transform(std::vector& vals, BoundType oldBoundType = boundTypes[col]; if (lprelaxation.isColIntegral(col)) { - if (lb == -kHighsInf || ub == kHighsInf) integersPositive = false; if (ub - lb <= 1.5 || boundDist[col] != 0.0 || simpleLbDist[col] == 0 || simpleUbDist[col] == 0) continue; @@ -255,8 +255,8 @@ bool HighsTransformedLp::transform(std::vector& vals, tmpRhs -= bestVlb[col].second.constant * vals[i]; vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); // arbitrarily initialize bound type for vlb variable in order to - // distinguish from variable 'col'. the bound type will be set properly - // in subsequently. + // distinguish from already processed integer-constrained variables. the + // bound type will be set properly in subsequently. boundTypes[bestVlb[col].first] = BoundType::kSimpleLb; if (vals[i] > 0) { boundTypes[col] = oldBoundType; @@ -266,11 +266,12 @@ bool HighsTransformedLp::transform(std::vector& vals, case BoundType::kVariableUb: tmpRhs -= bestVub[col].second.constant * vals[i]; vectorsum.add(bestVub[col].first, vals[i] * bestVub[col].second.coef); + vals[i] = -vals[i]; // arbitrarily initialize bound type for vub variable in order to - // distinguish from variable 'col'. the bound type will be set properly - // in subsequently. + // distinguish from already processed integer-constrained variables. the + // bound type will be set properly in subsequently. boundTypes[bestVub[col].first] = BoundType::kSimpleLb; - if (vals[i] < 0) { + if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; } @@ -318,21 +319,22 @@ bool HighsTransformedLp::transform(std::vector& vals, inds.resize(numNz); } - // integersPositive == true: complement integers to make coefficients positive - // integersPositive == false: complement integers with closest bound for (HighsInt j = 0; j != numNz; ++j) { HighsInt col = inds[j]; - // skip non-integer variables as their bound types were set above - if (!lprelaxation.isColIntegral(col)) continue; - - // skip integral vlb / vub variables since their status was also already set - // (and it would otherwise be overwritten) - if (boundTypes[col] == BoundType::kVariableLb || + // set bound type for previously unprocessed integer-constrained variables. + // do not overwrite bound type for integral slacks from vlb / vub + // constraints. + if (!lprelaxation.isColIntegral(col) || + boundTypes[col] == BoundType::kVariableLb || boundTypes[col] == BoundType::kVariableUb) continue; - if ((integersPositive && vals[j] > 0) || + // complement integers to make coefficients positive if both bounds are + // finite; otherwise, complement integers with closest bound. + // take into account 'integersPositive' provided by caller. + if ((integersPositive && getLb(col) != -kHighsInf && + getUb(col) != kHighsInf && vals[j] > 0) || (!integersPositive && lbDist[col] < ubDist[col])) boundTypes[col] = BoundType::kSimpleLb; else @@ -345,17 +347,8 @@ bool HighsTransformedLp::transform(std::vector& vals, for (HighsInt j = 0; j != numNz; ++j) { HighsInt col = inds[j]; - double lb; - double ub; - - if (col < slackOffset) { - lb = mip.mipdata_->domain.col_lower_[col]; - ub = mip.mipdata_->domain.col_upper_[col]; - } else { - HighsInt row = col - slackOffset; - lb = lprelaxation.slackLower(row); - ub = lprelaxation.slackUpper(row); - } + double lb = getLb(col); + double ub = getUb(col); upper[j] = ub - lb; @@ -380,11 +373,14 @@ bool HighsTransformedLp::transform(std::vector& vals, break; } case BoundType::kVariableUb: { - vals[j] = -vals[j]; solval[j] = ubDist[col]; - continue; + break; } } + + // check if all integer-constrained variables have positive coefficients + if (lprelaxation.isColIntegral(col)) + integersPositive = integersPositive && vals[j] > 0; } rhs = double(tmpRhs); From 9726a7f23d9650840cbd0f1e946192c1bbe8e5f2 Mon Sep 17 00:00:00 2001 From: fwesselm Date: Mon, 25 Nov 2024 10:33:58 +0100 Subject: [PATCH 03/10] Fix if-block --- src/mip/HighsTransformedLp.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index a6b06fc9af..47d3bf05e9 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -330,15 +330,30 @@ bool HighsTransformedLp::transform(std::vector& vals, boundTypes[col] == BoundType::kVariableUb) continue; + // get bounds + double lb = getLb(col); + double ub = getUb(col); + + // make sure that variable is bounded + if (lb == -kHighsInf && ub == kHighsInf) { + vectorsum.clear(); + return false; + } + // complement integers to make coefficients positive if both bounds are // finite; otherwise, complement integers with closest bound. // take into account 'integersPositive' provided by caller. - if ((integersPositive && getLb(col) != -kHighsInf && - getUb(col) != kHighsInf && vals[j] > 0) || - (!integersPositive && lbDist[col] < ubDist[col])) - boundTypes[col] = BoundType::kSimpleLb; - else - boundTypes[col] = BoundType::kSimpleUb; + if (integersPositive) { + if ((lb != -kHighsInf && vals[j] > 0) || ub == kHighsInf) + boundTypes[col] = BoundType::kSimpleLb; + else + boundTypes[col] = BoundType::kSimpleUb; + } else { + if (lbDist[col] < ubDist[col]) + boundTypes[col] = BoundType::kSimpleLb; + else + boundTypes[col] = BoundType::kSimpleUb; + } } upper.resize(numNz); From 6b8382127531235b7d7486a367de3d396a2ee946 Mon Sep 17 00:00:00 2001 From: fwesselm Date: Mon, 25 Nov 2024 10:37:40 +0100 Subject: [PATCH 04/10] Fix typo --- src/mip/HighsTransformedLp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 47d3bf05e9..2db2a14987 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -256,7 +256,7 @@ bool HighsTransformedLp::transform(std::vector& vals, vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); // arbitrarily initialize bound type for vlb variable in order to // distinguish from already processed integer-constrained variables. the - // bound type will be set properly in subsequently. + // bound type will be set properly subsequently. boundTypes[bestVlb[col].first] = BoundType::kSimpleLb; if (vals[i] > 0) { boundTypes[col] = oldBoundType; @@ -269,7 +269,7 @@ bool HighsTransformedLp::transform(std::vector& vals, vals[i] = -vals[i]; // arbitrarily initialize bound type for vub variable in order to // distinguish from already processed integer-constrained variables. the - // bound type will be set properly in subsequently. + // bound type will be set properly subsequently. boundTypes[bestVub[col].first] = BoundType::kSimpleLb; if (vals[i] > 0) { boundTypes[col] = oldBoundType; From 16bc17cc7f528fc65f4ea616ae35b1fc077ed5ef Mon Sep 17 00:00:00 2001 From: fwesselm Date: Tue, 26 Nov 2024 09:40:30 +0100 Subject: [PATCH 05/10] Add an assertion --- src/mip/HighsTransformedLp.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 2db2a14987..56361885c1 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -132,6 +132,9 @@ bool HighsTransformedLp::transform(std::vector& vals, std::vector& solval, std::vector& inds, double& rhs, bool& integersPositive, bool preferVbds) { + // vector sum should be empty + assert(vectorsum.getNonzeros().empty()); + HighsCDouble tmpRhs = rhs; const HighsMipSolver& mip = lprelaxation.getMipSolver(); @@ -335,10 +338,7 @@ bool HighsTransformedLp::transform(std::vector& vals, double ub = getUb(col); // make sure that variable is bounded - if (lb == -kHighsInf && ub == kHighsInf) { - vectorsum.clear(); - return false; - } + if (lb == -kHighsInf && ub == kHighsInf) return false; // complement integers to make coefficients positive if both bounds are // finite; otherwise, complement integers with closest bound. From 8bddf0b5f9e3e8e5f262b96b6de8f3adfce19227 Mon Sep 17 00:00:00 2001 From: fwesselm Date: Tue, 26 Nov 2024 14:29:24 +0100 Subject: [PATCH 06/10] Store indices in set instead of manipulating bound type --- src/mip/HighsTransformedLp.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 56361885c1..491dcd96e2 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -135,6 +135,9 @@ bool HighsTransformedLp::transform(std::vector& vals, // vector sum should be empty assert(vectorsum.getNonzeros().empty()); + // set for storing indices of integral slacks from variable bound constraints + std::set intVariableBndSlacks; + HighsCDouble tmpRhs = rhs; const HighsMipSolver& mip = lprelaxation.getMipSolver(); @@ -257,10 +260,9 @@ bool HighsTransformedLp::transform(std::vector& vals, case BoundType::kVariableLb: tmpRhs -= bestVlb[col].second.constant * vals[i]; vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); - // arbitrarily initialize bound type for vlb variable in order to - // distinguish from already processed integer-constrained variables. the - // bound type will be set properly subsequently. - boundTypes[bestVlb[col].first] = BoundType::kSimpleLb; + // store integral slack in set + if (lprelaxation.isColIntegral(bestVlb[col].first)) + intVariableBndSlacks.insert(bestVlb[col].first); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; @@ -270,10 +272,9 @@ bool HighsTransformedLp::transform(std::vector& vals, tmpRhs -= bestVub[col].second.constant * vals[i]; vectorsum.add(bestVub[col].first, vals[i] * bestVub[col].second.coef); vals[i] = -vals[i]; - // arbitrarily initialize bound type for vub variable in order to - // distinguish from already processed integer-constrained variables. the - // bound type will be set properly subsequently. - boundTypes[bestVub[col].first] = BoundType::kSimpleLb; + // store integral slack in set + if (lprelaxation.isColIntegral(bestVub[col].first)) + intVariableBndSlacks.insert(bestVub[col].first); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; @@ -325,13 +326,8 @@ bool HighsTransformedLp::transform(std::vector& vals, for (HighsInt j = 0; j != numNz; ++j) { HighsInt col = inds[j]; - // set bound type for previously unprocessed integer-constrained variables. - // do not overwrite bound type for integral slacks from vlb / vub - // constraints. - if (!lprelaxation.isColIntegral(col) || - boundTypes[col] == BoundType::kVariableLb || - boundTypes[col] == BoundType::kVariableUb) - continue; + // set bound type for previously unprocessed integer-constrained variables + if (!lprelaxation.isColIntegral(col)) continue; // get bounds double lb = getLb(col); @@ -340,6 +336,10 @@ bool HighsTransformedLp::transform(std::vector& vals, // make sure that variable is bounded if (lb == -kHighsInf && ub == kHighsInf) return false; + // do not overwrite bound type for integral slacks from vlb / vub + // constraints + if (intVariableBndSlacks.find(col) != intVariableBndSlacks.end()) continue; + // complement integers to make coefficients positive if both bounds are // finite; otherwise, complement integers with closest bound. // take into account 'integersPositive' provided by caller. From 083f617b08ed80825a543472597a8499b8ec9363 Mon Sep 17 00:00:00 2001 From: fwesselm Date: Tue, 26 Nov 2024 14:53:00 +0100 Subject: [PATCH 07/10] Fix index error --- src/mip/HighsTransformedLp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 491dcd96e2..9c06d96328 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -261,8 +261,7 @@ bool HighsTransformedLp::transform(std::vector& vals, tmpRhs -= bestVlb[col].second.constant * vals[i]; vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); // store integral slack in set - if (lprelaxation.isColIntegral(bestVlb[col].first)) - intVariableBndSlacks.insert(bestVlb[col].first); + if (lprelaxation.isColIntegral(col)) intVariableBndSlacks.insert(col); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; @@ -273,8 +272,7 @@ bool HighsTransformedLp::transform(std::vector& vals, vectorsum.add(bestVub[col].first, vals[i] * bestVub[col].second.coef); vals[i] = -vals[i]; // store integral slack in set - if (lprelaxation.isColIntegral(bestVub[col].first)) - intVariableBndSlacks.insert(bestVub[col].first); + if (lprelaxation.isColIntegral(col)) intVariableBndSlacks.insert(col); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; From 0052f8f7701c4f3090460ee625ca27635bf41ccd Mon Sep 17 00:00:00 2001 From: fwesselm Date: Tue, 26 Nov 2024 14:56:10 +0100 Subject: [PATCH 08/10] Restructure a little bit --- src/mip/HighsTransformedLp.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 9c06d96328..66caf9e485 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -260,22 +260,24 @@ bool HighsTransformedLp::transform(std::vector& vals, case BoundType::kVariableLb: tmpRhs -= bestVlb[col].second.constant * vals[i]; vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); - // store integral slack in set - if (lprelaxation.isColIntegral(col)) intVariableBndSlacks.insert(col); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; + } else if (lprelaxation.isColIntegral(col)) { + // store integral slack in set + intVariableBndSlacks.insert(col); } break; case BoundType::kVariableUb: tmpRhs -= bestVub[col].second.constant * vals[i]; vectorsum.add(bestVub[col].first, vals[i] * bestVub[col].second.coef); vals[i] = -vals[i]; - // store integral slack in set - if (lprelaxation.isColIntegral(col)) intVariableBndSlacks.insert(col); if (vals[i] > 0) { boundTypes[col] = oldBoundType; vals[i] = 0; + } else if (lprelaxation.isColIntegral(col)) { + // store integral slack in set + intVariableBndSlacks.insert(col); } } } From 0373ffdb8792916d9ec67b2a800a961ab561033c Mon Sep 17 00:00:00 2001 From: fwesselm Date: Wed, 27 Nov 2024 10:14:22 +0100 Subject: [PATCH 09/10] Minor changes to loop --- src/mip/HighsTransformedLp.cpp | 53 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 66caf9e485..8900cd6121 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -144,7 +144,6 @@ bool HighsTransformedLp::transform(std::vector& vals, const HighsInt slackOffset = lprelaxation.numCols(); HighsInt numNz = inds.size(); - bool removeZeros = false; auto getLb = [&](HighsInt col) { return (col < slackOffset ? mip.mipdata_->domain.col_lower_[col] @@ -156,7 +155,16 @@ bool HighsTransformedLp::transform(std::vector& vals, : lprelaxation.slackUpper(col - slackOffset)); }; - for (HighsInt i = 0; i != numNz; ++i) { + auto remove = [&](HighsInt position) { + numNz--; + inds[position] = inds[numNz]; + vals[position] = vals[numNz]; + inds[numNz] = 0; + vals[numNz] = 0; + }; + + HighsInt i = 0; + while (i < numNz) { HighsInt col = inds[i]; double lb = getLb(col); @@ -164,8 +172,7 @@ bool HighsTransformedLp::transform(std::vector& vals, if (ub - lb < mip.options_mip_->small_matrix_value) { tmpRhs -= std::min(lb, ub) * vals[i]; - vals[i] = 0.0; - removeZeros = true; + remove(i); continue; } @@ -187,8 +194,10 @@ bool HighsTransformedLp::transform(std::vector& vals, if (lprelaxation.isColIntegral(col)) { if (ub - lb <= 1.5 || boundDist[col] != 0.0 || simpleLbDist[col] == 0 || - simpleUbDist[col] == 0) + simpleUbDist[col] == 0) { + i++; continue; + } if (bestVlb[col].first == -1 || ubDist[col] < lbDist[col] - mip.mipdata_->feastol) { assert(bestVub[col].first != -1); @@ -243,18 +252,18 @@ bool HighsTransformedLp::transform(std::vector& vals, if (vals[i] > 0) { // relax away using lower bound tmpRhs -= lb * vals[i]; - vals[i] = 0.0; - removeZeros = true; boundTypes[col] = oldBoundType; + remove(i); + continue; } break; case BoundType::kSimpleUb: if (vals[i] < 0) { // relax away using upper bound tmpRhs -= ub * vals[i]; - vals[i] = 0.0; - removeZeros = true; boundTypes[col] = oldBoundType; + remove(i); + continue; } break; case BoundType::kVariableLb: @@ -262,7 +271,8 @@ bool HighsTransformedLp::transform(std::vector& vals, vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef); if (vals[i] > 0) { boundTypes[col] = oldBoundType; - vals[i] = 0; + remove(i); + continue; } else if (lprelaxation.isColIntegral(col)) { // store integral slack in set intVariableBndSlacks.insert(col); @@ -274,12 +284,15 @@ bool HighsTransformedLp::transform(std::vector& vals, vals[i] = -vals[i]; if (vals[i] > 0) { boundTypes[col] = oldBoundType; - vals[i] = 0; + remove(i); + continue; } else if (lprelaxation.isColIntegral(col)) { // store integral slack in set intVariableBndSlacks.insert(col); } } + // move to next element + i++; } if (!vectorsum.getNonzeros().empty()) { @@ -289,9 +302,7 @@ bool HighsTransformedLp::transform(std::vector& vals, double maxError = 0.0; auto IsZero = [&](HighsInt col, double val) { - double absval = std::abs(val); - if (absval <= mip.options_mip_->small_matrix_value) return true; - + if (std::abs(val) <= mip.options_mip_->small_matrix_value) return true; return false; }; @@ -308,17 +319,7 @@ bool HighsTransformedLp::transform(std::vector& vals, for (HighsInt j = 0; j != numNz; ++j) vals[j] = vectorsum.getValue(inds[j]); vectorsum.clear(); - } else if (removeZeros) { - for (HighsInt i = numNz - 1; i >= 0; --i) { - if (vals[i] == 0) { - --numNz; - vals[i] = vals[numNz]; - inds[i] = inds[numNz]; - std::swap(vals[i], vals[numNz]); - std::swap(inds[i], inds[numNz]); - } - } - + } else { vals.resize(numNz); inds.resize(numNz); } @@ -333,7 +334,7 @@ bool HighsTransformedLp::transform(std::vector& vals, double lb = getLb(col); double ub = getUb(col); - // make sure that variable is bounded + // make sure that variable is not free if (lb == -kHighsInf && ub == kHighsInf) return false; // do not overwrite bound type for integral slacks from vlb / vub From 8465762ee48a806033f75aa9866cf942f1ad160c Mon Sep 17 00:00:00 2001 From: fwesselm Date: Wed, 27 Nov 2024 13:58:43 +0100 Subject: [PATCH 10/10] Simplify --- src/mip/HighsCutGeneration.cpp | 21 +++++++++++++++------ src/mip/HighsTransformedLp.cpp | 23 ++++++++--------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/mip/HighsCutGeneration.cpp b/src/mip/HighsCutGeneration.cpp index 0db97b9e52..d265c24b78 100644 --- a/src/mip/HighsCutGeneration.cpp +++ b/src/mip/HighsCutGeneration.cpp @@ -1184,11 +1184,6 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, } } while (false); - // save data that might otherwise be overwritten when calling the cmir - // separator - bool saveIntegalSupport = integralSupport; - bool saveIntegralCoefficients = integralCoefficients; - double minMirEfficacy = minEfficacy; if (success) { double violation = -double(rhs); @@ -1210,7 +1205,7 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, minMirEfficacy += efficacy; if (!complementation.empty()) { // remove the complementation if it exists, so that the values stored - // values are uncomplemented + // are uncomplemented for (HighsInt i = 0; i != rowlen; ++i) { if (complementation[i]) { rhs -= upper[i] * vals[i]; @@ -1226,6 +1221,11 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, inds = tmpInds.data(); vals = tmpVals.data(); + // save data that might otherwise be overwritten when calling the cmir + // separator + bool saveIntegalSupport = integralSupport; + bool saveIntegralCoefficients = integralCoefficients; + bool cmirSuccess = cmirCutGenerationHeuristic(minMirEfficacy, onlyInitialCMIRScale); @@ -1244,6 +1244,7 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp, complementation.clear(); inds = inds_.data(); vals = vals_.data(); + // restore indicators integralSupport = saveIntegalSupport; integralCoefficients = saveIntegralCoefficients; } else @@ -1423,6 +1424,11 @@ bool HighsCutGeneration::generateConflict(HighsDomain& localdomain, inds = tmpInds.data(); vals = tmpVals.data(); + // save data that might otherwise be overwritten when calling the cmir + // separator + bool saveIntegalSupport = integralSupport; + bool saveIntegralCoefficients = integralCoefficients; + bool cmirSuccess = cmirCutGenerationHeuristic(minEfficacy); if (cmirSuccess) { @@ -1438,6 +1444,9 @@ bool HighsCutGeneration::generateConflict(HighsDomain& localdomain, complementation.swap(tmpComplementation); inds = proofinds.data(); vals = proofvals.data(); + // restore indicators + integralSupport = saveIntegalSupport; + integralCoefficients = saveIntegralCoefficients; } else // neither cmir nor lifted cut successful return false; diff --git a/src/mip/HighsTransformedLp.cpp b/src/mip/HighsTransformedLp.cpp index 8900cd6121..68be21844b 100644 --- a/src/mip/HighsTransformedLp.cpp +++ b/src/mip/HighsTransformedLp.cpp @@ -135,9 +135,6 @@ bool HighsTransformedLp::transform(std::vector& vals, // vector sum should be empty assert(vectorsum.getNonzeros().empty()); - // set for storing indices of integral slacks from variable bound constraints - std::set intVariableBndSlacks; - HighsCDouble tmpRhs = rhs; const HighsMipSolver& mip = lprelaxation.getMipSolver(); @@ -273,9 +270,6 @@ bool HighsTransformedLp::transform(std::vector& vals, boundTypes[col] = oldBoundType; remove(i); continue; - } else if (lprelaxation.isColIntegral(col)) { - // store integral slack in set - intVariableBndSlacks.insert(col); } break; case BoundType::kVariableUb: @@ -286,9 +280,6 @@ bool HighsTransformedLp::transform(std::vector& vals, boundTypes[col] = oldBoundType; remove(i); continue; - } else if (lprelaxation.isColIntegral(col)) { - // store integral slack in set - intVariableBndSlacks.insert(col); } } // move to next element @@ -327,19 +318,21 @@ bool HighsTransformedLp::transform(std::vector& vals, for (HighsInt j = 0; j != numNz; ++j) { HighsInt col = inds[j]; - // set bound type for previously unprocessed integer-constrained variables - if (!lprelaxation.isColIntegral(col)) continue; - // get bounds double lb = getLb(col); double ub = getUb(col); - // make sure that variable is not free - if (lb == -kHighsInf && ub == kHighsInf) return false; + // variable should not be free + assert(lb != -kHighsInf || ub != kHighsInf); + + // set bound type for previously unprocessed integer-constrained variables + if (!lprelaxation.isColIntegral(col)) continue; // do not overwrite bound type for integral slacks from vlb / vub // constraints - if (intVariableBndSlacks.find(col) != intVariableBndSlacks.end()) continue; + if (boundTypes[col] == BoundType::kVariableLb || + boundTypes[col] == BoundType::kVariableUb) + continue; // complement integers to make coefficients positive if both bounds are // finite; otherwise, complement integers with closest bound.