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 }