OR-Tools  9.6
model_summary.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_MATH_OPT_CORE_MODEL_SUMMARY_H_
15 #define OR_TOOLS_MATH_OPT_CORE_MODEL_SUMMARY_H_
16 
17 #include <cstdint>
18 #include <initializer_list>
19 #include <limits>
20 #include <list>
21 #include <optional>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/algorithm/container.h"
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/status/status.h"
29 #include "absl/status/statusor.h"
30 #include "absl/strings/string_view.h"
31 #include "absl/types/span.h"
32 #include "absl/log/check.h"
35 #include "ortools/math_opt/model.pb.h"
36 #include "ortools/math_opt/model_update.pb.h"
37 
38 namespace operations_research {
39 namespace math_opt {
40 
41 // Maintains a bidirectional mapping between names and ids, e.g. as used for
42 // variables and linear constraints.
43 //
44 // The following invariants are enforced:
45 // * Ids must be unique and increasing (in insertion order).
46 // * Ids are non-negative.
47 // * Ids are not equal to std::numeric_limits<int64_t>::max()
48 // * Ids removed are never reused.
49 // * Names must be either empty or unique when built with check_names=true.
50 class IdNameBiMap {
51  public:
52  // If check_names=false, the names need not be unique and the reverse mapping
53  // of name to id is not available.
54  explicit IdNameBiMap(bool check_names = true)
55  : nonempty_name_to_id_(
56  check_names ? std::make_optional<
57  absl::flat_hash_map<absl::string_view, int64_t>>()
58  : std::nullopt) {}
59 
60  // Needs a custom copy constructor/assign because absl::string_view to
61  // internal data is held as a member. No custom move is needed.
62  IdNameBiMap(const IdNameBiMap& other);
63  IdNameBiMap& operator=(const IdNameBiMap& other);
64  IdNameBiMap(IdNameBiMap&& other) = default;
65  IdNameBiMap& operator=(IdNameBiMap&& other) = default;
66 
67  // This constructor CHECKs that the input ids are sorted in increasing
68  // order. This constructor is expected to be used only for unit tests of
69  // validation code.
70  IdNameBiMap(std::initializer_list<std::pair<int64_t, absl::string_view>> ids);
71 
72  // Inserts the provided id and associate the provided name to it.
73  //
74  // An error is returned if:
75  // * id is negative
76  // * id is not at least next_free_id()
77  // * id is max(int64_t)
78  // * name is a duplicated and check_names was true at construction.
79  //
80  // As a side effect it updates next_free_id to id + 1.
81  inline absl::Status Insert(int64_t id, std::string name);
82 
83  // Removes the given id, or returns an error if id is not present.
84  inline absl::Status Erase(int64_t id);
85 
86  inline bool HasId(int64_t id) const;
87 
88  // Will always return false if name is empty or if check_names was false.
89  inline bool HasName(absl::string_view name) const;
90  inline bool Empty() const;
91  inline int Size() const;
92 
93  // The next id that has never been used (0 initially since ids are
94  // non-negative).
95  inline int64_t next_free_id() const;
96 
97  // Updates next_free_id(). Succeeds when the provided id is greater than every
98  // exiting id and is non-negative.
99  inline absl::Status SetNextFreeId(int64_t new_next_free_id);
100 
101  // Iteration order is in increasing id order.
103  return id_to_name_;
104  }
105 
106  // Is std::nullopt if check_names was false at construction.
107  const std::optional<absl::flat_hash_map<absl::string_view, int64_t>>&
109  return nonempty_name_to_id_;
110  }
111 
112  // Warning: this may be mutated (partially updated) if an error is returned.
113  absl::Status BulkUpdate(absl::Span<const int64_t> deleted_ids,
114  absl::Span<const int64_t> new_ids,
115  absl::Span<const std::string* const> names);
116 
117  private:
118  // Next unused id.
119  int64_t next_free_id_ = 0;
120 
121  // Pointer stability for name strings and iterating in insertion order are
122  // both needed (so we do not use flat_hash_map).
124  std::optional<absl::flat_hash_map<absl::string_view, int64_t>>
125  nonempty_name_to_id_;
126 };
127 
128 // TODO(b/232619901): In the guide for how to add new constraints, include how
129 // this class must updated.
130 struct ModelSummary {
131  explicit ModelSummary(bool check_names = true);
132  static absl::StatusOr<ModelSummary> Create(const ModelProto& model,
133  bool check_names = true);
134  absl::Status Update(const ModelUpdateProto& model_update);
135 
144 };
145 
147 // Inline function implementations
149 
150 absl::Status IdNameBiMap::Insert(const int64_t id, std::string name) {
151  if (id < next_free_id_) {
153  << "expected id=" << id
154  << " to be at least next_free_id_=" << next_free_id_
155  << " (ids should be nonnegative and inserted in strictly increasing "
156  "order)";
157  }
158  if (id == std::numeric_limits<int64_t>::max()) {
159  return absl::InvalidArgumentError("id of max(int64_t) is not allowed");
160  }
161  next_free_id_ = id + 1;
162 
163  const auto [it, success] = id_to_name_.emplace(id, std::move(name));
164  CHECK(success); // CHECK is okay, we have the invariant that next_free_id_ is
165  // larger than everything in the map.
166  const absl::string_view name_view(it->second);
167  if (nonempty_name_to_id_.has_value() && !name_view.empty()) {
168  const auto [it, success] = nonempty_name_to_id_->insert({name_view, id});
169  if (!success) {
171  << "duplicate name inserted: " << name_view;
172  }
173  }
174  return absl::OkStatus();
175 }
176 
177 absl::Status IdNameBiMap::Erase(const int64_t id) {
178  const auto it = id_to_name_.find(id);
179  if (it == id_to_name_.end()) {
181  << "cannot delete missing id " << id;
182  }
183  const absl::string_view name_view(it->second);
184  if (nonempty_name_to_id_.has_value() && !name_view.empty()) {
185  // CHECK OK, name_view being in nonempty_name_to_id_ when the above is met
186  // is a class invariant.
187  CHECK_EQ(nonempty_name_to_id_->erase(name_view), 1)
188  << "name: " << name_view << " id: " << id;
189  }
190  id_to_name_.erase(it);
191  return absl::OkStatus();
192 }
193 
194 bool IdNameBiMap::HasId(const int64_t id) const {
195  return id_to_name_.contains(id);
196 }
197 bool IdNameBiMap::HasName(const absl::string_view name) const {
198  if (name.empty()) {
199  return false;
200  }
201  if (!nonempty_name_to_id_.has_value()) {
202  return false;
203  }
204  return nonempty_name_to_id_->contains(name);
205 }
206 
207 bool IdNameBiMap::Empty() const { return id_to_name_.empty(); }
208 
209 int IdNameBiMap::Size() const { return id_to_name_.size(); }
210 
211 int64_t IdNameBiMap::next_free_id() const { return next_free_id_; }
212 
213 absl::Status IdNameBiMap::SetNextFreeId(const int64_t new_next_free_id) {
214  if (!Empty()) {
215  const int64_t largest_id = id_to_name_.back().first;
216  if (new_next_free_id <= largest_id) {
218  << "new_next_free_id=" << new_next_free_id
219  << " must be greater than largest_id=" << largest_id;
220  }
221  } else {
222  if (new_next_free_id < 0) {
224  << "new_next_free_id=" << new_next_free_id
225  << " must be nonnegative";
226  }
227  }
228  next_free_id_ = new_next_free_id;
229  return absl::OkStatus();
230 }
231 
232 namespace internal {
233 
234 // TODO(b/232526223): this is an exact copy of
235 // CheckIdsRangeAndStrictlyIncreasing from ids_validator.h, find a way to share
236 // the code.
237 // NOTE: This function is only exposed in the header because we need
238 // UpdateBiMapFromMappedData here for testing purposes.
239 absl::Status CheckIdsRangeAndStrictlyIncreasing2(absl::Span<const int64_t> ids);
240 
241 // NOTE: This is only exposed in the header for testing purposes.
242 template <typename DataProto>
244  const absl::Span<const int64_t> deleted_ids,
245  const google::protobuf::Map<int64_t, DataProto>& proto_map,
246  IdNameBiMap& bimap) {
248  << "invalid deleted ids";
249  for (const int64_t id : deleted_ids) {
250  RETURN_IF_ERROR(bimap.Erase(id));
251  }
252  std::vector<int64_t> new_ids;
253  new_ids.reserve(proto_map.size());
254  for (const auto& [id, value] : proto_map) {
255  new_ids.push_back(id);
256  }
257  absl::c_sort(new_ids);
258  for (const int64_t id : new_ids) {
259  RETURN_IF_ERROR(bimap.Insert(id, proto_map.at(id).name()));
260  }
261  return absl::OkStatus();
262 }
263 
264 } // namespace internal
265 
266 } // namespace math_opt
267 } // namespace operations_research
268 
269 #endif // OR_TOOLS_MATH_OPT_CORE_MODEL_SUMMARY_H_
int64_t max
Definition: alldiff_cst.cc:140
#define RETURN_IF_ERROR(expr)
bool contains(const key_arg< K > &key) const
std::pair< iterator, bool > emplace(Args &&... args)
size_type size() const
iterator find(const key_arg< K > &key)
size_type erase(const key_arg< K > &key)
bool HasName(absl::string_view name) const
IdNameBiMap & operator=(IdNameBiMap &&other)=default
IdNameBiMap & operator=(const IdNameBiMap &other)
absl::Status BulkUpdate(absl::Span< const int64_t > deleted_ids, absl::Span< const int64_t > new_ids, absl::Span< const std::string *const > names)
const std::optional< absl::flat_hash_map< absl::string_view, int64_t > > & nonempty_name_to_id() const
IdNameBiMap(IdNameBiMap &&other)=default
absl::Status Insert(int64_t id, std::string name)
absl::Status SetNextFreeId(int64_t new_next_free_id)
const gtl::linked_hash_map< int64_t, std::string > & id_to_name() const
const std::string name
int64_t value
GRBmodel * model
Definition: cleanup.h:22
absl::Status UpdateBiMapFromMappedData(const absl::Span< const int64_t > deleted_ids, const google::protobuf::Map< int64_t, DataProto > &proto_map, IdNameBiMap &bimap)
absl::Status CheckIdsRangeAndStrictlyIncreasing2(absl::Span< const int64_t > ids)
Collection of objects used to extend the Constraint Solver library.
StatusBuilder InvalidArgumentErrorBuilder()
absl::Status Update(const ModelUpdateProto &model_update)
static absl::StatusOr< ModelSummary > Create(const ModelProto &model, bool check_names=true)