Skip to content

Commit 76b30a4

Browse files
committed
Unify comment notation. Add tryGenFlowCut
1 parent b97863e commit 76b30a4

File tree

3 files changed

+85
-88
lines changed

3 files changed

+85
-88
lines changed

highs/mip/HighsCutGeneration.cpp

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -514,32 +514,29 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
514514
double mp = kHighsInf;
515515
HighsCDouble ml = 0;
516516
HighsCDouble d1 = 0;
517-
HighsCDouble d2 = 0;
518517
};
519518
LiftingData ld;
520519
ld.m.resize(snfr.numNnzs);
521520
HighsCDouble sumN2mC2LE = 0.0;
522521
HighsCDouble sumC1LE = 0.0;
523-
HighsCDouble sumN2mC2GT = 0.0;
524522
HighsCDouble sumC2 = 0.0;
525523

526524
for (HighsInt i = 0; i != snfr.numNnzs; ++i) {
527-
// col is in N2 \ C2
525+
// col is in N- \ C-
528526
if (snfr.flowCoverStatus[i] == -1 && snfr.coef[i] == -1) {
529527
assert(snfr.vubCoef[i] >= 0);
530528
if (snfr.vubCoef[i] > snfr.lambda + 1e-8) {
531-
sumN2mC2GT += snfr.vubCoef[i];
532529
ld.m[ld.r] = snfr.vubCoef[i];
533530
++ld.r;
534531
} else {
535532
sumN2mC2LE += snfr.vubCoef[i];
536533
}
537534
} else if (snfr.flowCoverStatus[i] == 1 && snfr.coef[i] == -1) {
538-
// col is in C2
535+
// col is in C-
539536
assert(snfr.vubCoef[i] > 0);
540537
sumC2 += snfr.vubCoef[i];
541538
} else if (snfr.flowCoverStatus[i] == 1 && snfr.coef[i] == 1) {
542-
// col is in C1
539+
// col is in C+
543540
assert(snfr.vubCoef[i] > 0);
544541
if (snfr.vubCoef[i] > snfr.lambda + 1e-8) {
545542
ld.m[ld.r] = snfr.vubCoef[i];
@@ -549,7 +546,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
549546
sumC1LE += snfr.vubCoef[i];
550547
}
551548
} else {
552-
// col is in N1 \ C1
549+
// col is in N+ \ C+
553550
assert(snfr.flowCoverStatus[i] == -1 && snfr.coef[i] == 1);
554551
}
555552
}
@@ -559,9 +556,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
559556
ld.M.resize(ld.r + 1);
560557
ld.ml = std::min(snfr.lambda, static_cast<double>(sumC1LE + sumN2mC2LE));
561558
ld.d1 = sumC2 + snfr.rhs;
562-
ld.d2 = ld.d1 + sumN2mC2GT + sumN2mC2LE;
563559

564-
// Sort into non-increasing order (TODO: Remove comment after checking)
565560
pdqsort_branchless(
566561
ld.m.begin(), ld.m.end(),
567562
[&](const HighsCDouble a, const HighsCDouble b) { return a > b; });
@@ -639,11 +634,13 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
639634
ld.M[i]);
640635
};
641636

