Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
elemental_differencer.cc
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
15
16#include <cstdint>
17#include <string>
18#include <vector>
19
20#include "absl/container/flat_hash_set.h"
21#include "absl/log/check.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/escaping.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_join.h"
26#include "absl/strings/string_view.h"
30
32namespace {
33
34template <typename T>
35absl::flat_hash_set<T> ToSet(const std::vector<T>& vec) {
36 return absl::flat_hash_set<T>(vec.begin(), vec.end());
37}
38
39template <typename T>
40std::vector<T> Sorted(absl::flat_hash_set<T> items) {
41 std::vector<T> result(items.begin(), items.end());
42 absl::c_sort(result);
43 return result;
44}
45
46} // namespace
47
49 return ids.Empty() && different_names.empty() && !next_id_different;
50}
51
53 if (model_name_different_ || primary_objective_name_different_) {
54 return false;
55 }
56 for (ElementType e : kElements) {
57 if (!element_difference(e).Empty()) {
58 return false;
59 }
60 }
61 bool empty = true;
62 AllAttrs::ForEachAttr([this, &empty](auto attr) {
63 empty = empty && this->attr_difference(attr).Empty();
64 });
65 return empty;
66}
67
69 const Elemental& first, const Elemental& second,
70 const ElementalDifferenceOptions& options) {
72 if (options.check_names) {
73 result.model_name_different_ = first.model_name() != second.model_name();
74 result.primary_objective_name_different_ =
76 }
77 for (const ElementType e : kElements) {
78 ElementDifference& element_diff = result.mutable_element_difference(e);
79 const absl::flat_hash_set<int64_t> first_elements =
80 ToSet(first.AllElementsUntyped(e));
81 const absl::flat_hash_set<int64_t> second_elements =
82 ToSet(second.AllElementsUntyped(e));
83 element_diff.ids =
84 SymmetricDifference<int64_t>(first_elements, second_elements);
85 if (options.check_names) {
86 for (int64_t element : IntersectSets(first_elements, second_elements)) {
87 absl::StatusOr<absl::string_view> first_name =
88 first.GetElementNameUntyped(e, element);
89
90 absl::StatusOr<absl::string_view> second_name =
91 second.GetElementNameUntyped(e, element);
92 // We can CHECK here, we just read the element from the set.
93 CHECK_OK(first_name);
94 CHECK_OK(second_name);
95 if (*first_name != *second_name) {
96 element_diff.different_names.insert(element);
97 }
98 }
99 }
100 if (options.check_next_id) {
101 element_diff.next_id_different =
102 first.NextElementId(e) != second.NextElementId(e);
103 }
104 }
106 // NOLINTNEXTLINE(clang-diagnostic-pre-c++20-compat)
107 [&result, &first, &second]<typename AttrType>(AttrType attr) {
108 using Key = typename AttributeDifference<AttrType>::Key;
110 result.mutable_attr_difference(attr);
111 const absl::flat_hash_set<Key> first_non_defaults =
112 ToSet(first.AttrNonDefaults(attr));
113 const absl::flat_hash_set<Key> second_non_defaults =
114 ToSet(second.AttrNonDefaults(attr));
115 attr_difference.keys =
116 SymmetricDifference<Key>(first_non_defaults, second_non_defaults);
117 for (const Key key :
118 IntersectSets(first_non_defaults, second_non_defaults)) {
119 if (first.GetAttr(attr, key) != second.GetAttr(attr, key)) {
120 attr_difference.different_values.insert(key);
121 }
122 }
123 });
124 return result;
125}
126
127namespace {
128
129std::string ElementDebugString(const Elemental& elemental, const ElementType e,
130 const int64_t id) {
131 const absl::StatusOr<absl::string_view> name =
132 elemental.GetElementNameUntyped(e, id);
133 CHECK_OK(name);
134 return absl::StrCat(id, ": (name: \"", absl::CEscape(*name), "\")");
135}
136
137template <typename AttrType>
138std::string KeyDebugString(const Elemental& elemental, const AttrType attr,
139 const AttrKeyFor<AttrType> key) {
140 std::vector<std::string> element_names;
141 for (int i = 0; i < GetAttrKeySize<AttrType>(); ++i) {
142 auto name =
143 elemental.GetElementNameUntyped(GetElementTypes(attr)[i], key[i]);
144 if (name.ok()) {
145 element_names.push_back(absl::StrCat("\"", absl::CEscape(*name), "\""));
146 } else {
147 element_names.push_back("__missing__");
148 }
149 }
150 return absl::StrCat("(", absl::StrJoin(element_names, ", "), ")");
151}
152
153} // namespace
154
156 const Elemental& first, const Elemental& second,
157 const ElementalDifference& difference) {
158 if (difference.Empty()) {
159 return "No difference";
160 }
161 std::vector<std::string> lines;
162 if (difference.model_name_different_) {
163 lines.push_back("model name disagrees:");
164 lines.push_back(absl::StrCat(" first_name: \"",
165 absl::CEscape(first.model_name()), "\""));
166 lines.push_back(absl::StrCat(" second_name: \"",
167 absl::CEscape(second.model_name()), "\""));
168 }
169 if (difference.primary_objective_name_different_) {
170 lines.push_back("primary objective name disagrees:");
171 lines.push_back(absl::StrCat(" first_name: \"",
172 absl::CEscape(first.primary_objective_name()),
173 "\""));
174 lines.push_back(absl::StrCat(" second_name: \"",
175 absl::CEscape(second.primary_objective_name()),
176 "\""));
177 }
178 for (ElementType e : kElements) {
179 const ElementDifference& el_diff = difference.element_difference(e);
180 if (!el_diff.Empty()) {
181 lines.push_back(absl::StrCat(e, ":"));
182 if (!el_diff.ids.Empty()) {
183 if (!el_diff.ids.only_in_first.empty()) {
184 lines.push_back(" element ids in first but not second:");
185 for (const int64_t id : Sorted(el_diff.ids.only_in_first)) {
186 lines.push_back(
187 absl::StrCat(" ", ElementDebugString(first, e, id)));
188 }
189 }
190 if (!el_diff.ids.only_in_second.empty()) {
191 lines.push_back(" element ids in second but not first:");
192 for (const int64_t id : Sorted(el_diff.ids.only_in_second)) {
193 lines.push_back(
194 absl::StrCat(" ", ElementDebugString(second, e, id)));
195 }
196 }
197 }
198 if (!el_diff.different_names.empty()) {
199 lines.push_back(" element ids with disagreeing names:");
200 for (const int64_t id : Sorted(el_diff.different_names)) {
201 absl::StatusOr<absl::string_view> first_name =
202 first.GetElementNameUntyped(e, id);
203 absl::StatusOr<absl::string_view> second_name =
204 second.GetElementNameUntyped(e, id);
205 CHECK_OK(first_name);
206 CHECK_OK(second_name);
207 lines.push_back(absl::StrCat(
208 " id: ", id, " first_name: \"", absl::CEscape(*first_name),
209 "\" second_name: \"", absl::CEscape(*second_name), "\""));
210 }
211 }
212 if (el_diff.next_id_different) {
213 lines.push_back(" next_id does not agree:");
214 lines.push_back(absl::StrCat(" first: ", first.NextElementId(e)));
215 lines.push_back(absl::StrCat(" second: ", second.NextElementId(e)));
216 }
217 }
218 }
220 // NOLINTNEXTLINE(clang-diagnostic-pre-c++20-compat)
221 [&lines, &difference, &first, &second]<typename AttrType>(AttrType attr) {
222 const auto& attr_diff = difference.attr_difference(attr);
223 auto attr_value_str = [attr](const Elemental& elemental,
225 auto value = elemental.GetAttr<Elemental::StatusPolicy>(attr, key);
226 if (!value.ok()) {
227 return std::string("__missing__");
228 }
229 return FormatAttrValue(*value);
230 };
231 if (!attr_diff.Empty()) {
232 lines.push_back(absl::StrCat("For attribute ", attr,
233 " errors on the following keys:"));
234 for (AttrKeyFor<AttrType> key : attr_diff.AllKeysSorted()) {
235 lines.push_back(absl::StrCat(
236 " key: ", key,
237 " (name in first: ", KeyDebugString(first, attr, key),
238 ") value in first: ", attr_value_str(first, key),
239 " (name in second: ", KeyDebugString(second, attr, key),
240 ") value in second: ", attr_value_str(second, key)));
241 }
242 }
243 });
244 return absl::StrJoin(lines, "\n");
245}
246
248 const Elemental& first, const Elemental& second,
249 const ElementalDifferenceOptions& options) {
250 return Describe(first, second, Create(first, second, options));
251}
252
253} // namespace operations_research::math_opt
const AttributeDifference< AttrType > & attr_difference(const AttrType a) const
AttributeDifference< AttrType > & mutable_attr_difference(const AttrType a)
static ElementalDifference Create(const Elemental &first, const Elemental &second, const ElementalDifferenceOptions &options={})
Returns the difference between two Elementals.
ElementDifference & mutable_element_difference(const ElementType e)
const ElementDifference & element_difference(const ElementType e) const
static std::string Describe(const Elemental &first, const Elemental &second, const ElementalDifference &difference)
static std::string DescribeDifference(const Elemental &first, const Elemental &second, const ElementalDifferenceOptions &options={})
Returns a string describing the difference between two models.
absl::StatusOr< absl::string_view > GetElementNameUntyped(const ElementType e, const int64_t id) const
Type-erased version of GetElementName. Prefer the latter.
Definition elemental.h:128
std::vector< AttrKeyFor< AttrType > > AttrNonDefaults(const AttrType a) const
Return the vector of attribute keys where a is non-default.
Definition elemental.h:215
const std::string & primary_objective_name() const
The name of the primary objective of this optimization model.
Definition elemental.h:75
std::vector< int64_t > AllElementsUntyped(const ElementType e) const
Type-erased version of AllElements. Prefer the latter.
Definition elemental.h:141
const std::string & model_name() const
The name of this optimization model.
Definition elemental.h:72
Policy::template Wrapped< ValueTypeFor< AttrType > > GetAttr(AttrType a, AttrKeyFor< AttrType > key) const
Definition elemental.h:434
int64_t NextElementId(const ElementType e) const
Definition elemental.h:149
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
AttrKey< AttrTypeDescriptorT< AttrType >::kNumKeyElements, typename AttrTypeDescriptorT< AttrType >::Symmetry > AttrKeyFor
The type of the AttrKey for attribute type AttrType.
absl::flat_hash_set< T > IntersectSets(const absl::flat_hash_set< T > &first, const absl::flat_hash_set< T > &second)
Returns the elements in both first and second.
constexpr std::array< ElementType, GetAttrKeySize< attr >()> GetElementTypes()
std::string FormatAttrValue(const ValueType v)
bool next_id_different
The value of next_id for this ElementType differs.
bool Empty() const
Indicates there are no differences for this ElementType.
SymmetricDifference< int64_t > ids
Element ids in one elemental but not in the other.
absl::flat_hash_set< int64_t > different_names
Element ids in both elementals where the element names disagree.