OR-Tools  9.3
variable_and_expressions.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <cmath>
17#include <limits>
18#include <utility>
19#include <vector>
20
21#include "absl/base/attributes.h"
25
26namespace operations_research {
27namespace math_opt {
28
29constexpr double kInf = std::numeric_limits<double>::infinity();
30
31#ifdef MATH_OPT_USE_EXPRESSION_COUNTERS
32LinearExpression::LinearExpression() { ++num_calls_default_constructor_; }
33
34LinearExpression::LinearExpression(const LinearExpression& other)
35 : terms_(other.terms_), offset_(other.offset_) {
36 ++num_calls_copy_constructor_;
37}
38
39LinearExpression::LinearExpression(LinearExpression&& other)
40 : terms_(std::move(other.terms_)),
41 offset_(std::exchange(other.offset_, 0.0)) {
42 ++num_calls_move_constructor_;
43}
44
45LinearExpression& LinearExpression::operator=(const LinearExpression& other) {
46 terms_ = other.terms_;
47 offset_ = other.offset_;
48 return *this;
49}
50
51ABSL_CONST_INIT thread_local int
52 LinearExpression::num_calls_default_constructor_ = 0;
53ABSL_CONST_INIT thread_local int LinearExpression::num_calls_copy_constructor_ =
54 0;
55ABSL_CONST_INIT thread_local int LinearExpression::num_calls_move_constructor_ =
56 0;
57ABSL_CONST_INIT thread_local int
58 LinearExpression::num_calls_initializer_list_constructor_ = 0;
59
60void LinearExpression::ResetCounters() {
61 num_calls_default_constructor_ = 0;
62 num_calls_copy_constructor_ = 0;
63 num_calls_move_constructor_ = 0;
64 num_calls_initializer_list_constructor_ = 0;
65}
66#endif // MATH_OPT_USE_EXPRESSION_COUNTERS
67
68double LinearExpression::Evaluate(
69 const VariableMap<double>& variable_values) const {
70 if (variable_values.storage() != nullptr && storage() != nullptr) {
71 CHECK_EQ(variable_values.storage(), storage())
73 }
74 double result = offset_;
75 for (const auto& [variable_id, coef] : terms_.raw_map()) {
76 result += coef * variable_values.raw_map().at(variable_id);
77 }
78 return result;
79}
80
81double LinearExpression::EvaluateWithDefaultZero(
82 const VariableMap<double>& variable_values) const {
83 if (variable_values.storage() != nullptr && storage() != nullptr) {
84 CHECK_EQ(variable_values.storage(), storage())
86 }
87 double result = offset_;
88 for (const auto& [variable_id, coef] : terms_.raw_map()) {
89 result +=
90 coef * gtl::FindWithDefault(variable_values.raw_map(), variable_id);
91 }
92 return result;
93}
94
95namespace {
96
97// Streaming formatter for a coefficient of a linear/quadratic term, along with
98// any leading "+"/"-"'s to connect it with preceding terms in a sum, and
99// potentially a "*" postfix. The `is_first` parameter specifies if the term is
100// the first appearing in the sum, in which case the handling of the +/-
101// connectors is different.
102struct LeadingCoefficientFormatter {
103 LeadingCoefficientFormatter(const double coeff, const bool is_first)
105 const double coeff;
106 const bool is_first;
107};
108
109std::ostream& operator<<(std::ostream& out,
110 const LeadingCoefficientFormatter formatter) {
111 const double coeff = formatter.coeff;
112 if (formatter.is_first) {
113 if (coeff == 1.0) {
114 // Do nothing.
115 } else if (coeff == -1.0) {
116 out << "-";
117 } else {
118 out << coeff << "*";
119 }
120 } else {
121 if (coeff == 1.0) {
122 out << " + ";
123 } else if (coeff == -1.0) {
124 out << " - ";
125 } else if (std::isnan(coeff)) {
126 out << " + nan*";
127 } else if (coeff >= 0) {
128 out << " + " << coeff << "*";
129 } else {
130 out << " - " << -coeff << "*";
131 }
132 }
133 return out;
134}
135
136// Streaming formatter for a constant in a linear/quadratic expression.
137struct ConstantFormatter {
138 ConstantFormatter(const double constant, const bool is_first)
140 const double constant;
141 const bool is_first;
142};
143
144std::ostream& operator<<(std::ostream& out, const ConstantFormatter formatter) {
145 const double constant = formatter.constant;
146 if (formatter.is_first) {
147 out << constant;
148 } else if (constant == 0) {
149 // Do nothing.
150 } else if (std::isnan(constant)) {
151 out << " + nan";
152 } else if (constant > 0) {
153 out << " + " << constant;
154 } else {
155 out << " - " << -constant;
156 }
157 return out;
158}
159
160} // namespace
161
162std::ostream& operator<<(std::ostream& ostr,
163 const LinearExpression& expression) {
164 // TODO(b/169415597): improve linear expression format:
165 // - use bijective formatting in base10 of the double factors.
166 // - make sure to quote the variable name so that we support:
167 // * variable names contains +, -, ...
168 // * variable names resembling anonymous variable names.
169 const std::vector<Variable> sorted_variables = expression.terms_.SortedKeys();
170 bool first = true;
171 for (const auto v : sorted_variables) {
172 const double coeff = expression.terms_.at(v);
173 if (coeff != 0) {
174 ostr << LeadingCoefficientFormatter(coeff, first) << v;
175 first = false;
176 }
177 }
178 ostr << ConstantFormatter(expression.offset(), first);
179
180 return ostr;
181}
182
183std::ostream& operator<<(std::ostream& ostr,
184 const BoundedLinearExpression& bounded_expression) {
185 // TODO(b/170991498): use bijective conversion from double to base-10 string
186 // to make sure we can reproduce bugs.
187 const double lb = bounded_expression.lower_bound;
188 const double ub = bounded_expression.upper_bound;
189 if (lb == ub) {
190 ostr << bounded_expression.expression << " = " << lb;
191 } else if (lb == -kInf) {
192 ostr << bounded_expression.expression << " ≤ " << ub;
193 } else if (ub == kInf) {
194 ostr << bounded_expression.expression << " ≥ " << lb;
195 } else {
196 ostr << lb << " ≤ " << bounded_expression.expression << " ≤ " << ub;
197 }
198 return ostr;
199}
200
201double QuadraticExpression::Evaluate(
202 const VariableMap<double>& variable_values) const {
203 if (variable_values.storage() != nullptr && storage() != nullptr) {
204 CHECK_EQ(variable_values.storage(), storage())
206 }
207 double result = offset();
208 for (const auto& [variable_id, coef] : linear_terms_.raw_map()) {
209 result += coef * variable_values.raw_map().at(variable_id);
210 }
211 for (const auto& [variable_ids, coef] : quadratic_terms_.raw_map()) {
212 result += coef * variable_values.raw_map().at(variable_ids.first) *
213 variable_values.raw_map().at(variable_ids.second);
214 }
215 return result;
216}
217
218double QuadraticExpression::EvaluateWithDefaultZero(
219 const VariableMap<double>& variable_values) const {
220 if (variable_values.storage() != nullptr && storage() != nullptr) {
221 CHECK_EQ(variable_values.storage(), storage())
223 }
224 double result = offset();
225 for (const auto& [variable_id, coef] : linear_terms_.raw_map()) {
226 result +=
227 coef * gtl::FindWithDefault(variable_values.raw_map(), variable_id);
228 }
229 for (const auto& [variable_ids, coef] : quadratic_terms_.raw_map()) {
230 result +=
231 coef *
232 gtl::FindWithDefault(variable_values.raw_map(), variable_ids.first) *
233 gtl::FindWithDefault(variable_values.raw_map(), variable_ids.second);
234 }
235 return result;
236}
237
238std::ostream& operator<<(std::ostream& ostr, const QuadraticExpression& expr) {
239 // TODO(b/169415597): improve quadratic expression formatting. See b/170991498
240 // for desired improvements for LinearExpression streaming which are also
241 // applicable here.
242 bool first = true;
243 for (const auto v : expr.quadratic_terms().SortedKeys()) {
244 const double coeff = expr.quadratic_terms().at(v);
245 if (coeff != 0) {
246 ostr << LeadingCoefficientFormatter(coeff, first);
247 first = false;
248 }
249 const Variable first_variable(expr.quadratic_terms().storage(),
250 v.typed_id().first);
251 const Variable second_variable(expr.quadratic_terms().storage(),
252 v.typed_id().second);
253 if (first_variable == second_variable) {
254 ostr << first_variable << "²";
255 } else {
256 ostr << first_variable << "*" << second_variable;
257 }
258 }
259 for (const auto v : expr.linear_terms().SortedKeys()) {
260 const double coeff = expr.linear_terms().at(v);
261 if (coeff != 0) {
262 ostr << LeadingCoefficientFormatter(coeff, first) << v;
263 first = false;
264 }
265 }
266 ostr << ConstantFormatter(expr.offset(), first);
267 return ostr;
268}
269
270#ifdef MATH_OPT_USE_EXPRESSION_COUNTERS
271QuadraticExpression::QuadraticExpression() { ++num_calls_default_constructor_; }
272
273QuadraticExpression::QuadraticExpression(const QuadraticExpression& other)
274 : quadratic_terms_(other.quadratic_terms_),
275 linear_terms_(other.linear_terms_),
276 offset_(other.offset_) {
277 ++num_calls_copy_constructor_;
278}
279
280QuadraticExpression::QuadraticExpression(QuadraticExpression&& other)
281 : quadratic_terms_(std::move(other.quadratic_terms_)),
282 linear_terms_(std::move(other.linear_terms_)),
283 offset_(std::exchange(other.offset_, 0.0)) {
284 ++num_calls_move_constructor_;
285}
286
287QuadraticExpression& QuadraticExpression::operator=(
288 const QuadraticExpression& other) {
289 quadratic_terms_ = other.quadratic_terms_;
290 linear_terms_ = other.linear_terms_;
291 offset_ = other.offset_;
292 return *this;
293}
294
295ABSL_CONST_INIT thread_local int
296 QuadraticExpression::num_calls_default_constructor_ = 0;
297ABSL_CONST_INIT thread_local int
298 QuadraticExpression::num_calls_copy_constructor_ = 0;
299ABSL_CONST_INIT thread_local int
300 QuadraticExpression::num_calls_move_constructor_ = 0;
301ABSL_CONST_INIT thread_local int
302 QuadraticExpression::num_calls_initializer_list_constructor_ = 0;
303ABSL_CONST_INIT thread_local int
304 QuadraticExpression::num_calls_linear_expression_constructor_ = 0;
305
306void QuadraticExpression::ResetCounters() {
307 num_calls_default_constructor_ = 0;
308 num_calls_copy_constructor_ = 0;
309 num_calls_move_constructor_ = 0;
310 num_calls_initializer_list_constructor_ = 0;
311 num_calls_linear_expression_constructor_ = 0;
312}
313#endif // MATH_OPT_USE_EXPRESSION_COUNTERS
314
315} // namespace math_opt
316} // namespace operations_research
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
const StorageType & raw_map() const
Definition: id_map.h:227
const ModelStorage * storage() const
Definition: id_map.h:228
const QuadraticTermMap< double > & quadratic_terms() const
int64_t coef
Definition: expr_array.cc:1875
absl::Span< const int64_t > variable_ids
const int64_t offset_
Definition: interval.cc:2108
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
constexpr absl::string_view kObjectsFromOtherModelStorage
Definition: key_types.h:56
Collection of objects used to extend the Constraint Solver library.
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
STL namespace.
const double coeff
const double constant
const bool is_first