Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
highs_proto_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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
15
16#include <cassert>
17#include <cmath>
18#include <limits>
19#include <string>
20#include <vector>
21
22#include "Highs.h"
23#include "absl/status/status.h"
24#include "absl/status/statusor.h"
25#include "absl/strings/str_cat.h"
26#include "absl/strings/str_join.h"
27#include "absl/strings/str_split.h"
28#include "absl/strings/string_view.h"
29#include "absl/time/clock.h"
30#include "absl/time/time.h"
31#include "absl/types/optional.h"
32#include "google/protobuf/repeated_field.h"
33#include "lp_data/HConst.h"
34#include "lp_data/HighsStatus.h"
35#include "ortools/base/timer.h"
36#include "ortools/linear_solver/linear_solver.pb.h"
39
40namespace operations_research {
41
42absl::Status SetSolverSpecificParameters(const std::string& parameters,
43 Highs& highs);
44
45absl::StatusOr<MPSolutionResponse> HighsSolveProto(
47 MPSolutionResponse response;
48 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
49 GetMPModelOrPopulateResponse(request, &response);
50 if (!optional_model) return response;
51 const MPModelProto& model = **optional_model;
52
53 Highs highs;
54 // TODO(user): Set model name.
55
56 if (request->has_solver_specific_parameters()) {
57 const auto parameters_status = SetSolverSpecificParameters(
58 request->solver_specific_parameters(), highs);
59 if (!parameters_status.ok()) {
60 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
61 response.set_status_str(parameters_status.message());
62 return response;
63 }
64 }
65
66 if (request->solver_time_limit_seconds() > 0) {
67 HighsStatus status = highs.setOptionValue(
68 "time_limit", request->solver_time_limit_seconds());
69 if (status == HighsStatus::kError) {
70 response.set_status(MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
71 response.set_status_str("time_limit");
72 return response;
73 }
74 }
75
76 const int variable_size = model.variable_size();
77 bool has_integer_variables = false;
78 {
79 std::vector<double> obj_coeffs(variable_size, 0);
80 std::vector<double> lb(variable_size);
81 std::vector<double> ub(variable_size);
82 std::vector<HighsVarType> integrality(variable_size);
83 for (int v = 0; v < variable_size; ++v) {
84 const MPVariableProto& variable = model.variable(v);
85 obj_coeffs[v] = variable.objective_coefficient();
86 lb[v] = variable.lower_bound();
87 ub[v] = variable.upper_bound();
88 integrality[v] =
89 variable.is_integer() &&
90 request->solver_type() ==
91 MPModelRequest::HIGHS_MIXED_INTEGER_PROGRAMMING
92 ? HighsVarType::kInteger
93 : HighsVarType::kContinuous;
94 if (integrality[v] == HighsVarType::kInteger) {
95 has_integer_variables = true;
96 }
97 }
98
99 highs.addVars(variable_size, lb.data(), ub.data());
100
101 // Mark integrality.
102 if (has_integer_variables) {
103 for (int v = 0; v < variable_size; ++v) {
104 highs.changeColIntegrality(v, integrality[v]);
105 }
106 }
107
108 // Objective coefficients.
109 for (int column = 0; column < variable_size; column++) {
110 highs.changeColCost(column, obj_coeffs[column]);
111 }
112
113 // TODO(user): Support variable names.
114 // TODO(user): Support hints.
115 }
116
117 {
118 for (int c = 0; c < model.constraint_size(); ++c) {
119 const MPConstraintProto& constraint = model.constraint(c);
120 if (constraint.lower_bound() ==
121 -std::numeric_limits<double>::infinity()) {
122 HighsStatus status =
123 highs.addRow(/*lhs=*/-kHighsInf,
124 /*rhs=*/constraint.upper_bound(),
125 /*numnz=*/constraint.var_index_size(),
126 /*cind=*/constraint.var_index().data(),
127 /*cval=*/constraint.coefficient().data());
128 if (status == HighsStatus::kError) {
129 response.set_status(MPSOLVER_MODEL_INVALID);
130 response.set_status_str("ct addRow");
131 return response;
132 }
133 } else if (constraint.upper_bound() ==
134 std::numeric_limits<double>::infinity()) {
135 HighsStatus status =
136 highs.addRow(/*lhs=*/constraint.lower_bound(),
137 /*rhs=*/kHighsInf,
138 /*numnz=*/constraint.var_index_size(),
139 /*cind=*/constraint.var_index().data(),
140 /*cval=*/constraint.coefficient().data());
141 if (status == HighsStatus::kError) {
142 response.set_status(MPSOLVER_MODEL_INVALID);
143 response.set_status_str("ct addRow");
144 return response;
145 }
146 } else {
147 HighsStatus status =
148 highs.addRow(/*lhs=*/constraint.lower_bound(),
149 /*rhs=*/constraint.upper_bound(),
150 /*numnz=*/constraint.var_index_size(),
151 /*cind=*/constraint.var_index().data(),
152 /*cval=*/constraint.coefficient().data());
153 if (status == HighsStatus::kError) {
154 response.set_status(MPSOLVER_MODEL_INVALID);
155 response.set_status_str("ct addRow");
156 return response;
157 }
158 }
159 // TODO(user): Support constraint names.
160 }
161
162 if (!model.general_constraint().empty()) {
163 response.set_status(MPSOLVER_MODEL_INVALID);
164 response.set_status_str("general constraints are not supported in Highs");
165 return response;
166 }
167 }
168
169 if (model.maximize()) {
170 const ObjSense pass_sense = ObjSense::kMaximize;
171 highs.changeObjectiveSense(pass_sense);
172 }
173
174 if (model.objective_offset()) {
175 const double offset = model.objective_offset();
176 highs.changeObjectiveOffset(offset);
177 }
178
179 // Logging.
180 if (request->enable_internal_solver_output()) {
181 highs.setOptionValue("log_to_console", true);
182 highs.setOptionValue("output_flag", true);
183 } else {
184 highs.setOptionValue("log_to_console", false);
185 highs.setOptionValue("output_flag", false);
186 }
187
188 const absl::Time time_before = absl::Now();
189 UserTimer user_timer;
190 user_timer.Start();
191 HighsStatus run_status = highs.run();
192 switch (run_status) {
193 case HighsStatus::kError: {
194 response.set_status(MPSOLVER_NOT_SOLVED);
195 response.set_status_str("Error running HiGHS run()");
196 return response;
197 }
198 case HighsStatus::kWarning: {
199 response.set_status_str("Warning HiGHS run()");
200 break;
201 }
202 case HighsStatus::kOk: {
203 HighsModelStatus model_status = highs.getModelStatus();
204 switch (model_status) {
205 case HighsModelStatus::kOptimal:
206 response.set_status(MPSOLVER_OPTIMAL);
207 break;
208 case HighsModelStatus::kUnboundedOrInfeasible:
209 response.set_status_str(
210 "The model may actually be unbounded: HiGHS returned "
211 "kUnboundedOrInfeasible");
212 response.set_status(MPSOLVER_INFEASIBLE);
213 break;
214 case HighsModelStatus::kInfeasible:
215 response.set_status(MPSOLVER_INFEASIBLE);
216 break;
217 case HighsModelStatus::kUnbounded:
218 response.set_status(MPSOLVER_UNBOUNDED);
219 break;
220 default: {
221 // TODO(user): report feasible status.
222 break;
223 }
224 }
225 }
226 }
227
228 const absl::Duration solving_duration = absl::Now() - time_before;
229 user_timer.Stop();
230 response.mutable_solve_info()->set_solve_wall_time_seconds(
231 absl::ToDoubleSeconds(solving_duration));
232 response.mutable_solve_info()->set_solve_user_time_seconds(
233 absl::ToDoubleSeconds(user_timer.GetDuration()));
234
235 if (response.status() == MPSOLVER_OPTIMAL) {
236 double objective_value = highs.getObjectiveValue();
237 response.set_objective_value(objective_value);
238 response.set_best_objective_bound(objective_value);
239
240 response.mutable_variable_value()->Resize(variable_size, 0);
241 for (int column = 0; column < variable_size; column++) {
242 response.mutable_variable_value()->mutable_data()[column] =
243 highs.getSolution().col_value[column];
244 }
245
246 if (has_integer_variables) {
247 for (int v = 0; v < variable_size; ++v) {
248 if (model.variable(v).is_integer()) {
249 response.set_variable_value(v,
250 std::round(response.variable_value(v)));
251 }
252 }
253 }
254
255 if (!has_integer_variables && model.general_constraint_size() == 0) {
256 response.mutable_dual_value()->Resize(model.constraint_size(), 0);
257 for (int row = 0; row < model.constraint_size(); row++) {
258 response.set_dual_value(row, highs.getSolution().row_value[row]);
259 }
260 }
261 }
262
263 return response;
264}
265
266absl::Status SetSolverSpecificParameters(const std::string& parameters,
267 Highs& highs) {
268 if (parameters.empty()) return absl::OkStatus();
269 std::vector<std::string> error_messages;
270 for (absl::string_view line : absl::StrSplit(parameters, '\n')) {
271 // Comment tokens end at the next new-line, or the end of the string.
272 // The first character must be '#'
273 if (line[0] == '#') continue;
274 for (absl::string_view token :
275 absl::StrSplit(line, ',', absl::SkipWhitespace())) {
276 if (token.empty()) continue;
277 std::vector<std::string> key_value =
278 absl::StrSplit(token, absl::ByAnyChar(" ="), absl::SkipWhitespace());
279 // If one parameter fails, we keep processing the list of parameters.
280 if (key_value.size() != 2) {
281 const std::string current_message =
282 absl::StrCat("Cannot parse parameter '", token,
283 "'. Expected format is 'ParameterName value' or "
284 "'ParameterName=value'");
285 error_messages.push_back(current_message);
286 continue;
287 }
288 HighsStatus status = highs.setOptionValue(key_value[0], key_value[1]);
289 if (status == HighsStatus::kError) {
290 const std::string current_message =
291 absl::StrCat("Error setting parameter '", key_value[0],
292 "' to value '", key_value[1], "': ");
293 error_messages.push_back(current_message);
294 continue;
295 }
296 }
297 }
298
299 if (error_messages.empty()) return absl::OkStatus();
300 return absl::InvalidArgumentError(absl::StrJoin(error_messages, "\n"));
301}
302
303} // namespace operations_research
absl::Duration GetDuration() const
Definition timer.h:49
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
void Stop()
Definition timer.h:40
SatParameters parameters
absl::Status status
Definition g_gurobi.cc:44
GRBmodel * model
RowIndex row
Definition markowitz.cc:186
In SWIG mode, we don't want anything besides these top-level includes.
std::optional< LazyMutableCopy< MPModelProto > > GetMPModelOrPopulateResponse(LazyMutableCopy< MPModelRequest > &request, MPSolutionResponse *response)
absl::StatusOr< MPSolutionResponse > HighsSolveProto(LazyMutableCopy< MPModelRequest > request)
Solve the input MIP model with the HIGHS solver.
absl::Status SetSolverSpecificParameters(absl::string_view parameters, GRBenv *gurobi)
int line
int column
double objective_value
The value objective_vector^T * (solution - center_point).