// Copyright 2010-2022 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "ortools/math_opt/cpp/model.h" #include #include #include #include #include #include #include #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "ortools/base/logging.h" #include "ortools/base/status_macros.h" #include "ortools/base/strong_int.h" #include "ortools/math_opt/cpp/linear_constraint.h" #include "ortools/math_opt/storage/model_storage.h" namespace operations_research { namespace math_opt { constexpr double kInf = std::numeric_limits::infinity(); absl::StatusOr> Model::FromModelProto( const ModelProto& model_proto) { ASSIGN_OR_RETURN(std::unique_ptr storage, ModelStorage::FromModelProto(model_proto)); return std::make_unique(std::move(storage)); } Model::Model(const absl::string_view name) : storage_(std::make_shared(name)) {} Model::Model(std::unique_ptr storage) : storage_(std::move(storage)) {} std::unique_ptr Model::Clone( const std::optional new_name) const { return std::make_unique(storage_->Clone(new_name)); } LinearConstraint Model::AddLinearConstraint( const BoundedLinearExpression& bounded_expr, absl::string_view name) { CheckOptionalModel(bounded_expr.expression.storage()); const LinearConstraintId constraint = storage()->AddLinearConstraint( bounded_expr.lower_bound_minus_offset(), bounded_expr.upper_bound_minus_offset(), name); for (auto [variable, coef] : bounded_expr.expression.raw_terms()) { storage()->set_linear_constraint_coefficient(constraint, variable, coef); } return LinearConstraint(storage(), constraint); } std::vector Model::Variables() const { std::vector result; result.reserve(storage()->num_variables()); for (const VariableId var_id : storage()->variables()) { result.push_back(Variable(storage(), var_id)); } return result; } std::vector Model::SortedVariables() const { std::vector result = Variables(); std::sort(result.begin(), result.end(), [](const Variable& l, const Variable& r) { return l.typed_id() < r.typed_id(); }); return result; } std::vector Model::ColumnNonzeros( const Variable variable) const { CheckModel(variable.storage()); std::vector result; for (const LinearConstraintId constraint : storage()->linear_constraints_with_variable(variable.typed_id())) { result.push_back(LinearConstraint(storage(), constraint)); } return result; } std::vector Model::RowNonzeros( const LinearConstraint constraint) const { CheckModel(constraint.storage()); std::vector result; for (const VariableId variable : storage()->variables_in_linear_constraint(constraint.typed_id())) { result.push_back(Variable(storage(), variable)); } return result; } BoundedLinearExpression Model::AsBoundedLinearExpression( const LinearConstraint constraint) const { CheckModel(constraint.storage()); LinearExpression terms; for (const VariableId var : storage()->variables_in_linear_constraint(constraint.typed_id())) { terms += Variable(storage(), var) * storage()->linear_constraint_coefficient(constraint.typed_id(), var); } return storage()->linear_constraint_lower_bound(constraint.typed_id()) <= std::move(terms) <= storage()->linear_constraint_upper_bound(constraint.typed_id()); } std::vector Model::LinearConstraints() const { std::vector result; result.reserve(storage()->num_linear_constraints()); for (const LinearConstraintId lin_con_id : storage()->linear_constraints()) { result.push_back(LinearConstraint(storage(), lin_con_id)); } return result; } std::vector Model::SortedLinearConstraints() const { std::vector result = LinearConstraints(); std::sort(result.begin(), result.end(), [](const LinearConstraint& l, const LinearConstraint& r) { return l.typed_id() < r.typed_id(); }); return result; } void Model::SetObjective(const LinearExpression& objective, const bool is_maximize) { CheckOptionalModel(objective.storage()); storage()->clear_objective(); storage()->set_is_maximize(is_maximize); storage()->set_objective_offset(objective.offset()); for (auto [var, coef] : objective.raw_terms()) { storage()->set_linear_objective_coefficient(var, coef); } } void Model::SetObjective(const QuadraticExpression& objective, const bool is_maximize) { CheckOptionalModel(objective.storage()); storage()->clear_objective(); storage()->set_is_maximize(is_maximize); storage()->set_objective_offset(objective.offset()); for (auto [var, coef] : objective.raw_linear_terms()) { storage()->set_linear_objective_coefficient(var, coef); } for (auto [vars, coef] : objective.raw_quadratic_terms()) { storage()->set_quadratic_objective_coefficient(vars.first, vars.second, coef); } } void Model::AddToObjective(const LinearExpression& objective_terms) { CheckOptionalModel(objective_terms.storage()); storage()->set_objective_offset(objective_terms.offset() + storage()->objective_offset()); for (auto [var, coef] : objective_terms.raw_terms()) { storage()->set_linear_objective_coefficient( var, coef + storage()->linear_objective_coefficient(var)); } } void Model::AddToObjective(const QuadraticExpression& objective_terms) { CheckOptionalModel(objective_terms.storage()); storage()->set_objective_offset(objective_terms.offset() + storage()->objective_offset()); for (auto [var, coef] : objective_terms.raw_linear_terms()) { storage()->set_linear_objective_coefficient( var, coef + storage()->linear_objective_coefficient(var)); } for (auto [vars, coef] : objective_terms.raw_quadratic_terms()) { storage()->set_quadratic_objective_coefficient( vars.first, vars.second, coef + storage()->quadratic_objective_coefficient(vars.first, vars.second)); } } LinearExpression Model::ObjectiveAsLinearExpression() const { CHECK_EQ(storage()->num_quadratic_objective_terms(), 0) << "The objective function contains quadratic terms and cannot be " "represented as a LinearExpression"; LinearExpression result = storage()->objective_offset(); for (const auto& [v, coef] : storage()->linear_objective()) { result += Variable(storage(), v) * coef; } return result; } QuadraticExpression Model::ObjectiveAsQuadraticExpression() const { QuadraticExpression result = storage()->objective_offset(); for (const auto& [v, coef] : storage()->linear_objective()) { result += Variable(storage(), v) * coef; } for (const auto& [v1, v2, coef] : storage()->quadratic_objective_terms()) { result += QuadraticTerm(Variable(storage(), v1), Variable(storage(), v2), coef); } return result; } ModelProto Model::ExportModel() const { return storage()->ExportModel(); } std::unique_ptr Model::NewUpdateTracker() { return std::make_unique(storage_); } absl::Status Model::ApplyUpdateProto(const ModelUpdateProto& update_proto) { return storage()->ApplyUpdateProto(update_proto); } std::ostream& operator<<(std::ostream& ostr, const Model& model) { ostr << "Model"; if (!model.name().empty()) ostr << " " << model.name(); ostr << ":\n"; ostr << " Objective:\n" << (model.is_maximize() ? " maximize " : " minimize ") << model.ObjectiveAsQuadraticExpression() << "\n"; ostr << " Linear constraints:\n"; for (const LinearConstraint constraint : model.SortedLinearConstraints()) { ostr << " " << constraint << ": " << model.AsBoundedLinearExpression(constraint) << "\n"; } ostr << " Variables:\n"; for (const Variable v : model.SortedVariables()) { ostr << " " << v; if (v.is_integer()) { if (v.lower_bound() == 0 && v.upper_bound() == 1) { ostr << " (binary)\n"; continue; } ostr << " (integer)"; } ostr << " in "; if (v.lower_bound() == -kInf) { ostr << "(-∞"; } else { ostr << "[" << v.lower_bound(); } ostr << ", "; if (v.upper_bound() == kInf) { ostr << "+∞)"; } else { ostr << v.upper_bound() << "]"; } ostr << "\n"; } return ostr; } } // namespace math_opt } // namespace operations_research