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