Skip to content
Closed
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ed17c2e
Add strongcg draft
Opt-Mucca Jun 6, 2025
213d1f7
Add missing 1
Opt-Mucca Jun 6, 2025
0eccd2c
Correct viol for strongcg.
Opt-Mucca Jun 6, 2025
7ea5364
Add some tolerance to constraint improvement check
Opt-Mucca Jun 6, 2025
2a2fdae
Add tolerance to actual cut
Opt-Mucca Jun 6, 2025
6d41859
Update strongcg logic
Opt-Mucca Jun 10, 2025
a8b8444
Add explicit tolerances not tied to feastol
Opt-Mucca Jun 10, 2025
0ba53e0
Format else statements
Opt-Mucca Jun 10, 2025
2d7900a
Catch case where all coefs are rounded 0
Opt-Mucca Jun 10, 2025
26ee0ba
Merge branch 'latest' into strongcg
Opt-Mucca Jun 11, 2025
b2ffd80
Change KHighsTiny to epsilon in approximate cut calc
Opt-Mucca Jun 11, 2025
a024849
Added additional numerical safeguard
Opt-Mucca Jun 11, 2025
ee50b06
Relax approx tolerances. Enforce tolerance for ceil
Opt-Mucca Jun 11, 2025
9903204
Make clang format happy
Opt-Mucca Jun 11, 2025
8c54b3e
Calc strongcg for conflict + modk
Opt-Mucca Jun 17, 2025
bf28f6a
Update tolerances
Opt-Mucca Aug 4, 2025
0c57776
Merge branch 'latest' into strongcg
Opt-Mucca Aug 4, 2025
fa58cc5
Merge branch 'latest' into strongcg
Opt-Mucca Oct 14, 2025
775b04a
Make linter happy
Opt-Mucca Oct 14, 2025
b89e5ce
Make strongCG more conersvative
Opt-Mucca Nov 25, 2025
bdfe69d
Merge branch 'latest' into strongcg
Opt-Mucca Nov 25, 2025
13a5edf
Add an additional complementation attempt for strongcg
Opt-Mucca Dec 18, 2025
70837fe
Remove attempt at complementing strongcg
Opt-Mucca Mar 4, 2026
05b4fef
Merge pull request #3101 from ERGO-Code/latest
galabovaa Jun 27, 2026
8396001
Merge pull request #3103 from ERGO-Code/latest
galabovaa Jun 27, 2026
5cc13c5
Merge v1.15.0 into branch
Opt-Mucca Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 72 additions & 5 deletions highs/mip/HighsCutGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ bool HighsCutGeneration::separateLiftedMixedIntegerCover() {
return true;
}

static double fast_ceil(double x) { return (int64_t)x + (x > (int64_t)x); }
Comment thread
Opt-Mucca marked this conversation as resolved.

static double fast_floor(double x) { return (int64_t)x - (x < (int64_t)x); }

bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
Expand All @@ -520,6 +522,7 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
integerinds.clear();
integerinds.reserve(rowlen);

bool strongcg = !onlyInitialCMIRScale;
double maxabsdelta = 0.0;
constexpr double maxCMirScale = 1e6;
constexpr double f0min = 0.005;
Expand All @@ -541,9 +544,12 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
maxabsdelta = max(maxabsdelta, delta);
deltas.push_back(delta);
}
} else {
} else if (vals[i] < 0) {
// Positive coefficient values later set to 0 so have no contribution
updateViolationAndNorm(i, vals[i], continuouscontribution,
continuoussqrnorm);
// StrongCG cannot be computed when negative coefficients for cont exist
strongcg = false;
}
}

Expand Down Expand Up @@ -687,13 +693,60 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
}
}

// Calc strongcg cut as potential alternative to CMIR
if (bestefficacy <= 0 || rowlen == 0) {
strongcg = false;
}
if (strongcg) {
double delta = bestdelta;
double scale = 1.0 / delta;
double scalrhs = double(rhs) * scale;
double downrhs = fast_floor(scalrhs);
Comment thread
Opt-Mucca marked this conversation as resolved.
double f0 = scalrhs - downrhs;
double oneoveroneminusf0 = 1.0 / (1.0 - f0);
// Skip numerically troublesome cuts
if (fractionality(1 / f0) < 1e-3) {
strongcg = false;
} else {
double k = fast_ceil(1 / f0) - 1;
// All coefficients of continuous variables are 0 in strong CG cut
double sqrnorm = 0;
double viol = -downrhs;

for (HighsInt j : integerinds) {
double scalaj = vals[j] * scale;
double downaj = fast_floor(scalaj + kHighsTiny);
double fj = scalaj - downaj;
double aj = downaj;
if (fj >= f0 + 10 * feastol) {
double pj = fast_ceil(k * (fj - f0) * oneoveroneminusf0 - 1e-4);
aj += pj / (k + 1);
}
updateViolationAndNorm(j, aj, viol, sqrnorm);
}
if (sqrnorm <= kHighsTiny) {
strongcg = false;
} else {
double efficacy = viol / sqrt(sqrnorm);
// Use the strongCG cut instead of the CMIR if efficacy is larger
if (efficacy < bestefficacy + epsilon) {
strongcg = false;
} else {
bestefficacy = efficacy;
}
}
}
}

HighsCDouble scale = 1.0 / HighsCDouble(bestdelta);
HighsCDouble scalrhs = rhs * scale;
double downrhs = floor(double(scalrhs));

HighsCDouble f0 = scalrhs - downrhs;
HighsCDouble oneoveroneminusf0 = 1.0 / (1.0 - f0);

double k = strongcg ? ceil(static_cast<double>(1 / f0)) - 1 : 0;

rhs = downrhs * bestdelta;
integralSupport = true;
integralCoefficients = false;
Expand All @@ -711,8 +764,16 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
double downaj = floor(double(scalaj + kHighsTiny));
HighsCDouble fj = scalaj - downaj;
HighsCDouble aj = downaj;
if (fj > f0) aj += (fj - f0) * oneoveroneminusf0;

if (fj > f0) {
if (strongcg) {
if (fj - f0 > epsilon) {
HighsCDouble pj = ceil(k * (fj - f0) * oneoveroneminusf0 - feastol);
aj += pj / (k + 1);
}
} else {
aj += (fj - f0) * oneoveroneminusf0;
}
}
vals[j] = double(aj * bestdelta);
}
}
Expand All @@ -729,8 +790,14 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy,
if (checknorm != 0.0) {
double checkefficacy = checkviol / sqrt(checknorm);
// the efficacy can become infinite if the cut 0 <= -1 is derived
assert(fabs(checkefficacy - bestefficacy) < 0.001 ||
fabs(checkefficacy) >= 1e30);
if (!strongcg) {
assert(fabs(checkefficacy - bestefficacy) < 0.001 ||
fabs(checkefficacy) >= 1e30);
} else {
// Rounded conservatively for scoring => strongcg cut can be stronger
assert(bestefficacy - checkefficacy < 0.001 ||
fabs(checkefficacy) >= 1e30);
}
}
}
#endif
Expand Down
Loading