Skip to content

Commit

Permalink
Merge pull request #2066 from fwesselm/fix-1954
Browse files Browse the repository at this point in the history
Fix 1954
  • Loading branch information
jajhall authored Dec 3, 2024
2 parents dcd8bcc + 938c655 commit f3967c6
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 88 deletions.
18 changes: 17 additions & 1 deletion src/mip/HighsCutGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1205,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];
Expand All @@ -1221,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);

Expand All @@ -1239,6 +1244,9 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
complementation.clear();
inds = inds_.data();
vals = vals_.data();
// restore indicators
integralSupport = saveIntegalSupport;
integralCoefficients = saveIntegralCoefficients;
} else
// neither cmir nor lifted cut successful
return false;
Expand Down Expand Up @@ -1416,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) {
Expand All @@ -1431,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;
Expand Down
5 changes: 3 additions & 2 deletions src/mip/HighsPathSeparator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
177 changes: 92 additions & 85 deletions src/mip/HighsTransformedLp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,33 +132,44 @@ bool HighsTransformedLp::transform(std::vector<double>& vals,
std::vector<double>& solval,
std::vector<HighsInt>& inds, double& rhs,
bool& integersPositive, bool preferVbds) {
// vector sum should be empty
assert(vectorsum.getNonzeros().empty());

HighsCDouble tmpRhs = rhs;

const HighsMipSolver& mip = lprelaxation.getMipSolver();
const HighsInt slackOffset = lprelaxation.numCols();

HighsInt numNz = inds.size();
bool removeZeros = false;

for (HighsInt i = 0; i != numNz; ++i) {
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));
};

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;
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];
vals[i] = 0.0;
removeZeros = true;
remove(i);
continue;
}

Expand All @@ -179,32 +190,26 @@ bool HighsTransformedLp::transform(std::vector<double>& vals,
BoundType oldBoundType = boundTypes[col];

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) {
i++;
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)
Expand Down Expand Up @@ -242,26 +247,29 @@ bool HighsTransformedLp::transform(std::vector<double>& 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;
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:
tmpRhs -= bestVlb[col].second.constant * vals[i];
vectorsum.add(bestVlb[col].first, vals[i] * bestVlb[col].second.coef);
if (vals[i] > 0) {
boundTypes[col] = oldBoundType;
vals[i] = 0;
remove(i);
continue;
}
break;
case BoundType::kVariableUb:
Expand All @@ -270,9 +278,12 @@ bool HighsTransformedLp::transform(std::vector<double>& vals,
vals[i] = -vals[i];
if (vals[i] > 0) {
boundTypes[col] = oldBoundType;
vals[i] = 0;
remove(i);
continue;
}
}
// move to next element
i++;
}

if (!vectorsum.getNonzeros().empty()) {
Expand All @@ -282,9 +293,7 @@ bool HighsTransformedLp::transform(std::vector<double>& 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;
};

Expand All @@ -301,38 +310,39 @@ bool HighsTransformedLp::transform(std::vector<double>& 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);
}

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;
for (HighsInt j = 0; j != numNz; ++j) {
HighsInt col = inds[j];

// get bounds
double lb = getLb(col);
double ub = getUb(col);

// 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 (boundTypes[col] == BoundType::kVariableLb ||
boundTypes[col] == BoundType::kVariableUb)
continue;

if (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) {
if ((lb != -kHighsInf && vals[j] > 0) || ub == kHighsInf)
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;

} else {
if (lbDist[col] < ubDist[col])
boundTypes[col] = BoundType::kSimpleLb;
else
Expand All @@ -346,28 +356,21 @@ bool HighsTransformedLp::transform(std::vector<double>& 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;

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];
Expand All @@ -380,9 +383,13 @@ bool HighsTransformedLp::transform(std::vector<double>& vals,
}
case BoundType::kVariableUb: {
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);
Expand Down

0 comments on commit f3967c6

Please sign in to comment.