Skip to content

Commit 5af1a62

Browse files
committed
Create sepa stats
1 parent a4777bd commit 5af1a62

File tree

7 files changed

+45
-46
lines changed

7 files changed

+45
-46
lines changed

highs/mip/HighsCutGeneration.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "mip/HighsCutGeneration.h"
99

1010
#include "../extern/pdqsort/pdqsort.h"
11-
#include "HighsDomain.h"
11+
#include "mip/HighsDomain.h"
1212
#include "mip/HighsMipSolverData.h"
1313
#include "mip/HighsTransformedLp.h"
1414
#include "util/HighsIntegers.h"

highs/mip/HighsCutPool.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ void HighsCutPool::performAging() {
171171

172172
for (HighsInt i = 0; i != cutIndexEnd; ++i) {
173173
// Catch buffered changes (should only occur in parallel case)
174-
// TODO: This misses the case where a cut is added then deleted before aging
175-
// TODO: has been called once. We'd miss resetting the age in this case.
174+
// TODO MT: Misses the case where a cut is added then deleted before aging
175+
// TODO MT: has been called once. We'd miss resetting the age in this case.
176176
if (numLps_[i] > 0 && ages_[i] >= 0) {
177177
// Cut has been added to the LP, but age changes haven't been made
178178
--ageDistribution[ages_[i]];
@@ -245,7 +245,9 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
245245

246246
for (HighsInt i = 0; i < nrows; ++i) {
247247
// cuts with an age of -1 are already in the LP and are therefore skipped
248-
// TODO: Parallel case here loops over cuts potentially added in current LP
248+
// TODO MT: Parallel case tries to add cuts already in current LP.
249+
// TODO MT: Inefficient. Not sure what happens if added twice.
250+
// TODO MT: The cut shouldn't have enough violation to be added though.
249251
if (ages_[i] < 0) continue;
250252

251253
HighsInt start = matrix_.getRowStart(i);
@@ -402,8 +404,9 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
402404
break;
403405
}
404406
} else {
405-
// TODO MT: Is this safe for the future? If we copy an LP with a cut
406-
// from a local pool then this is not thread safe
407+
// TODO MT: This assumes the cuts in the pool are not changing during,
408+
// TODO MT: this query, i.e., the worker's pool and the global pool.
409+
// TODO MT: Currently safe, but doesn't generalise to all designs.
407410
if (getParallelism(p.second, cutset.cutindices[i],
408411
cutpools[cutset.cutpools[i]]) > maxpar) {
409412
discard = true;
@@ -498,8 +501,7 @@ void HighsCutPool::separateLpCutsAfterRestart(HighsCutSet& cutset) {
498501
HighsInt HighsCutPool::addCut(const HighsMipSolver& mipsolver, HighsInt* Rindex,
499502
double* Rvalue, HighsInt Rlen, double rhs,
500503
bool integral, bool propagate,
501-
bool extractCliques, bool isConflict,
502-
HighsCutPool* globalpool) {
504+
bool extractCliques, bool isConflict) {
503505
mipsolver.mipdata_->debugSolution.checkCut(Rindex, Rvalue, Rlen, rhs);
504506

505507
sortBuffer.resize(Rlen);
@@ -527,10 +529,6 @@ HighsInt HighsCutPool::addCut(const HighsMipSolver& mipsolver, HighsInt* Rindex,
527529

528530
if (isDuplicate(h, normalization, Rindex, Rvalue, Rlen, rhs)) return -1;
529531

530-
if (globalpool != nullptr &&
531-
globalpool->isDuplicate(h, normalization, Rindex, Rvalue, Rlen, rhs))
532-
return -1;
533-
534532
// if (Rlen > 0.15 * matrix_.numCols())
535533
// printf("cut with len %d not propagated\n", Rlen);
536534
if (propagate) {
@@ -645,6 +643,8 @@ void HighsCutPool::syncCutPool(const HighsMipSolver& mipsolver,
645643
std::vector<double> vals(Rvalue, Rvalue + Rlen);
646644
syncpool.addCut(mipsolver, idxs.data(), vals.data(), Rlen, rhs_[i],
647645
rowintegral[i]);
646+
// TODO MT: Should I check whether the cut is accepted before changing
647+
// hasSynced?
648648
hasSynced_[i] = true;
649649
}
650650
}

highs/mip/HighsCutPool.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ class HighsCutPool {
174174
HighsInt addCut(const HighsMipSolver& mipsolver, HighsInt* Rindex,
175175
double* Rvalue, HighsInt Rlen, double rhs,
176176
bool integral = false, bool propagate = true,
177-
bool extractCliques = true, bool isConflict = false,
178-
HighsCutPool* globalpool = nullptr);
177+
bool extractCliques = true, bool isConflict = false);
179178

180179
HighsInt getRowLength(HighsInt row) const {
181180
return matrix_.getRowEnd(row) - matrix_.getRowStart(row);

highs/mip/HighsMipSolver.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -734,15 +734,22 @@ void HighsMipSolver::run() {
734734
applyTask(doSeparate, tg, true, search_indices);
735735
analysis_.mipTimerStop(kMipClockNodeSearchSeparation);
736736

737-
for (const HighsInt i : search_indices) {
738-
// Sync numNeighbourhoodQueries
737+
auto syncSepaStats = [&](HighsMipWorker& worker) {
739738
mipdata_->cliquetable.getNumNeighbourhoodQueries() +=
740-
mipdata_->workers[i].numNeighbourhoodQueries;
741-
mipdata_->workers[i].numNeighbourhoodQueries = 0;
742-
if (mipdata_->workers[i].getGlobalDomain().infeasible()) {
743-
mipdata_->workers[i].search_ptr_->cutoffNode();
739+
worker.sepa_stats.numNeighbourhoodQueries;
740+
worker.sepa_stats.numNeighbourhoodQueries = 0;
741+
mipdata_->sepa_lp_iterations += worker.sepa_stats.sepa_lp_iterations;
742+
mipdata_->total_lp_iterations += worker.sepa_stats.sepa_lp_iterations;
743+
worker.sepa_stats.sepa_lp_iterations = 0;
744+
};
745+
746+
for (const HighsInt i : search_indices) {
747+
HighsMipWorker& worker = mipdata_->workers[i];
748+
syncSepaStats(worker);
749+
if (worker.getGlobalDomain().infeasible()) {
750+
worker.search_ptr_->cutoffNode();
744751
analysis_.mipTimerStart(kMipClockOpenNodesToQueue1);
745-
mipdata_->workers[i].search_ptr_->openNodesToQueue(mipdata_->nodequeue);
752+
worker.search_ptr_->openNodesToQueue(mipdata_->nodequeue);
746753
analysis_.mipTimerStop(kMipClockOpenNodesToQueue1);
747754
mipdata_->nodequeue.clear();
748755
mipdata_->pruned_treeweight = 1.0;

highs/mip/HighsMipWorker.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ HighsMipWorker::HighsMipWorker(const HighsMipSolver& mipsolver,
2828
search_ptr_ =
2929
std::unique_ptr<HighsSearch>(new HighsSearch(*this, getPseudocost()));
3030
sepa_ptr_ = std::unique_ptr<HighsSeparation>(new HighsSeparation(*this));
31-
numNeighbourhoodQueries = 0;
3231
search_ptr_->setLpRelaxation(lp_);
3332
sepa_ptr_->setLpRelaxation(lp_);
3433
}

highs/mip/HighsMipWorker.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ class HighsSearch;
2222

2323
class HighsMipWorker {
2424
public:
25+
struct SepaStatistics {
26+
SepaStatistics() : numNeighbourhoodQueries(0), sepa_lp_iterations(0) {}
27+
28+
int64_t numNeighbourhoodQueries;
29+
int64_t sepa_lp_iterations;
30+
};
2531
const HighsMipSolver& mipsolver_;
2632
const HighsMipSolverData& mipdata_;
2733

@@ -43,11 +49,10 @@ class HighsMipWorker {
4349
std::vector<std::tuple<std::vector<double>, double, int>> solutions_;
4450

4551
HighsPrimalHeuristics::Statistics heur_stats;
52+
SepaStatistics sepa_stats;
4653

4754
HighsRandom randgen;
4855

49-
int64_t numNeighbourhoodQueries;
50-
5156
HighsMipWorker(const HighsMipSolver& mipsolver, HighsLpRelaxation* lp,
5257
HighsDomain* domain, HighsCutPool* cutpool,
5358
HighsConflictPool* conflictpool, HighsPseudocost* pseudocost);
@@ -67,7 +72,6 @@ class HighsMipWorker {
6772

6873
HighsPseudocost& getPseudocost() const { return *pseudocost_; };
6974

70-
7175
bool addIncumbent(const std::vector<double>& sol, double solobj,
7276
int solution_source);
7377

highs/mip/HighsSeparation.cpp

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ HighsInt HighsSeparation::separationRound(HighsDomain& propdomain,
5959
if (&propdomain == &mipdata.domain)
6060
mipdata.cliquetable.cleanupFixed(mipdata.domain);
6161

62-
// TODO: Currently adding a check for both. Should only need to check
63-
// mipworker
6462
if (mipworker_.getGlobalDomain().infeasible()) {
6563
status = HighsLpRelaxation::Status::kInfeasible;
6664
propdomain.clearChangedCols();
@@ -85,9 +83,7 @@ HighsInt HighsSeparation::separationRound(HighsDomain& propdomain,
8583
return numBoundChgs;
8684
};
8785

88-
// TODO MT: Look into delta implications (probing for global info locally and
89-
// buffer it)
90-
// TODO MT: Disabled timers because they fail for parallel mode
86+
// TODO MT: Look into delta implications
9187
// lp->getMipSolver().analysis_.mipTimerStart(implBoundClock);
9288
mipdata.implications.separateImpliedBounds(
9389
*lp, lp->getSolution().col_value, *mipworker_.cutpool_, mipdata.feastol,
@@ -101,15 +97,13 @@ HighsInt HighsSeparation::separationRound(HighsDomain& propdomain,
10197
else
10298
ncuts += numboundchgs;
10399

104-
// TODO: This can be enabled if randgen and cliquesubsumption are disabled for
105-
// parallel case
106100
// lp->getMipSolver().analysis_.mipTimerStart(cliqueClock);
107101
mipdata.cliquetable.separateCliques(
108102
lp->getMipSolver(), sol.col_value, *mipworker_.cutpool_, mipdata.feastol,
109103
mipdata.parallelLockActive() ? mipworker_.randgen
110104
: mipdata.cliquetable.getRandgen(),
111105
mipdata.parallelLockActive()
112-
? mipworker_.numNeighbourhoodQueries
106+
? mipworker_.sepa_stats.numNeighbourhoodQueries
113107
: mipdata.cliquetable.getNumNeighbourhoodQueries());
114108
// lp->getMipSolver().analysis_.mipTimerStop(cliqueClock);
115109

@@ -183,16 +177,16 @@ void HighsSeparation::separate(HighsDomain& propdomain) {
183177
while (lp->getObjective() < mipsolver.mipdata_->optimality_limit) {
184178
double lastobj = lp->getObjective();
185179

186-
size_t nlpiters = -lp->getNumLpIterations();
180+
int64_t nlpiters = -lp->getNumLpIterations();
187181
HighsInt ncuts = separationRound(propdomain, status);
188182
nlpiters += lp->getNumLpIterations();
189183

190-
// replace with mipworker iterations field
191-
// mipsolver.mipdata_->sepa_lp_iterations += nlpiters;
192-
// mipsolver.mipdata_->total_lp_iterations += nlpiters;
193-
194-
// todo:ig more stats for separation iterations?
195-
mipworker_.heur_stats.lp_iterations += nlpiters;
184+
if (mipsolver.mipdata_->parallelLockActive()) {
185+
mipworker_.sepa_stats.sepa_lp_iterations += nlpiters;
186+
} else {
187+
mipsolver.mipdata_->sepa_lp_iterations += nlpiters;
188+
mipsolver.mipdata_->total_lp_iterations += nlpiters;
189+
}
196190

197191
// printf("separated %" HIGHSINT_FORMAT " cuts\n", ncuts);
198192

@@ -217,11 +211,7 @@ void HighsSeparation::separate(HighsDomain& propdomain) {
217211
// (HighsInt)status);
218212
lp->performAging(true);
219213

220-
// mipsolver.mipdata_->cutpool.performAging();
221-
// ig: using worker cutpool
222-
// TODO MT: Is this thread safe? Depends if LP is only copied at the start.
223-
if (!mipsolver.mipdata_->parallelLockActive()) {
224-
mipworker_.cutpool_->performAging();
225-
}
214+
// TODO MT: If LP is only copied at start this should be thread safe.
215+
mipworker_.cutpool_->performAging();
226216
}
227217
}

0 commit comments

Comments
 (0)