Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
highs_solver.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_MATH_OPT_SOLVERS_HIGHS_SOLVER_H_
15#define ORTOOLS_MATH_OPT_SOLVERS_HIGHS_SOLVER_H_
16
17#include <cmath>
18#include <cstdint>
19#include <memory>
20#include <optional>
21#include <utility>
22#include <vector>
23
24#include "Highs.h"
25#include "absl/base/nullability.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/status/status.h"
28#include "absl/status/statusor.h"
29#include "lp_data/HConst.h"
30#include "lp_data/HighsInfo.h"
42
44
45class HighsSolver : public SolverInterface {
46 public:
47 static absl::StatusOr<std::unique_ptr<SolverInterface>> New(
48 const ModelProto& model, const InitArgs& init_args);
49
50 absl::StatusOr<SolveResultProto> Solve(
51 const SolveParametersProto& parameters,
52 const ModelSolveParametersProto& model_parameters,
53 MessageCallback message_cb,
54 const CallbackRegistrationProto& callback_registration, Callback cb,
55 const SolveInterrupter* absl_nullable interrupter) override;
56 absl::StatusOr<bool> Update(const ModelUpdateProto& model_update) override;
57 absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
59 const SolveParametersProto& parameters, MessageCallback message_cb,
60 const SolveInterrupter* absl_nullable interrupter) override;
61
62 private:
63 struct SolutionClaims {
64 bool highs_returned_primal_feasible_solution = false;
65 bool highs_returned_dual_feasible_solution = false;
66 bool highs_returned_primal_ray = false;
67 bool highs_returned_dual_ray = false;
68 };
69 struct SolutionsAndClaims {
70 std::vector<SolutionProto> solutions;
71 // TODO(b/271104776): add rays.
72 SolutionClaims solution_claims;
73 };
74
75 // Tracks the upper and lower bounds for either a variable or linear
76 // constraint in the HiGHS model.
77 //
78 // Note that HiGHS does not allow bounds to cross. If a bound would cross, it
79 // is set to zero in the actual HiGHS model and its true values are tracked
80 // here (they may uncross before solve time on a model update).
81 struct IndexAndBound {
82 // The position of the variable/linear constraint in the HiGHS model. Note
83 // that this is distinct from the MathOpt id.
84 int index;
85 double lb;
86 double ub;
87
88 // TODO(b/271595607): we won't need to track this once a bug in HiGHS is
89 // fixed. Always false for constraints.
90 bool is_integer;
91
92 bool bounds_cross() const { return lb > ub; }
93
94 // If we don't round the bounds for integer variables, HiGHS can give
95 // garbage results, see go/paste/4836036214521856 for details. See also
96 // b/271595607.
97 double rounded_lb() const { return is_integer ? std::ceil(lb) : lb; }
98 double rounded_ub() const { return is_integer ? std::floor(ub) : ub; }
99 bool rounded_bounds_cross() const { return rounded_lb() > rounded_ub(); }
100
101 IndexAndBound(int index, double lb, double ub, bool is_integer)
102 : index(index), lb(lb), ub(ub), is_integer(is_integer) {}
103 };
104 HighsSolver(std::unique_ptr<Highs> highs,
105 absl::flat_hash_map<int64_t, IndexAndBound> variable_data,
106 absl::flat_hash_map<int64_t, IndexAndBound> lin_con_data)
107 : highs_(std::move(highs)),
108 variable_data_(std::move(variable_data)),
109 lin_con_data_(std::move(lin_con_data)) {}
110
111 absl::StatusOr<bool> PrimalRayReturned() const;
112 absl::StatusOr<bool> DualRayReturned() const;
113
114 // Will append solutions and rays to result_out. No other modifications are
115 // made to result_out. Requires that highs_->getInfo is validated.
116 absl::StatusOr<SolutionsAndClaims> ExtractSolutionAndRays(
117 const ModelSolveParametersProto& model_params);
118
119 static absl::StatusOr<FeasibilityStatusProto> PrimalFeasibilityStatus(
120 SolutionClaims solution_claims);
121 static absl::StatusOr<FeasibilityStatusProto> DualFeasibilityStatus(
122 const HighsInfo& highs_info, bool is_integer,
123 SolutionClaims solution_claims);
124 static absl::StatusOr<TerminationProto> MakeTermination(
125 HighsModelStatus highs_model_status, const HighsInfo& highs_info,
126 bool is_integer, bool had_node_limit, bool had_solution_limit,
127 bool is_maximize, SolutionClaims solution_claims);
128
129 // Returns the current basis if it is available and MathOpt can represent it
130 // (all kNonBasic values can be made more precise, see b/272767311).
131 absl::StatusOr<std::optional<BasisProto>> ExtractBasis();
132
133 template <typename T>
134 absl::Status EnsureOneEntryPerVariable(const std::vector<T>& vec) {
135 if (vec.size() != variable_data_.size()) {
137 << "expected one entry per variable, but model had "
138 << variable_data_.size() << " variables and found " << vec.size()
139 << " elements";
140 }
141 return absl::OkStatus();
142 }
143
144 template <typename T>
145 absl::Status EnsureOneEntryPerLinearConstraint(const std::vector<T>& vec) {
146 if (vec.size() != lin_con_data_.size()) {
148 << "expected one entry per linear constraint, but model had "
149 << lin_con_data_.size() << " linear constraints and found "
150 << vec.size() << " elements";
151 }
152 return absl::OkStatus();
153 }
154
155 // Returns a SolveResult for when HiGHS returns the model status
156 // HighsModelStatus::kModelEmpty. This happens on models that have no
157 // variables, but may still have (potentially infeasible) linear constraints
158 // and an objective offset.
159 //
160 // Assumes that there are no inverted linear constraint bounds.
161 static SolveResultProto ResultForHighsModelStatusModelEmpty(
162 bool is_maximize, double objective_offset,
163 const absl::flat_hash_map<int64_t, IndexAndBound>& lin_con_data);
164
165 InvertedBounds ListInvertedBounds();
166
167 std::unique_ptr<Highs> highs_;
168
169 // Key is the mathopt id, value.index is the variable index in HiGHS.
170 absl::flat_hash_map<int64_t, IndexAndBound> variable_data_;
171
172 // Key is the mathopt id, value.index is the linear constraint index in HiGHS.
173 absl::flat_hash_map<int64_t, IndexAndBound> lin_con_data_;
174};
175
176} // namespace operations_research::math_opt
177
178#endif // ORTOOLS_MATH_OPT_SOLVERS_HIGHS_SOLVER_H_
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto &parameters, MessageCallback message_cb, const SolveInterrupter *absl_nullable interrupter) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, const SolveInterrupter *absl_nullable interrupter) override
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
StatusBuilder InvalidArgumentErrorBuilder()