642-
// Lift the flow cover cut
637+
// Calculate the lifted simple generalized flow cover cut
638+
// L- = {i \in N- \ C- : m_i > lambda}
639+
// L-- = N- \ (L- union C-)
643640
HighsCDouble tmpRhs = ld.d1;
644641
rowlen = 0;
645642
for (HighsInt i = 0; i != snfr.numNnzs; ++i) {
646-
// col is in N2 \ C2
643+
// col is in N- \ C-
647644
if (snfr.flowCoverStatus[i] == -1 && snfr.coef[i] == -1) {
648645
if (snfr.vubCoef[i] > snfr.lambda + 1e-8) {
649646
if (snfr.origBinCols[i] != -1) {
@@ -669,7 +666,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
669666
tmpRhs += snfr.aggrConstant[i];
670667
}
671668
} else if (snfr.flowCoverStatus[i] == 1 && snfr.coef[i] == -1) {
672-
// col is in C2
669+
// col is in C-
673670
if (snfr.origBinCols[i] != -1) {
674671
double liftedbincoef = evaluateLiftingFunction(snfr.vubCoef[i]);
675672
if (liftedbincoef != 0) {
@@ -680,7 +677,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
680677
}
681678
}
682679
} else if (snfr.flowCoverStatus[i] == -1 && snfr.coef[i] == 1) {
683-
// col is in N1 \ C1
680+
// col is in N+ \ C+
684681
std::pair<HighsInt, HighsCDouble> alphabeta =
685682
getAlphaBeta(snfr.vubCoef[i]);
686683
assert(alphabeta.first == 0 || alphabeta.first == 1);
@@ -704,7 +701,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
704701
tmpRhs -= snfr.aggrConstant[i];
705702
}
706703
} else {
707-
// col is in C1
704+
// col is in C+
708705
assert(snfr.flowCoverStatus[i] == 1 && snfr.coef[i] == 1);
709706
HighsCDouble bincoef = snfr.aggrBinCoef[i];
710707
HighsCDouble constant = snfr.aggrConstant[i];
@@ -730,8 +727,7 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
730727
#ifndef NDEBUG
731728
// TODO: Remove this if algorithm changes to allow multiple bin col usages
732729
for (HighsInt i = 0; i < rowlen; i++) {
733-
for (HighsInt j = i + 1; j < rowlen; j++)
734-
assert(inds[i] != inds[j]);
730+
for (HighsInt j = i + 1; j < rowlen; j++) assert(inds[i] != inds[j]);
735731
}
736732
#endif
737733

@@ -1349,6 +1345,7 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
13491345
}
13501346
#endif
13511347

1348+
// Generate a lifted simple generalized flow cover cut
13521349
bool flowCoverSuccess = false;
13531350
std::vector<double> flowCoverVals;
13541351
std::vector<HighsInt> flowCoverInds;
@@ -1357,49 +1354,18 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
13571354
if (!onlyInitialCMIRScale) {
13581355
flowCoverVals = vals_;
13591356
flowCoverInds = inds_;
1360-
rowlen = static_cast<HighsInt>(flowCoverInds.size());
1361-
this->inds = flowCoverInds.data();
1362-
this->vals = flowCoverVals.data();
1363-
this->rhs = rhs_;
1364-
flowCoverSuccess =
1365-
preprocessSNFRelaxation();
1366-
if (!flowCoverSuccess) goto cmir;
1367-
initSNFRelaxation();
1368-
assert(rowlen <= static_cast<HighsInt>(flowCoverVals.size()));
1369-
flowCoverVals.resize(rowlen);
1370-
flowCoverInds.resize(rowlen);
1371-
flowCoverRhs = static_cast<double>(rhs);
1372-
flowCoverSuccess = transLp.transformSNFRelaxation(
1373-
flowCoverInds, flowCoverVals, flowCoverRhs, snfr);
1374-
if (!flowCoverSuccess) goto cmir;
1375-
// Array resized as each continuous col may create a non-zero bin coef
1376-
flowCoverVals.resize(2 * snfr.numNnzs);
1377-
flowCoverInds.resize(2 * snfr.numNnzs);
1378-
this->inds = flowCoverInds.data();
1379-
this->vals = flowCoverVals.data();
1380-
this->rhs = flowCoverRhs;
1381-
flowCoverSuccess = computeFlowCover();
1382-
if (!flowCoverSuccess) goto cmir;
1383-
flowCoverSuccess =
1384-
separateLiftedFlowCover();
1385-
if (!flowCoverSuccess) goto cmir;
1386-
flowCoverInds.resize(rowlen);
1387-
flowCoverVals.resize(rowlen);
1388-
flowCoverRhs = static_cast<double>(rhs);
1389-
flowCoverSuccess = transLp.cleanup(flowCoverInds, flowCoverVals,
1390-
flowCoverRhs, flowCoverEfficacy);
1391-
if (flowCoverEfficacy < 10 * feastol) flowCoverSuccess = false;
1357+
flowCoverSuccess = tryGenerateFlowCoverCut(
1358+
transLp, flowCoverInds, flowCoverVals, flowCoverRhs, flowCoverEfficacy);
13921359
}
13931360

1394-
cmir:
13951361
bool cmirSuccess = false;
13961362
bool intsPositive = true;
13971363
bool hasUnboundedInts = false;
13981364
bool hasGeneralInts = false;
13991365
bool hasContinuous = false;
14001366
if (!transLp.transform(vals_, upper, solval, inds_, rhs_, intsPositive)) {
14011367
cmirSuccess = false;
1402-
goto untransform;
1368+
goto postprocess;
14031369
}
14041370

14051371
rowlen = inds_.size();
@@ -1410,7 +1376,7 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
14101376
if (!preprocessBaseInequality(hasUnboundedInts, hasGeneralInts,
14111377
hasContinuous)) {
14121378
cmirSuccess = false;
1413-
goto untransform;
1379+
goto postprocess;
14141380
}
14151381

14161382
// it can happen that there is an unbounded integer variable during the
@@ -1435,7 +1401,7 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
14351401
hasContinuous, std::max(flowCoverEfficacy, 10 * feastol),
14361402
onlyInitialCMIRScale)) {
14371403
cmirSuccess = false;
1438-
goto untransform;
1404+
goto postprocess;
14391405
}
14401406

