diff --git a/highs/mip/HighsCutGeneration.cpp b/highs/mip/HighsCutGeneration.cpp index 40139253a89..3ce3dfbc15a 100644 --- a/highs/mip/HighsCutGeneration.cpp +++ b/highs/mip/HighsCutGeneration.cpp @@ -499,6 +499,8 @@ bool HighsCutGeneration::separateLiftedMixedIntegerCover() { return true; } +static double fast_ceil(double x) { return (int64_t)x + (x > (int64_t)x); } + static double fast_floor(double x) { return (int64_t)x - (x < (int64_t)x); } bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy, @@ -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; @@ -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; } } @@ -687,6 +693,51 @@ 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); + 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)); @@ -694,6 +745,8 @@ bool HighsCutGeneration::cmirCutGenerationHeuristic(double minEfficacy, HighsCDouble f0 = scalrhs - downrhs; HighsCDouble oneoveroneminusf0 = 1.0 / (1.0 - f0); + double k = strongcg ? ceil(static_cast(1 / f0)) - 1 : 0; + rhs = downrhs * bestdelta; integralSupport = true; integralCoefficients = false; @@ -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); } } @@ -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