Skip to content

Commit 96f998e

Browse files
authored
Merge pull request #2695 from fwesselm/enumerateSolutions4
Solution enumeration
2 parents 884a336 + 58e406f commit 96f998e

File tree

11 files changed

+548
-148
lines changed

11 files changed

+548
-148
lines changed

highs/lp_data/HConst.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ enum PresolveRuleType : int {
274274
kPresolveRuleParallelRowsAndCols,
275275
kPresolveRuleSparsify,
276276
kPresolveRuleProbing,
277-
kPresolveRuleMax = kPresolveRuleProbing,
277+
kPresolveRuleEnumeration,
278+
kPresolveRuleMax = kPresolveRuleEnumeration,
278279
kPresolveRuleLastAllowOff = kPresolveRuleMax,
279280
kPresolveRuleCount
280281
};

highs/lp_data/HighsModelUtils.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,8 @@ std::string utilPresolveRuleTypeToString(const HighsInt rule_type) {
14551455
return "Sparsify";
14561456
} else if (rule_type == kPresolveRuleProbing) {
14571457
return "Probing";
1458+
} else if (rule_type == kPresolveRuleEnumeration) {
1459+
return "Enumeration";
14581460
}
14591461
assert(1 == 0);
14601462
return "????";

highs/mip/HighsDomain.cpp

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,10 +1548,7 @@ void HighsDomain::updateActivityLbChange(HighsInt col, double oldbound,
15481548
if (recordRedundantRows_ &&
15491549
mip->row_lower_[mip->a_matrix_.index_[i]] != -kHighsInf &&
15501550
mip->row_upper_[mip->a_matrix_.index_[i]] == kHighsInf)
1551-
updateRedundantRows(mip->a_matrix_.index_[i], HighsInt{1},
1552-
activitymininf_[mip->a_matrix_.index_[i]],
1553-
activitymin_[mip->a_matrix_.index_[i]],
1554-
mip->row_lower_[mip->a_matrix_.index_[i]]);
1551+
updateRedundantRows(mip->a_matrix_.index_[i]);
15551552

15561553
if (deltamin <= 0) {
15571554
updateThresholdLbChange(col, newbound, mip->a_matrix_.value_[i],
@@ -1600,10 +1597,7 @@ void HighsDomain::updateActivityLbChange(HighsInt col, double oldbound,
16001597
if (recordRedundantRows_ &&
16011598
mip->row_lower_[mip->a_matrix_.index_[i]] == -kHighsInf &&
16021599
mip->row_upper_[mip->a_matrix_.index_[i]] != kHighsInf)
1603-
updateRedundantRows(mip->a_matrix_.index_[i], HighsInt{-1},
1604-
activitymaxinf_[mip->a_matrix_.index_[i]],
1605-
activitymax_[mip->a_matrix_.index_[i]],
1606-
mip->row_upper_[mip->a_matrix_.index_[i]]);
1600+
updateRedundantRows(mip->a_matrix_.index_[i]);
16071601

16081602
if (deltamax >= 0) {
16091603
updateThresholdLbChange(col, newbound, mip->a_matrix_.value_[i],
@@ -1703,10 +1697,7 @@ void HighsDomain::updateActivityUbChange(HighsInt col, double oldbound,
17031697
if (recordRedundantRows_ &&
17041698
mip->row_lower_[mip->a_matrix_.index_[i]] == -kHighsInf &&
17051699
mip->row_upper_[mip->a_matrix_.index_[i]] != kHighsInf)
1706-
updateRedundantRows(mip->a_matrix_.index_[i], HighsInt{-1},
1707-
activitymaxinf_[mip->a_matrix_.index_[i]],
1708-
activitymax_[mip->a_matrix_.index_[i]],
1709-
mip->row_upper_[mip->a_matrix_.index_[i]]);
1700+
updateRedundantRows(mip->a_matrix_.index_[i]);
17101701

17111702
if (deltamax >= 0) {
17121703
updateThresholdUbChange(col, newbound, mip->a_matrix_.value_[i],
@@ -1758,10 +1749,7 @@ void HighsDomain::updateActivityUbChange(HighsInt col, double oldbound,
17581749
if (recordRedundantRows_ &&
17591750
mip->row_lower_[mip->a_matrix_.index_[i]] != -kHighsInf &&
17601751
mip->row_upper_[mip->a_matrix_.index_[i]] == kHighsInf)
1761-
updateRedundantRows(mip->a_matrix_.index_[i], HighsInt{1},
1762-
activitymininf_[mip->a_matrix_.index_[i]],
1763-
activitymin_[mip->a_matrix_.index_[i]],
1764-
mip->row_lower_[mip->a_matrix_.index_[i]]);
1752+
updateRedundantRows(mip->a_matrix_.index_[i]);
17651753

17661754
if (deltamin <= 0) {
17671755
updateThresholdUbChange(col, newbound, mip->a_matrix_.value_[i],
@@ -1845,11 +1833,8 @@ void HighsDomain::recomputeCapacityThreshold(HighsInt row) {
18451833
}
18461834
}
18471835

1848-
void HighsDomain::updateRedundantRows(HighsInt row, HighsInt direction,
1849-
HighsInt numinf, HighsCDouble activity,
1850-
double bound) {
1851-
if (numinf != 0 || direction * activity <=
1852-
direction * bound + mipsolver->mipdata_->feastol) {
1836+
void HighsDomain::updateRedundantRows(HighsInt row) {
1837+
if (!isRedundantRow(row)) {
18531838
// row that was found to be redundant should not be non-redundant
18541839
assert(redundantRows_.find(row) == nullptr);
18551840
return;
@@ -1870,6 +1855,12 @@ double HighsDomain::getRedundantRowValue(HighsInt row) const {
18701855
}
18711856
}
18721857

1858+
bool HighsDomain::isRedundantRow(HighsInt row) const {
1859+
return (
1860+
getMinActivity(row) >= mipsolver->model_->row_lower_[row] - feastol() &&
1861+
getMaxActivity(row) <= mipsolver->model_->row_upper_[row] + feastol());
1862+
}
1863+
18731864
void HighsDomain::markPropagateCut(Reason reason) {
18741865
switch (reason.type) {
18751866
case Reason::kUnknown:
@@ -1957,7 +1948,7 @@ double HighsDomain::doChangeBound(const HighsDomainChange& boundchg) {
19571948
if (!infeasible_)
19581949
updateActivityLbChange(boundchg.column, oldbound, boundchg.boundval);
19591950

1960-
if (!changedcolsflags_[boundchg.column]) {
1951+
if (!isChangedCol(boundchg.column)) {
19611952
changedcolsflags_[boundchg.column] = 1;
19621953
changedcols_.push_back(boundchg.column);
19631954
}
@@ -1969,7 +1960,7 @@ double HighsDomain::doChangeBound(const HighsDomainChange& boundchg) {
19691960
if (!infeasible_)
19701961
updateActivityUbChange(boundchg.column, oldbound, boundchg.boundval);
19711962

1972-
if (!changedcolsflags_[boundchg.column]) {
1963+
if (!isChangedCol(boundchg.column)) {
19731964
changedcolsflags_[boundchg.column] = 1;
19741965
changedcols_.push_back(boundchg.column);
19751966
}

highs/mip/HighsDomain.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,7 @@ class HighsDomain {
324324

325325
void recomputeCapacityThreshold(HighsInt row);
326326

327-
void updateRedundantRows(HighsInt row, HighsInt direction, HighsInt numInf,
328-
HighsCDouble activity, double bound);
327+
void updateRedundantRows(HighsInt row);
329328

330329
double doChangeBound(const HighsDomainChange& boundchg);
331330

@@ -444,18 +443,19 @@ class HighsDomain {
444443

445444
changedcols_.erase(
446445
std::remove_if(changedcols_.begin(), changedcols_.end(),
447-
[&](HighsInt i) { return !changedcolsflags_[i]; }),
446+
[&](HighsInt i) { return !isChangedCol(i); }),
448447
changedcols_.end());
449448
}
450449

451-
void clearChangedCols(HighsInt start) {
452-
HighsInt end = changedcols_.size();
453-
for (HighsInt i = start; i != end; ++i)
450+
void clearChangedCols(size_t start) {
451+
for (size_t i = start; i != changedcols_.size(); ++i)
454452
changedcolsflags_[changedcols_[i]] = 0;
455453

456454
changedcols_.resize(start);
457455
}
458456

457+
bool isChangedCol(HighsInt col) const { return changedcolsflags_[col] != 0; }
458+
459459
void markPropagate(HighsInt row);
460460

461461
bool isActive(const HighsDomainChange& domchg) const {
@@ -647,6 +647,8 @@ class HighsDomain {
647647
double getRedundantRowValue(HighsInt row) const;
648648

649649
void setRecordRedundantRows(bool val) { recordRedundantRows_ = val; };
650+
651+
bool isRedundantRow(HighsInt row) const;
650652
};
651653

652654
#endif

highs/mip/HighsImplications.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,7 @@ bool HighsImplications::runProbing(HighsInt col, HighsInt& numReductions) {
300300
if (globaldomain.isBinary(col) && !implicationsCached(col, 1) &&
301301
!implicationsCached(col, 0) &&
302302
mipsolver.mipdata_->cliquetable.getSubstitution(col) == nullptr) {
303-
bool infeasible;
304-
305-
infeasible = computeImplications(col, 1);
303+
bool infeasible = computeImplications(col, 1);
306304
if (globaldomain.infeasible()) return true;
307305
if (infeasible) return true;
308306
if (mipsolver.mipdata_->cliquetable.getSubstitution(col) != nullptr)

highs/mip/HighsMipSolverData.cpp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ std::string HighsMipSolverData::solutionSourceToString(
9898

9999
bool HighsMipSolverData::checkSolution(
100100
const std::vector<double>& solution) const {
101-
for (HighsInt i = 0; i != mipsolver.model_->num_col_; ++i) {
101+
for (HighsInt i = 0; i != mipsolver.numCol(); ++i) {
102102
if (solution[i] < mipsolver.model_->col_lower_[i] - feastol) return false;
103103
if (solution[i] > mipsolver.model_->col_upper_[i] + feastol) return false;
104104
if (mipsolver.isColInteger(i) && fractionality(solution[i]) > feastol)
105105
return false;
106106
}
107107

108-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
108+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
109109
double rowactivity = 0.0;
110110

111111
HighsInt start = ARstart_[i];
@@ -125,7 +125,7 @@ std::vector<std::tuple<HighsInt, HighsInt, double>>
125125
HighsMipSolverData::getInfeasibleRows(
126126
const std::vector<double>& solution) const {
127127
std::vector<std::tuple<HighsInt, HighsInt, double>> infeasibleRows;
128-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
128+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
129129
HighsInt start = ARstart_[i];
130130
HighsInt end = ARstart_[i + 1];
131131

@@ -149,11 +149,11 @@ HighsMipSolverData::getInfeasibleRows(
149149

150150
bool HighsMipSolverData::trySolution(const std::vector<double>& solution,
151151
const int solution_source) {
152-
if (int(solution.size()) != mipsolver.model_->num_col_) return false;
152+
if (int(solution.size()) != mipsolver.numCol()) return false;
153153

154154
HighsCDouble obj = 0;
155155

156-
for (HighsInt i = 0; i != mipsolver.model_->num_col_; ++i) {
156+
for (HighsInt i = 0; i != mipsolver.numCol(); ++i) {
157157
if (solution[i] < mipsolver.model_->col_lower_[i] - feastol) return false;
158158
if (solution[i] > mipsolver.model_->col_upper_[i] + feastol) return false;
159159
if (mipsolver.isColInteger(i) && fractionality(solution[i]) > feastol)
@@ -162,7 +162,7 @@ bool HighsMipSolverData::trySolution(const std::vector<double>& solution,
162162
obj += mipsolver.colCost(i) * solution[i];
163163
}
164164

165-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
165+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
166166
double rowactivity = 0.0;
167167

168168
HighsInt start = ARstart_[i];
@@ -180,7 +180,7 @@ bool HighsMipSolverData::trySolution(const std::vector<double>& solution,
180180

181181
bool HighsMipSolverData::solutionRowFeasible(
182182
const std::vector<double>& solution) const {
183-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
183+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
184184
HighsCDouble c_double_rowactivity = HighsCDouble(0.0);
185185

186186
HighsInt start = ARstart_[i];
@@ -260,7 +260,7 @@ HighsModelStatus HighsMipSolverData::trivialHeuristics() {
260260
const double feasibility_tolerance =
261261
mipsolver.options_mip_->mip_feasibility_tolerance;
262262
// Loop through the trivial heuristics
263-
std::vector<double> solution(mipsolver.model_->num_col_);
263+
std::vector<double> solution(mipsolver.numCol());
264264
for (HighsInt try_heuristic = 0; try_heuristic < num_try_heuristic;
265265
try_heuristic++) {
266266
if (try_heuristic == 0) {
@@ -271,15 +271,15 @@ HighsModelStatus HighsMipSolverData::trivialHeuristics() {
271271
if (!all_integer_lower_non_positive) continue;
272272
// Determine whether a zero row activity is feasible
273273
bool heuristic_failed = false;
274-
for (HighsInt iRow = 0; iRow < mipsolver.model_->num_row_; iRow++) {
274+
for (HighsInt iRow = 0; iRow < mipsolver.numRow(); iRow++) {
275275
if (row_lower[iRow] > feasibility_tolerance ||
276276
row_upper[iRow] < -feasibility_tolerance) {
277277
heuristic_failed = true;
278278
break;
279279
}
280280
}
281281
if (heuristic_failed) continue;
282-
solution.assign(mipsolver.model_->num_col_, 0);
282+
solution.assign(mipsolver.numCol(), 0);
283283
} else if (try_heuristic == 1) {
284284
// Second heuristic is to see whether all-lower for integer
285285
// variables (if distinct from all-zero) is feasible
@@ -320,7 +320,7 @@ HighsModelStatus HighsMipSolverData::trivialHeuristics() {
320320
}
321321

322322
HighsCDouble cdouble_obj = 0.0;
323-
for (HighsInt iCol = 0; iCol < mipsolver.model_->num_col_; iCol++)
323+
for (HighsInt iCol = 0; iCol < mipsolver.numCol(); iCol++)
324324
cdouble_obj += mipsolver.colCost(iCol) * solution[iCol];
325325
double obj = double(cdouble_obj);
326326
const double save_upper_bound = upper_bound;
@@ -692,8 +692,7 @@ void HighsMipSolverData::removeFixedIndices() {
692692
}
693693

694694
void HighsMipSolverData::init() {
695-
postSolveStack.initializeIndexMaps(mipsolver.model_->num_row_,
696-
mipsolver.model_->num_col_);
695+
postSolveStack.initializeIndexMaps(mipsolver.numRow(), mipsolver.numCol());
697696
mipsolver.orig_model_ = mipsolver.model_;
698697
feastol = mipsolver.options_mip_->mip_feasibility_tolerance;
699698
epsilon = mipsolver.options_mip_->small_matrix_value;
@@ -887,11 +886,11 @@ void HighsMipSolverData::runSetup() {
887886
}
888887
}
889888

890-
rowintegral.resize(mipsolver.model_->num_row_);
889+
rowintegral.resize(mipsolver.numRow());
891890

892891
// compute the maximal absolute coefficients to filter propagation
893-
maxAbsRowCoef.resize(mipsolver.model_->num_row_);
894-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
892+
maxAbsRowCoef.resize(mipsolver.numRow());
893+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
895894
double maxabsval = 0.0;
896895

897896
HighsInt start = ARstart_[i];
@@ -1074,7 +1073,7 @@ void HighsMipSolverData::runSetup() {
10741073
debugSolution.debugOrigSolution);
10751074
debugSolution.debugSolObjective = 0;
10761075
HighsCDouble debugsolobj = 0.0;
1077-
for (HighsInt i = 0; i != mipsolver.model_->num_col_; ++i)
1076+
for (HighsInt i = 0; i != mipsolver.numCol(); ++i)
10781077
debugsolobj +=
10791078
mipsolver.colCost(i) * HighsCDouble(debugSolution.debugSolution[i]);
10801079
debugSolution.debugSolObjective = static_cast<double>(debugsolobj);
@@ -1301,7 +1300,7 @@ void HighsMipSolverData::performRestart() {
13011300
root_basis.valid = true;
13021301
root_basis.useful = true;
13031302

1304-
for (HighsInt i = 0; i < mipsolver.model_->num_col_; ++i)
1303+
for (HighsInt i = 0; i < mipsolver.numCol(); ++i)
13051304
root_basis.col_status[postSolveStack.getOrigColIndex(i)] =
13061305
basis.col_status[i];
13071306

@@ -2570,8 +2569,8 @@ void HighsMipSolverData::setupDomainPropagation() {
25702569
pseudocost = HighsPseudocost(mipsolver);
25712570

25722571
// compute the maximal absolute coefficients to filter propagation
2573-
maxAbsRowCoef.resize(mipsolver.model_->num_row_);
2574-
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
2572+
maxAbsRowCoef.resize(mipsolver.numRow());
2573+
for (HighsInt i = 0; i != mipsolver.numRow(); ++i) {
25752574
double maxabsval = 0.0;
25762575

25772576
HighsInt start = ARstart_[i];

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,8 +1475,8 @@ void HighsPrimalHeuristics::feasibilityPump() {
14751475
std::vector<double> fracintcost;
14761476
std::vector<HighsInt> fracintset;
14771477

1478-
std::vector<HighsInt> mask(mipsolver.model_->num_col_, 1);
1479-
std::vector<double> cost(mipsolver.model_->num_col_, 0.0);
1478+
std::vector<HighsInt> mask(mipsolver.numCol(), 1);
1479+
std::vector<double> cost(mipsolver.numCol(), 0.0);
14801480

14811481
lprelax.getLpSolver().setOptionValue("simplex_strategy",
14821482
kSimplexStrategyPrimal);

highs/mip/HighsSearch.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ HighsInt HighsSearch::selectBranchingCandidate(int64_t maxSbIters,
412412

413413
auto analyzeSolution = [&](double objdelta,
414414
const std::vector<double>& sol) {
415-
HighsInt numChangedCols = localdom.getChangedCols().size();
415+
size_t numChangedCols = localdom.getChangedCols().size();
416416
HighsInt domchgStackSize = localdom.getDomainChangeStack().size();
417417
const auto& domchgstack = localdom.getDomainChangeStack();
418418

@@ -1517,7 +1517,7 @@ bool HighsSearch::backtrack(bool recoverBasis) {
15171517
// repropagate the node, as it may have become infeasible due to
15181518
// conflicts
15191519
HighsInt oldNumDomchgs = localdom.getNumDomainChanges();
1520-
HighsInt oldNumChangedCols = localdom.getChangedCols().size();
1520+
size_t oldNumChangedCols = localdom.getChangedCols().size();
15211521
localdom.propagate();
15221522
if (!localdom.infeasible() &&
15231523
oldNumDomchgs != localdom.getNumDomainChanges()) {
@@ -1564,7 +1564,7 @@ bool HighsSearch::backtrack(bool recoverBasis) {
15641564
if (fallbackbranch)
15651565
currnode.branching_point = currnode.branchingdecision.boundval;
15661566

1567-
HighsInt numChangedCols = localdom.getChangedCols().size();
1567+
size_t numChangedCols = localdom.getChangedCols().size();
15681568
bool passStabilizerToChildNode =
15691569
orbitsValidInChildNode(currnode.branchingdecision);
15701570
localdom.changeBound(currnode.branchingdecision);
@@ -1694,7 +1694,7 @@ bool HighsSearch::backtrackPlunge(HighsNodeQueue& nodequeue) {
16941694
currnode.branching_point = currnode.branchingdecision.boundval;
16951695

16961696
HighsInt domchgPos = domchgstack.size();
1697-
HighsInt numChangedCols = localdom.getChangedCols().size();
1697+
size_t numChangedCols = localdom.getChangedCols().size();
16981698
bool passStabilizerToChildNode =
16991699
orbitsValidInChildNode(currnode.branchingdecision);
17001700
localdom.changeBound(currnode.branchingdecision);

highs/mip/MipTimer.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum iClockMip {
2828
kMipClockSearch,
2929
// Search
3030
kMipClockProbingPresolve,
31+
kMipClockEnumerationPresolve,
3132
kMipClockPerformAging1,
3233
kMipClockDive,
3334
kMipClockOpenNodesToQueue0,
@@ -237,6 +238,9 @@ class MipTimer {
237238
clock[kMipClockProbingPresolve] =
238239
timer_pointer->clock_def("Probing - presolve");
239240

241+
clock[kMipClockEnumerationPresolve] =
242+
timer_pointer->clock_def("Enumeration - presolve");
243+
240244
// Search - Should correspond to kMipClockSearch
241245
clock[kMipClockPerformAging1] = timer_pointer->clock_def("Perform aging 1");
242246
clock[kMipClockDive] = timer_pointer->clock_def("Dive");
@@ -391,7 +395,8 @@ class MipTimer {
391395
};
392396

393397
void reportMipPresolveClock(const HighsTimerClock& mip_timer_clock) {
394-
const std::vector<HighsInt> mip_clock_list{kMipClockProbingPresolve};
398+
const std::vector<HighsInt> mip_clock_list{kMipClockProbingPresolve,
399+
kMipClockEnumerationPresolve};
395400
reportMipClockList("MipPrslv", mip_clock_list, mip_timer_clock,
396401
kMipClockRunPresolve, tolerance_percent_report);
397402
};

0 commit comments

Comments
 (0)