14411407
// remove the complementation if exists
@@ -1449,8 +1415,9 @@ bool HighsCutGeneration::generateCut(HighsTransformedLp& transLp,
14491415
vals[i] = vals[rowlen];
14501416
}
14511417
}
1418+
cmirSuccess = true;
14521419

1453-
untransform:
1420+
postprocess:
14541421
if (!cmirSuccess && !flowCoverSuccess) return false;
14551422

14561423
// transform the cut back into the original space, i.e. remove the bound
@@ -1705,8 +1672,8 @@ bool HighsCutGeneration::preprocessSNFRelaxation() {
17051672
}
17061673

17071674
bool HighsCutGeneration::computeFlowCover() {
1708-
// Compute the flow cover, i.e., get sets C1 subset N1 and C2 subset N2
1709-
// with sum_{j in C1} u_j - sum_{j in C2} u_j = b + lambda, lambda > 0
1675+
// Compute the flow cover, i.e., get sets C+ subset N+ and C- subset N-
1676+
// with sum_{j in C+} u_j - sum_{j in C-} u_j = b + lambda, lambda > 0
17101677
if (static_cast<HighsInt>(snfr.flowCoverStatus.size()) < snfr.numNnzs) {
17111678
snfr.flowCoverStatus.resize(snfr.numNnzs);
17121679
}
@@ -1719,7 +1686,7 @@ bool HighsCutGeneration::computeFlowCover() {
17191686
for (HighsInt i = 0; i < snfr.numNnzs; ++i) {
17201687
assert(snfr.coef[i] == 1 || snfr.coef[i] == -1);
17211688
assert(snfr.binSolval[i] >= -feastol && snfr.binSolval[i] <= 1 + feastol);
1722-
// if u_i = 0 put i into N1 \ C1 or N2 \ C2, i.e., not the cover
1689+
// if u_i = 0 put i into N+ \ C+ or N- \ C-, i.e., not the cover
17231690
if (abs(snfr.vubCoef[i]) < feastol) {
17241691
snfr.flowCoverStatus[i] = -1;
17251692
nNonFlowCover++;
@@ -1733,21 +1700,21 @@ bool HighsCutGeneration::computeFlowCover() {
17331700
n1itemsWeight += snfr.vubCoef[i];
17341701
}
17351702
} else if (snfr.coef[i] == 1 && snfr.binSolval[i] < 0.5) {
1736-
// i is in N1 and x_i = 0 -> don't put in cover
1703+
// i is in N+ and x_i = 0 -> don't put in cover
17371704
snfr.flowCoverStatus[i] = -1;
17381705
nNonFlowCover++;
17391706
} else if (snfr.coef[i] == 1 && snfr.binSolval[i] > 0.5) {
1740-
// i is in N1 and x_i = 1 -> put in cover
1707+
// i is in N+ and x_i = 1 -> put in cover
17411708
snfr.flowCoverStatus[i] = 1;
17421709
nFlowCover++;
17431710
flowCoverWeight += snfr.vubCoef[i];
17441711
} else if (snfr.coef[i] == -1 && snfr.binSolval[i] > 0.5) {
1745-
// i is in N2 and x_i = 1 -> put in cover
1712+
// i is in N- and x_i = 1 -> put in cover
17461713
snfr.flowCoverStatus[i] = 1;
17471714
nFlowCover++;
17481715
flowCoverWeight -= snfr.vubCoef[i];
17491716
} else {
1750-
// i is in N2 and x_i = 0 -> don't put in cover
1717+
// i is in N- and x_i = 0 -> don't put in cover
17511718
assert(snfr.coef[i] == -1 && snfr.binSolval[i] < 0.5);
17521719
snfr.flowCoverStatus[i] = -1;
17531720
nNonFlowCover++;
@@ -1757,9 +1724,11 @@ bool HighsCutGeneration::computeFlowCover() {
17571724

17581725
double capacity =
17591726
static_cast<double>(-snfr.rhs + flowCoverWeight + n1itemsWeight);
1727+
17601728
// There is no flow cover if capacity is less than zero after fixing
17611729
if (capacity < feastol) return false;
1762-
// Solve a knapsack greedily to assign items to C1, C2, N1\C1, N2\C2
1730+
1731+
// Solve a knapsack greedily to assign items to C+, C-, N+\C+, N-\C-
17631732
double knapsackWeight = 0;
17641733
std::vector<double> weights(nitems);
17651734
std::vector<double> profits(nitems);
@@ -1810,6 +1779,39 @@ bool HighsCutGeneration::computeFlowCover() {
18101779
return true;
18111780
}
18121781

1782+
bool HighsCutGeneration::tryGenerateFlowCoverCut(HighsTransformedLp& transLp,
1783+
std::vector<HighsInt>& inds_,
1784+
std::vector<double>& vals_,
1785+
double& rhs_,
1786+
double& efficacy) {
1787+
rowlen = static_cast<HighsInt>(inds_.size());
1788+
this->inds = inds_.data();
1789+
this->vals = vals_.data();
1790+
this->rhs = rhs_;
1791+
if (!preprocessSNFRelaxation()) return false;
1792+
initSNFRelaxation();
1793+
vals_.resize(rowlen);
1794+
inds_.resize(rowlen);
1795+
rhs_ = static_cast<double>(this->rhs);
1796+
if (!transLp.transformSNFRelaxation(inds_, vals_, rhs_, snfr)) return false;
1797+
1798+
// Array resized as each continuous col may create a non-zero bin coef
1799+
vals_.resize(2 * snfr.numNnzs);
1800+
inds_.resize(2 * snfr.numNnzs);
1801+
this->inds = inds_.data();
1802+
this->vals = vals_.data();
1803+
this->rhs = rhs_;
1804+
1805+
if (!computeFlowCover()) return false;
1806+
if (!separateLiftedFlowCover()) return false;
1807+
inds_.resize(rowlen);
1808+
vals_.resize(rowlen);
1809+
rhs_ = static_cast<double>(rhs);
1810+
if (!transLp.cleanup(inds_, vals_, rhs_, efficacy)) return false;
1811+
if (efficacy < 10 * feastol) return false;
1812+
return true;
1813+
}
1814+
18131815
bool HighsCutGeneration::finalizeAndAddCut(std::vector<HighsInt>& inds_,
18141816
std::vector<double>& vals_,
18151817
double& rhs_) {

highs/mip/HighsCutGeneration.h

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ class HighsCutGeneration {
7272

7373
bool preprocessSNFRelaxation();
7474

75+
bool tryGenerateFlowCoverCut(HighsTransformedLp& transLp,
76+
std::vector<HighsInt>& inds_,
77+
std::vector<double>& vals_, double& rhs_,
78+
double& efficacy);
79+
7580
double scale(double val);
7681

7782
bool postprocessCut();
@@ -113,21 +118,21 @@ class HighsCutGeneration {
113118

114119
/// Single Node Flow Relaxation for flow cover cuts
115120
struct SNFRelaxation {
116-
std::vector<bool> binColUsed; // has col been used in a vub
117-
std::vector<double> origBinColCoef; // original bin col coef
118-
119-
HighsInt numNnzs; // number of nonzeros
120-
std::vector<HighsInt> coef; // coefficients of cols in SNFR
121-
std::vector<double> vubCoef; // coefficients in vub of cols in SNFR
122-
std::vector<double> binSolval; // vub bin col sol in SNFR
123-
std::vector<double> contSolval; // real sol in SNFR
124-
std::vector<HighsInt> origBinCols; // orig bin col used in SNFR
125-
std::vector<HighsInt> origContCols; // orig cont cols used in SNFR
126-
std::vector<double> aggrBinCoef; // aggr coef of orignal bin-col in SNFR
127-
std::vector<double> aggrContCoef; // aggr coef of original cont-col in SNFR
128-
std::vector<double> aggrConstant; // aggr original constant in SNFR
129-
130-
std::vector<HighsInt> flowCoverStatus; // (+1) in fcover (-1) not in fcover
121+
std::vector<bool> binColUsed; // has col been used in a vub
122+
std::vector<double> origBinColCoef; // original bin col coef
123+
124+
HighsInt numNnzs; // number of nonzeros
125+
std::vector<HighsInt> coef; // coefficients of cols in SNFR
126+
std::vector<double> vubCoef; // coefficients in vub of cols in SNFR
127+
std::vector<double> binSolval; // vub bin col sol in SNFR
128+
std::vector<double> contSolval; // real sol in SNFR
129+
std::vector<HighsInt> origBinCols; // orig bin col used in SNFR
130+
std::vector<HighsInt> origContCols; // orig cont cols used in SNFR
131+
std::vector<double> aggrBinCoef; // aggr coef of orignal bin-col in SNFR
132+
std::vector<double> aggrContCoef; // aggr coef of original cont-col in SNFR
133+
std::vector<double> aggrConstant; // aggr original constant in SNFR
134+
135+
std::vector<HighsInt> flowCoverStatus; // (+1) in fcover (-1) not in fcover
131136
double rhs;
132137
double lambda;
133138
};

highs/mip/HighsTransformedLp.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ bool HighsTransformedLp::untransform(std::vector<double>& vals,
565565
// Create a single node flow relaxation (SNFR) from an aggregated
566566
// mixed-integer row and find a valid flow cover.
567567
// Turn \sum c_i x_i + \sum a_i y_i <= a_0 (x_i binary, y_i real non-neg)
568-
// into \sum_{j \in N1} y_j - \sum_{j \in N2} y_j <= b, where y_j <= u_j x_j
568+
// into \sum_{j \in N+} y_j - \sum_{j \in N-} y_j <= b, where y_j <= u_j x_j
569569
bool HighsTransformedLp::transformSNFRelaxation(
570570
std::vector<HighsInt>& inds, std::vector<double>& vals, double& rhs,
571571
HighsCutGeneration::SNFRelaxation& snfr) {
@@ -725,17 +725,6 @@ bool HighsTransformedLp::transformSNFRelaxation(
725725
infeasible, false);
726726
}
727727

728-
// store the old bound type so that we can restore it if the continuous
729-
// column is relaxed out anyways. This allows to correctly transform and
730-
// then untransform multiple base rows which is useful to compute cuts based
731-
// on several transformed base rows. It could otherwise lead to bugs if a
732-
// column is first transformed with a simple bound and not relaxed but for
733-
// another base row is transformed and relaxed with a variable bound. Should
734-
// the non-relaxed column now be untransformed we would wrongly use the
735-
// variable bound even though this is not the correct way to untransform the
736-
// column.
737-
// BoundType oldBoundType = boundTypes[col];
738-
739728
// Transform entry into the SNFR
740729
if (lprelaxation.isColIntegral(col) && lb == 0 && ub == 1) {
741730
if (snfr.binColUsed[col] == false) {
@@ -883,6 +872,7 @@ bool HighsTransformedLp::transformSNFRelaxation(
883872
return true;
884873
}
885874

875+
// Remove slack, small coefficients, and calculate the efficacy
886876
bool HighsTransformedLp::cleanup(std::vector<HighsInt>& inds,
887877
std::vector<double>& vals,
888878
double& rhs,

0 commit comments

Comments
 (0)