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 "util/HighsInt.h"
74constexpr absl::string_view kOutputFlag =
"output_flag";
75constexpr absl::string_view kLogToConsole =
"log_to_console";
77constexpr SupportedProblemStructures kHighsSupportedStructures = {
81absl::Status ToStatus(
const HighsStatus
status) {
83 case HighsStatus::kOk:
84 return absl::OkStatus();
85 case HighsStatus::kWarning:
90 return absl::OkStatus();
91 case HighsStatus::kError:
95 <<
"unexpected HighsStatus: " <<
static_cast<int>(
status);
99absl::Status ToStatus(
const OptionStatus option_status) {
100 switch (option_status) {
101 case OptionStatus::kOk:
102 return absl::OkStatus();
103 case OptionStatus::kUnknownOption:
104 return absl::InvalidArgumentError(
"option name was unknown");
105 case OptionStatus::kIllegalValue:
108 return absl::InvalidArgumentError(
"option value not valid for name");
111 <<
"unexpected option_status: " <<
static_cast<int>(option_status);
114absl::StatusOr<int> SafeIntCast(
const int64_t i,
const absl::string_view
name) {
115 if constexpr (
sizeof(int) >=
sizeof(int64_t)) {
116 return static_cast<int>(
i);
118 const int64_t kMin =
static_cast<int64_t
>(std::numeric_limits<int>::min());
119 const int64_t kMax =
static_cast<int64_t
>(std::numeric_limits<int>::max());
120 if (i < kMin || i > kMax) {
122 <<
name <<
" has value " <<
i
123 <<
" not representable as an int (the range [" << kMin <<
", "
124 << kMax <<
"]) and thus is not supported for HiGHS";
126 return static_cast<int>(
i);
131int64_t CastInt64StaticAssert(
const T
value) {
132 static_assert(std::is_integral_v<T>);
133 static_assert(
sizeof(T) <=
sizeof(int64_t));
134 return static_cast<int64_t
>(
value);
139absl::StatusOr<std::unique_ptr<HighsOptions>> MakeOptions(
140 const SolveParametersProto&
parameters,
const bool has_log_callback,
141 const bool is_integer) {
143 auto result = std::make_unique<HighsOptions>();
145 if (
parameters.highs().bool_options().contains(kOutputFlag)) {
146 result->output_flag =
parameters.highs().bool_options().at(kOutputFlag);
148 result->output_flag =
parameters.enable_output() || has_log_callback;
159 if (
parameters.highs().bool_options().contains(kLogToConsole)) {
160 result->log_to_console =
161 parameters.highs().bool_options().at(kLogToConsole);
163 result->log_to_console = result->output_flag;
169 _ <<
"invalid time_limit value for HiGHS.");
170 result->time_limit = absl::ToDoubleSeconds(
time_limit);
175 <<
"iteration_limit not supported for HiGHS on problems with "
179 const int iter_limit,
180 SafeIntCast(
parameters.iteration_limit(),
"iteration_limit"));
182 result->simplex_iteration_limit = iter_limit;
183 result->ipm_iteration_limit = iter_limit;
187 SafeIntCast(
parameters.node_limit(),
"node_limit"));
192 return absl::InvalidArgumentError(
"cutoff_limit not supported for HiGHS");
197 <<
"objective_limit not supported for HiGHS solver on integer "
202 return absl::InvalidArgumentError(
203 "objective_limit for LP appears to have a missing/broken HiGHS "
204 "implementation, see b/271616762");
210 <<
"best_bound_limit not supported for HiGHS solver on integer "
213 result->objective_bound =
parameters.best_bound_limit();
217 result->mip_max_improving_sols =
parameters.solution_limit();
224 <<
"threads not supported for HiGHS solver, this must be set using "
225 "globals, see HiGHS documentation";
228 result->random_seed =
parameters.random_seed();
230 if (
parameters.has_absolute_gap_tolerance()) {
231 result->mip_abs_gap =
parameters.absolute_gap_tolerance();
233 if (
parameters.has_relative_gap_tolerance()) {
234 result->mip_rel_gap =
parameters.relative_gap_tolerance();
238 <<
"solution_pool_size not supported for HiGHS";
240 if (
parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
243 <<
"lp_algorithm is not supported for HiGHS on problems with "
247 case LP_ALGORITHM_PRIMAL_SIMPLEX:
248 result->solver = ::kSimplexString;
249 result->simplex_strategy = ::kSimplexStrategyPrimal;
251 case LP_ALGORITHM_DUAL_SIMPLEX:
252 result->solver = ::kSimplexString;
253 result->simplex_strategy = ::kSimplexStrategyDual;
255 case LP_ALGORITHM_BARRIER:
256 result->solver = ::kIpmString;
260 <<
"unsupported lp_algorithm: "
261 << LPAlgorithmProto_Name(
parameters.lp_algorithm());
264 if (
parameters.presolve() != EMPHASIS_UNSPECIFIED) {
266 result->presolve = ::kHighsOffString;
268 result->presolve = ::kHighsOnString;
271 if (
parameters.cuts() != EMPHASIS_UNSPECIFIED) {
273 <<
"cuts solve parameter unsupported for HiGHS";
275 if (
parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
278 result->mip_heuristic_effort = 0.0;
281 result->mip_heuristic_effort = 0.025;
283 case EMPHASIS_MEDIUM:
284 result->mip_heuristic_effort = 0.05;
287 result->mip_heuristic_effort = 0.1;
289 case EMPHASIS_VERY_HIGH:
290 result->mip_heuristic_effort = 0.2;
294 <<
"unexpected value for solve_parameters.heuristics of: "
298 if (
parameters.scaling() != EMPHASIS_UNSPECIFIED) {
301 result->simplex_scale_strategy = ::kSimplexScaleStrategyOff;
305 if (
name == kOutputFlag ||
name == kLogToConsole) {
312 result->records,
value)))
313 <<
"error setting string option name: " <<
name
314 <<
" to value:" <<
value;
318 setLocalOptionValue(result->log_options,
name, result->records,
value)))
319 <<
"error setting double option name: " <<
name
320 <<
" to value:" <<
value;
324 setLocalOptionValue(result->log_options,
name, result->records,
value)))
325 <<
"error setting int option name: " <<
name <<
" to value:" <<
value;
329 setLocalOptionValue(result->log_options,
name, result->records,
value)))
330 <<
"error setting bool option name: " <<
name <<
" to value:" <<
value;
335double DualObjective(
const HighsInfo& highs_info,
const bool is_integer) {
338 return is_integer ? highs_info.mip_dual_bound
339 : highs_info.objective_function_value;
343void HighsLogCallback(HighsLogType,
const char*
const message,
344 void*
const log_callback_data) {
345 BufferedMessageCallback& buffered_callback =
346 *
static_cast<BufferedMessageCallback*
>(log_callback_data);
347 buffered_callback.OnMessage(
message);
351absl::StatusOr<SolveStatsProto> ToSolveStats(
const HighsInfo& highs_info) {
352 SolveStatsProto result;
358 result.set_simplex_iterations(std::max(
359 int64_t{0}, CastInt64StaticAssert(highs_info.simplex_iteration_count)));
360 result.set_barrier_iterations(std::max(
361 int64_t{0}, CastInt64StaticAssert(highs_info.ipm_iteration_count)));
362 result.set_node_count(std::max(int64_t{0}, highs_info.mip_node_count));
368absl::StatusOr<std::optional<BasisStatusProto>> ToBasisStatus(
369 const HighsBasisStatus highs_basis,
const double lb,
const double ub,
370 const std::optional<double>
value) {
371 switch (highs_basis) {
372 case HighsBasisStatus::kBasic:
373 return BASIS_STATUS_BASIC;
374 case HighsBasisStatus::kUpper:
375 return BASIS_STATUS_AT_UPPER_BOUND;
376 case HighsBasisStatus::kLower:
380 return BASIS_STATUS_AT_LOWER_BOUND;
381 case HighsBasisStatus::kZero:
382 return BASIS_STATUS_FREE;
386 case HighsBasisStatus::kNonbasic: {
387 const bool lb_finite = std::isfinite(lb);
388 const bool ub_finite = std::isfinite(ub);
392 constexpr double kAtBoundTolerance = 1.0e-10;
393 if (lb_finite && ub_finite) {
395 return BASIS_STATUS_FIXED_VALUE;
396 }
else if (
value.has_value() &&
397 std::abs(lb - *
value) < kAtBoundTolerance) {
398 return BASIS_STATUS_AT_LOWER_BOUND;
399 }
else if (
value.has_value() &&
400 std::abs(ub - *
value) < kAtBoundTolerance) {
401 return BASIS_STATUS_AT_UPPER_BOUND;
406 }
else if (lb_finite) {
407 return BASIS_STATUS_AT_LOWER_BOUND;
408 }
else if (ub_finite) {
409 return BASIS_STATUS_AT_LOWER_BOUND;
411 return BASIS_STATUS_FREE;
416 <<
"unexpected highs basis: " <<
static_cast<int>(highs_basis);
419absl::StatusOr<SolutionStatusProto> ToSolutionStatus(
420 const HighsInt highs_solution_status) {
421 switch (highs_solution_status) {
422 case ::kSolutionStatusInfeasible:
423 return SOLUTION_STATUS_INFEASIBLE;
424 case ::kSolutionStatusFeasible:
425 return SOLUTION_STATUS_FEASIBLE;
426 case ::kSolutionStatusNone:
427 return SOLUTION_STATUS_UNDETERMINED;
430 <<
"unimplemented highs SolutionStatus: " << highs_solution_status;
435absl::StatusOr<FeasibilityStatusProto> HighsSolver::DualFeasibilityStatus(
436 const HighsInfo& highs_info,
const bool is_integer,
437 const SolutionClaims solution_claims) {
438 const bool dual_feasible_solution_exists =
439 solution_claims.highs_returned_dual_feasible_solution ||
440 (is_integer && std::isfinite(highs_info.mip_dual_bound));
441 if (dual_feasible_solution_exists &&
442 solution_claims.highs_returned_primal_ray) {
444 <<
"Found dual feasible solution and primal ray";
446 if (dual_feasible_solution_exists) {
447 return FEASIBILITY_STATUS_FEASIBLE;
449 if (solution_claims.highs_returned_primal_ray) {
450 return FEASIBILITY_STATUS_INFEASIBLE;
452 return FEASIBILITY_STATUS_UNDETERMINED;
455absl::StatusOr<FeasibilityStatusProto> HighsSolver::PrimalFeasibilityStatus(
456 const SolutionClaims solution_claims) {
457 if (solution_claims.highs_returned_primal_feasible_solution &&
458 solution_claims.highs_returned_dual_ray) {
460 <<
"Found primal feasible solution and dual ray";
462 if (solution_claims.highs_returned_primal_feasible_solution) {
463 return FEASIBILITY_STATUS_FEASIBLE;
465 if (solution_claims.highs_returned_dual_ray) {
466 return FEASIBILITY_STATUS_INFEASIBLE;
468 return FEASIBILITY_STATUS_UNDETERMINED;
471absl::StatusOr<TerminationProto> HighsSolver::MakeTermination(
472 const HighsModelStatus highs_model_status,
const HighsInfo& highs_info,
473 const bool is_integer,
const bool had_node_limit,
474 const bool had_solution_limit,
const bool is_maximize,
475 const SolutionClaims solution_claims) {
477 const FeasibilityStatusProto dual_feasibility_status,
478 DualFeasibilityStatus(highs_info, is_integer, solution_claims));
480 PrimalFeasibilityStatus(solution_claims));
482 const std::optional<double> optional_finite_primal_objective =
483 (primal_feasibility_status == FEASIBILITY_STATUS_FEASIBLE)
484 ? std::make_optional(highs_info.objective_function_value)
486 const std::optional<double> optional_dual_objective =
487 (dual_feasibility_status == FEASIBILITY_STATUS_FEASIBLE)
488 ? std::make_optional(DualObjective(highs_info, is_integer))
490 switch (highs_model_status) {
491 case HighsModelStatus::kNotset:
492 case HighsModelStatus::kLoadError:
493 case HighsModelStatus::kModelError:
494 case HighsModelStatus::kPresolveError:
495 case HighsModelStatus::kSolveError:
496 case HighsModelStatus::kPostsolveError:
497 case HighsModelStatus::kUnknown:
500 case HighsModelStatus::kModelEmpty:
502 <<
"HighsModelStatus was "
503 << utilModelStatusToString(highs_model_status);
504 case HighsModelStatus::kOptimal: {
506 DualObjective(highs_info, is_integer),
507 "HighsModelStatus is kOptimal");
509 case HighsModelStatus::kInfeasible:
513 ? FEASIBILITY_STATUS_FEASIBLE
514 : dual_feasibility_status);
515 case HighsModelStatus::kUnboundedOrInfeasible:
517 is_maximize, dual_feasibility_status,
518 "HighsModelStatus is kUnboundedOrInfeasible");
519 case HighsModelStatus::kUnbounded: {
524 if (highs_info.primal_solution_status == ::kSolutionStatusFeasible) {
529 FEASIBILITY_STATUS_INFEASIBLE,
530 "HighsModelStatus is kUnbounded");
533 case HighsModelStatus::kObjectiveBound:
535 is_maximize, LIMIT_OBJECTIVE, optional_finite_primal_objective,
536 optional_dual_objective,
"HighsModelStatus is kObjectiveBound");
537 case HighsModelStatus::kObjectiveTarget:
539 is_maximize, LIMIT_OBJECTIVE, optional_finite_primal_objective,
540 optional_dual_objective,
"HighsModelStatus is kObjectiveTarget");
541 case HighsModelStatus::kTimeLimit:
543 optional_finite_primal_objective,
544 optional_dual_objective);
545 case HighsModelStatus::kIterationLimit: {
547 if (had_node_limit && had_solution_limit) {
549 is_maximize, LIMIT_UNDETERMINED, optional_finite_primal_objective,
550 optional_dual_objective,
551 "Both node limit and solution limit were requested, cannot "
552 "determine reason for termination");
553 }
else if (had_node_limit) {
555 optional_finite_primal_objective,
556 optional_dual_objective);
557 }
else if (had_solution_limit) {
559 optional_finite_primal_objective,
560 optional_dual_objective);
566 optional_finite_primal_objective,
567 optional_dual_objective);
572 <<
static_cast<int>(highs_model_status);
575SolveResultProto HighsSolver::ResultForHighsModelStatusModelEmpty(
576 const bool is_maximize,
const double objective_offset,
577 const absl::flat_hash_map<int64_t, IndexAndBound>& lin_con_data) {
578 SolveResultProto result;
579 bool feasible =
true;
580 for (
const auto& [unused, lin_con_bounds] : lin_con_data) {
581 if (lin_con_bounds.lb > 0 || lin_con_bounds.ub < 0) {
586 result.mutable_termination()->set_reason(
587 feasible ? TERMINATION_REASON_OPTIMAL : TERMINATION_REASON_INFEASIBLE);
588 result.mutable_termination()->set_detail(
"HighsModelStatus was kEmptyModel");
590 auto solution = result.add_solutions()->mutable_primal_solution();
591 solution->set_objective_value(objective_offset);
592 solution->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
593 *result.mutable_termination() =
598 *result.mutable_termination() =
600 FEASIBILITY_STATUS_FEASIBLE);
606InvertedBounds HighsSolver::ListInvertedBounds() {
607 const auto find_crossed =
608 [](
const absl::flat_hash_map<int64_t, IndexAndBound>& id_to_bound_data) {
609 std::vector<int64_t> result;
610 for (
const auto& [
id, bound_data] : id_to_bound_data) {
611 if (bound_data.bounds_cross()) {
612 result.push_back(
id);
615 absl::c_sort(result);
618 return {.variables = find_crossed(variable_data_),
619 .linear_constraints = find_crossed(lin_con_data_)};
622absl::StatusOr<std::optional<BasisProto>> HighsSolver::ExtractBasis() {
623 const HighsInfo& highs_info = highs_->getInfo();
624 const HighsBasis& highs_basis = highs_->getBasis();
625 const HighsSolution& highs_solution = highs_->getSolution();
626 if (highs_info.basis_validity != ::kBasisValidityValid) {
631 if (!highs_solution.value_valid || !highs_solution.dual_valid) {
636 <<
"invalid highs_solution.col_value";
638 <<
"invalid highs_solution.col_dual";
641 <<
"invalid highs_basis.col_status";
642 RETURN_IF_ERROR(EnsureOneEntryPerLinearConstraint(highs_basis.row_status))
643 <<
"invalid highs_basis.row_status";
646 if (highs_->getModelStatus() == HighsModelStatus::kOptimal) {
647 basis.set_basic_dual_feasibility(SOLUTION_STATUS_FEASIBLE);
648 }
else if (highs_info.dual_solution_status == kSolutionStatusInfeasible) {
649 basis.set_basic_dual_feasibility(SOLUTION_STATUS_INFEASIBLE);
652 basis.set_basic_dual_feasibility(SOLUTION_STATUS_UNDETERMINED);
655 const IndexAndBound& index_and_bounds = variable_data_.at(var_id);
656 const double var_value = highs_solution.col_value[index_and_bounds.index];
658 const std::optional<BasisStatusProto>
status,
659 ToBasisStatus(highs_basis.col_status[variable_data_.at(var_id).index],
660 index_and_bounds.lb, index_and_bounds.ub, var_value),
661 _ <<
"invalid highs_basis.col_status for variable with id: " << var_id);
662 if (!
status.has_value()) {
665 basis.mutable_variable_status()->add_ids(var_id);
666 basis.mutable_variable_status()->add_values(*
status);
668 for (
const int64_t lin_con_id :
SortedMapKeys(lin_con_data_)) {
669 const IndexAndBound& index_and_bounds = lin_con_data_.at(lin_con_id);
670 const double dual_value = highs_solution.row_dual[index_and_bounds.index];
672 const std::optional<BasisStatusProto>
status,
673 ToBasisStatus(highs_basis.row_status[index_and_bounds.index],
674 index_and_bounds.lb, index_and_bounds.ub, dual_value),
675 _ <<
"invalid highs_basis.row_status for linear constraint with id: "
677 if (!
status.has_value()) {
680 basis.mutable_constraint_status()->add_ids(lin_con_id);
681 basis.mutable_constraint_status()->add_values(*
status);
686absl::StatusOr<bool> HighsSolver::PrimalRayReturned()
const {
687 if (!highs_->hasInvert()) {
690 bool has_primal_ray =
false;
695 return has_primal_ray;
698absl::StatusOr<bool> HighsSolver::DualRayReturned()
const {
699 if (!highs_->hasInvert()) {
702 bool has_dual_ray =
false;
710absl::StatusOr<HighsSolver::SolutionsAndClaims>
711HighsSolver::ExtractSolutionAndRays(
712 const ModelSolveParametersProto& model_params) {
713 const HighsInfo& highs_info = highs_->getInfo();
714 const HighsSolution& highs_solution = highs_->getSolution();
715 SolutionsAndClaims solution_and_claims;
716 if (highs_info.primal_solution_status == ::kSolutionStatusFeasible &&
717 !highs_solution.value_valid) {
718 return absl::InternalError(
719 "highs_info.primal_solution_status==::kSolutionStatusFeasible, but no "
720 "valid primal solution returned");
722 if (highs_solution.value_valid || highs_solution.dual_valid) {
724 solution_and_claims.solutions.emplace_back(SolutionProto());
725 if (highs_solution.value_valid) {
727 <<
"invalid highs_solution.col_value";
729 *
solution.mutable_primal_solution();
730 primal_solution.set_objective_value(highs_info.objective_function_value);
732 ToSolutionStatus(highs_info.primal_solution_status),
733 _ <<
"invalid highs_info.primal_solution_status");
735 solution_and_claims.solution_claims
736 .highs_returned_primal_feasible_solution =
741 highs_solution.col_value[variable_data_.at(var_id).index]);
744 if (highs_solution.dual_valid) {
746 <<
"invalid highs_solution.col_dual";
748 EnsureOneEntryPerLinearConstraint(highs_solution.row_dual))
749 <<
"invalid highs_solution.row_dual";
750 DualSolutionProto& dual_solution = *
solution.mutable_dual_solution();
751 dual_solution.set_objective_value(highs_info.objective_function_value);
753 ToSolutionStatus(highs_info.dual_solution_status),
754 _ <<
"invalid highs_info.dual_solution_status");
755 dual_solution.set_feasibility_status(dual_solution_status);
756 solution_and_claims.solution_claims
757 .highs_returned_dual_feasible_solution =
758 dual_solution.feasibility_status() == SOLUTION_STATUS_FEASIBLE;
760 dual_solution.mutable_reduced_costs()->add_ids(var_id);
761 dual_solution.mutable_reduced_costs()->add_values(
762 highs_solution.col_dual[variable_data_.at(var_id).index]);
764 for (
const int64_t lin_con_id :
SortedMapKeys(lin_con_data_)) {
765 dual_solution.mutable_dual_values()->add_ids(lin_con_id);
766 dual_solution.mutable_dual_values()->add_values(
767 highs_solution.row_dual[lin_con_data_.at(lin_con_id).index]);
771 HighsSolver::ExtractBasis());
772 if (basis_proto.has_value()) {
773 *
solution.mutable_basis() = *std::move(basis_proto);
779 solution_and_claims.solution_claims.highs_returned_primal_ray,
780 PrimalRayReturned());
781 ASSIGN_OR_RETURN(solution_and_claims.solution_claims.highs_returned_dual_ray,
784 return solution_and_claims;
790 HighsModel highs_model;
791 HighsLp& lp = highs_model.lp_;
792 lp.model_name_ =
model.name();
793 lp.objective_name_ =
model.objective().name();
794 const int num_vars =
model.variables().ids_size();
795 lp.num_col_ = num_vars;
803 bool has_integer_var =
false;
804 for (
const bool is_integer :
model.variables().integers()) {
806 has_integer_var =
true;
811 absl::flat_hash_map<int64_t, IndexAndBound> variable_data;
812 for (
int i = 0; i < num_vars; ++i) {
813 const double raw_lb =
model.variables().lower_bounds(i);
814 const double raw_ub =
model.variables().upper_bounds(i);
815 const IndexAndBound index_and_bound(i, raw_lb,
817 model.variables().integers(i));
818 variable_data.try_emplace(
model.variables().ids(i), index_and_bound);
819 lp.col_names_.push_back(
820 model.variables().names_size() > 0 ?
model.variables().names(i) :
"");
828 if (index_and_bound.rounded_bounds_cross()) {
829 lp.col_lower_.push_back(0.0);
830 lp.col_upper_.push_back(0.0);
834 lp.col_lower_.push_back(index_and_bound.rounded_lb());
835 lp.col_upper_.push_back(index_and_bound.rounded_ub());
837 if (has_integer_var) {
838 lp.integrality_.push_back(
model.variables().integers(i)
839 ? HighsVarType::kInteger
840 : HighsVarType::kContinuous);
843 lp.offset_ =
model.objective().offset();
845 model.objective().maximize() ? ObjSense::kMaximize : ObjSense::kMinimize;
846 lp.col_cost_.resize(num_vars);
847 for (
const auto [var_id, lin_obj] :
849 lp.col_cost_[variable_data.at(var_id).index] = lin_obj;
852 const int num_lin_cons =
model.linear_constraints().ids_size();
853 lp.num_row_ = num_lin_cons;
854 absl::flat_hash_map<int64_t, IndexAndBound> lin_con_data;
855 for (
int i = 0; i < num_lin_cons; ++i) {
856 const double lb =
model.linear_constraints().lower_bounds(i);
857 const double ub =
model.linear_constraints().upper_bounds(i);
858 lin_con_data.try_emplace(
model.linear_constraints().ids(i),
859 IndexAndBound(i, lb, ub,
861 lp.row_names_.push_back(
model.linear_constraints().names_size() > 0
862 ?
model.linear_constraints().names(i)
867 lp.row_lower_.push_back(0.0);
868 lp.row_upper_.push_back(0.0);
870 lp.row_lower_.push_back(lb);
871 lp.row_upper_.push_back(ub);
874 lp.a_matrix_.format_ = MatrixFormat::kRowwise;
875 lp.a_matrix_.num_col_ = num_vars;
876 lp.a_matrix_.num_row_ = num_lin_cons;
877 lp.a_matrix_.start_.clear();
878 const SparseDoubleMatrixProto& lin_con_mat =
model.linear_constraint_matrix();
880 for (
int highs_con = 0; highs_con < lin_con_data.size(); highs_con++) {
881 lp.a_matrix_.start_.push_back(mat_index);
882 while (mat_index < lin_con_mat.row_ids_size() &&
883 lin_con_data.at(lin_con_mat.row_ids(mat_index)).index <= highs_con) {
887 lp.a_matrix_.start_.push_back(lin_con_mat.row_ids_size());
888 for (
int i = 0; i < lin_con_mat.row_ids_size(); ++i) {
889 const int var = variable_data.at(lin_con_mat.column_ids(i)).index;
890 const double coef = lin_con_mat.coefficients(i);
891 lp.a_matrix_.index_.push_back(
var);
892 lp.a_matrix_.value_.push_back(
coef);
894 auto highs = std::make_unique<Highs>();
897 HighsOptions disable_output;
898 disable_output.output_flag =
false;
899 disable_output.log_to_console =
false;
903 std::move(highs), std::move(variable_data), std::move(lin_con_data)));
908 const ModelSolveParametersProto& model_parameters,
911 const absl::Time
start = absl::Now();
912 auto set_solve_time = [&
start](SolveResultProto& result) -> absl::Status {
913 const absl::Duration solve_time = absl::Now() -
start;
916 _ <<
"error encoding solve_stats.solve_time");
917 return absl::OkStatus();
923 const bool is_maximize = highs_->getModel().lp_.sense_ == ObjSense::kMaximize;
924 for (
const auto& [var_id, bounds] : variable_data_) {
925 if (bounds.rounded_bounds_cross()) {
926 SolveResultProto result =
936 highs_->setLogCallback(&HighsLogCallback, &buffered_message_callback)))
937 <<
"failed to register logging callback";
939 auto message_cb_cleanup =
940 absl::MakeCleanup([
this, &buffered_message_callback]() {
949 CHECK_OK(ToStatus(highs_->setLogCallback(
nullptr,
nullptr)));
950 buffered_message_callback.
Flush();
954 bool is_integer =
false;
956 for (
const HighsVarType var_type : highs_->getModel().lp_.integrality_) {
957 if (var_type == HighsVarType::kInteger) {
962 auto it =
parameters.highs().bool_options().find(
"solve_relaxation");
963 if (it !=
parameters.highs().bool_options().end() && it->second) {
967 const std::unique_ptr<HighsOptions> options,
973 std::move(message_cb_cleanup).Invoke();
975 if (highs_->getModelStatus() == HighsModelStatus::kModelEmpty) {
976 SolveResultProto result = ResultForHighsModelStatusModelEmpty(
977 is_maximize, highs_->getModel().lp_.offset_, lin_con_data_);
981 const HighsInfo& info = highs_->getInfo();
983 return absl::InternalError(
"HighsInfo not valid");
986 SolveResultProto result;
988 ExtractSolutionAndRays(model_parameters));
989 for (SolutionProto&
solution : solutions_and_claims.solutions) {
990 *result.add_solutions() = std::move(
solution);
993 MakeTermination(highs_->getModelStatus(), info, is_integer,
996 solutions_and_claims.solution_claims));
1008absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
1012 return absl::UnimplementedError(
1013 "HiGHS does not provide a method to compute an infeasible subsystem");
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
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
const std::string name
A name for logging purposes.
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)
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)
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)