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