Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_model_utils.h
Go to the documentation of this file.
1// Copyright 2010-2024 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
14#ifndef OR_TOOLS_SAT_CP_MODEL_UTILS_H_
15#define OR_TOOLS_SAT_CP_MODEL_UTILS_H_
16
17#include <algorithm>
18#include <cstdint>
19#include <functional>
20#include <limits>
21#include <string>
22#include <vector>
23
24#if !defined(__PORTABLE_PLATFORM__)
26#endif // !defined(__PORTABLE_PLATFORM__)
27#include "absl/log/check.h"
28#include "absl/status/status.h"
29#include "absl/strings/match.h"
30#include "absl/strings/string_view.h"
31#include "absl/types/span.h"
32#include "google/protobuf/message.h"
33#include "google/protobuf/text_format.h"
34#include "ortools/base/hash.h"
36#include "ortools/sat/cp_model.pb.h"
38
39namespace operations_research {
40namespace sat {
41
42// Small utility functions to deal with negative variable/literal references.
43inline int NegatedRef(int ref) { return -ref - 1; }
44inline int PositiveRef(int ref) { return std::max(ref, NegatedRef(ref)); }
45inline bool RefIsPositive(int ref) { return ref >= 0; }
46
47// Small utility functions to deal with half-reified constraints.
48inline bool HasEnforcementLiteral(const ConstraintProto& ct) {
49 return !ct.enforcement_literal().empty();
50}
51inline int EnforcementLiteral(const ConstraintProto& ct) {
52 return ct.enforcement_literal(0);
53}
54
55// Returns the gcd of the given LinearExpressionProto.
56// Specifying the second argument will take the gcd with it.
57int64_t LinearExpressionGcd(const LinearExpressionProto& expr, int64_t gcd = 0);
58
59// Divide the expression in place by 'divisor'.
60// It will DCHECK that 'divisor' divides all constants.
61void DivideLinearExpression(int64_t divisor, LinearExpressionProto* expr);
62
63// Fills the target as negated ref.
64void SetToNegatedLinearExpression(const LinearExpressionProto& input_expr,
65 LinearExpressionProto* output_negated_expr);
66
67// Collects all the references used by a constraint. This function is used in a
68// few places to have a "generic" code dealing with constraints. Note that the
69// enforcement_literal is NOT counted here and that the vectors can have
70// duplicates.
72 std::vector<int> variables;
73 std::vector<int> literals;
74};
76void GetReferencesUsedByConstraint(const ConstraintProto& ct,
77 std::vector<int>* variables,
78 std::vector<int>* literals);
79
80// Applies the given function to all variables/literals/intervals indices of the
81// constraint. This function is used in a few places to have a "generic" code
82// dealing with constraints.
83void ApplyToAllVariableIndices(const std::function<void(int*)>& function,
84 ConstraintProto* ct);
85void ApplyToAllLiteralIndices(const std::function<void(int*)>& function,
86 ConstraintProto* ct);
87void ApplyToAllIntervalIndices(const std::function<void(int*)>& function,
88 ConstraintProto* ct);
89
90// Returns the name of the ConstraintProto::ConstraintCase oneof enum.
91// Note(user): There is no such function in the proto API as of 16/01/2017.
92absl::string_view ConstraintCaseName(
93 ConstraintProto::ConstraintCase constraint_case);
94
95// Returns the sorted list of variables used by a constraint.
96// Note that this include variable used as a literal.
97std::vector<int> UsedVariables(const ConstraintProto& ct);
98
99// Returns the sorted list of interval used by a constraint.
100std::vector<int> UsedIntervals(const ConstraintProto& ct);
101
102// Insert variables in a constraint into a set.
103template <typename Set>
104void InsertVariablesFromConstraint(const CpModelProto& model_proto, int index,
105 Set& output) {
106 const ConstraintProto& ct = model_proto.constraints(index);
107 for (const int var : UsedVariables(ct)) output.insert(var);
108}
109
110// Returns true if a proto.domain() contain the given value.
111// The domain is expected to be encoded as a sorted disjoint interval list.
112template <typename ProtoWithDomain>
113bool DomainInProtoContains(const ProtoWithDomain& proto, int64_t value) {
114 for (int i = 0; i < proto.domain_size(); i += 2) {
115 if (value >= proto.domain(i) && value <= proto.domain(i + 1)) return true;
116 }
117 return false;
118}
119
120// Serializes a Domain into the domain field of a proto.
121template <typename ProtoWithDomain>
122void FillDomainInProto(const Domain& domain, ProtoWithDomain* proto) {
123 proto->clear_domain();
124 proto->mutable_domain()->Reserve(domain.NumIntervals());
125 for (const ClosedInterval& interval : domain) {
126 proto->add_domain(interval.start);
127 proto->add_domain(interval.end);
128 }
129}
130
131// Reads a Domain from the domain field of a proto.
132template <typename ProtoWithDomain>
133Domain ReadDomainFromProto(const ProtoWithDomain& proto) {
134#if defined(__PORTABLE_PLATFORM__)
136 {proto.domain().begin(), proto.domain().end()});
137#else
138 return Domain::FromFlatSpanOfIntervals(proto.domain());
139#endif
140}
141
142// Returns the list of values in a given domain.
143// This will fail if the domain contains more than one millions values.
144//
145// TODO(user): work directly on the Domain class instead.
146template <typename ProtoWithDomain>
147std::vector<int64_t> AllValuesInDomain(const ProtoWithDomain& proto) {
148 std::vector<int64_t> result;
149 for (int i = 0; i < proto.domain_size(); i += 2) {
150 for (int64_t v = proto.domain(i); v <= proto.domain(i + 1); ++v) {
151 CHECK_LE(result.size(), 1e6);
152 result.push_back(v);
153 }
154 }
155 return result;
156}
157
158// Scales back a objective value to a double value from the original model.
159inline double ScaleObjectiveValue(const CpObjectiveProto& proto,
160 int64_t value) {
161 double result = static_cast<double>(value);
162 if (value == std::numeric_limits<int64_t>::min())
163 result = -std::numeric_limits<double>::infinity();
164 if (value == std::numeric_limits<int64_t>::max())
165 result = std::numeric_limits<double>::infinity();
166 result += proto.offset();
167 if (proto.scaling_factor() == 0) return result;
168 return proto.scaling_factor() * result;
169}
170
171// Similar to ScaleObjectiveValue() but uses the integer version.
172inline int64_t ScaleInnerObjectiveValue(const CpObjectiveProto& proto,
173 int64_t value) {
174 if (proto.integer_scaling_factor() == 0) {
175 return value + proto.integer_before_offset();
176 }
177 return (value + proto.integer_before_offset()) *
178 proto.integer_scaling_factor() +
179 proto.integer_after_offset();
180}
181
182// Removes the objective scaling and offset from the given value.
183inline double UnscaleObjectiveValue(const CpObjectiveProto& proto,
184 double value) {
185 double result = value;
186 if (proto.scaling_factor() != 0) {
187 result /= proto.scaling_factor();
188 }
189 return result - proto.offset();
190}
191
192// Computes the "inner" objective of a response that contains a solution.
193// This is the objective without offset and scaling. Call ScaleObjectiveValue()
194// to get the user facing objective.
195int64_t ComputeInnerObjective(const CpObjectiveProto& objective,
196 absl::Span<const int64_t> solution);
197
198// Returns true if a linear expression can be reduced to a single ref.
199bool ExpressionContainsSingleRef(const LinearExpressionProto& expr);
200
201// Checks if the expression is affine or constant.
202bool ExpressionIsAffine(const LinearExpressionProto& expr);
203
204// Returns the reference the expression can be reduced to. It will DCHECK that
205// ExpressionContainsSingleRef(expr) is true.
206int GetSingleRefFromExpression(const LinearExpressionProto& expr);
207
208// Adds a linear expression proto to a linear constraint in place.
209//
210// Important: The domain must already be set, otherwise the offset will be lost.
211// We also do not do any duplicate detection, so the constraint might need
212// presolving afterwards.
213void AddLinearExpressionToLinearConstraint(const LinearExpressionProto& expr,
214 int64_t coefficient,
215 LinearConstraintProto* linear);
216
217// Same method, but returns if the addition was possible without overflowing.
219 const LinearExpressionProto& expr, int64_t coefficient,
220 LinearConstraintProto* linear);
221
222// Returns true iff a == b * b_scaling.
223bool LinearExpressionProtosAreEqual(const LinearExpressionProto& a,
224 const LinearExpressionProto& b,
225 int64_t b_scaling = 1);
226
227// Returns true if there exactly one variable appearing in all the expressions.
228template <class ExpressionList>
229bool ExpressionsContainsOnlyOneVar(const ExpressionList& exprs) {
230 int unique_var = -1;
231 for (const LinearExpressionProto& expr : exprs) {
232 for (const int var : expr.vars()) {
233 CHECK(RefIsPositive(var));
234 if (unique_var == -1) {
235 unique_var = var;
236 } else if (var != unique_var) {
237 return false;
238 }
239 }
240 }
241 return unique_var != -1;
242}
243
244// Default seed for fingerprints.
245constexpr uint64_t kDefaultFingerprintSeed = 0xa5b85c5e198ed849;
246
247template <class T>
249 const google::protobuf::RepeatedField<T>& sequence, uint64_t seed) {
250 if (sequence.empty()) return seed;
251 return fasthash64(reinterpret_cast<const char*>(sequence.data()),
252 sequence.size() * sizeof(T), seed);
253}
254
255template <class T>
256inline uint64_t FingerprintSingleField(const T& field, uint64_t seed) {
257 return fasthash64(reinterpret_cast<const char*>(&field), sizeof(T), seed);
258}
259
260// Returns a stable fingerprint of a linear expression.
261uint64_t FingerprintExpression(const LinearExpressionProto& lin, uint64_t seed);
262
263// Returns a stable fingerprint of a model.
264uint64_t FingerprintModel(const CpModelProto& model,
265 uint64_t seed = kDefaultFingerprintSeed);
266
267#if !defined(__PORTABLE_PLATFORM__)
268
269// We register a few custom printers to display variables and linear
270// expression on one line. This is especially nice for variables where it is
271// easy to recover their indices from the line number now.
272//
273// ex:
274//
275// variables { domain: [0, 1] }
276// variables { domain: [0, 1] }
277// variables { domain: [0, 1] }
278//
279// constraints {
280// linear {
281// vars: [0, 1, 2]
282// coeffs: [2, 4, 5 ]
283// domain: [11, 11]
284// }
285// }
286void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer* printer);
287#endif // !defined(__PORTABLE_PLATFORM__)
288
289template <class M>
290bool WriteModelProtoToFile(const M& proto, absl::string_view filename) {
291#if defined(__PORTABLE_PLATFORM__)
292 return false;
293#else // !defined(__PORTABLE_PLATFORM__)
294 if (absl::EndsWith(filename, "txt") ||
295 absl::EndsWith(filename, "textproto")) {
296 std::string proto_string;
297 google::protobuf::TextFormat::Printer printer;
298 SetupTextFormatPrinter(&printer);
299 printer.PrintToString(proto, &proto_string);
300 return file::SetContents(filename, proto_string, file::Defaults()).ok();
301 } else {
302 return file::SetBinaryProto(filename, proto, file::Defaults()).ok();
303 }
304#endif // !defined(__PORTABLE_PLATFORM__)
305}
306
307// hashing support.
308//
309// Currently limited to a few inner types of ConstraintProto.
310inline bool operator==(const BoolArgumentProto& lhs,
311 const BoolArgumentProto& rhs) {
312 if (absl::MakeConstSpan(lhs.literals()) !=
313 absl::MakeConstSpan(rhs.literals())) {
314 return false;
315 }
316 if (lhs.literals_size() != rhs.literals_size()) return false;
317 for (int i = 0; i < lhs.literals_size(); ++i) {
318 if (lhs.literals(i) != rhs.literals(i)) return false;
319 }
320 return true;
321}
322
323template <typename H>
324H AbslHashValue(H h, const BoolArgumentProto& m) {
325 for (const int lit : m.literals()) {
326 h = H::combine(std::move(h), lit);
327 }
328 return h;
329}
330
331inline bool operator==(const LinearConstraintProto& lhs,
332 const LinearConstraintProto& rhs) {
333 if (absl::MakeConstSpan(lhs.vars()) != absl::MakeConstSpan(rhs.vars())) {
334 return false;
335 }
336 if (absl::MakeConstSpan(lhs.coeffs()) != absl::MakeConstSpan(rhs.coeffs())) {
337 return false;
338 }
339 if (absl::MakeConstSpan(lhs.domain()) != absl::MakeConstSpan(rhs.domain())) {
340 return false;
341 }
342 return true;
343}
344
345template <typename H>
346H AbslHashValue(H h, const LinearConstraintProto& m) {
347 for (const int var : m.vars()) {
348 h = H::combine(std::move(h), var);
349 }
350 for (const int64_t coeff : m.coeffs()) {
351 h = H::combine(std::move(h), coeff);
352 }
353 for (const int64_t bound : m.domain()) {
354 h = H::combine(std::move(h), bound);
355 }
356 return h;
357}
358
359bool ConvertCpModelProtoToCnf(const CpModelProto& cp_mode, std::string* out);
360
361// We assume delta >= 0 and we only use the low bit of delta.
362int CombineSeed(int base_seed, int64_t delta);
363
364} // namespace sat
365} // namespace operations_research
366
367#endif // OR_TOOLS_SAT_CP_MODEL_UTILS_H_
static Domain FromFlatIntervals(const std::vector< int64_t > &flat_intervals)
static Domain FromFlatSpanOfIntervals(absl::Span< const int64_t > flat_intervals)
int64_t a
Definition table.cc:44
CpModelProto proto
The output proto.
const Constraint * ct
int64_t value
IntVar * var
GRBmodel * model
int lit
int index
double solution
absl::Status SetBinaryProto(absl::string_view filename, const google::protobuf::Message &proto, Options options)
Definition file.cc:360
absl::Status SetContents(absl::string_view filename, absl::string_view contents, Options options)
Definition file.cc:242
Options Defaults()
Definition file.h:109
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
Fills the target as negated ref.
constexpr uint64_t kDefaultFingerprintSeed
Default seed for fingerprints.
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
double UnscaleObjectiveValue(const CpObjectiveProto &proto, double value)
Removes the objective scaling and offset from the given value.
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
bool operator==(const BoolArgumentProto &lhs, const BoolArgumentProto &rhs)
bool HasEnforcementLiteral(const ConstraintProto &ct)
Small utility functions to deal with half-reified constraints.
int CombineSeed(int base_seed, int64_t delta)
We assume delta >= 0 and we only use the low bit of delta.
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
bool SafeAddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
Same method, but returns if the addition was possible without overflowing.
uint64_t FingerprintSingleField(const T &field, uint64_t seed)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
Scales back a objective value to a double value from the original model.
bool ConvertCpModelProtoToCnf(const CpModelProto &cp_model, std::string *out)
uint64_t FingerprintExpression(const LinearExpressionProto &lin, uint64_t seed)
Returns a stable fingerprint of a linear expression.
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
Returns a stable fingerprint of a model.
bool ExpressionIsAffine(const LinearExpressionProto &expr)
Checks if the expression is affine or constant.
std::vector< int > UsedVariables(const ConstraintProto &ct)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
Returns the sorted list of interval used by a constraint.
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
H AbslHashValue(H h, const IntVar &i)
– ABSL HASHING SUPPORT --------------------------------------------------—
Definition cp_model.h:515
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
bool ExpressionsContainsOnlyOneVar(const ExpressionList &exprs)
Returns true if there exactly one variable appearing in all the expressions.
void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer *printer)
absl::string_view ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
bool ExpressionContainsSingleRef(const LinearExpressionProto &expr)
Returns true if a linear expression can be reduced to a single ref.
int64_t LinearExpressionGcd(const LinearExpressionProto &expr, int64_t gcd)
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void DivideLinearExpression(int64_t divisor, LinearExpressionProto *expr)
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
int GetSingleRefFromExpression(const LinearExpressionProto &expr)
int64_t ScaleInnerObjectiveValue(const CpObjectiveProto &proto, int64_t value)
Similar to ScaleObjectiveValue() but uses the integer version.
int EnforcementLiteral(const ConstraintProto &ct)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
Returns true iff a == b * b_scaling.
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
void InsertVariablesFromConstraint(const CpModelProto &model_proto, int index, Set &output)
Insert variables in a constraint into a set.
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
std::vector< int64_t > AllValuesInDomain(const ProtoWithDomain &proto)
In SWIG mode, we don't want anything besides these top-level includes.
uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
Definition hash.cc:36
int64_t delta
Definition resource.cc:1709
IntervalVar * interval
Definition resource.cc:101
int64_t bound
int64_t coefficient