OR-Tools  9.6
cp_model_utils.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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 
25 #include "ortools/base/logging.h"
26 #if !defined(__PORTABLE_PLATFORM__)
27 #include "google/protobuf/text_format.h"
28 #include "ortools/base/helpers.h"
29 #endif // !defined(__PORTABLE_PLATFORM__)
30 #include "absl/container/flat_hash_set.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/string_view.h"
33 #include "ortools/base/hash.h"
34 #include "ortools/sat/cp_model.pb.h"
36 
37 namespace operations_research {
38 namespace sat {
39 
40 // Small utility functions to deal with negative variable/literal references.
41 inline int NegatedRef(int ref) { return -ref - 1; }
42 inline int PositiveRef(int ref) { return std::max(ref, NegatedRef(ref)); }
43 inline bool RefIsPositive(int ref) { return ref >= 0; }
44 
45 // Small utility functions to deal with half-reified constraints.
46 inline bool HasEnforcementLiteral(const ConstraintProto& ct) {
47  return !ct.enforcement_literal().empty();
48 }
49 inline int EnforcementLiteral(const ConstraintProto& ct) {
50  return ct.enforcement_literal(0);
51 }
52 
53 // Fills the target as negated ref.
54 void SetToNegatedLinearExpression(const LinearExpressionProto& input_expr,
55  LinearExpressionProto* output_negated_expr);
56 
57 // Collects all the references used by a constraint. This function is used in a
58 // few places to have a "generic" code dealing with constraints. Note that the
59 // enforcement_literal is NOT counted here and that the vectors can have
60 // duplicates.
62  std::vector<int> variables;
63  std::vector<int> literals;
64 };
65 IndexReferences GetReferencesUsedByConstraint(const ConstraintProto& ct);
66 
67 // Applies the given function to all variables/literals/intervals indices of the
68 // constraint. This function is used in a few places to have a "generic" code
69 // dealing with constraints.
70 void ApplyToAllVariableIndices(const std::function<void(int*)>& function,
71  ConstraintProto* ct);
72 void ApplyToAllLiteralIndices(const std::function<void(int*)>& function,
73  ConstraintProto* ct);
74 void ApplyToAllIntervalIndices(const std::function<void(int*)>& function,
75  ConstraintProto* ct);
76 
77 // Returns the name of the ConstraintProto::ConstraintCase oneof enum.
78 // Note(user): There is no such function in the proto API as of 16/01/2017.
79 std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case);
80 
81 // Returns the sorted list of variables used by a constraint.
82 // Note that this include variable used as a literal.
83 std::vector<int> UsedVariables(const ConstraintProto& ct);
84 
85 // Returns the sorted list of interval used by a constraint.
86 std::vector<int> UsedIntervals(const ConstraintProto& ct);
87 
88 // Returns true if a proto.domain() contain the given value.
89 // The domain is expected to be encoded as a sorted disjoint interval list.
90 template <typename ProtoWithDomain>
91 bool DomainInProtoContains(const ProtoWithDomain& proto, int64_t value) {
92  for (int i = 0; i < proto.domain_size(); i += 2) {
93  if (value >= proto.domain(i) && value <= proto.domain(i + 1)) return true;
94  }
95  return false;
96 }
97 
98 // Serializes a Domain into the domain field of a proto.
99 template <typename ProtoWithDomain>
100 void FillDomainInProto(const Domain& domain, ProtoWithDomain* proto) {
101  proto->clear_domain();
102  proto->mutable_domain()->Reserve(domain.NumIntervals());
103  for (const ClosedInterval& interval : domain) {
104  proto->add_domain(interval.start);
105  proto->add_domain(interval.end);
106  }
107 }
108 
109 // Reads a Domain from the domain field of a proto.
110 template <typename ProtoWithDomain>
111 Domain ReadDomainFromProto(const ProtoWithDomain& proto) {
112 #if defined(__PORTABLE_PLATFORM__)
114  {proto.domain().begin(), proto.domain().end()});
115 #else
116  return Domain::FromFlatSpanOfIntervals(proto.domain());
117 #endif
118 }
119 
120 // Returns the list of values in a given domain.
121 // This will fail if the domain contains more than one millions values.
122 //
123 // TODO(user): work directly on the Domain class instead.
124 template <typename ProtoWithDomain>
125 std::vector<int64_t> AllValuesInDomain(const ProtoWithDomain& proto) {
126  std::vector<int64_t> result;
127  for (int i = 0; i < proto.domain_size(); i += 2) {
128  for (int64_t v = proto.domain(i); v <= proto.domain(i + 1); ++v) {
129  CHECK_LE(result.size(), 1e6);
130  result.push_back(v);
131  }
132  }
133  return result;
134 }
135 
136 // Scales back a objective value to a double value from the original model.
137 inline double ScaleObjectiveValue(const CpObjectiveProto& proto,
138  int64_t value) {
139  double result = static_cast<double>(value);
141  result = -std::numeric_limits<double>::infinity();
143  result = std::numeric_limits<double>::infinity();
144  result += proto.offset();
145  if (proto.scaling_factor() == 0) return result;
146  return proto.scaling_factor() * result;
147 }
148 
149 // Similar to ScaleObjectiveValue() but uses the integer version.
150 inline int64_t ScaleInnerObjectiveValue(const CpObjectiveProto& proto,
151  int64_t value) {
152  if (proto.integer_scaling_factor() == 0) {
153  return value + proto.integer_before_offset();
154  }
155  return (value + proto.integer_before_offset()) *
156  proto.integer_scaling_factor() +
157  proto.integer_after_offset();
158 }
159 
160 // Removes the objective scaling and offset from the given value.
161 inline double UnscaleObjectiveValue(const CpObjectiveProto& proto,
162  double value) {
163  double result = value;
164  if (proto.scaling_factor() != 0) {
165  result /= proto.scaling_factor();
166  }
167  return result - proto.offset();
168 }
169 
170 // Computes the "inner" objective of a response that contains a solution.
171 // This is the objective without offset and scaling. Call ScaleObjectiveValue()
172 // to get the user facing objective.
173 int64_t ComputeInnerObjective(const CpObjectiveProto& objective,
174  absl::Span<const int64_t> solution);
175 
176 // Returns true if a linear expression can be reduced to a single ref.
177 bool ExpressionContainsSingleRef(const LinearExpressionProto& expr);
178 
179 // Checks if the expression is affine or constant.
180 bool ExpressionIsAffine(const LinearExpressionProto& expr);
181 
182 // Returns the reference the expression can be reduced to. It will DCHECK that
183 // ExpressionContainsSingleRef(expr) is true.
184 int GetSingleRefFromExpression(const LinearExpressionProto& expr);
185 
186 // Adds a linear expression proto to a linear constraint in place.
187 //
188 // Important: The domain must already be set, otherwise the offset will be lost.
189 // We also do not do any duplicate detection, so the constraint might need
190 // presolving afterwards.
191 void AddLinearExpressionToLinearConstraint(const LinearExpressionProto& expr,
192  int64_t coefficient,
193  LinearConstraintProto* linear);
194 
195 // Returns true iff a == b * b_scaling.
196 bool LinearExpressionProtosAreEqual(const LinearExpressionProto& a,
197  const LinearExpressionProto& b,
198  int64_t b_scaling = 1);
199 
200 // Default seed for fingerprints.
201 constexpr uint64_t kDefaultFingerprintSeed = 0xa5b85c5e198ed849;
202 
203 // T must be castable to uint64_t.
204 template <class T>
205 inline uint64_t FingerprintRepeatedField(
206  const google::protobuf::RepeatedField<T>& sequence, uint64_t seed) {
207  return fasthash64(reinterpret_cast<const char*>(sequence.data()),
208  sequence.size() * sizeof(T), seed);
209 }
210 
211 // T must be castable to uint64_t.
212 template <class T>
213 inline uint64_t FingerprintSingleField(const T& field, uint64_t seed) {
214  return fasthash64(reinterpret_cast<const char*>(&field), sizeof(T), seed);
215 }
216 
217 // Returns a stable fingerprint of a linear expression.
218 uint64_t FingerprintExpression(const LinearExpressionProto& lin, uint64_t seed);
219 
220 // Returns a stable fingerprint of a model.
221 uint64_t FingerprintModel(const CpModelProto& model,
222  uint64_t seed = kDefaultFingerprintSeed);
223 
224 #if !defined(__PORTABLE_PLATFORM__)
225 
226 // We register a few custom printers to display variables and linear
227 // expression on one line. This is especially nice for variables where it is
228 // easy to recover their indices from the line number now.
229 //
230 // ex:
231 //
232 // variables { domain: [0, 1] }
233 // variables { domain: [0, 1] }
234 // variables { domain: [0, 1] }
235 //
236 // constraints {
237 // linear {
238 // vars: [0, 1, 2]
239 // coeffs: [2, 4, 5 ]
240 // domain: [11, 11]
241 // }
242 // }
243 void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer* printer);
244 #endif // !defined(__PORTABLE_PLATFORM__)
245 
246 template <class M>
247 bool WriteModelProtoToFile(const M& proto, absl::string_view filename) {
248 #if defined(__PORTABLE_PLATFORM__)
249  return false;
250 #else // !defined(__PORTABLE_PLATFORM__)
251  if (absl::EndsWith(filename, "txt")) {
252  std::string proto_string;
253  google::protobuf::TextFormat::Printer printer;
254  SetupTextFormatPrinter(&printer);
255  printer.PrintToString(proto, &proto_string);
256  return file::SetContents(filename, proto_string, file::Defaults()).ok();
257  } else {
258  return file::SetBinaryProto(filename, proto, file::Defaults()).ok();
259  }
260 #endif // !defined(__PORTABLE_PLATFORM__)
261 }
262 
263 } // namespace sat
264 } // namespace operations_research
265 
266 #endif // OR_TOOLS_SAT_CP_MODEL_UTILS_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
We call domain any subset of Int64 = [kint64min, kint64max].
static Domain FromFlatSpanOfIntervals(absl::Span< const int64_t > flat_intervals)
Same as FromIntervals() for a flattened representation (start, end, start, end, .....
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
static Domain FromFlatIntervals(const std::vector< int64_t > &flat_intervals)
This method is available in Python, Java and .NET.
int64_t b
int64_t a
CpModelProto proto
const Constraint * ct
int64_t value
GRBmodel * model
Options Defaults()
Definition: base/file.h:123
absl::Status SetBinaryProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:322
absl::Status SetContents(const absl::string_view &filename, const absl::string_view &contents, int flags)
Definition: base/file.cc:205
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
std::vector< int > UsedVariables(const ConstraintProto &ct)
double UnscaleObjectiveValue(const CpObjectiveProto &proto, double value)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::vector< int64_t > AllValuesInDomain(const ProtoWithDomain &proto)
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
uint64_t FingerprintSingleField(const T &field, uint64_t seed)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
void ApplyToAllLiteralIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
void ApplyToAllIntervalIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
uint64_t FingerprintExpression(const LinearExpressionProto &lin, uint64_t seed)
bool ExpressionIsAffine(const LinearExpressionProto &expr)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void ApplyToAllVariableIndices(const std::function< void(int *)> &f, ConstraintProto *ct)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer *printer)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
constexpr uint64_t kDefaultFingerprintSeed
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
int64_t ScaleInnerObjectiveValue(const CpObjectiveProto &proto, int64_t value)
int GetSingleRefFromExpression(const LinearExpressionProto &expr)
int EnforcementLiteral(const ConstraintProto &ct)
bool ExpressionContainsSingleRef(const LinearExpressionProto &expr)
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
Collection of objects used to extend the Constraint Solver library.
uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
Definition: hash.cc:36
IntervalVar * interval
Definition: resource.cc:101
int64_t coefficient
Represents a closed interval [start, end].