Skip to content

Commit 9988e99

Browse files
committed
Working code
1 parent 299db82 commit 9988e99

File tree

1 file changed

+136
-22
lines changed

1 file changed

+136
-22
lines changed

highs/presolve/HPresolve.cpp

Lines changed: 136 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3727,21 +3727,18 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
37273727
rowCoefs.reserve(rowsize[row]);
37283728
rowIndex.reserve(rowsize[row]);
37293729

3730-
double deltaDown = model->row_lower_[row] == -kHighsInf
3731-
? primal_feastol
3732-
: options->small_matrix_value;
3733-
double deltaUp = model->row_upper_[row] == kHighsInf
3734-
? primal_feastol
3735-
: options->small_matrix_value;
3736-
37373730
for (const HighsSliceNonzero& nonz : getStoredRow()) {
37383731
assert(nonz.value() != 0.0);
37393732
rowCoefs.push_back(nonz.value());
37403733
rowIndex.push_back(nonz.index());
37413734
}
37423735

3743-
double intScale =
3744-
HighsIntegers::integralScale(rowCoefs, deltaDown, deltaUp);
3736+
double intScale = HighsIntegers::integralScale(
3737+
rowCoefs,
3738+
model->row_lower_[row] == -kHighsInf ? primal_feastol
3739+
: options->small_matrix_value,
3740+
model->row_upper_[row] == kHighsInf ? primal_feastol
3741+
: options->small_matrix_value);
37453742

