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