Skip to content

Commit ee6b8cd

Browse files
committed
Allow vlb / vub to be complemented
1 parent 84573f8 commit ee6b8cd

File tree

3 files changed

+180
-74
lines changed

3 files changed

+180
-74
lines changed

highs/mip/HighsCutGeneration.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,10 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
648648
// col is in L-
649649
vals[rowlen] = -snfr.lambda;
650650
inds[rowlen] = snfr.origBinCols[i];
651+
if (snfr.complementation[i]) {
652+
tmpRhs -= vals[rowlen];
653+
vals[rowlen] = -vals[rowlen];
654+
}
651655
rowlen++;
652656
} else {
653657
tmpRhs += snfr.lambda;
@@ -662,6 +666,10 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
662666
if (snfr.origBinCols[i] != -1 && snfr.aggrBinCoef[i] != 0) {
663667
vals[rowlen] = -snfr.aggrBinCoef[i];
664668
inds[rowlen] = snfr.origBinCols[i];
669+
if (snfr.complementation[i]) {
670+
tmpRhs -= vals[rowlen];
671+
vals[rowlen] = -vals[rowlen];
672+
}
665673
rowlen++;
666674
}
667675
tmpRhs += snfr.aggrConstant[i];
@@ -673,6 +681,10 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
673681
if (liftedbincoef != 0) {
674682
vals[rowlen] = -liftedbincoef;
675683
inds[rowlen] = snfr.origBinCols[i];
684+
if (snfr.complementation[i]) {
685+
tmpRhs -= vals[rowlen];
686+
vals[rowlen] = -vals[rowlen];
687+
}
676688
rowlen++;
677689
tmpRhs -= liftedbincoef;
678690
}
@@ -694,6 +706,10 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
694706
if (binvarcoef != 0) {
695707
vals[rowlen] = static_cast<double>(binvarcoef);
696708
inds[rowlen] = snfr.origBinCols[i];
709+
if (snfr.complementation[i]) {
710+
tmpRhs -= vals[rowlen];
711+
vals[rowlen] = -vals[rowlen];
712+
}
697713
rowlen++;
698714
}
699715
} else {
@@ -714,6 +730,10 @@ bool HighsCutGeneration::separateLiftedFlowCover() {
714730
if (snfr.origBinCols[i] != -1 && bincoef != 0) {
715731
vals[rowlen] = static_cast<double>(bincoef);
716732
inds[rowlen] = snfr.origBinCols[i];
733+
if (snfr.complementation[i]) {
734+
tmpRhs -= vals[rowlen];
735+
vals[rowlen] = -vals[rowlen];
736+
}
717737
rowlen++;
718738
}
719739
if (snfr.origContCols[i] != -1 && snfr.aggrContCoef[i] != 0) {
@@ -1574,6 +1594,7 @@ void HighsCutGeneration::initSNFRelaxation() {
15741594
snfr.aggrConstant.resize(rowlen);
15751595
snfr.aggrBinCoef.resize(rowlen);
15761596
snfr.aggrContCoef.resize(rowlen);
1597+
snfr.complementation.resize(rowlen);
15771598
}
15781599
snfr.rhs = 0;
15791600
snfr.lambda = 0;

highs/mip/HighsCutGeneration.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ class HighsCutGeneration {
127127
std::vector<HighsInt> origContCols; // orig y_i used to make y'_j in SNFR
128128
std::vector<double> aggrBinCoef; // c_i row coef of x_i in orig aggrrow
129129
std::vector<double> aggrContCoef; // a_i row coef of y_i in orig aggrrow
130-
std::vector<double> aggrConstant; // constant shift used in SNFR transform
130+
std::vector<double> aggrConstant; // constant shift used in SNFR transform
131+
std::vector<bool> complementation; // was the original bincol complemented
131132

132133
std::vector<HighsInt>
133134
flowCoverStatus; // (+1) in f-cover (-1) notin f-cover

highs/mip/HighsTransformedLp.cpp

Lines changed: 157 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,8 @@ bool HighsTransformedLp::transformSNFRelaxation(
625625

626626
auto checkValidityVB = [&](HighsInt bincol, HighsImplications::VarBound vb,
627627
double coef, double origbincoef, double lb,
628-
double ub, bool isVub) {
628+
double ub, bool isVub, bool& complement,
629+
bool& inclbincoef) {
629630
// a_j coefficient of y_j in row. c_j coefficient of x_j in row.
630631
// u_j, l_j, closest simple bounds imposed on y_j.
631632
// y_j <= u'_j x_j + d_j || y_j >= l'_j x_j + d_j
@@ -648,41 +649,99 @@ bool HighsTransformedLp::transformSNFRelaxation(
648649
// Note: c_j may change during transform if two columns use same bin col
649650
if (bincol == -1) return false;
650651
if (abs(vb.coef) >= 1e+6) return false;
651-
if (isVub && lb < vb.constant) return false;
652-
if (!isVub && ub > vb.constant) return false;
652+
complement = false;
653+
inclbincoef = true;
654+
if (isVub && lb < vb.constant) {
655+
if (lb >= vb.constant + vb.coef) {
656+
complement = true;
657+
} else {
658+
return false;
659+
}
660+
}
661+
if (!isVub && ub > vb.constant) {
662+
if (ub <= vb.constant + vb.coef) {
663+
complement = true;
664+
} else {
665+
return false;
666+
}
667+
}
653668
const double sign = coef >= 0 ? 1 : -1;
654669
if (isVub) {
655-
double val = sign * ((coef * vb.coef) + origbincoef);
656-
if (val < 0 || val > kHighsInf) return false;
657-
val = sign * ((coef * (lb - vb.constant)) + origbincoef);
658-
if (val < 0) return false;
670+
double val = complement ? sign * (coef * -vb.coef + origbincoef)
671+
: sign * (coef * vb.coef + origbincoef);
672+
if (val > kHighsInf) return false;
673+
if (val < 0) {
674+
val = complement ? sign * (coef * -vb.coef) : sign * (coef * vb.coef);
675+
if (val < 0) return false;
676+
inclbincoef = false;
677+
}
678+
if (inclbincoef) {
679+
val = complement
680+
? sign * (coef * (lb - (vb.constant + vb.coef)) + origbincoef)
681+
: sign * (coef * (lb - vb.constant) + origbincoef);
682+
if (val < 0) {
683+
val = complement ? sign * (coef * (lb - (vb.constant + vb.coef)))
684+
: sign * (coef * (lb - vb.constant));
685+
if (val < 0) return false;
686+
inclbincoef = false;
687+
val = complement ? sign * (coef * -vb.coef) : sign * (coef * vb.coef);
688+
if (val < 0) return false;
689+
}
690+
} else {
691+
val = complement ? sign * (coef * (lb - (vb.constant + vb.coef)))
692+
: sign * (coef * (lb - vb.constant));
693+
if (val < 0) return false;
694+
}
659695
} else {
660-
double val = sign * ((coef * vb.coef) + origbincoef);
661-
if (val > 0 || -val > kHighsInf) return false;
662-
val = sign * ((coef * (ub - vb.constant)) + origbincoef);
663-
if (val > 0) return false;
696+
double val = complement ? sign * ((coef * -vb.coef) + origbincoef)
697+
: sign * ((coef * vb.coef) + origbincoef);
698+
if (-val > kHighsInf) return false;
699+
if (val > 0) {
700+
val = complement ? sign * (coef * -vb.coef) : sign * (coef * vb.coef);
701+
if (val > 0) return false;
702+
inclbincoef = false;
703+
}
704+
if (inclbincoef) {
705+
val =
706+
complement
707+
? sign * ((coef * (ub - (vb.constant + vb.coef))) + origbincoef)
708+
: sign * ((coef * (ub - vb.constant)) + origbincoef);
709+
if (val > 0) {
710+
val = complement ? sign * (coef * (ub - (vb.constant + vb.coef)))
711+
: sign * (coef * (ub - vb.constant));
712+
if (val > 0) return false;
713+
inclbincoef = false;
714+
val = complement ? sign * (coef * -vb.coef) : sign * (coef * vb.coef);
715+
if (val > 0) return false;
716+
}
717+
} else {
718+
val = complement ? sign * (coef * (ub - (vb.constant + vb.coef)))
719+
: sign * (coef * (ub - vb.constant));
720+
if (val > 0) return false;
721+
}
664722
}
665723
return true;
666724
};
667725

668-
auto addSNFRentry = [&](HighsInt origbincol, HighsInt origcontcol,
669-
double binsolval, double contsolval, HighsInt coef,
670-
double vubcoef, double aggrconstant,
671-
double aggrbincoef, double aggrcontcoef) {
672-
assert(binsolval >= -lprelaxation.getMipSolver().mipdata_->feastol &&
673-
binsolval <= 1 + lprelaxation.getMipSolver().mipdata_->feastol);
674-
assert(vubcoef >= -1e-10);
675-
snfr.origBinCols[snfr.numNnzs] = origbincol;
676-
snfr.origContCols[snfr.numNnzs] = origcontcol;
677-
snfr.binSolval[snfr.numNnzs] = binsolval;
678-
snfr.contSolval[snfr.numNnzs] = contsolval;
679-
snfr.coef[snfr.numNnzs] = coef;
680-
snfr.vubCoef[snfr.numNnzs] = std::max(vubcoef, 0.0);
681-
snfr.aggrConstant[snfr.numNnzs] = aggrconstant;
682-
snfr.aggrBinCoef[snfr.numNnzs] = aggrbincoef;
683-
snfr.aggrContCoef[snfr.numNnzs] = aggrcontcoef;
684-
snfr.numNnzs++;
685-
};
726+
auto addSNFRentry =
727+
[&](HighsInt origbincol, HighsInt origcontcol, double binsolval,
728+
double contsolval, HighsInt coef, double vubcoef, double aggrconstant,
729+
double aggrbincoef, double aggrcontcoef, bool complement) {
730+
assert(binsolval >= -lprelaxation.getMipSolver().mipdata_->feastol &&
731+
binsolval <= 1 + lprelaxation.getMipSolver().mipdata_->feastol);
732+
assert(vubcoef >= -1e-10);
733+
snfr.origBinCols[snfr.numNnzs] = origbincol;
734+
snfr.origContCols[snfr.numNnzs] = origcontcol;
735+
snfr.binSolval[snfr.numNnzs] = binsolval;
736+
snfr.contSolval[snfr.numNnzs] = contsolval;
737+
snfr.coef[snfr.numNnzs] = coef;
738+
snfr.vubCoef[snfr.numNnzs] = std::max(vubcoef, 0.0);
739+
snfr.aggrConstant[snfr.numNnzs] = aggrconstant;
740+
snfr.aggrBinCoef[snfr.numNnzs] = aggrbincoef;
741+
snfr.aggrContCoef[snfr.numNnzs] = aggrcontcoef;
742+
snfr.complementation[snfr.numNnzs] = complement;
743+
snfr.numNnzs++;
744+
};
686745

687746
// Place the non-binary columns to the front (all general ints relaxed)
688747
// Use vectorsum to track original row coefficients of binary columns.
@@ -761,36 +820,39 @@ bool HighsTransformedLp::transformSNFRelaxation(
761820
// Binary columns can be added directly to the SNFR
762821
if (vals[i] >= 0) {
763822
addSNFRentry(col, -1, getLpSolution(col), getLpSolution(col) * vals[i],
764-
1, vals[i], 0, vals[i], 0);
823+
1, vals[i], 0, vectorsum.getValue(col), 0, false);
765824
} else {
766825
addSNFRentry(col, -1, getLpSolution(col), -getLpSolution(col) * vals[i],
767-
-1, -vals[i], 0, -vals[i], 0);
826+
-1, -vals[i], 0, -vectorsum.getValue(col), 0, false);
768827
}
769828
} else {
770829
// Decide whether to use {simple, variable} {lower, upper} bound
771-
bool vlbValid = checkValidityVB(bestVlb[col].first, bestVlb[col].second, vals[i],
772-
bestVlb[col].first == -1
773-
? 0
774-
: vectorsum.getValue(bestVlb[col].first),
775-
lb, ub, false);
776-
bool vubValid = checkValidityVB(bestVub[col].first, bestVub[col].second, vals[i],
777-
bestVub[col].first == -1
778-
? 0
779-
: vectorsum.getValue(bestVub[col].first),
780-
lb, ub, true);
781-
BoundType boundType = BoundType::kSimpleLb;
830+
bool complementvlb = false;
831+
bool inclbincolvlb = true;
832+
bool vlbValid = checkValidityVB(
833+
bestVlb[col].first, bestVlb[col].second, vals[i],
834+
bestVlb[col].first == -1 ? 0 : vectorsum.getValue(bestVlb[col].first),
835+
lb, ub, false, complementvlb, inclbincolvlb);
836+
bool complementvub = false;
837+
bool inclbincolvub = true;
838+
bool vubValid = checkValidityVB(
839+
bestVub[col].first, bestVub[col].second, vals[i],
840+
bestVub[col].first == -1 ? 0 : vectorsum.getValue(bestVub[col].first),
841+
lb, ub, true, complementvub, inclbincolvub);
842+
auto boundType = BoundType::kSimpleLb;
782843
if (lbDist[col] < ubDist[col] - mip.mipdata_->feastol && vlbValid) {
783844
boundType = BoundType::kVariableLb;
784-
} else if (ubDist[col] < lbDist[col] - mip.mipdata_->feastol && vubValid) {
845+
} else if (ubDist[col] < lbDist[col] - mip.mipdata_->feastol &&
846+
vubValid) {
785847
boundType = BoundType::kVariableUb;
786848
} else if (vals[i] > 0 && vlbValid) {
787849
boundType = BoundType::kVariableLb;
788850
} else if (vals[i] < 0 && vubValid) {
789851
boundType = BoundType::kVariableUb;
790852
} else if (vlbValid) {
791-
boundType = BoundType::kVariableLb;
853+
boundType = BoundType::kVariableLb;
792854
} else if (vubValid) {
793-
boundType = BoundType::kVariableUb;
855+
boundType = BoundType::kVariableUb;
794856
} else if (lbDist[col] < ubDist[col] - mip.mipdata_->feastol) {
795857
boundType = BoundType::kSimpleLb;
796858
} else if (ubDist[col] < lbDist[col] - mip.mipdata_->feastol) {
@@ -817,10 +879,10 @@ bool HighsTransformedLp::transformSNFRelaxation(
817879
aggrconstant = static_cast<double>(HighsCDouble(vals[i]) * ub);
818880
if (vals[i] >= 0) {
819881
addSNFRentry(-1, col, 1.0, -substsolval, -1, vbcoef, aggrconstant,
820-
0, -vals[i]);
882+
0, -vals[i], false);
821883
} else {
822884
addSNFRentry(-1, col, 1, substsolval, 1, -vbcoef, -aggrconstant, 0,
823-
vals[i]);
885+
vals[i], false);
824886
}
825887
tmpSnfrRhs -= aggrconstant;
826888
break;
@@ -836,10 +898,10 @@ bool HighsTransformedLp::transformSNFRelaxation(
836898
aggrconstant = static_cast<double>(HighsCDouble(vals[i]) * lb);
837899
if (vals[i] >= 0) {
838900
addSNFRentry(-1, col, 1, substsolval, 1, vbcoef, -aggrconstant, 0,
839-
vals[i]);
901+
vals[i], false);
840902
} else {
841903
addSNFRentry(-1, col, 1, -substsolval, -1, -vbcoef, aggrconstant, 0,
842-
-vals[i]);
904+
-vals[i], false);
843905
}
844906
tmpSnfrRhs -= aggrconstant;
845907
break;
@@ -857,21 +919,32 @@ bool HighsTransformedLp::transformSNFRelaxation(
857919
bestVlb[col].second.constant) +
858920
(HighsCDouble(lpSolution.col_value[vbcol]) *
859921
vectorsum.getValue(vbcol)));
860-
vbcoef = static_cast<double>(HighsCDouble(vals[i]) *
861-
bestVlb[col].second.coef +
862-
vectorsum.getValue(vbcol));
863-
aggrconstant = static_cast<double>(HighsCDouble(vals[i]) *
864-
bestVlb[col].second.constant);
922+
vbcoef = static_cast<double>(
923+
HighsCDouble(vals[i]) * (complementvlb
924+
? -bestVlb[col].second.coef
925+
: bestVlb[col].second.coef) +
926+
(inclbincolvlb ? vectorsum.getValue(vbcol) : 0));
927+
aggrconstant = static_cast<double>(
928+
HighsCDouble(vals[i]) *
929+
(complementvlb
930+
? bestVlb[col].second.constant + bestVlb[col].second.coef
931+
: bestVlb[col].second.constant));
865932
if (vals[i] >= 0) {
866-
addSNFRentry(vbcol, col, lpSolution.col_value[vbcol], -substsolval,
867-
-1, -vbcoef, aggrconstant, -vectorsum.getValue(vbcol),
868-
-vals[i]);
933+
addSNFRentry(vbcol, col,
934+
complementvlb ? 1 - lpSolution.col_value[vbcol]
935+
: lpSolution.col_value[vbcol],
936+
-substsolval, -1, -vbcoef, aggrconstant,
937+
inclbincolvlb ? -vectorsum.getValue(vbcol) : 0,
938+
-vals[i], complementvlb);
869939
} else {
870-
addSNFRentry(vbcol, col, lpSolution.col_value[vbcol], substsolval,
871-
1, vbcoef, -aggrconstant, vectorsum.getValue(vbcol),
872-
vals[i]);
940+
addSNFRentry(vbcol, col,
941+
complementvlb ? 1 - lpSolution.col_value[vbcol]
942+
: lpSolution.col_value[vbcol],
943+
substsolval, 1, vbcoef, -aggrconstant,
944+
inclbincolvlb ? vectorsum.getValue(vbcol) : 0, vals[i],
945+
complementvlb);
873946
}
874-
vectorsum.values[vbcol] = 0;
947+
if (inclbincolvlb) vectorsum.values[vbcol] = 0;
875948
tmpSnfrRhs -= aggrconstant;
876949
break;
877950
case BoundType::kVariableUb:
@@ -888,21 +961,32 @@ bool HighsTransformedLp::transformSNFRelaxation(
888961
bestVub[col].second.constant) +
889962
(HighsCDouble(lpSolution.col_value[vbcol]) *
890963
vectorsum.getValue(vbcol)));
891-
vbcoef = static_cast<double>(HighsCDouble(vals[i]) *
892-
bestVub[col].second.coef +
893-
vectorsum.getValue(vbcol));
894-
aggrconstant = static_cast<double>(HighsCDouble(vals[i]) *
895-
bestVub[col].second.constant);
964+
vbcoef = static_cast<double>(
965+
HighsCDouble(vals[i]) * (complementvub
966+
? -bestVub[col].second.coef
967+
: bestVub[col].second.coef) +
968+
(inclbincolvub ? vectorsum.getValue(vbcol) : 0));
969+
aggrconstant = static_cast<double>(
970+
HighsCDouble(vals[i]) *
971+
(complementvub
972+
? bestVub[col].second.constant + bestVub[col].second.coef
973+
: bestVub[col].second.constant));
896974
if (vals[i] >= 0) {
897-
addSNFRentry(vbcol, col, lpSolution.col_value[vbcol], substsolval,
898-
1, vbcoef, -aggrconstant, vectorsum.getValue(vbcol),
899-
vals[i]);
975+
addSNFRentry(vbcol, col,
976+
complementvub ? 1 - lpSolution.col_value[vbcol]
977+
: lpSolution.col_value[vbcol],
978+
substsolval, 1, vbcoef, -aggrconstant,
979+
inclbincolvub ? vectorsum.getValue(vbcol) : 0, vals[i],
980+
complementvub);
900981
} else {
901-
addSNFRentry(vbcol, col, lpSolution.col_value[vbcol], -substsolval,
902-
-1, -vbcoef, aggrconstant, -vectorsum.getValue(vbcol),
903-
-vals[i]);
982+
addSNFRentry(vbcol, col,
983+
complementvub ? 1 - lpSolution.col_value[vbcol]
984+
: lpSolution.col_value[vbcol],
985+
-substsolval, -1, -vbcoef, aggrconstant,
986+
inclbincolvub ? -vectorsum.getValue(vbcol) : 0,
987+
-vals[i], complementvub);
904988
}
905-
vectorsum.values[vbcol] = 0;
989+
if (inclbincolvub) vectorsum.values[vbcol] = 0;
906990
tmpSnfrRhs -= aggrconstant;
907991
break;
908992
}

0 commit comments

Comments
 (0)