37463743
auto roundRhs = [&](HighsCDouble rhs, HighsCDouble& roundedRhs,
37473744
HighsCDouble& fractionRhs, double minRhsTightening,
@@ -3904,26 +3901,32 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
39043901
}
39053902
} else if (model->row_lower_[row] == -kHighsInf ||
39063903
model->row_upper_[row] == kHighsInf) {
3907-
// try Chvatal-Gomory strengthening
3908-
std::vector<HighsInt> complementedOrShifted;
3909-
complementedOrShifted.resize(rowCoefs.size());
3910-
3911-
HighsInt direction =
3912-
model->row_upper_[row] != kHighsInf ? HighsInt{1} : HighsInt{-1};
3913-
HighsCDouble rhs =
3914-
direction > 0 ? model->row_upper_[row] : -model->row_lower_[row];
3915-
3916-
auto complementOrShift = [&]() {
3904+
// Chvatal-Gomory strengthening
3905+
std::vector<double> roundedRowCoefs;
3906+
roundedRowCoefs.resize(rowsize[row]);
3907+
std::set<double> scalars;
3908+
3909+
// lambda for reformulating row to only contain variables with a lower
3910+
// bound of zero (if possible)
3911+
auto complementOrShift = [&](HighsInt direction, HighsCDouble& rhs,
3912+
double& minAbsCoef, double& maxAbsCoef) {
3913+
minAbsCoef = kHighsInf;
3914+
maxAbsCoef = 0.0;
39173915
for (size_t i = 0; i < rowCoefs.size(); ++i) {
3916+
// get column index and (absolute) coefficient
39183917
HighsInt col = rowIndex[i];
39193918
double val = direction * rowCoefs[i];
3919+
double absval = std::abs(val);
3920+
// compute minimum and maximum absolute coefficients along the way
3921+
minAbsCoef = std::min(minAbsCoef, absval);
3922+
maxAbsCoef = std::max(maxAbsCoef, absval);
39203923
if (val < 0.0 && model->col_upper_[col] != kHighsInf) {
3924+
// complement
39213925
rhs -= val * static_cast<HighsCDouble>(model->col_upper_[col]);
39223926
rowCoefs[i] = -rowCoefs[i];
3923-
complementedOrShifted[i] = -1;
39243927
} else if (val > 0.0 && model->col_lower_[col] != -kHighsInf) {
3928+
// shift
39253929
rhs -= val * static_cast<HighsCDouble>(model->col_lower_[col]);
3926-
complementedOrShifted[i] = 1;
39273930
} else {
39283931
// unbounded variable; cannot shift or complement!
39293932
return false;
@@ -3932,11 +3935,122 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
39323935
return true;
39333936
};
39343937

3935-
if (complementOrShift()) {
3938+
// undo shifting and complementation
3939+
auto undoComplementOrShift = [&](HighsInt direction,
3940+
HighsCDouble& roundedRhs) {
3941+
for (size_t i = 0; i < rowCoefs.size(); ++i) {
3942+
HighsInt col = rowIndex[i];
3943+
double val = direction * rowCoefs[i];
3944+
if (val < 0.0 && model->col_upper_[col] != kHighsInf) {
3945+
// un-complement
3946+
roundedRowCoefs[i] = -roundedRowCoefs[i];
3947+
roundedRhs += roundedRowCoefs[i] *
3948+
static_cast<HighsCDouble>(model->col_upper_[col]);
3949+
} else if (val > 0.0 && model->col_lower_[col] != -kHighsInf) {
3950+
// unshift
3951+
roundedRhs += roundedRowCoefs[i] *
3952+
static_cast<HighsCDouble>(model->col_lower_[col]);
3953+
}
3954+
// flip coefficient sign for >= inequality
3955+
roundedRowCoefs[i] *= direction;
3956+
}
3957+
// flip rhs sign for >= inequality
3958+
roundedRhs *= direction;
3959+
};
3960+
3961+
// round row using given scalar
3962+
auto roundRow = [&](double s, const HighsCDouble& rhs,
3963+
HighsCDouble& roundedRhs) {
3964+
bool accept = false;
3965+
// round rhs (using feasibility tolerance)
3966+
roundedRhs = floor(rhs * s + primal_feastol);
3967+
for (size_t i = 0; i < rowCoefs.size(); ++i) {
3968+
// round coefficient
3969+
roundedRowCoefs[i] = std::floor(rowCoefs[i] * s + kHighsTiny);
3970+
// compute "normalised" coefficients, i.e. coefficient divided by
3971+
// rhs
3972+
double normalisedCoef =
3973+
static_cast<double>(rowCoefs[i] * roundedRhs);
3974+
double normalisedRoundedCoef =
3975+
static_cast<double>(roundedRowCoefs[i] * rhs);
3976+
// return if coefficient is weaker
3977+
if (normalisedRoundedCoef <
3978+
normalisedCoef - options->small_matrix_value)
3979+
return false;
3980+
// accept rounding if at least one coefficient is improved
3981+
accept = accept || (normalisedRoundedCoef >
3982+
normalisedCoef + options->small_matrix_value);
3983+
}
3984+
return accept;
3985+
};
3986+
3987+
// set up scalars suggested by Achterberg et al.
3988+
auto setScalars = [&](double minAbsCoef, double maxAbsCoef) {
3989+
scalars.clear();
3990+
scalars.emplace(1.0);
3991+
for (HighsInt t = 1; t <= 5; t++) {
3992+
scalars.emplace(t / maxAbsCoef);
3993+
scalars.emplace(t / minAbsCoef);
3994+
scalars.emplace((2 * t - 1) / (2 * minAbsCoef));
3995+
}
3996+
};
3997+
3998+
// try different scalars and return an improving one
3999+
auto scalarToTightenRow = [&](const HighsCDouble& rhs,
4000+
HighsCDouble& roundedRhs) {
4001+
for (double s : scalars) {
4002+
if (roundRow(s, rhs, roundedRhs)) return s;
4003+
}
4004+
return 0.0;
4005+
};
4006+
4007+
// replace the model row by the rounded one
4008+
auto updateRow = [&](HighsInt row, HighsInt direction,
4009+
HighsCDouble roundedRhs) {
4010+
if (direction > 0)
4011+
model->row_upper_[row] = static_cast<double>(roundedRhs);
4012+
else
4013+
model->row_lower_[row] = static_cast<double>(roundedRhs);
4014+
for (size_t i = 0; i < rowCoefs.size(); ++i) {
4015+
double delta = static_cast<double>(
4016+
static_cast<HighsCDouble>(roundedRowCoefs[i]) - rowCoefs[i]);
4017+
if (std::fabs(delta) > options->small_matrix_value)
4018+
addToMatrix(row, rowIndex[i], delta);
4019+
}
4020+
};
4021+
4022+
// direction = 1: <= constraint, direction = -1: >= constraint
4023+
HighsInt direction =
4024+
model->row_upper_[row] != kHighsInf ? HighsInt{1} : HighsInt{-1};
4025+
// get rhs
4026+
HighsCDouble rhs =
4027+
direction > 0 ? model->row_upper_[row] : -model->row_lower_[row];
4028+
4029+
// initialise
4030+
HighsCDouble roundedRhs = 0.0;
4031+
double minAbsCoef = 0.0;
4032+
double maxAbsCoef = 0.0;
4033+
4034+
// complement or shift variables to have lower bounds of zero
4035+
if (complementOrShift(direction, rhs, minAbsCoef, maxAbsCoef)) {
4036+
// identify scalars for row
4037+
setScalars(minAbsCoef, maxAbsCoef);
4038+
// find a scalar that produces improved coefficients
4039+
double s = scalarToTightenRow(rhs, roundedRhs);
4040+
if (s != 0.0) {
4041+
// undo complementation and shifting
4042+
undoComplementOrShift(direction, roundedRhs);
4043+
// replace row by rounded one
4044+
updateRow(row, direction, roundedRhs);
4045+
}
39364046
}
39374047
}
39384048
}
39394049

4050+
// check for redundancy again
4051+
HPRESOLVE_CHECKED_CALL(checkRowRedundant(row));
4052+
if (rowDeleted[row]) return Result::kOk;
4053+
39404054
auto strengthenCoefs = [&](HighsCDouble& rhs, HighsInt direction,
39414055
HighsCDouble maxAbsCoefValue) {
39424056
// iterate over non-zero positions instead of iterating over the

0 commit comments

Comments
 (0)