34#include "absl/algorithm/container.h"
35#include "absl/cleanup/cleanup.h"
36#include "absl/container/flat_hash_map.h"
37#include "absl/log/check.h"
38#include "absl/memory/memory.h"
39#include "absl/status/status.h"
40#include "absl/status/statusor.h"
41#include "absl/strings/string_view.h"
42#include "absl/time/clock.h"
43#include "absl/time/time.h"
44#include "io/HighsIO.h"
45#include "lp_data/HConst.h"
46#include "lp_data/HStruct.h"
47#include "lp_data/HighsInfo.h"
48#include "lp_data/HighsLp.h"
49#include "lp_data/HighsModelUtils.h"
50#include "lp_data/HighsOptions.h"
51#include "lp_data/HighsStatus.h"
52#include "model/HighsModel.h"
62#include "ortools/math_opt/parameters.pb.h"
63#include "ortools/math_opt/result.pb.h"
64#include "ortools/math_opt/solution.pb.h"
65#include "ortools/math_opt/solvers/highs.pb.h"
69#include "simplex/SimplexConst.h"
70#include "util/HighsInt.h"
75constexpr absl::string_view kOutputFlag =
"output_flag";
76constexpr absl::string_view kLogToConsole =
"log_to_console";
82absl::Status ToStatus(
const HighsStatus status) {
84 case HighsStatus::kOk:
85 return absl::OkStatus();
86 case HighsStatus::kWarning:
91 return absl::OkStatus();
92 case HighsStatus::kError:
96 <<
"unexpected HighsStatus: " <<
static_cast<int>(status);
100absl::Status ToStatus(
const OptionStatus option_status) {
101 switch (option_status) {
102 case OptionStatus::kOk:
103 return absl::OkStatus();
104 case OptionStatus::kUnknownOption:
105 return absl::InvalidArgumentError(
"option name was unknown");
106 case OptionStatus::kIllegalValue:
109 return absl::InvalidArgumentError(
"option value not valid for name");
112 <<
"unexpected option_status: " <<
static_cast<int>(option_status);
115absl::StatusOr<int> SafeIntCast(
const int64_t i,
const absl::string_view name) {
116 if constexpr (
sizeof(int) >=
sizeof(int64_t)) {
117 return static_cast<int>(
i);
119 const int64_t kMin =
static_cast<int64_t
>(std::numeric_limits<int>::min());
120 const int64_t kMax =
static_cast<int64_t
>(std::numeric_limits<int>::max());
121 if (i < kMin || i > kMax) {
123 << name <<
" has value " <<
i
124 <<
" not representable as an int (the range [" << kMin <<
", "
125 << kMax <<
"]) and thus is not supported for HiGHS";
127 return static_cast<int>(
i);
132int64_t CastInt64StaticAssert(
const T value) {
133 static_assert(std::is_integral_v<T>);
134 static_assert(
sizeof(
T) <=
sizeof(int64_t));
135 return static_cast<int64_t
>(value);
140absl::StatusOr<std::unique_ptr<HighsOptions>> MakeOptions(
141 const SolveParametersProto& parameters,
const bool has_log_callback,
142 const bool is_integer) {
144 auto result = std::make_unique<HighsOptions>();
146 if (parameters.highs().bool_options().contains(kOutputFlag)) {
147 result->output_flag = parameters.highs().bool_options().at(kOutputFlag);
149 result->output_flag = parameters.enable_output() || has_log_callback;
160 if (parameters.highs().bool_options().contains(kLogToConsole)) {
161 result->log_to_console =
162 parameters.highs().bool_options().at(kLogToConsole);
164 result->log_to_console = result->output_flag;
166 if (parameters.has_time_limit()) {
170 _ <<
"invalid time_limit value for HiGHS.");
171 result->time_limit = absl::ToDoubleSeconds(
time_limit);
173 if (parameters.has_iteration_limit()) {
176 <<
"iteration_limit not supported for HiGHS on problems with "
180 const int iter_limit,
181 SafeIntCast(parameters.iteration_limit(),
"iteration_limit"));
183 result->simplex_iteration_limit = iter_limit;
184 result->ipm_iteration_limit = iter_limit;
186 if (parameters.has_node_limit()) {
188 SafeIntCast(parameters.node_limit(),
"node_limit"));
190 if (parameters.has_cutoff_limit()) {
193 return absl::InvalidArgumentError(
"cutoff_limit not supported for HiGHS");
195 if (parameters.has_objective_limit()) {
198 <<
"objective_limit not supported for HiGHS solver on integer "
203 return absl::InvalidArgumentError(
204 "objective_limit for LP appears to have a missing/broken HiGHS "
205 "implementation, see b/271616762");
208 if (parameters.has_best_bound_limit()) {
211 <<
"best_bound_limit not supported for HiGHS solver on integer "
214 result->objective_bound = parameters.best_bound_limit();
217 if (parameters.has_solution_limit()) {
218 result->mip_max_improving_sols = parameters.solution_limit();
220 if (parameters.has_threads()) {
225 <<
"threads not supported for HiGHS solver, this must be set using "
226 "globals, see HiGHS documentation";
228 if (parameters.has_random_seed()) {
229 result->random_seed = parameters.random_seed();
231 if (parameters.has_absolute_gap_tolerance()) {
232 result->mip_abs_gap = parameters.absolute_gap_tolerance();
234 if (parameters.has_relative_gap_tolerance()) {
235 result->mip_rel_gap = parameters.relative_gap_tolerance();
237 if (parameters.has_solution_pool_size()) {
239 <<
"solution_pool_size not supported for HiGHS";
241 if (parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
244 <<
"lp_algorithm is not supported for HiGHS on problems with "
247 switch (parameters.lp_algorithm()) {
248 case LP_ALGORITHM_PRIMAL_SIMPLEX:
249 result->solver = ::kSimplexString;
250 result->simplex_strategy = ::kSimplexStrategyPrimal;
252 case LP_ALGORITHM_DUAL_SIMPLEX:
253 result->solver = ::kSimplexString;
254 result->simplex_strategy = ::kSimplexStrategyDual;
256 case LP_ALGORITHM_BARRIER:
257 result->solver = ::kIpmString;
261 <<
"unsupported lp_algorithm: "
262 << LPAlgorithmProto_Name(parameters.lp_algorithm());
265 if (parameters.presolve() != EMPHASIS_UNSPECIFIED) {
266 if (parameters.presolve() == EMPHASIS_OFF) {
267 result->presolve = ::kHighsOffString;
269 result->presolve = ::kHighsOnString;
272 if (parameters.cuts() != EMPHASIS_UNSPECIFIED) {
274 <<
"cuts solve parameter unsupported for HiGHS";
276 if (parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
277 switch (parameters.heuristics()) {
279 result->mip_heuristic_effort = 0.0;
282 result->mip_heuristic_effort = 0.025;
284 case EMPHASIS_MEDIUM:
285 result->mip_heuristic_effort = 0.05;
288 result->mip_heuristic_effort = 0.1;
290 case EMPHASIS_VERY_HIGH:
291 result->mip_heuristic_effort = 0.2;
295 <<
"unexpected value for solve_parameters.heuristics of: "
296 << parameters.heuristics();
299 if (parameters.scaling() != EMPHASIS_UNSPECIFIED) {
301 if (parameters.scaling() == EMPHASIS_OFF) {
302 result->simplex_scale_strategy = ::kSimplexScaleStrategyOff;
305 for (
const auto& [name, value] : parameters.highs().string_options()) {
306 if (name == kOutputFlag || name == kLogToConsole) {
311 RETURN_IF_ERROR(ToStatus(setLocalOptionValue(result->log_options, name,
313 result->records, value)))
314 <<
"error setting string option name: " << name
315 <<
" to value:" << value;
317 for (
const auto& [name, value] : parameters.highs().double_options()) {
319 setLocalOptionValue(result->log_options, name, result->records, value)))
320 <<
"error setting double option name: " << name
321 <<
" to value:" << value;
323 for (
const auto& [name, value] : parameters.highs().int_options()) {
325 setLocalOptionValue(result->log_options, name, result->records, value)))
326 <<
"error setting int option name: " << name <<
" to value:" << value;
328 for (
const auto& [name, value] : parameters.highs().bool_options()) {
330 setLocalOptionValue(result->log_options, name, result->records, value)))
331 <<
"error setting bool option name: " << name <<
" to value:" << value;
336double DualObjective(
const HighsInfo& highs_info,
const bool is_integer) {
339 return is_integer ? highs_info.mip_dual_bound
340 : highs_info.objective_function_value;
344void HighsLogCallback(HighsLogType,
const char*
const message,
345 void*
const log_callback_data) {
352absl::StatusOr<SolveStatsProto> ToSolveStats(
const HighsInfo& highs_info) {
353 SolveStatsProto result;
359 result.set_simplex_iterations(std::max(
360 int64_t{0}, CastInt64StaticAssert(highs_info.simplex_iteration_count)));
361 result.set_barrier_iterations(std::max(
362 int64_t{0}, CastInt64StaticAssert(highs_info.ipm_iteration_count)));
363 result.set_node_count(std::max(int64_t{0}, highs_info.mip_node_count));
369absl::StatusOr<std::optional<BasisStatusProto>> ToBasisStatus(
370 const HighsBasisStatus highs_basis,
const double lb,
const double ub,
371 const std::optional<double> value) {
372 switch (highs_basis) {
373 case HighsBasisStatus::kBasic:
374 return BASIS_STATUS_BASIC;
375 case HighsBasisStatus::kUpper:
376 return BASIS_STATUS_AT_UPPER_BOUND;
377 case HighsBasisStatus::kLower:
381 return BASIS_STATUS_AT_LOWER_BOUND;
382 case HighsBasisStatus::kZero:
383 return BASIS_STATUS_FREE;
387 case HighsBasisStatus::kNonbasic: {
388 const bool lb_finite = std::isfinite(lb);
389 const bool ub_finite = std::isfinite(ub);
393 constexpr double kAtBoundTolerance = 1.0e-10;
394 if (lb_finite && ub_finite) {
396 return BASIS_STATUS_FIXED_VALUE;
397 }
else if (value.has_value() &&
398 std::abs(lb - *value) < kAtBoundTolerance) {
399 return BASIS_STATUS_AT_LOWER_BOUND;
400 }
else if (value.has_value() &&
401 std::abs(ub - *value) < kAtBoundTolerance) {
402 return BASIS_STATUS_AT_UPPER_BOUND;
407 }
else if (lb_finite) {
408 return BASIS_STATUS_AT_LOWER_BOUND;
409 }
else if (ub_finite) {
410 return BASIS_STATUS_AT_LOWER_BOUND;
412 return BASIS_STATUS_FREE;
417 <<
"unexpected highs basis: " <<
static_cast<int>(highs_basis);
420absl::StatusOr<SolutionStatusProto> ToSolutionStatus(
421 const HighsInt highs_solution_status) {
422 switch (highs_solution_status) {
423 case ::kSolutionStatusInfeasible:
424 return SOLUTION_STATUS_INFEASIBLE;
425 case ::kSolutionStatusFeasible:
426 return SOLUTION_STATUS_FEASIBLE;
427 case ::kSolutionStatusNone:
428 return SOLUTION_STATUS_UNDETERMINED;
431 <<
"unimplemented highs SolutionStatus: " << highs_solution_status;
436absl::StatusOr<FeasibilityStatusProto> HighsSolver::DualFeasibilityStatus(
437 const HighsInfo& highs_info,
const bool is_integer,
438 const SolutionClaims solution_claims) {
439 const bool dual_feasible_solution_exists =
440 solution_claims.highs_returned_dual_feasible_solution ||
441 (is_integer && std::isfinite(highs_info.mip_dual_bound));
442 if (dual_feasible_solution_exists &&
443 solution_claims.highs_returned_primal_ray) {
445 <<
"Found dual feasible solution and primal ray";
447 if (dual_feasible_solution_exists) {
448 return FEASIBILITY_STATUS_FEASIBLE;
450 if (solution_claims.highs_returned_primal_ray) {
451 return FEASIBILITY_STATUS_INFEASIBLE;
453 return FEASIBILITY_STATUS_UNDETERMINED;
456absl::StatusOr<FeasibilityStatusProto> HighsSolver::PrimalFeasibilityStatus(
457 const SolutionClaims solution_claims) {
458 if (solution_claims.highs_returned_primal_feasible_solution &&
459 solution_claims.highs_returned_dual_ray) {
461 <<
"Found primal feasible solution and dual ray";
463 if (solution_claims.highs_returned_primal_feasible_solution) {
464 return FEASIBILITY_STATUS_FEASIBLE;
466 if (solution_claims.highs_returned_dual_ray) {
467 return FEASIBILITY_STATUS_INFEASIBLE;
469 return FEASIBILITY_STATUS_UNDETERMINED;
472absl::StatusOr<TerminationProto> HighsSolver::MakeTermination(
473 const HighsModelStatus highs_model_status,
const HighsInfo& highs_info,
474 const bool is_integer,
const bool had_node_limit,
475 const bool had_solution_limit,
const bool is_maximize,
476 const SolutionClaims solution_claims) {
478 const FeasibilityStatusProto dual_feasibility_status,
479 DualFeasibilityStatus(highs_info, is_integer, solution_claims));
481 PrimalFeasibilityStatus(solution_claims));
483 const std::optional<double> optional_finite_primal_objective =
484 (primal_feasibility_status == FEASIBILITY_STATUS_FEASIBLE)
485 ? std::make_optional(highs_info.objective_function_value)
487 const std::optional<double> optional_dual_objective =
488 (dual_feasibility_status == FEASIBILITY_STATUS_FEASIBLE)
489 ? std::make_optional(DualObjective(highs_info, is_integer))
491 switch (highs_model_status) {
492 case HighsModelStatus::kNotset:
493 case HighsModelStatus::kLoadError:
494 case HighsModelStatus::kModelError:
495 case HighsModelStatus::kPresolveError:
496 case HighsModelStatus::kSolveError:
497 case HighsModelStatus::kPostsolveError:
498 case HighsModelStatus::kUnknown:
501 case HighsModelStatus::kModelEmpty:
503 <<
"HighsModelStatus was "
504 << utilModelStatusToString(highs_model_status);
505 case HighsModelStatus::kOptimal: {
507 DualObjective(highs_info, is_integer),
508 "HighsModelStatus is kOptimal");
510 case HighsModelStatus::kInfeasible:
514 ? FEASIBILITY_STATUS_FEASIBLE
515 : dual_feasibility_status);
516 case HighsModelStatus::kUnboundedOrInfeasible:
518 is_maximize, dual_feasibility_status,
519 "HighsModelStatus is kUnboundedOrInfeasible");
520 case HighsModelStatus::kUnbounded: {
525 if (highs_info.primal_solution_status == ::kSolutionStatusFeasible) {
530 FEASIBILITY_STATUS_INFEASIBLE,
531 "HighsModelStatus is kUnbounded");
534 case HighsModelStatus::kObjectiveBound:
536 is_maximize, LIMIT_OBJECTIVE, optional_finite_primal_objective,
537 optional_dual_objective,
"HighsModelStatus is kObjectiveBound");
538 case HighsModelStatus::kObjectiveTarget:
540 is_maximize, LIMIT_OBJECTIVE, optional_finite_primal_objective,
541 optional_dual_objective,
"HighsModelStatus is kObjectiveTarget");
542 case HighsModelStatus::kTimeLimit:
544 optional_finite_primal_objective,
545 optional_dual_objective);
546 case HighsModelStatus::kIterationLimit: {
548 if (had_node_limit && had_solution_limit) {
550 is_maximize, LIMIT_UNDETERMINED, optional_finite_primal_objective,
551 optional_dual_objective,
552 "Both node limit and solution limit were requested, cannot "
553 "determine reason for termination");
554 }
else if (had_node_limit) {
556 optional_finite_primal_objective,
557 optional_dual_objective);
558 }
else if (had_solution_limit) {
560 optional_finite_primal_objective,
561 optional_dual_objective);
567 optional_finite_primal_objective,
568 optional_dual_objective);
573 <<
static_cast<int>(highs_model_status);
576SolveResultProto HighsSolver::ResultForHighsModelStatusModelEmpty(
577 const bool is_maximize,
const double objective_offset,
578 const absl::flat_hash_map<int64_t, IndexAndBound>& lin_con_data) {
579 SolveResultProto result;
580 bool feasible =
true;
581 for (
const auto& [unused, lin_con_bounds] : lin_con_data) {
582 if (lin_con_bounds.lb > 0 || lin_con_bounds.ub < 0) {
587 result.mutable_termination()->set_reason(
588 feasible ? TERMINATION_REASON_OPTIMAL : TERMINATION_REASON_INFEASIBLE);
589 result.mutable_termination()->set_detail(
"HighsModelStatus was kEmptyModel");
591 auto solution = result.add_solutions()->mutable_primal_solution();
592 solution->set_objective_value(objective_offset);
593 solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
594 *result.mutable_termination() =
599 *result.mutable_termination() =
601 FEASIBILITY_STATUS_FEASIBLE);
608 const auto find_crossed =
609 [](
const absl::flat_hash_map<int64_t, IndexAndBound>& id_to_bound_data) {
610 std::vector<int64_t> result;
611 for (
const auto& [
id, bound_data] : id_to_bound_data) {
612 if (bound_data.bounds_cross()) {
613 result.push_back(
id);
616 absl::c_sort(result);
619 return {.variables = find_crossed(variable_data_),
620 .linear_constraints = find_crossed(lin_con_data_)};
623absl::StatusOr<std::optional<BasisProto>> HighsSolver::ExtractBasis() {
624 const HighsInfo& highs_info = highs_->getInfo();
625 const HighsBasis& highs_basis = highs_->getBasis();
626 const HighsSolution& highs_solution = highs_->getSolution();
627 if (highs_info.basis_validity != ::kBasisValidityValid) {
632 if (!highs_solution.value_valid || !highs_solution.dual_valid) {
637 <<
"invalid highs_solution.col_value";
639 <<
"invalid highs_solution.col_dual";
642 <<
"invalid highs_basis.col_status";
643 RETURN_IF_ERROR(EnsureOneEntryPerLinearConstraint(highs_basis.row_status))
644 <<
"invalid highs_basis.row_status";
647 if (highs_->getModelStatus() == HighsModelStatus::kOptimal) {
648 basis.set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
649 }
else if (highs_info.dual_solution_status == kSolutionStatusInfeasible) {
650 basis.set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
653 basis.set_basic_dual_feasibility(SOLUTION_STATUS_UNDETERMINED);
656 const IndexAndBound& index_and_bounds = variable_data_.at(var_id);
657 const double var_value = highs_solution.col_value[index_and_bounds.index];
659 const std::optional<BasisStatusProto> status,
660 ToBasisStatus(highs_basis.col_status[variable_data_.at(var_id).index],
661 index_and_bounds.lb, index_and_bounds.ub, var_value),
662 _ <<
"invalid highs_basis.col_status for variable with id: " << var_id);
663 if (!status.has_value()) {
666 basis.mutable_variable_status()->add_ids(var_id);
667 basis.mutable_variable_status()->add_values(*status);
669 for (
const int64_t lin_con_id :
SortedMapKeys(lin_con_data_)) {
670 const IndexAndBound& index_and_bounds = lin_con_data_.at(lin_con_id);
671 const double dual_value = highs_solution.row_dual[index_and_bounds.index];
673 const std::optional<BasisStatusProto> status,
674 ToBasisStatus(highs_basis.row_status[index_and_bounds.index],
675 index_and_bounds.lb, index_and_bounds.ub, dual_value),
676 _ <<
"invalid highs_basis.row_status for linear constraint with id: "
678 if (!status.has_value()) {
681 basis.mutable_constraint_status()->add_ids(lin_con_id);
682 basis.mutable_constraint_status()->add_values(*status);
687absl::StatusOr<bool> HighsSolver::PrimalRayReturned()
const {
688 if (!highs_->hasInvert()) {
691 bool has_primal_ray =
false;
696 return has_primal_ray;
699absl::StatusOr<bool> HighsSolver::DualRayReturned()
const {
700 if (!highs_->hasInvert()) {
703 bool has_dual_ray =
false;
711absl::StatusOr<HighsSolver::SolutionsAndClaims>
712HighsSolver::ExtractSolutionAndRays(
713 const ModelSolveParametersProto& model_params) {
714 const HighsInfo& highs_info = highs_->getInfo();
715 const HighsSolution& highs_solution = highs_->getSolution();
716 SolutionsAndClaims solution_and_claims;
717 if (highs_info.primal_solution_status == ::kSolutionStatusFeasible &&
718 !highs_solution.value_valid) {
719 return absl::InternalError(
720 "highs_info.primal_solution_status==::kSolutionStatusFeasible, but no "
721 "valid primal solution returned");
723 if (highs_solution.value_valid || highs_solution.dual_valid) {
725 solution_and_claims.solutions.emplace_back(SolutionProto());
726 if (highs_solution.value_valid) {
728 <<
"invalid highs_solution.col_value";
730 *
solution.mutable_primal_solution();
731 primal_solution.set_objective_value(highs_info.objective_function_value);
733 ToSolutionStatus(highs_info.primal_solution_status),
734 _ <<
"invalid highs_info.primal_solution_status");
736 solution_and_claims.solution_claims
737 .highs_returned_primal_feasible_solution =
742 highs_solution.col_value[variable_data_.at(var_id).index]);
745 if (highs_solution.dual_valid) {
747 <<
"invalid highs_solution.col_dual";
749 EnsureOneEntryPerLinearConstraint(highs_solution.row_dual))
750 <<
"invalid highs_solution.row_dual";
751 DualSolutionProto& dual_solution = *
solution.mutable_dual_solution();
752 dual_solution.set_objective_value(highs_info.objective_function_value);
754 ToSolutionStatus(highs_info.dual_solution_status),
755 _ <<
"invalid highs_info.dual_solution_status");
756 dual_solution.set_feasibility_status(dual_solution_status);
757 solution_and_claims.solution_claims
758 .highs_returned_dual_feasible_solution =
759 dual_solution.feasibility_status() == SOLUTION_STATUS_FEASIBLE;
761 dual_solution.mutable_reduced_costs()->add_ids(var_id);
762 dual_solution.mutable_reduced_costs()->add_values(
763 highs_solution.col_dual[variable_data_.at(var_id).index]);
765 for (
const int64_t lin_con_id :
SortedMapKeys(lin_con_data_)) {
766 dual_solution.mutable_dual_values()->add_ids(lin_con_id);
767 dual_solution.mutable_dual_values()->add_values(
768 highs_solution.row_dual[lin_con_data_.at(lin_con_id).index]);
772 HighsSolver::ExtractBasis());
773 if (basis_proto.has_value()) {
774 *
solution.mutable_basis() = *std::move(basis_proto);
780 solution_and_claims.solution_claims.highs_returned_primal_ray,
781 PrimalRayReturned());
782 ASSIGN_OR_RETURN(solution_and_claims.solution_claims.highs_returned_dual_ray,
785 return solution_and_claims;
789 const ModelProto& model,
const InitArgs&) {
791 HighsModel highs_model;
792 HighsLp& lp = highs_model.lp_;
793 lp.model_name_ = model.name();
794 lp.objective_name_ = model.objective().name();
795 const int num_vars = model.variables().ids_size();
796 lp.num_col_ = num_vars;
804 bool has_integer_var =
false;
805 for (
const bool is_integer : model.variables().integers()) {
807 has_integer_var =
true;
812 absl::flat_hash_map<int64_t, IndexAndBound> variable_data;
813 for (
int i = 0; i < num_vars; ++i) {
814 const double raw_lb = model.variables().lower_bounds(i);
815 const double raw_ub = model.variables().upper_bounds(i);
816 const IndexAndBound index_and_bound(i, raw_lb,
818 model.variables().integers(i));
819 variable_data.try_emplace(model.variables().ids(i), index_and_bound);
820 lp.col_names_.push_back(
821 model.variables().names_size() > 0 ? model.variables().names(i) :
"");
829 if (index_and_bound.rounded_bounds_cross()) {
830 lp.col_lower_.push_back(0.0);
831 lp.col_upper_.push_back(0.0);
835 lp.col_lower_.push_back(index_and_bound.rounded_lb());
836 lp.col_upper_.push_back(index_and_bound.rounded_ub());
838 if (has_integer_var) {
839 lp.integrality_.push_back(model.variables().integers(i)
840 ? HighsVarType::kInteger
841 : HighsVarType::kContinuous);
844 lp.offset_ = model.objective().offset();
846 model.objective().maximize() ? ObjSense::kMaximize : ObjSense::kMinimize;
847 lp.col_cost_.resize(num_vars);
848 for (
const auto [var_id, lin_obj] :
849 MakeView(model.objective().linear_coefficients())) {
850 lp.col_cost_[variable_data.at(var_id).index] = lin_obj;
853 const int num_lin_cons = model.linear_constraints().ids_size();
854 lp.num_row_ = num_lin_cons;
855 absl::flat_hash_map<int64_t, IndexAndBound> lin_con_data;
856 for (
int i = 0; i < num_lin_cons; ++i) {
857 const double lb = model.linear_constraints().lower_bounds(i);
858 const double ub = model.linear_constraints().upper_bounds(i);
859 lin_con_data.try_emplace(model.linear_constraints().ids(i),
860 IndexAndBound(i, lb, ub,
862 lp.row_names_.push_back(model.linear_constraints().names_size() > 0
863 ? model.linear_constraints().names(i)
868 lp.row_lower_.push_back(0.0);
869 lp.row_upper_.push_back(0.0);
871 lp.row_lower_.push_back(lb);
872 lp.row_upper_.push_back(ub);
875 lp.a_matrix_.format_ = MatrixFormat::kRowwise;
876 lp.a_matrix_.num_col_ = num_vars;
877 lp.a_matrix_.num_row_ = num_lin_cons;
878 lp.a_matrix_.start_.clear();
879 const SparseDoubleMatrixProto& lin_con_mat = model.linear_constraint_matrix();
881 for (
int highs_con = 0; highs_con < lin_con_data.size(); highs_con++) {
882 lp.a_matrix_.start_.push_back(mat_index);
883 while (mat_index < lin_con_mat.row_ids_size() &&
884 lin_con_data.at(lin_con_mat.row_ids(mat_index)).index <= highs_con) {
888 lp.a_matrix_.start_.push_back(lin_con_mat.row_ids_size());
889 for (
int i = 0; i < lin_con_mat.row_ids_size(); ++i) {
890 const int var = variable_data.at(lin_con_mat.column_ids(i)).index;
891 const double coef = lin_con_mat.coefficients(i);
892 lp.a_matrix_.index_.push_back(var);
893 lp.a_matrix_.value_.push_back(coef);
895 auto highs = std::make_unique<Highs>();
898 HighsOptions disable_output;
899 disable_output.output_flag =
false;
900 disable_output.log_to_console =
false;
903 return absl::WrapUnique(
new HighsSolver(
904 std::move(highs), std::move(variable_data), std::move(lin_con_data)));
908 const SolveParametersProto& parameters,
909 const ModelSolveParametersProto& model_parameters,
913 model_parameters, kHighsSupportedStructures,
"Highs"));
914 const absl::Time start = absl::Now();
915 auto set_solve_time = [&start](SolveResultProto& result) -> absl::Status {
916 const absl::Duration solve_time = absl::Now() - start;
919 _ <<
"error encoding solve_stats.solve_time");
920 return absl::OkStatus();
926 const bool is_maximize = highs_->getModel().lp_.sense_ == ObjSense::kMaximize;
927 for (
const auto& [var_id, bounds] : variable_data_) {
928 if (bounds.rounded_bounds_cross()) {
929 SolveResultProto result =
939 highs_->setLogCallback(&HighsLogCallback, &buffered_message_callback)))
940 <<
"failed to register logging callback";
942 auto message_cb_cleanup =
943 absl::MakeCleanup([
this, &buffered_message_callback]() {
952 CHECK_OK(ToStatus(highs_->setLogCallback(
nullptr,
nullptr)));
953 buffered_message_callback.
Flush();
957 bool is_integer =
false;
959 for (
const HighsVarType var_type : highs_->getModel().lp_.integrality_) {
960 if (var_type == HighsVarType::kInteger) {
965 auto it = parameters.highs().bool_options().find(
"solve_relaxation");
966 if (it != parameters.highs().bool_options().end() && it->second) {
970 const std::unique_ptr<HighsOptions> options,
971 MakeOptions(parameters,
976 std::move(message_cb_cleanup).Invoke();
978 if (highs_->getModelStatus() == HighsModelStatus::kModelEmpty) {
979 SolveResultProto result = ResultForHighsModelStatusModelEmpty(
980 is_maximize, highs_->getModel().lp_.offset_, lin_con_data_);
984 const HighsInfo& info = highs_->getInfo();
986 return absl::InternalError(
"HighsInfo not valid");
989 SolveResultProto result;
991 ExtractSolutionAndRays(model_parameters));
992 for (SolutionProto&
solution : solutions_and_claims.solutions) {
993 *result.add_solutions() = std::move(
solution);
996 MakeTermination(highs_->getModelStatus(), info, is_integer,
997 parameters.has_node_limit(),
998 parameters.has_solution_limit(), is_maximize,
999 solutions_and_claims.solution_claims));
1011absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
1015 return absl::UnimplementedError(
1016 "HiGHS does not provide a method to compute an infeasible subsystem");
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
void OnMessage(absl::string_view message)
bool has_user_message_callback() const
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, const SolveInterrupter *interrupter) override
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto ¶meters, MessageCallback message_cb, const SolveInterrupter *interrupter) override
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
An object oriented wrapper for quadratic constraints in ModelStorage.
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
absl::Status ModelSolveParametersAreSupported(const ModelSolveParametersProto &model_parameters, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
TerminationProto OptimalTerminationProto(const double finite_primal_objective, const double dual_objective, const absl::string_view detail)
TerminationProto LimitTerminationProto(const bool is_maximize, const LimitProto limit, const std::optional< double > optional_finite_primal_objective, const std::optional< double > optional_dual_objective, const absl::string_view detail)
TerminationProto InfeasibleOrUnboundedTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
TerminationProto InfeasibleTerminationProto(bool is_maximize, const FeasibilityStatusProto dual_feasibility_status, const absl::string_view detail)
TerminationProto UnboundedTerminationProto(const bool is_maximize, const absl::string_view detail)
std::vector< K > SortedMapKeys(const absl::flat_hash_map< K, V > &in_map)
void ApplyAllFilters(const ModelSolveParametersProto &model_solve_params, SolutionProto &solution)
SolveResultProto ResultForIntegerInfeasible(const bool is_maximize, const int64_t bad_variable_id, const double lb, const double ub)
dual_gradient T(y - `dual_solution`) class DiagonalTrustRegionProblemFromQp
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)
Initialization arguments.
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)