25 #include "absl/container/flat_hash_map.h"
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_join.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/time/clock.h"
33 #include "absl/time/time.h"
34 #include "absl/types/span.h"
42 #include "ortools/math_opt/callback.pb.h"
44 #include "ortools/math_opt/model.pb.h"
45 #include "ortools/math_opt/model_parameters.pb.h"
46 #include "ortools/math_opt/model_update.pb.h"
47 #include "ortools/math_opt/parameters.pb.h"
48 #include "ortools/math_opt/result.pb.h"
49 #include "ortools/math_opt/solution.pb.h"
51 #include "ortools/math_opt/sparse_containers.pb.h"
54 #include "absl/status/status.h"
62 absl::string_view SafeName(
const VariablesProto& variables,
int index) {
63 if (variables.names().empty()) {
66 return variables.names(
index);
69 absl::string_view SafeName(
const LinearConstraintsProto& linear_constraints,
71 if (linear_constraints.names().empty()) {
74 return linear_constraints.names(
index);
78 const bool is_integer) {
85 GlopSolver::GlopSolver() : linear_program_(), lp_solver_() {}
87 void GlopSolver::AddVariables(
const VariablesProto& variables) {
89 const glop::ColIndex col_index = linear_program_.CreateNewVariable();
90 linear_program_.SetVariableBounds(col_index, variables.lower_bounds(i),
91 variables.upper_bounds(i));
92 linear_program_.SetVariableName(col_index, SafeName(variables, i));
93 linear_program_.SetVariableType(
94 col_index, GlopVarTypeFromIsInteger(variables.integers(i)));
102 template <
typename IndexType>
105 IndexType num_indices,
106 absl::flat_hash_map<int64_t, IndexType>& id_index_map) {
108 num_indices.value(), IndexType(0));
109 IndexType new_index(0);
111 if (indices_to_delete[
index]) {
113 new_indices[
index] = IndexType(-1);
115 new_indices[
index] = new_index;
119 for (
auto it = id_index_map.begin(); it != id_index_map.end();) {
120 IndexType
index = it->second;
121 if (indices_to_delete[
index]) {
123 id_index_map.erase(it++);
125 it->second = new_indices[
index];
131 void GlopSolver::DeleteVariables(absl::Span<const int64_t> ids_to_delete) {
132 const glop::ColIndex num_cols = linear_program_.num_variables();
135 for (
const int64_t deleted_variable_id : ids_to_delete) {
136 columns_to_delete[variables_.at(deleted_variable_id)] =
true;
138 linear_program_.DeleteColumns(columns_to_delete);
139 UpdateIdIndexMap<glop::ColIndex>(columns_to_delete, num_cols, variables_);
142 void GlopSolver::DeleteLinearConstraints(
143 absl::Span<const int64_t> ids_to_delete) {
144 const glop::RowIndex num_rows = linear_program_.num_constraints();
146 for (
const int64_t deleted_constraint_id : ids_to_delete) {
147 rows_to_delete[linear_constraints_.at(deleted_constraint_id)] =
true;
149 linear_program_.DeleteRows(rows_to_delete);
150 UpdateIdIndexMap<glop::RowIndex>(rows_to_delete, num_rows,
151 linear_constraints_);
154 void GlopSolver::AddLinearConstraints(
155 const LinearConstraintsProto& linear_constraints) {
157 const glop::RowIndex row_index = linear_program_.CreateNewConstraint();
158 linear_program_.SetConstraintBounds(row_index,
159 linear_constraints.lower_bounds(i),
160 linear_constraints.upper_bounds(i));
161 linear_program_.SetConstraintName(row_index,
162 SafeName(linear_constraints, i));
168 void GlopSolver::SetOrUpdateObjectiveCoefficients(
169 const SparseDoubleVectorProto& linear_objective_coefficients) {
170 for (
int i = 0; i < linear_objective_coefficients.ids_size(); ++i) {
171 const glop::ColIndex col_index =
172 variables_.at(linear_objective_coefficients.ids(i));
173 linear_program_.SetObjectiveCoefficient(
174 col_index, linear_objective_coefficients.values(i));
178 void GlopSolver::SetOrUpdateConstraintMatrix(
179 const SparseDoubleMatrixProto& linear_constraint_matrix) {
181 const glop::ColIndex col_index =
182 variables_.at(linear_constraint_matrix.column_ids(j));
183 const glop::RowIndex row_index =
184 linear_constraints_.at(linear_constraint_matrix.row_ids(j));
185 const double coefficient = linear_constraint_matrix.coefficients(j);
186 linear_program_.SetCoefficient(row_index, col_index,
coefficient);
190 void GlopSolver::UpdateVariableBounds(
191 const VariableUpdatesProto& variable_updates) {
192 for (
const auto [
id, lb] :
MakeView(variable_updates.lower_bounds())) {
193 const auto col_index = variables_.at(
id);
194 linear_program_.SetVariableBounds(
195 col_index, lb, linear_program_.variable_upper_bounds()[col_index]);
197 for (
const auto [
id, ub] :
MakeView(variable_updates.upper_bounds())) {
198 const auto col_index = variables_.at(
id);
199 linear_program_.SetVariableBounds(
200 col_index, linear_program_.variable_lower_bounds()[col_index], ub);
204 void GlopSolver::UpdateLinearConstraintBounds(
205 const LinearConstraintUpdatesProto& linear_constraint_updates) {
206 for (
const auto [
id, lb] :
207 MakeView(linear_constraint_updates.lower_bounds())) {
208 const auto row_index = linear_constraints_.at(
id);
209 linear_program_.SetConstraintBounds(
210 row_index, lb, linear_program_.constraint_upper_bounds()[row_index]);
212 for (
const auto [
id, ub] :
213 MakeView(linear_constraint_updates.upper_bounds())) {
214 const auto row_index = linear_constraints_.at(
id);
215 linear_program_.SetConstraintBounds(
216 row_index, linear_program_.constraint_lower_bounds()[row_index], ub);
220 std::pair<glop::GlopParameters, std::vector<std::string>>
221 GlopSolver::MergeCommonParameters(
222 const CommonSolveParametersProto& common_solver_parameters,
223 const glop::GlopParameters& glop_parameters) {
224 glop::GlopParameters result = glop_parameters;
225 std::vector<std::string> warnings;
226 if (!result.has_max_time_in_seconds() &&
227 common_solver_parameters.has_time_limit()) {
231 result.set_max_time_in_seconds(absl::ToDoubleSeconds(
time_limit));
233 if (!result.has_log_search_progress()) {
234 result.set_log_search_progress(common_solver_parameters.enable_output());
236 if (!result.has_num_omp_threads() && common_solver_parameters.has_threads()) {
237 result.set_num_omp_threads(common_solver_parameters.threads());
239 if (!result.has_random_seed() && common_solver_parameters.has_random_seed()) {
240 const int random_seed =
std::max(0, common_solver_parameters.random_seed());
241 result.set_random_seed(random_seed);
243 if (!result.has_use_dual_simplex() &&
244 common_solver_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
245 switch (common_solver_parameters.lp_algorithm()) {
246 case LP_ALGORITHM_PRIMAL_SIMPLEX:
247 result.set_use_dual_simplex(
false);
249 case LP_ALGORITHM_DUAL_SIMPLEX:
250 result.set_use_dual_simplex(
true);
252 case LP_ALGORITHM_BARRIER:
254 "GLOP does not support 'LP_ALGORITHM_BARRIER' value for "
255 "'lp_algorithm' parameter.");
260 <<
" unknown, error setting GLOP parameters";
263 if (!result.has_use_scaling() && !result.has_scaling_method() &&
264 common_solver_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
265 switch (common_solver_parameters.scaling()) {
267 result.set_use_scaling(
false);
270 case EMPHASIS_MEDIUM:
271 result.set_use_scaling(
true);
272 result.set_scaling_method(glop::GlopParameters::EQUILIBRATION);
275 case EMPHASIS_VERY_HIGH:
276 result.set_use_scaling(
true);
277 result.set_scaling_method(glop::GlopParameters::LINEAR_PROGRAM);
282 <<
" unknown, error setting GLOP parameters";
285 if (!result.has_use_preprocessing() &&
286 common_solver_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
287 switch (common_solver_parameters.presolve()) {
289 result.set_use_preprocessing(
false);
292 case EMPHASIS_MEDIUM:
294 case EMPHASIS_VERY_HIGH:
295 result.set_use_preprocessing(
true);
300 <<
" unknown, error setting GLOP parameters";
303 if (common_solver_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
304 warnings.push_back(absl::StrCat(
305 "GLOP does not support 'cuts' parameters, but cuts was set to: ",
308 if (common_solver_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
310 absl::StrCat(
"GLOP does not support 'heuristics' parameter, but "
311 "heuristics was set to: ",
314 return std::make_pair(std::move(result), std::move(warnings));
317 bool GlopSolver::CanUpdate(
const ModelUpdateProto& model_update) {
321 template <
typename IndexType>
323 const std::vector<int64_t>& ids_in_order,
324 const absl::flat_hash_map<int64_t, IndexType>& id_map,
326 const SparseVectorFilterProto& filter) {
328 SparseDoubleVectorProto result;
329 for (
const int64_t variable_id : ids_in_order) {
330 const double value = values[id_map.at(variable_id)];
332 result.add_ids(variable_id);
333 result.add_values(
value);
339 template <
typename T>
341 const absl::flat_hash_map<int64_t, T>& id_map) {
342 std::vector<int64_t> sorted;
343 sorted.reserve(id_map.size());
344 for (
const auto& entry : id_map) {
345 sorted.emplace_back(entry.first);
347 std::sort(sorted.begin(), sorted.end());
351 void GlopSolver::FillSolveResult(
353 const ModelSolveParametersProto& model_parameters,
354 SolveResultProto& solve_result) {
355 solve_result.mutable_solve_stats()->set_simplex_iterations(
356 lp_solver_.GetNumberOfSimplexIterations());
359 const bool is_maximize = linear_program_.IsMaximizationProblem();
360 constexpr
double kInf = std::numeric_limits<double>::infinity();
361 solve_result.mutable_solve_stats()->set_best_primal_bound(is_maximize ? -
kInf
363 solve_result.mutable_solve_stats()->set_best_dual_bound(is_maximize ?
kInf
367 solve_result.mutable_solve_stats()->set_best_primal_bound(
368 lp_solver_.GetObjectiveValue());
369 solve_result.mutable_solve_stats()->set_best_dual_bound(
370 lp_solver_.GetObjectiveValue());
373 auto sorted_constraints =
GetSortedIs(linear_constraints_);
375 PrimalSolutionProto*
const primal_solution =
376 solve_result.add_primal_solutions();
377 primal_solution->set_objective_value(lp_solver_.GetObjectiveValue());
379 sorted_variables, variables_, lp_solver_.variable_values(),
380 model_parameters.primal_variables_filter());
382 DualSolutionProto*
const dual_solution = solve_result.add_dual_solutions();
383 dual_solution->set_objective_value(lp_solver_.GetObjectiveValue());
385 sorted_constraints, linear_constraints_, lp_solver_.dual_values(),
386 model_parameters.dual_linear_constraints_filter());
388 sorted_variables, variables_, lp_solver_.reduced_costs(),
389 model_parameters.dual_variables_filter());
392 }
else if (status == glop::ProblemStatus::PRIMAL_INFEASIBLE ||
393 status == glop::ProblemStatus::DUAL_UNBOUNDED) {
395 }
else if (status == glop::ProblemStatus::PRIMAL_UNBOUNDED) {
396 solve_result.set_termination_reason(SolveResultProto::UNBOUNDED);
397 }
else if (status == glop::ProblemStatus::DUAL_INFEASIBLE ||
398 status == glop::ProblemStatus::INFEASIBLE_OR_UNBOUNDED) {
399 solve_result.set_termination_reason(SolveResultProto::DUAL_INFEASIBLE);
401 LOG(DFATAL) <<
"Termination not implemented.";
402 solve_result.set_termination_reason(
403 SolveResultProto::TERMINATION_REASON_UNSPECIFIED);
404 solve_result.set_termination_detail(absl::StrCat(
"Glop status: ", status));
410 const ModelSolveParametersProto& model_parameters,
411 const CallbackRegistrationProto& callback_registration,
const Callback cb) {
412 const absl::Time start = absl::Now();
413 SolveResultProto result;
415 auto [glop_parameters, warnings] = MergeCommonParameters(
417 if (!warnings.empty()) {
418 if (
parameters.common_parameters().strictness().bad_parameter()) {
419 return absl::InvalidArgumentError(absl::StrJoin(warnings,
"; "));
421 for (std::string& warning : warnings) {
422 result.add_warnings(std::move(warning));
426 lp_solver_.SetParameters(glop_parameters);
430 FillSolveResult(status, model_parameters, result);
432 absl::Now() - start, result.mutable_solve_stats()->mutable_solve_time()));
436 absl::StatusOr<std::unique_ptr<SolverInterface>> GlopSolver::New(
437 const ModelProto&
model,
const SolverInitializerProto& initializer) {
438 auto solver = absl::WrapUnique(
new GlopSolver);
439 solver->linear_program_.SetName(
model.name());
440 solver->linear_program_.SetMaximizationProblem(
model.objective().maximize());
441 solver->linear_program_.SetObjectiveOffset(
model.objective().offset());
443 solver->AddVariables(
model.variables());
444 solver->SetOrUpdateObjectiveCoefficients(
445 model.objective().linear_coefficients());
447 solver->AddLinearConstraints(
model.linear_constraints());
448 solver->SetOrUpdateConstraintMatrix(
model.linear_constraint_matrix());
449 solver->linear_program_.CleanUp();
453 absl::Status GlopSolver::Update(
const ModelUpdateProto& model_update) {
454 if (model_update.objective_updates().has_direction_update()) {
455 linear_program_.SetMaximizationProblem(
456 model_update.objective_updates().direction_update());
458 if (model_update.objective_updates().has_offset_update()) {
459 linear_program_.SetObjectiveOffset(
460 model_update.objective_updates().offset_update());
463 DeleteVariables(model_update.deleted_variable_ids());
464 AddVariables(model_update.new_variables());
466 SetOrUpdateObjectiveCoefficients(
467 model_update.objective_updates().linear_coefficients());
468 UpdateVariableBounds(model_update.variable_updates());
470 DeleteLinearConstraints(model_update.deleted_linear_constraint_ids());
471 AddLinearConstraints(model_update.new_linear_constraints());
472 UpdateLinearConstraintBounds(model_update.linear_constraint_updates());
474 SetOrUpdateConstraintMatrix(model_update.linear_constraint_matrix_updates());
476 linear_program_.CleanUp();
478 return absl::OkStatus();
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
bool AcceptsAndUpdate(const int64_t id, const Value &value)
SharedTimeLimit * time_limit
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
StrictITIVector< RowIndex, bool > DenseBooleanColumn
int NumMatrixNonzeros(const SparseDoubleMatrixProto &matrix)
void UpdateIdIndexMap(glop::StrictITIVector< IndexType, bool > indices_to_delete, IndexType num_indices, absl::flat_hash_map< int64_t, IndexType > &id_index_map)
int NumVariables(const VariablesProto &variables)
SparseDoubleVectorProto FillSparseDoubleVector(const std::vector< int64_t > &ids_in_order, const absl::flat_hash_map< int64_t, IndexType > &id_map, const glop::StrictITIVector< IndexType, glop::Fractional > &values, const SparseVectorFilterProto &filter)
std::vector< int64_t > GetSortedIs(const absl::flat_hash_map< int64_t, T > &id_map)
int NumConstraints(const LinearConstraintsProto &linear_constraints)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory)