1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                       */
3 /*    This file is part of the HiGHS linear optimization suite           */
4 /*                                                                       */
5 /*    Written and engineered 2008-2021 at the University of Edinburgh    */
6 /*                                                                       */
7 /*    Available as open-source under the MIT License                     */
8 /*                                                                       */
9 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 #include "mip/HighsLpRelaxation.h"
11 
12 #include "mip/HighsCutPool.h"
13 #include "mip/HighsDomain.h"
14 #include "mip/HighsMipSolver.h"
15 #include "mip/HighsMipSolverData.h"
16 #include "mip/HighsPseudocost.h"
17 #include "util/HighsCDouble.h"
18 
HighsLpRelaxation(const HighsMipSolver & mipsolver)19 HighsLpRelaxation::HighsLpRelaxation(const HighsMipSolver& mipsolver)
20     : mipsolver(mipsolver) {
21   HighsLp lpmodel = *mipsolver.model_;
22   lpmodel.integrality_.clear();
23   lpsolver.setHighsLogfile();
24   lpsolver.setHighsOutput();
25   lpsolver.passModel(std::move(lpmodel));
26   lpsolver.setHighsOptionValue(
27       "primal_feasibility_tolerance",
28       mipsolver.options_mip_->mip_feasibility_tolerance);
29   lpsolver.setHighsOptionValue(
30       "dual_feasibility_tolerance",
31       mipsolver.options_mip_->mip_feasibility_tolerance * 0.1);
32   status = Status::NotSet;
33   numlpiters = 0;
34   currentbasisstored = false;
35 }
36 
HighsLpRelaxation(const HighsLpRelaxation & other)37 HighsLpRelaxation::HighsLpRelaxation(const HighsLpRelaxation& other)
38     : mipsolver(other.mipsolver),
39       lp2cutpoolindex(other.lp2cutpoolindex),
40       fractionalints(other.fractionalints),
41       objective(other.objective),
42       basischeckpoint(other.basischeckpoint),
43       currentbasisstored(other.currentbasisstored) {
44   lpsolver.setHighsLogfile();
45   lpsolver.setHighsOutput();
46   lpsolver.passHighsOptions(other.lpsolver.getHighsOptions());
47   lpsolver.passModel(other.lpsolver.getLp());
48   lpsolver.setBasis(other.lpsolver.getBasis());
49   numlpiters = 0;
50 }
51 
computeBestEstimate(const HighsPseudocost & ps) const52 double HighsLpRelaxation::computeBestEstimate(const HighsPseudocost& ps) const {
53   HighsCDouble estimate = objective;
54 
55   for (const std::pair<int, double>& f : fractionalints)
56     estimate += std::min(ps.getPseudocostUp(f.first, f.second),
57                          ps.getPseudocostDown(f.first, f.second));
58 
59   return double(estimate);
60 }
61 
addCuts(HighsCutSet & cutset)62 void HighsLpRelaxation::addCuts(HighsCutSet& cutset) {
63   int numcuts = cutset.numCuts();
64   if (numcuts > 0) {
65     status = Status::NotSet;
66     currentbasisstored = false;
67     basischeckpoint.reset();
68     lp2cutpoolindex.insert(lp2cutpoolindex.end(), cutset.cutindices.begin(),
69                            cutset.cutindices.end());
70     lpsolver.addRows(numcuts, cutset.lower_.data(), cutset.upper_.data(),
71                      cutset.ARvalue_.size(), cutset.ARstart_.data(),
72                      cutset.ARindex_.data(), cutset.ARvalue_.data());
73     cutset.clear();
74   }
75 }
76 
removeCuts(int ndelcuts,std::vector<int> & deletemask)77 void HighsLpRelaxation::removeCuts(int ndelcuts, std::vector<int>& deletemask) {
78   if (ndelcuts > 0) {
79     HighsBasis basis = lpsolver.getBasis();
80     int nlprows = lpsolver.getNumRows();
81     lpsolver.deleteRows(deletemask.data());
82     for (int i = mipsolver.numRow(); i != nlprows; ++i) {
83       if (deletemask[i] >= 0) {
84         lp2cutpoolindex[deletemask[i] - mipsolver.numRow()] =
85             lp2cutpoolindex[i - mipsolver.numRow()];
86         basis.row_status[deletemask[i]] = basis.row_status[i];
87       }
88     }
89 
90     basis.row_status.resize(basis.row_status.size() - ndelcuts);
91     lp2cutpoolindex.resize(lp2cutpoolindex.size() - ndelcuts);
92     lpsolver.setBasis(basis);
93     lpsolver.run();
94   }
95 }
96 
removeCuts()97 void HighsLpRelaxation::removeCuts() {
98   int nlprows = lpsolver.getNumRows();
99   lpsolver.deleteRows(mipsolver.numRow(), nlprows - 1);
100   lp2cutpoolindex.clear();
101 }
102 
flushDomain(HighsDomain & domain,bool continuous)103 void HighsLpRelaxation::flushDomain(HighsDomain& domain, bool continuous) {
104   if (!domain.getChangedCols().empty()) {
105     currentbasisstored = false;
106     for (int col : domain.getChangedCols()) {
107       if (!continuous &&
108           mipsolver.variableType(col) == HighsVarType::CONTINUOUS)
109         continue;
110       lpsolver.changeColBounds(col, domain.colLower_[col],
111                                domain.colUpper_[col]);
112     }
113 
114     domain.clearChangedCols();
115   }
116 }
117 
computeDualProof(const HighsDomain & globaldomain,double upperbound,std::vector<int> & inds,std::vector<double> & vals,double & rhs) const118 bool HighsLpRelaxation::computeDualProof(const HighsDomain& globaldomain,
119                                          double upperbound,
120                                          std::vector<int>& inds,
121                                          std::vector<double>& vals,
122                                          double& rhs) const {
123   const std::vector<double>& row_dual = lpsolver.getSolution().row_dual;
124 
125   const HighsLp& lp = lpsolver.getLp();
126 
127   assert(std::isfinite(upperbound));
128   HighsCDouble upper = upperbound;
129 
130   double dualfeastol =
131       std::max(mipsolver.mipdata_->feastol,
132                2 * lpsolver.getHighsInfo().max_dual_infeasibility);
133   for (int i = 0; i != lp.numRow_; ++i) {
134     if (std::abs(row_dual[i]) <= dualfeastol) continue;
135     if (row_dual[i] > 0)
136       upper += row_dual[i] * lp.rowUpper_[i];
137     else
138       upper += row_dual[i] * lp.rowLower_[i];
139   }
140 
141   inds.clear();
142   vals.clear();
143   for (int i = 0; i != lp.numCol_; ++i) {
144     int start = lp.Astart_[i];
145     int end = lp.Astart_[i + 1];
146 
147     HighsCDouble sum = lp.colCost_[i];
148 
149     for (int j = start; j != end; ++j) {
150       if (std::abs(row_dual[lp.Aindex_[j]]) <= dualfeastol) continue;
151       sum += lp.Avalue_[j] * row_dual[lp.Aindex_[j]];
152     }
153 
154     double val = double(sum);
155 
156     if (std::abs(val) <= mipsolver.mipdata_->epsilon) continue;
157 
158     if (mipsolver.variableType(i) == HighsVarType::CONTINUOUS ||
159         std::abs(val) <= mipsolver.mipdata_->feastol ||
160         globaldomain.colLower_[i] == globaldomain.colUpper_[i]) {
161       if (val < 0) {
162         if (globaldomain.colUpper_[i] == HIGHS_CONST_INF) return false;
163         upper -= val * globaldomain.colUpper_[i];
164       } else {
165         if (globaldomain.colLower_[i] == -HIGHS_CONST_INF) return false;
166 
167         upper -= val * globaldomain.colLower_[i];
168       }
169 
170       continue;
171     }
172 
173     vals.push_back(val);
174     inds.push_back(i);
175   }
176 
177   rhs = double(upper);
178   assert(std::isfinite(rhs));
179   globaldomain.tightenCoefficients(inds.data(), vals.data(), inds.size(), rhs);
180 
181 #ifdef HIGHS_DEBUGSOL
182   HighsCDouble debugactivity = 0;
183   for (size_t i = 0; i != inds.size(); ++i)
184     debugactivity += highsDebugSolution[inds[i]] * vals[i];
185 
186   assert(debugactivity <= rhs + mipsolver.mipdata_->feastol);
187 #endif
188 
189   return true;
190 }
191 
storeDualInfProof()192 void HighsLpRelaxation::storeDualInfProof() {
193   assert(lpsolver.getModelStatus(true) == HighsModelStatus::PRIMAL_INFEASIBLE);
194 
195   int numrow = lpsolver.getNumRows();
196   hasdualproof = false;
197   lpsolver.getDualRay(hasdualproof);
198 
199   if (!hasdualproof) {
200     HighsPrintMessage(mipsolver.options_mip_->output,
201                       mipsolver.options_mip_->message_level, ML_VERBOSE,
202                       "no dual ray stored\n");
203     return;
204   }
205 
206   dualproofinds.clear();
207   dualproofvals.clear();
208   dualproofrhs = HIGHS_CONST_INF;
209   const HighsLp& lp = lpsolver.getLp();
210   dualproofbuffer.resize(numrow);
211 
212   lpsolver.getDualRay(hasdualproof, dualproofbuffer.data());
213   std::vector<double>& dualray = dualproofbuffer;
214 
215   HighsCDouble upper = 0.0;
216   double scale = 0.0;
217 
218   for (int i = 0; i != lp.numRow_; ++i) {
219     if (std::abs(dualray[i]) <=
220         lpsolver.getHighsOptions().dual_feasibility_tolerance) {
221       dualray[i] = 0.0;
222       continue;
223     }
224 
225     if (scale * dualray[i] <= 0.0) {
226       if (lp.rowUpper_[i] == HIGHS_CONST_INF) {
227         if (scale == 0.0)
228           scale = copysign(1.0, dualray[i]);
229         else
230           return;
231       }
232     }
233 
234     if (scale * dualray[i] >= 0.0) {
235       if (lp.rowLower_[i] == -HIGHS_CONST_INF) {
236         if (scale == 0.0)
237           scale = -copysign(1.0, dualray[i]);
238         else
239           return;
240       }
241     }
242   }
243 
244   if (scale == 0.0) scale = 1.0;
245 
246   assert(scale == 1.0);
247 
248   for (int i = 0; i != lp.numRow_; ++i) {
249     if (dualray[i] == 0.0) continue;
250 
251     if (scale * dualray[i] < 0) {
252       assert(lp.rowUpper_[i] != HIGHS_CONST_INF);
253       upper -= scale * dualray[i] * lp.rowUpper_[i];
254     } else {
255       assert(lp.rowLower_[i] != -HIGHS_CONST_INF);
256       upper -= scale * dualray[i] * lp.rowLower_[i];
257     }
258   }
259 
260   for (int i = 0; i != lp.numCol_; ++i) {
261     int start = lp.Astart_[i];
262     int end = lp.Astart_[i + 1];
263 
264     HighsCDouble sum = 0.0;
265 
266     for (int j = start; j != end; ++j) {
267       if (dualray[lp.Aindex_[j]] == 0.0) continue;
268       sum -= lp.Avalue_[j] * dualray[lp.Aindex_[j]];
269     }
270 
271     double val = scale * double(sum);
272 
273     if (std::abs(val) <= mipsolver.mipdata_->epsilon) continue;
274 
275     if (mipsolver.variableType(i) == HighsVarType::CONTINUOUS ||
276         std::abs(val) <= mipsolver.mipdata_->feastol ||
277         mipsolver.mipdata_->domain.colLower_[i] ==
278             mipsolver.mipdata_->domain.colUpper_[i]) {
279       if (val < 0) {
280         if (mipsolver.mipdata_->domain.colUpper_[i] == HIGHS_CONST_INF) return;
281         upper -= val * mipsolver.mipdata_->domain.colUpper_[i];
282       } else {
283         if (mipsolver.mipdata_->domain.colLower_[i] == -HIGHS_CONST_INF) return;
284         upper -= val * mipsolver.mipdata_->domain.colLower_[i];
285       }
286 
287       continue;
288     }
289 
290     dualproofvals.push_back(val);
291     dualproofinds.push_back(i);
292   }
293 
294   dualproofrhs = double(upper);
295   mipsolver.mipdata_->domain.tightenCoefficients(
296       dualproofinds.data(), dualproofvals.data(), dualproofinds.size(),
297       dualproofrhs);
298 
299 #ifdef HIGHS_DEBUGSOL
300   HighsCDouble debugactivity = 0;
301   for (size_t i = 0; i != dualproofinds.size(); ++i)
302     debugactivity += highsDebugSolution[dualproofinds[i]] * dualproofvals[i];
303 
304   assert(debugactivity <= dualproofrhs + mipsolver.mipdata_->feastol);
305 #endif
306 }
307 
storeDualUBProof()308 void HighsLpRelaxation::storeDualUBProof() {
309   dualproofinds.clear();
310   dualproofvals.clear();
311   dualproofrhs = HIGHS_CONST_INF;
312   assert(lpsolver.getModelStatus(true) ==
313          HighsModelStatus::REACHED_DUAL_OBJECTIVE_VALUE_UPPER_BOUND);
314 
315   int numrow = lpsolver.getNumRows();
316   bool hasdualray = false;
317   lpsolver.getDualRay(hasdualray);
318 
319   if (!hasdualray) return;
320 
321   const HighsLp& lp = lpsolver.getLp();
322   dualproofbuffer.resize(numrow);
323 
324   lpsolver.getDualRay(hasdualray, dualproofbuffer.data());
325   std::vector<double>& dualray = dualproofbuffer;
326 
327   double scale = 0.0;
328 
329   for (int i = 0; i != lp.numRow_; ++i) {
330     if (std::abs(dualray[i]) <= mipsolver.mipdata_->feastol) {
331       dualray[i] = 0.0;
332       continue;
333     }
334 
335     if (scale * dualray[i] <= 0.0) {
336       if (lp.rowUpper_[i] == HIGHS_CONST_INF) {
337         if (scale == 0.0)
338           scale = copysign(1.0, dualray[i]);
339         else
340           return;
341       }
342     }
343 
344     if (scale * dualray[i] >= 0.0) {
345       if (lp.rowLower_[i] == -HIGHS_CONST_INF) {
346         if (scale == 0.0)
347           scale = -copysign(1.0, dualray[i]);
348         else
349           return;
350       }
351     }
352   }
353 
354   if (scale == 0.0) scale = 1.0;
355 
356   assert(scale == 1.0);
357 
358   HighsCDouble upper =
359       lpsolver.getHighsOptions().dual_objective_value_upper_bound;
360   for (int i = 0; i != lp.numRow_; ++i) {
361     if (dualray[i] == 0.0) continue;
362 
363     if (scale * dualray[i] < 0) {
364       assert(lp.rowUpper_[i] != HIGHS_CONST_INF);
365       upper -= scale * dualray[i] * lp.rowUpper_[i];
366     } else {
367       assert(lp.rowLower_[i] != -HIGHS_CONST_INF);
368       upper -= scale * dualray[i] * lp.rowLower_[i];
369     }
370   }
371 
372   for (int i = 0; i != lp.numCol_; ++i) {
373     int start = lp.Astart_[i];
374     int end = lp.Astart_[i + 1];
375 
376     HighsCDouble sum = scale * mipsolver.colCost(i);
377 
378     for (int j = start; j != end; ++j) {
379       if (dualray[lp.Aindex_[j]] == 0.0) continue;
380       sum -= lp.Avalue_[j] * dualray[lp.Aindex_[j]];
381     }
382 
383     double val = scale * double(sum);
384 
385     if (std::abs(val) <= 1e-12) continue;
386 
387     if (mipsolver.variableType(i) == HighsVarType::CONTINUOUS ||
388         std::abs(val) < mipsolver.mipdata_->feastol ||
389         mipsolver.mipdata_->domain.colLower_[i] ==
390             mipsolver.mipdata_->domain.colUpper_[i]) {
391       if (val < 0) {
392         if (mipsolver.mipdata_->domain.colUpper_[i] == HIGHS_CONST_INF) return;
393         upper -= val * mipsolver.mipdata_->domain.colUpper_[i];
394       } else {
395         if (mipsolver.mipdata_->domain.colLower_[i] == -HIGHS_CONST_INF) return;
396 
397         upper -= val * mipsolver.mipdata_->domain.colLower_[i];
398       }
399 
400       continue;
401     }
402 
403     dualproofvals.push_back(val);
404     dualproofinds.push_back(i);
405   }
406 
407   dualproofrhs = double(upper);
408   mipsolver.mipdata_->domain.tightenCoefficients(
409       dualproofinds.data(), dualproofvals.data(), dualproofinds.size(),
410       dualproofrhs);
411 
412 #ifdef HIGHS_DEBUGSOL
413   HighsCDouble debugactivity = 0;
414   for (size_t i = 0; i != dualproofinds.size(); ++i)
415     debugactivity += highsDebugSolution[dualproofinds[i]] * dualproofvals[i];
416 
417   assert(debugactivity <= dualproofrhs + mipsolver.mipdata_->feastol);
418 #endif
419 }
420 
checkDualProof() const421 bool HighsLpRelaxation::checkDualProof() const {
422   if (!hasdualproof) return true;
423   if (dualproofrhs == HIGHS_CONST_INF) return false;
424 
425   int len = dualproofinds.size();
426 
427   HighsCDouble viol = -dualproofrhs;
428 
429   const HighsLp& lp = lpsolver.getLp();
430 
431   for (int i = 0; i != len; ++i) {
432     int col = dualproofinds[i];
433     if (dualproofvals[i] > 0) {
434       if (lp.colLower_[col] == -HIGHS_CONST_INF) return false;
435       viol += dualproofvals[i] * lp.colLower_[col];
436     } else {
437       assert(dualproofvals[i] < 0);
438       if (lp.colUpper_[col] == HIGHS_CONST_INF) return false;
439       viol += dualproofvals[i] * lp.colUpper_[col];
440     }
441   }
442 
443   return viol > mipsolver.mipdata_->feastol;
444 }
445 
computeDualInfProof(const HighsDomain & globaldomain,std::vector<int> & inds,std::vector<double> & vals,double & rhs)446 bool HighsLpRelaxation::computeDualInfProof(const HighsDomain& globaldomain,
447                                             std::vector<int>& inds,
448                                             std::vector<double>& vals,
449                                             double& rhs) {
450   if (!hasdualproof) return false;
451 
452   assert(checkDualProof());
453 
454   inds = dualproofinds;
455   vals = dualproofvals;
456   rhs = dualproofrhs;
457   return true;
458 }
459 
recoverBasis()460 void HighsLpRelaxation::recoverBasis() {
461   if (basischeckpoint) lpsolver.setBasis(*basischeckpoint);
462 }
463 
run(bool resolve_on_error)464 HighsLpRelaxation::Status HighsLpRelaxation::run(bool resolve_on_error) {
465   HighsStatus callstatus;
466 #ifdef HIGHS_DEBUGSOL
467   bool debugsolactive = true;
468   HighsCDouble debugsolobj = 0;
469   for (int i = 0; i != mipsolver.numCol(); ++i) {
470     if (highsDebugSolution[i] + mipsolver.mipdata_->epsilon <
471             lpsolver.getLp().colLower_[i] ||
472         highsDebugSolution[i] - mipsolver.mipdata_->epsilon >
473             lpsolver.getLp().colUpper_[i]) {
474       debugsolactive = false;
475     }
476 
477     debugsolobj += highsDebugSolution[i] * lpsolver.getLp().colCost_[i];
478   }
479 #endif
480 
481   try {
482     callstatus = lpsolver.run();
483   } catch (const std::runtime_error&) {
484     callstatus = HighsStatus::Error;
485   }
486 
487   const HighsInfo& info = lpsolver.getHighsInfo();
488   assert(info.max_primal_infeasibility >= 0);
489   assert(info.max_dual_infeasibility >= 0);
490   numlpiters += std::max(0, info.simplex_iteration_count);
491 
492   if (callstatus == HighsStatus::Error) {
493     lpsolver.clearSolver();
494     recoverBasis();
495     if (resolve_on_error) return run(false);
496 
497     return Status::Error;
498   }
499 
500   HighsModelStatus scaledmodelstatus = lpsolver.getModelStatus(true);
501 
502   switch (scaledmodelstatus) {
503     case HighsModelStatus::REACHED_DUAL_OBJECTIVE_VALUE_UPPER_BOUND:
504       storeDualUBProof();
505       if (checkDualProof()) return Status::Infeasible;
506 
507       return Status::Error;
508     case HighsModelStatus::PRIMAL_INFEASIBLE:
509       storeDualInfProof();
510       if (checkDualProof()) {
511 #ifdef HIGHS_DEBUGSOL
512         assert(!debugsolactive);
513 #endif
514         return Status::Infeasible;
515       }
516       hasdualproof = false;
517 
518       if (resolve_on_error) {
519         int scalestrategy = lpsolver.getHighsOptions().simplex_scale_strategy;
520         lpsolver.setHighsOptionValue("simplex_scale_strategy", 0);
521         HighsBasis basis = lpsolver.getBasis();
522         lpsolver.clearSolver();
523         lpsolver.setBasis(basis);
524         auto tmp = run(false);
525         lpsolver.setHighsOptionValue("simplex_scale_strategy", scalestrategy);
526         return tmp;
527       } else if (lpsolver.getModelStatus() ==
528                  HighsModelStatus::PRIMAL_INFEASIBLE) {
529         // the LP was solved from the basis without scaling and highs still says
530         // infeasible trust the LP solver in this case
531         return Status::Infeasible;
532       }
533 
534       HighsLogMessage(mipsolver.options_mip_->logfile,
535                       HighsMessageType::WARNING,
536                       "LP failed to reliably determine infeasibility");
537 
538       return Status::Error;
539     case HighsModelStatus::OPTIMAL:
540       if (info.max_primal_infeasibility <= mipsolver.mipdata_->feastol &&
541           info.max_dual_infeasibility <= mipsolver.mipdata_->feastol) {
542 #ifdef HIGHS_DEBUGSOL
543         assert(!debugsolactive ||
544                lpsolver.getObjectiveValue() <=
545                    debugsolobj + mipsolver.mipdata_->epsilon);
546 #endif
547         return Status::Optimal;
548       }
549 
550       if (resolve_on_error) {
551         int scalestrategy = lpsolver.getHighsOptions().simplex_scale_strategy;
552         lpsolver.setHighsOptionValue("simplex_scale_strategy", 0);
553         HighsBasis basis = lpsolver.getBasis();
554         lpsolver.clearSolver();
555         lpsolver.setBasis(basis);
556         auto tmp = run(false);
557         lpsolver.setHighsOptionValue("simplex_scale_strategy", scalestrategy);
558         return tmp;
559       }
560 
561       if (info.max_primal_infeasibility <= mipsolver.mipdata_->feastol) {
562         return Status::UnscaledPrimalFeasible;
563       }
564 
565       if (info.max_dual_infeasibility <= mipsolver.mipdata_->feastol) {
566 #ifdef HIGHS_DEBUGSOL
567         assert(!debugsolactive ||
568                lpsolver.getObjectiveValue() <=
569                    debugsolobj + mipsolver.mipdata_->epsilon);
570 #endif
571         return Status::UnscaledDualFeasible;
572       }
573 
574       return Status::UnscaledInfeasible;
575     case HighsModelStatus::PRIMAL_DUAL_INFEASIBLE:
576     // case HighsModelStatus::PRIMAL_INFEASIBLE:
577     //  if (lpsolver.getModelStatus(false) == scaledmodelstatus)
578     //    return Status::Infeasible;
579     //  return Status::Error;
580     default:
581       HighsLogMessage(
582           mipsolver.options_mip_->logfile, HighsMessageType::WARNING,
583           "LP solved to unexpected status (%d)", (int)scaledmodelstatus);
584       return Status::Error;
585   }
586 }
587 
resolveLp()588 HighsLpRelaxation::Status HighsLpRelaxation::resolveLp() {
589   fractionalints.clear();
590 
591   status = run();
592 
593   switch (status) {
594     case Status::UnscaledInfeasible:
595     case Status::UnscaledDualFeasible:
596     case Status::UnscaledPrimalFeasible:
597     case Status::Optimal: {
598       const HighsSolution& sol = lpsolver.getSolution();
599 
600       HighsCDouble objsum = 0;
601       for (int i = 0; i != mipsolver.numCol(); ++i) {
602         if (mipsolver.variableType(i) == HighsVarType::CONTINUOUS) continue;
603 
604         // for the fractionality we assume that LP bounds are not violated
605         // bounds that are violated by the unscaled LP are indicated by the
606         // return status already
607         double val =
608             std::max(std::min(sol.col_value[i], lpsolver.getLp().colUpper_[i]),
609                      lpsolver.getLp().colLower_[i]);
610         double intval = std::floor(val + 0.5);
611 
612         if (std::abs(val - intval) > mipsolver.mipdata_->feastol)
613           fractionalints.emplace_back(i, val);
614       }
615 
616       for (int i = 0; i != mipsolver.numCol(); ++i)
617         objsum += sol.col_value[i] * mipsolver.colCost(i);
618 
619       objective = double(objsum);
620       break;
621     }
622     case Status::Infeasible:
623       objective = HIGHS_CONST_INF;
624       break;
625     default:
626       break;
627   }
628 
629   return status;
630 }