OR-Tools  9.6
math_opt/cpp/model.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <algorithm>
17 #include <limits>
18 #include <memory>
19 #include <optional>
20 #include <ostream>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/log/check.h"
43 
44 namespace operations_research {
45 namespace math_opt {
46 
47 constexpr double kInf = std::numeric_limits<double>::infinity();
48 
49 absl::StatusOr<std::unique_ptr<Model>> Model::FromModelProto(
50  const ModelProto& model_proto) {
51  ASSIGN_OR_RETURN(std::unique_ptr<ModelStorage> storage,
53  return std::make_unique<Model>(std::move(storage));
54 }
55 
56 Model::Model(const absl::string_view name)
57  : storage_(std::make_shared<ModelStorage>(name)) {}
58 
59 Model::Model(std::unique_ptr<ModelStorage> storage)
60  : storage_(std::move(storage)) {}
61 
62 std::unique_ptr<Model> Model::Clone(
63  const std::optional<absl::string_view> new_name) const {
64  return std::make_unique<Model>(storage_->Clone(new_name));
65 }
66 
68  const BoundedLinearExpression& bounded_expr, absl::string_view name) {
69  CheckOptionalModel(bounded_expr.expression.storage());
70 
71  const LinearConstraintId constraint = storage()->AddLinearConstraint(
72  bounded_expr.lower_bound_minus_offset(),
73  bounded_expr.upper_bound_minus_offset(), name);
74  for (auto [variable, coef] : bounded_expr.expression.raw_terms()) {
76  }
77  return LinearConstraint(storage(), constraint);
78 }
79 
80 std::vector<Variable> Model::Variables() const {
81  std::vector<Variable> result;
82  result.reserve(storage()->num_variables());
83  for (const VariableId var_id : storage()->variables()) {
84  result.push_back(Variable(storage(), var_id));
85  }
86  return result;
87 }
88 
89 std::vector<Variable> Model::SortedVariables() const {
90  std::vector<Variable> result = Variables();
91  std::sort(result.begin(), result.end(),
92  [](const Variable& l, const Variable& r) {
93  return l.typed_id() < r.typed_id();
94  });
95  return result;
96 }
97 
98 std::vector<LinearConstraint> Model::ColumnNonzeros(
99  const Variable variable) const {
100  CheckModel(variable.storage());
101  std::vector<LinearConstraint> result;
102  for (const LinearConstraintId constraint :
103  storage()->linear_constraints_with_variable(variable.typed_id())) {
104  result.push_back(LinearConstraint(storage(), constraint));
105  }
106  return result;
107 }
108 
109 std::vector<Variable> Model::RowNonzeros(
110  const LinearConstraint constraint) const {
111  CheckModel(constraint.storage());
112  std::vector<Variable> result;
113  for (const VariableId variable :
114  storage()->variables_in_linear_constraint(constraint.typed_id())) {
115  result.push_back(Variable(storage(), variable));
116  }
117  return result;
118 }
119 
120 std::vector<LinearConstraint> Model::LinearConstraints() const {
121  std::vector<LinearConstraint> result;
122  result.reserve(storage()->num_linear_constraints());
123  for (const LinearConstraintId lin_con_id : storage()->LinearConstraints()) {
124  result.push_back(LinearConstraint(storage(), lin_con_id));
125  }
126  return result;
127 }
128 
129 std::vector<LinearConstraint> Model::SortedLinearConstraints() const {
130  std::vector<LinearConstraint> result = LinearConstraints();
131  std::sort(result.begin(), result.end(),
132  [](const LinearConstraint& l, const LinearConstraint& r) {
133  return l.typed_id() < r.typed_id();
134  });
135  return result;
136 }
137 
138 void Model::SetObjective(const LinearExpression& objective,
139  const bool is_maximize) {
140  CheckOptionalModel(objective.storage());
143  storage()->set_objective_offset(objective.offset());
144  for (auto [var, coef] : objective.raw_terms()) {
146  }
147 }
148 
150  const bool is_maximize) {
151  CheckOptionalModel(objective.storage());
154  storage()->set_objective_offset(objective.offset());
155  for (auto [var, coef] : objective.raw_linear_terms()) {
157  }
158  for (auto [vars, coef] : objective.raw_quadratic_terms()) {
159  storage()->set_quadratic_objective_coefficient(vars.first, vars.second,
160  coef);
161  }
162 }
163 
164 void Model::AddToObjective(const LinearExpression& objective_terms) {
165  CheckOptionalModel(objective_terms.storage());
166  storage()->set_objective_offset(objective_terms.offset() +
168  for (auto [var, coef] : objective_terms.raw_terms()) {
170  var, coef + storage()->linear_objective_coefficient(var));
171  }
172 }
173 
174 void Model::AddToObjective(const QuadraticExpression& objective_terms) {
175  CheckOptionalModel(objective_terms.storage());
176  storage()->set_objective_offset(objective_terms.offset() +
178  for (auto [var, coef] : objective_terms.raw_linear_terms()) {
180  var, coef + storage()->linear_objective_coefficient(var));
181  }
182  for (auto [vars, coef] : objective_terms.raw_quadratic_terms()) {
184  vars.first, vars.second,
185  coef + storage()->quadratic_objective_coefficient(vars.first,
186  vars.second));
187  }
188 }
189 
191  CHECK_EQ(storage()->num_quadratic_objective_terms(), 0)
192  << "The objective function contains quadratic terms and cannot be "
193  "represented as a LinearExpression";
195  for (const auto& [v, coef] : storage()->linear_objective()) {
196  result += Variable(storage(), v) * coef;
197  }
198  return result;
199 }
200 
203  for (const auto& [v, coef] : storage()->linear_objective()) {
204  result += Variable(storage(), v) * coef;
205  }
206  for (const auto& [v1, v2, coef] : storage()->quadratic_objective_terms()) {
207  result +=
209  }
210  return result;
211 }
212 
213 ModelProto Model::ExportModel() const { return storage()->ExportModel(); }
214 
215 std::unique_ptr<UpdateTracker> Model::NewUpdateTracker() {
216  return std::make_unique<UpdateTracker>(storage_);
217 }
218 
219 absl::Status Model::ApplyUpdateProto(const ModelUpdateProto& update_proto) {
220  return storage()->ApplyUpdateProto(update_proto);
221 }
222 
223 std::ostream& operator<<(std::ostream& ostr, const Model& model) {
224  ostr << "Model";
225  if (!model.name().empty()) ostr << " " << model.name();
226  ostr << ":\n";
227 
228  ostr << " Objective:\n"
229  << (model.is_maximize() ? " maximize " : " minimize ")
230  << model.ObjectiveAsQuadraticExpression() << "\n";
231 
232  ostr << " Linear constraints:\n";
233  for (const LinearConstraint constraint : model.SortedLinearConstraints()) {
234  ostr << " " << constraint << ": " << constraint.AsBoundedLinearExpression()
235  << "\n";
236  }
237 
238  if (model.num_quadratic_constraints() > 0) {
239  ostr << " Quadratic constraints:\n";
240  for (const QuadraticConstraint constraint :
241  model.SortedQuadraticConstraints()) {
242  ostr << " " << constraint << ": "
243  << constraint.AsBoundedQuadraticExpression() << "\n";
244  }
245  }
246 
247  if (model.num_sos1_constraints() > 0) {
248  ostr << " SOS1 constraints:\n";
249  for (const Sos1Constraint constraint : model.SortedSos1Constraints()) {
250  ostr << " " << constraint << ": " << constraint.ToString() << "\n";
251  }
252  }
253 
254  if (model.num_sos2_constraints() > 0) {
255  ostr << " SOS2 constraints:\n";
256  for (const Sos2Constraint constraint : model.SortedSos2Constraints()) {
257  ostr << " " << constraint << ": " << constraint.ToString() << "\n";
258  }
259  }
260 
261  if (model.num_indicator_constraints() > 0) {
262  ostr << " Indicator constraints:\n";
263  for (const IndicatorConstraint constraint :
264  model.SortedIndicatorConstraints()) {
265  ostr << " " << constraint << ": " << constraint.ToString() << "\n";
266  }
267  }
268 
269  ostr << " Variables:\n";
270  for (const Variable v : model.SortedVariables()) {
271  ostr << " " << v;
272  if (v.is_integer()) {
273  if (v.lower_bound() == 0 && v.upper_bound() == 1) {
274  ostr << " (binary)\n";
275  continue;
276  }
277  ostr << " (integer)";
278  }
279  ostr << " in ";
280  if (v.lower_bound() == -kInf) {
281  ostr << "(-∞";
282  } else {
283  ostr << "[" << RoundTripDoubleFormat(v.lower_bound());
284  }
285  ostr << ", ";
286  if (v.upper_bound() == kInf) {
287  ostr << "+∞)";
288  } else {
289  ostr << RoundTripDoubleFormat(v.upper_bound()) << "]";
290  }
291  ostr << "\n";
292  }
293  return ostr;
294 }
295 
296 // ------------------------- Quadratic constraints -----------------------------
297 
299  const BoundedQuadraticExpression& bounded_expr,
300  const absl::string_view name) {
301  CheckOptionalModel(bounded_expr.expression.storage());
302  SparseCoefficientMap linear_terms;
303  for (const auto [var, coeff] : bounded_expr.expression.linear_terms()) {
304  linear_terms.set(var.typed_id(), coeff);
305  }
306  SparseSymmetricMatrix quadratic_terms;
307  for (const auto& [var_ids, coeff] :
308  bounded_expr.expression.raw_quadratic_terms()) {
309  quadratic_terms.set(var_ids.first, var_ids.second, coeff);
310  }
311  const QuadraticConstraintId id =
313  .lower_bound = bounded_expr.lower_bound_minus_offset(),
314  .upper_bound = bounded_expr.upper_bound_minus_offset(),
315  .linear_terms = std::move(linear_terms),
316  .quadratic_terms = std::move(quadratic_terms),
317  .name = std::string(name),
318  });
319  return QuadraticConstraint(storage(), id);
320 }
321 
322 // --------------------------- SOS1 constraints --------------------------------
323 
324 namespace {
325 
326 template <typename SosData>
327 SosData MakeSosData(const std::vector<LinearExpression>& expressions,
328  std::vector<double> weights, const absl::string_view name) {
329  std::vector<typename SosData::LinearExpression> storage_expressions;
330  storage_expressions.reserve(expressions.size());
331  for (const LinearExpression& expr : expressions) {
332  typename SosData::LinearExpression& storage_expr =
333  storage_expressions.emplace_back();
334  storage_expr.offset = expr.offset();
335  for (const auto [var, coeff] : expr.raw_terms()) {
336  storage_expr.terms[var] = coeff;
337  }
338  }
339  return SosData(std::move(storage_expressions), std::move(weights),
340  std::string(name));
341 }
342 
343 } // namespace
344 
346  const std::vector<LinearExpression>& expressions,
347  std::vector<double> weights, const absl::string_view name) {
348  for (const LinearExpression& expr : expressions) {
349  CheckOptionalModel(expr.storage());
350  }
351  const Sos1ConstraintId id = storage()->AddAtomicConstraint(
352  MakeSosData<Sos1ConstraintData>(expressions, std::move(weights), name));
353  return Sos1Constraint(storage(), id);
354 }
355 
356 // --------------------------- SOS2 constraints --------------------------------
357 
359  const std::vector<LinearExpression>& expressions,
360  std::vector<double> weights, const absl::string_view name) {
361  for (const LinearExpression& expr : expressions) {
362  CheckOptionalModel(expr.storage());
363  }
364  const Sos2ConstraintId id = storage()->AddAtomicConstraint(
365  MakeSosData<Sos2ConstraintData>(expressions, std::move(weights), name));
366  return Sos2Constraint(storage(), id);
367 }
368 
369 // --------------------------- Indicator constraints ---------------------------
370 
372  const Variable indicator_variable,
373  const BoundedLinearExpression& implied_constraint,
374  const bool activate_on_zero, const absl::string_view name) {
375  CheckModel(indicator_variable.storage());
376  CheckOptionalModel(implied_constraint.expression.storage());
377  // We ignore the offset while unpacking here; instead, we account for it below
378  // by using the `{lower,upper}_bound_minus_offset` member functions.
379  auto [expr, _] = FromLinearExpression(implied_constraint.expression);
380  const IndicatorConstraintId id =
382  .lower_bound = implied_constraint.lower_bound_minus_offset(),
383  .upper_bound = implied_constraint.upper_bound_minus_offset(),
384  .linear_terms = std::move(expr),
385  .indicator = indicator_variable.typed_id(),
386  .activate_on_zero = activate_on_zero,
387  .name = std::string(name),
388  });
389  return IndicatorConstraint(storage(), id);
390 }
391 
392 } // namespace math_opt
393 } // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
const absl::flat_hash_map< VariableId, double > & raw_terms() const
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
std::unique_ptr< Model > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
const std::string & name() const
void SetObjective(double objective, bool is_maximize)
std::unique_ptr< UpdateTracker > NewUpdateTracker()
LinearExpression ObjectiveAsLinearExpression() const
std::vector< LinearConstraint > ColumnNonzeros(Variable variable) const
Variable variable(int64_t id) const
LinearConstraint AddLinearConstraint(absl::string_view name="")
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
QuadraticConstraint AddQuadraticConstraint(const BoundedQuadraticExpression &bounded_expr, absl::string_view name="")
double upper_bound(Variable variable) const
QuadraticExpression ObjectiveAsQuadraticExpression() const
const ModelStorage * storage() const
std::vector< Variable > Variables() const
Sos1Constraint AddSos1Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
std::vector< LinearConstraint > LinearConstraints() const
std::vector< Variable > RowNonzeros(LinearConstraint constraint) const
Sos2Constraint AddSos2Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
std::vector< LinearConstraint > SortedLinearConstraints() const
IndicatorConstraint AddIndicatorConstraint(Variable indicator_variable, const BoundedLinearExpression &implied_constraint, bool activate_on_zero=false, absl::string_view name={})
std::vector< Variable > SortedVariables() const
void set_quadratic_objective_coefficient(VariableId first_variable, VariableId second_variable, double value)
static absl::StatusOr< std::unique_ptr< ModelStorage > > FromModelProto(const ModelProto &model_proto)
void set_linear_objective_coefficient(VariableId variable, double value)
ConstraintData::IdType AddAtomicConstraint(ConstraintData data)
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
void set_linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable, double value)
LinearConstraintId AddLinearConstraint(absl::string_view name="")
const absl::flat_hash_map< QuadraticProductId, double > & raw_quadratic_terms() const
const absl::flat_hash_map< VariableId, double > & raw_linear_terms() const
bool set(const VariableId id, const double coeff)
bool set(VariableId first, VariableId second, double value)
CpModelProto const * model_proto
const std::string name
IntVar * var
Definition: expr_array.cc:1874
int64_t coef
Definition: expr_array.cc:1875
GRBmodel * model
std::pair< SparseCoefficientMap, double > FromLinearExpression(const LinearExpression &expression)
Definition: model_util.cc:33
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
Collection of objects used to extend the Constraint Solver library.