@@ -295,6 +295,195 @@ std::pair<HighsInt, HighsImplications::VarBound> HighsImplications::getBestVlb(
295295 return bestVlb;
296296}
297297
298+ std::pair<HighsInt, HighsImplications::VarBound> HighsImplications::getBestVub (
299+ HighsInt col, const double lb, const double ub, const double coef,
300+ const HighsSolution& lpSolution, const HighsSparseVectorSum& bincoef,
301+ bool & complementvub, bool & inclbincolvub) const {
302+ std::pair<HighsInt, VarBound> bestVub =
303+ std::make_pair (-1 , VarBound{0.0 , kHighsInf });
304+ double minbestUb = ub;
305+ double bestUbDist = kHighsInf ;
306+ int64_t bestvubnodes = 0 ;
307+
308+ auto isVubBetter = [&](double ubDist, int64_t vubNodes, double minVubVal) {
309+ if (ubDist < bestUbDist - mipsolver.mipdata_ ->feastol ) return true ;
310+ if (vubNodes > bestvubnodes) return true ;
311+ if (vubNodes < bestvubnodes) return false ;
312+ if (minVubVal < minbestUb - mipsolver.mipdata_ ->feastol ) return true ;
313+
314+ return false ;
315+ };
316+
317+ double scale = mipsolver.mipdata_ ->domain .col_upper_ [col] -
318+ mipsolver.mipdata_ ->domain .col_lower_ [col];
319+ if (scale == kHighsInf )
320+ scale = 1.0 ;
321+ else
322+ scale = 1.0 / scale;
323+
324+ vubs[col].for_each ([&](HighsInt vubCol, const VarBound& vub) {
325+ if (vub.coef == kHighsInf ) return ;
326+ if (mipsolver.mipdata_ ->domain .isFixed (vubCol)) return ;
327+ assert (mipsolver.mipdata_ ->domain .isBinary (vubCol));
328+ double vubval = lpSolution.col_value [vubCol] * vub.coef + vub.constant ;
329+ double ubDist = std::max (0.0 , vubval - lpSolution.col_value [col]);
330+
331+ double yDist = mipsolver.mipdata_ ->feastol +
332+ (vub.coef > 0 ? 1 - lpSolution.col_value [vubCol]
333+ : lpSolution.col_value [vubCol]);
334+ double norm2 = 1.0 + vub.coef * vub.coef ;
335+ if (ubDist * ubDist > yDist * yDist * norm2) return ;
336+
337+ assert (vubCol >= 0 && vubCol < mipsolver.numCol ());
338+ ubDist *= scale;
339+ if (ubDist <= bestUbDist + mipsolver.mipdata_ ->feastol ) {
340+ bool complement = false ;
341+ bool inclbincol = true ;
342+ if (lb < vub.constant ) {
343+ if (lb >= vub.constant + vub.coef ) {
344+ complement = true ;
345+ } else {
346+ return ;
347+ }
348+ }
349+ const double sign = coef >= 0 ? 1 : -1 ;
350+ double complorigbincoef = complement ? -bincoef.getValue (vubCol) : bincoef.getValue (vubCol);
351+ double vbconstant = complement ? vub.constant + vub.coef : vub.constant ;
352+ double vbcoef = complement ? -vub.coef : vub.coef ;
353+ double val;
354+ val = sign * (coef * vbcoef + complorigbincoef);
355+ if (val > kHighsInf ) return ;
356+ if (val < 0 ) {
357+ val = sign * (coef * vbcoef);
358+ if (val < 0 ) return ;
359+ inclbincol = false ;
360+ }
361+ if (inclbincol) {
362+ val = sign * (coef * (lb - vbconstant) + complorigbincoef);
363+ if (val < 0 ) {
364+ val = sign * (coef * (lb - vbconstant));
365+ if (val < 0 ) return ;
366+ inclbincol = false ;
367+ val = sign * (coef * vbcoef);
368+ if (val < 0 ) return ;
369+ }
370+ } else {
371+ val = sign * (coef * (lb - vbconstant));
372+ if (val < 0 ) return ;
373+ }
374+ double minvubval = vub.minValue ();
375+ int64_t vubnodes =
376+ vub.coef > 0 ? mipsolver.mipdata_ ->nodequeue .numNodesDown (vubCol)
377+ : mipsolver.mipdata_ ->nodequeue .numNodesUp (vubCol);
378+
379+ if (isVubBetter (ubDist, vubnodes, minvubval)) {
380+ minbestUb = minvubval;
381+ bestVub = std::make_pair (vubCol, vub);
382+ bestvubnodes = vubnodes;
383+ bestUbDist = ubDist;
384+ complementvub = complement;
385+ inclbincolvub = inclbincol;
386+ }
387+ }
388+ });
389+
390+ return bestVub;
391+ }
392+
393+ std::pair<HighsInt, HighsImplications::VarBound> HighsImplications::getBestVlb (
394+ HighsInt col, const double lb, const double ub, const double coef,
395+ const HighsSolution& lpSolution, const HighsSparseVectorSum& bincoef,
396+ bool & complementvlb, bool & inclbincolvlb) const {
397+ std::pair<HighsInt, VarBound> bestVlb =
398+ std::make_pair (-1 , VarBound{0.0 , -kHighsInf });
399+ double maxbestlb = lb;
400+ double bestLbDist = kHighsInf ;
401+ int64_t bestvlbnodes = 0 ;
402+
403+ auto isVlbBetter = [&](double lbDist, int64_t vlbNodes, double maxVlbVal) {
404+ if (lbDist < bestLbDist - mipsolver.mipdata_ ->feastol ) return true ;
405+ if (vlbNodes > bestvlbnodes) return true ;
406+ if (vlbNodes < bestvlbnodes) return false ;
407+ if (maxVlbVal > maxbestlb + mipsolver.mipdata_ ->feastol ) return true ;
408+ return false ;
409+ };
410+
411+ double scale = mipsolver.mipdata_ ->domain .col_upper_ [col] -
412+ mipsolver.mipdata_ ->domain .col_lower_ [col];
413+ if (scale == kHighsInf )
414+ scale = 1.0 ;
415+ else
416+ scale = 1.0 / scale;
417+
418+ vlbs[col].for_each ([&](HighsInt vlbCol, const VarBound& vlb) {
419+ if (vlb.coef == -kHighsInf ) return ;
420+ if (mipsolver.mipdata_ ->domain .isFixed (vlbCol)) return ;
421+ assert (mipsolver.mipdata_ ->domain .isBinary (vlbCol));
422+ assert (vlbCol >= 0 && vlbCol < mipsolver.numCol ());
423+ double vlbval = lpSolution.col_value [vlbCol] * vlb.coef + vlb.constant ;
424+ double lbDist = std::max (0.0 , lpSolution.col_value [col] - vlbval);
425+
426+ double yDist = mipsolver.mipdata_ ->feastol +
427+ (vlb.coef > 0 ? lpSolution.col_value [vlbCol]
428+ : 1 - lpSolution.col_value [vlbCol]);
429+
430+ double norm2 = 1.0 + vlb.coef * vlb.coef ;
431+ if (lbDist * lbDist > yDist * yDist * norm2) return ;
432+ lbDist *= scale;
433+ if (lbDist <= bestLbDist + mipsolver.mipdata_ ->feastol ) {
434+ bool complement = false ;
435+ bool inclbincol = true ;
436+ if (ub > vlb.constant ) {
437+ if (ub <= vlb.constant + vlb.coef ) {
438+ complement = true ;
439+ } else {
440+ return ;
441+ }
442+ }
443+ const double sign = coef >= 0 ? 1 : -1 ;
444+ double complorigbincoef = complement ? -bincoef.getValue (vlbCol) : bincoef.getValue (vlbCol);
445+ double vbconstant = complement ? vlb.constant + vlb.coef : vlb.constant ;
446+ double vbcoef = complement ? -vlb.coef : vlb.coef ;
447+ double val;
448+ val = sign * (coef * vbcoef + complorigbincoef);
449+ if (-val > kHighsInf ) return ;
450+ if (val > 0 ) {
451+ val = sign * (coef * vbcoef);
452+ if (val > 0 ) return ;
453+ inclbincol = false ;
454+ }
455+ if (inclbincol) {
456+ val = sign * (coef * (ub - vbconstant) + complorigbincoef);
457+ if (val > 0 ) {
458+ val = sign * (coef * (ub - vbconstant));
459+ if (val > 0 ) return ;
460+ inclbincol = false ;
461+ val = sign * (coef * vbcoef);
462+ if (val > 0 ) return ;
463+ }
464+ } else {
465+ val = sign * (coef * (ub - vbconstant));
466+ if (val > 0 ) return ;
467+ }
468+ double maxvlbval = vlb.maxValue ();
469+ int64_t vlbnodes =
470+ vlb.coef > 0 ? mipsolver.mipdata_ ->nodequeue .numNodesUp (vlbCol)
471+ : mipsolver.mipdata_ ->nodequeue .numNodesDown (vlbCol);
472+
473+ if (isVlbBetter (lbDist, vlbnodes, maxvlbval)) {
474+ maxbestlb = maxvlbval;
475+ bestVlb = std::make_pair (vlbCol, vlb);
476+ bestvlbnodes = vlbnodes;
477+ bestLbDist = lbDist;
478+ inclbincolvlb = inclbincol;
479+ complementvlb = complement;
480+ }
481+ }
482+ });
483+
484+ return bestVlb;
485+ }
486+
298487bool HighsImplications::runProbing (HighsInt col, HighsInt& numReductions) {
299488 HighsDomain& globaldomain = mipsolver.mipdata_ ->domain ;
300489 if (globaldomain.isBinary (col) && !implicationsCached (col, 1 ) &&
0 commit comments