| 150 | |
| 151 | template <typename TensorProtosEquality> |
| 152 | bool AreAttrValuesEqual(const AttrValue& a, const AttrValue& b, |
| 153 | TensorProtosEquality tensor_equality) { |
| 154 | if (a.type() != b.type()) { |
| 155 | return false; |
| 156 | } else if (a.type() != DT_INVALID && b.type() != DT_INVALID) { |
| 157 | return a.type() == b.type(); |
| 158 | } |
| 159 | |
| 160 | if (a.has_tensor() != b.has_tensor()) { |
| 161 | return false; |
| 162 | } else if (a.has_tensor() && b.has_tensor()) { |
| 163 | return tensor_equality(a.tensor(), b.tensor()); |
| 164 | } |
| 165 | |
| 166 | // `func` field contains a nested AttrValue. Compare such AttrValues |
| 167 | // recursively. |
| 168 | if (a.has_func() != b.has_func()) { |
| 169 | return false; |
| 170 | } else if (a.has_func() && b.has_func()) { |
| 171 | const NameAttrList& af = a.func(); |
| 172 | const NameAttrList& bf = b.func(); |
| 173 | if (af.name() != bf.name()) return false; |
| 174 | std::unordered_map<string, AttrValue> am(af.attr().begin(), |
| 175 | af.attr().end()); |
| 176 | for (const auto& bm_pair : bf.attr()) { |
| 177 | const auto& iter = am.find(bm_pair.first); |
| 178 | if (iter == am.end()) return false; |
| 179 | if (!AreAttrValuesEqual(iter->second, bm_pair.second, tensor_equality)) |
| 180 | return false; |
| 181 | am.erase(iter); |
| 182 | } |
| 183 | if (!am.empty()) return false; |
| 184 | return true; |
| 185 | } |
| 186 | |
| 187 | // All other fields in AttrValue have deterministic representations. |
| 188 | // It is safe to compare their serialized strings. |
| 189 | return AreSerializedProtosEqual(a, b); |
| 190 | } |
| 191 | |
| 192 | string SummarizeString(const string& str) { |
| 193 | string escaped = absl::CEscape(str); |