• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/core/kernels/data/hash_utils.h"
17 
18 #include "tensorflow/core/framework/function.h"
19 #include "tensorflow/core/framework/function.pb.h"
20 #include "tensorflow/core/framework/node_def_builder.h"
21 #include "tensorflow/core/framework/op.h"
22 #include "tensorflow/core/framework/types.pb.h"
23 #include "tensorflow/core/framework/variant.h"
24 #include "tensorflow/core/kernels/data/dataset_test_base.h"
25 #include "tensorflow/core/lib/core/status_test_util.h"
26 #include "tensorflow/core/platform/test.h"
27 #include "tensorflow/core/protobuf/error_codes.pb.h"
28 #include "tensorflow/core/util/work_sharder.h"
29 
30 namespace tensorflow {
31 namespace data {
32 namespace {
33 using ::testing::ContainsRegex;
34 
35 class DatasetHashUtilsTest : public ::testing::Test {
36  protected:
GetHash(const FunctionDefLibrary & library,const FunctionDef & fn)37   uint64 GetHash(const FunctionDefLibrary& library, const FunctionDef& fn) {
38     // Construct a node with a function as an attr.
39     GraphDef graph_def;
40     *graph_def.mutable_library() = library;
41     NodeDef* node = graph_def.add_node();
42     node->set_op("RemoteCall");
43     NameAttrList func;
44     func.set_name(fn.signature().name());
45     AddNodeAttr("f", func, node);
46     uint64 hash = 0;
47     TF_CHECK_OK(HashNode(graph_def, *node, &hash));
48     return hash;
49   }
50 
CheckEqual(const FunctionDefLibrary & library,const FunctionDef & fn1,const FunctionDef & fn2)51   Status CheckEqual(const FunctionDefLibrary& library, const FunctionDef& fn1,
52                     const FunctionDef& fn2) {
53     // Construct nodes with a function as an attr.
54     GraphDef graph_def;
55     *graph_def.mutable_library() = library;
56 
57     NodeDef* node1 = graph_def.add_node();
58     node1->set_op("RemoteCall");
59     NameAttrList func1;
60     func1.set_name(fn1.signature().name());
61     AddNodeAttr("f", func1, node1);
62 
63     NodeDef* node2 = graph_def.add_node();
64     node2->set_op("RemoteCall");
65     NameAttrList func2;
66     func2.set_name(fn2.signature().name());
67     AddNodeAttr("f", func2, node2);
68 
69     return CheckSubgraphsEqual(graph_def, node1, graph_def, node2);
70   }
71 
GetHash(const GraphDef & graph,const NodeDef & node)72   uint64 GetHash(const GraphDef& graph, const NodeDef& node) {
73     uint64 hash = 0;
74     TF_CHECK_OK(HashNode(graph, node, &hash));
75     return hash;
76   }
77 
GetHash(const Tensor & tensor)78   uint64 GetHash(const Tensor& tensor) {
79     uint64 hash = 0;
80     TF_CHECK_OK(HashTensor(tensor, &hash));
81     return hash;
82   }
83 };
84 
TEST_F(DatasetHashUtilsTest,HashFunctionSameFunctionDifferentNames)85 TEST_F(DatasetHashUtilsTest, HashFunctionSameFunctionDifferentNames) {
86   FunctionDefLibrary fl;
87 
88   FunctionDef* f1 = fl.add_function();
89   *f1 = FunctionDefHelper::Create(
90       "AddAndMul", {"i: float"}, {"o: float"}, {},
91       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
92        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
93       /*ret_def=*/{{"o", "ret:z:0"}},
94       /*control_ret_def=*/{{"must_execute", "add"}});
95 
96   FunctionDef* f2 = fl.add_function();
97   *f2 = FunctionDefHelper::Create(
98       "AddAndMul2", {"input: float"}, {"o: float"}, {},
99       {{{"add"}, "Add", {"input", "input"}, {{"T", DT_FLOAT}}},
100        {{"ret"}, "Mul", {"input", "input"}, {{"T", DT_FLOAT}}}},
101       /*ret_def=*/{{"o", "ret:z:0"}},
102       /*control_ret_def=*/{{"must_execute", "add"}});
103 
104   EXPECT_EQ(GetHash(fl, *f1), GetHash(fl, *f2));
105   TF_EXPECT_OK(CheckEqual(fl, *f1, *f2));
106 }
107 
TEST_F(DatasetHashUtilsTest,HashFunctionDifferentFunctions)108 TEST_F(DatasetHashUtilsTest, HashFunctionDifferentFunctions) {
109   FunctionDefLibrary fl;
110 
111   FunctionDef* f1 = fl.add_function();
112   *f1 = FunctionDefHelper::Create(
113       "AddAndMul", {"i: float"}, {"o: float"}, {},
114       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
115        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
116       /*ret_def=*/{{"o", "ret:z:0"}},
117       /*control_ret_def=*/{{"must_execute", "add"}});
118 
119   FunctionDef* f2 = fl.add_function();
120   *f2 = FunctionDefHelper::Create(
121       "AddAndAdd", {"i: float"}, {"o: float"}, {},
122       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
123        {{"ret"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}}},
124       /*ret_def=*/{{"o", "ret:z:0"}},
125       /*control_ret_def=*/{{"must_execute", "add"}});
126 
127   // The second op in `f2` is changed to "Add"
128   EXPECT_NE(GetHash(fl, *f1), GetHash(fl, *f2));
129   Status s = CheckEqual(fl, *f1, *f2);
130   EXPECT_NE(s.code(), error::OK);
131   EXPECT_THAT(s.error_message(), ContainsRegex("Add"));
132 }
133 
TEST_F(DatasetHashUtilsTest,HashFunctionDifferentInternalNodeNames)134 TEST_F(DatasetHashUtilsTest, HashFunctionDifferentInternalNodeNames) {
135   FunctionDefLibrary fl;
136 
137   FunctionDef* f1 = fl.add_function();
138   *f1 = FunctionDefHelper::Create(
139       "AddAndMul", {"i: float", "j: float", "k: float"}, {"o: float"}, {},
140       {{{"add"}, "Add", {"i", "j"}, {{"T", DT_FLOAT}}},
141        {{"ret"}, "Mul", {"add:z:0", "k"}, {{"T", DT_FLOAT}}}},
142       /*ret_def=*/{{"o", "ret:z:0"}},
143       /*control_ret_def=*/{{"must_execute", "ret"}});
144 
145   FunctionDef* f2 = fl.add_function();
146   *f2 = FunctionDefHelper::Create(
147       "AddAndMul", {"a: float", "b: float", "c: float"}, {"o: float"}, {},
148       {{{"add"}, "Add", {"a", "b"}, {{"T", DT_FLOAT}}},
149        {{"mul"}, "Mul", {"add:z:0", "c"}, {{"T", DT_FLOAT}}}},
150       /*ret_def=*/{{"o", "mul:z:0"}},
151       /*control_ret_def=*/{{"must_execute", "mul"}});
152 
153   EXPECT_EQ(GetHash(fl, *f1), GetHash(fl, *f2));
154   TF_EXPECT_OK(CheckEqual(fl, *f1, *f2));
155 }
156 
TEST_F(DatasetHashUtilsTest,HashGraphWithMultipleCycles)157 TEST_F(DatasetHashUtilsTest, HashGraphWithMultipleCycles) {
158   uint64 hash = 0;
159   for (int i = 0; i < 1000; ++i) {
160     GraphDef g;
161     NodeDef* output_node = g.add_node();
162     TF_CHECK_OK(NodeDefBuilder("O", "Add")
163                     .Input("A", 0, DT_FLOAT)
164                     .Input("D", 0, DT_FLOAT)
165                     .Finalize(output_node));
166     TF_CHECK_OK(NodeDefBuilder("A", "Abs")
167                     .Input("B", 0, DT_FLOAT)
168                     .Finalize(g.add_node()));
169     TF_CHECK_OK(NodeDefBuilder("B", "Add")
170                     .Input("C", 0, DT_FLOAT)
171                     .Input("D", 0, DT_FLOAT)
172                     .Finalize(g.add_node()));
173     TF_CHECK_OK(NodeDefBuilder("C", "Ceil")
174                     .Input("A", 0, DT_FLOAT)
175                     .Finalize(g.add_node()));
176     TF_CHECK_OK(NodeDefBuilder("D", "Cos")
177                     .Input("E", 0, DT_FLOAT)
178                     .Finalize(g.add_node()));
179     TF_CHECK_OK(NodeDefBuilder("E", "Floor")
180                     .Input("B", 0, DT_FLOAT)
181                     .Finalize(g.add_node()));
182     uint64 t = GetHash(g, *output_node);
183     if (hash == 0) {
184       hash = t;
185     } else {
186       EXPECT_EQ(t, hash);
187     }
188   }
189 }
190 
TEST_F(DatasetHashUtilsTest,HashNodeSameGraphDifferentNames)191 TEST_F(DatasetHashUtilsTest, HashNodeSameGraphDifferentNames) {
192   GraphDef gd;
193 
194   NodeDef* n1 = gd.add_node();
195   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
196                   .Attr("value", 1)
197                   .Device("CPU:0")
198                   .Finalize(n1));
199 
200   NodeDef* n2 = gd.add_node();
201   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
202                   .Attr("value", 2)
203                   .Device("CPU:0")
204                   .Finalize(n2));
205 
206   NodeDef* n3 = gd.add_node();
207   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
208                   .Device("CPU:0")
209                   .Input(n1->name(), 0, DT_INT32)
210                   .Input(n2->name(), 0, DT_INT32)
211                   .Finalize(n3));
212 
213   NodeDef* n4 = gd.add_node();
214   TF_CHECK_OK(NodeDefBuilder("graph_3/node_7", "Const")
215                   .Attr("value", 1)
216                   .Device("CPU:0")
217                   .Finalize(n4));
218 
219   NodeDef* n5 = gd.add_node();
220   TF_CHECK_OK(NodeDefBuilder("graph_4/node_9", "Const")
221                   .Attr("value", 2)
222                   .Device("CPU:0")
223                   .Finalize(n5));
224 
225   NodeDef* n6 = gd.add_node();
226   TF_CHECK_OK(NodeDefBuilder("graph_5/node_11", "Add")
227                   .Device("CPU:0")
228                   .Input(n4->name(), 0, DT_INT32)
229                   .Input(n5->name(), 0, DT_INT32)
230                   .Finalize(n6));
231 
232   uint64 hash1 = GetHash(gd, *n3);
233   uint64 hash2 = GetHash(gd, *n6);
234   EXPECT_EQ(hash1, hash2);
235   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n3, gd, n6));
236 }
237 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentGraphs)238 TEST_F(DatasetHashUtilsTest, HashNodeDifferentGraphs) {
239   GraphDef gd;
240 
241   NodeDef* n1 = gd.add_node();
242   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
243                   .Attr("value", 1)
244                   .Device("CPU:0")
245                   .Finalize(n1));
246 
247   NodeDef* n2 = gd.add_node();
248   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
249                   .Attr("value", 2)
250                   .Device("CPU:0")
251                   .Finalize(n2));
252 
253   NodeDef* n3 = gd.add_node();
254   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
255                   .Device("CPU:0")
256                   .Input(n1->name(), 0, DT_INT32)
257                   .Input(n2->name(), 0, DT_INT32)
258                   .Finalize(n3));
259 
260   NodeDef* n4 = gd.add_node();
261   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Mul")
262                   .Device("CPU:0")
263                   .Input(n1->name(), 0, DT_INT32)
264                   .Input(n2->name(), 0, DT_INT32)
265                   .Finalize(n4));
266 
267   uint64 hash1 = GetHash(gd, *n3);
268   uint64 hash2 = GetHash(gd, *n4);
269   // We expect different hashes because the op has changed.
270   EXPECT_NE(hash1, hash2);
271   Status s = CheckSubgraphsEqual(gd, n3, gd, n4);
272   EXPECT_NE(s.code(), error::OK);
273   EXPECT_THAT(s.error_message(), ContainsRegex("Add"));
274   EXPECT_THAT(s.error_message(), ContainsRegex("Mul"));
275 }
276 
TEST_F(DatasetHashUtilsTest,HashSameGraphDifferentSeeds)277 TEST_F(DatasetHashUtilsTest, HashSameGraphDifferentSeeds) {
278   GraphDef gd;
279 
280   NodeDef* n1 = gd.add_node();
281   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
282                   .Attr("value", 1)
283                   .Device("CPU:0")
284                   .Finalize(n1));
285 
286   NodeDef* seed = gd.add_node();
287   TF_CHECK_OK(NodeDefBuilder("graph_1/seed", "Const")
288                   .Attr("value", 123)
289                   .Device("CPU:0")
290                   .Finalize(seed));
291 
292   NodeDef* seed2 = gd.add_node();
293   TF_CHECK_OK(NodeDefBuilder("graph_1/seed2", "Const")
294                   .Attr("value", 456)
295                   .Device("CPU:0")
296                   .Finalize(seed2));
297 
298   NodeDef* range_ds = gd.add_node();
299   TF_CHECK_OK(NodeDefBuilder("graph_1/range", "RangeDataset")
300                   .Input(n1->name(), 0, DT_INT64)
301                   .Input(n1->name(), 0, DT_INT64)
302                   .Input(n1->name(), 0, DT_INT64)
303                   .Device("CPU:0")
304                   .Finalize(range_ds));
305 
306   NodeDef* shuffle_ds = gd.add_node();
307   TF_CHECK_OK(NodeDefBuilder("graph_1/shuffle", "ShuffleDataset")
308                   .Input(range_ds->name(), 0, DT_VARIANT)
309                   .Input(n1->name(), 0, DT_INT64)
310                   .Input(seed->name(), 0, DT_INT64)
311                   .Input(seed2->name(), 0, DT_INT64)
312                   .Device("CPU:0")
313                   .Finalize(shuffle_ds));
314 
315   NodeDef* different_seed = gd.add_node();
316   TF_CHECK_OK(NodeDefBuilder("graph_1/different_seed", "Const")
317                   .Attr("value", 789)
318                   .Device("CPU:0")
319                   .Finalize(different_seed));
320   NodeDef* different_seed2 = gd.add_node();
321   TF_CHECK_OK(NodeDefBuilder("graph_1/different_seed2", "Const")
322                   .Attr("value", 654)
323                   .Device("CPU:0")
324                   .Finalize(different_seed2));
325 
326   NodeDef* range_ds_2 = gd.add_node();
327   TF_CHECK_OK(NodeDefBuilder("graph_1/range_2", "RangeDataset")
328                   .Input(n1->name(), 0, DT_INT64)
329                   .Input(n1->name(), 0, DT_INT64)
330                   .Input(n1->name(), 0, DT_INT64)
331                   .Device("CPU:0")
332                   .Finalize(range_ds_2));
333 
334   NodeDef* shuffle_ds_2 = gd.add_node();
335   TF_CHECK_OK(NodeDefBuilder("graph_1/shuffle_2", "ShuffleDataset")
336                   .Input(range_ds_2->name(), 0, DT_VARIANT)
337                   .Input(n1->name(), 0, DT_INT64)
338                   .Input(different_seed->name(), 0, DT_INT64)
339                   .Input(different_seed2->name(), 0, DT_INT64)
340                   .Device("CPU:0")
341                   .Finalize(shuffle_ds_2));
342 
343   uint64 hash1 = GetHash(gd, *shuffle_ds);
344   uint64 hash2 = GetHash(gd, *shuffle_ds_2);
345   EXPECT_EQ(hash1, hash2);
346   TF_EXPECT_OK(CheckSubgraphsEqual(gd, shuffle_ds, gd, shuffle_ds_2));
347 }
348 
TEST_F(DatasetHashUtilsTest,HashNodeSameGraphDifferentColocationNames)349 TEST_F(DatasetHashUtilsTest, HashNodeSameGraphDifferentColocationNames) {
350   GraphDef gd;
351 
352   NodeDef* n1 = gd.add_node();
353   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
354                   .Attr("value", 1)
355                   .Attr("_class", {"graph_1/node_2"})
356                   .Device("CPU:0")
357                   .Finalize(n1));
358 
359   NodeDef* n2 = gd.add_node();
360   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
361                   .Attr("value", 2)
362                   .Device("CPU:0")
363                   .Finalize(n2));
364 
365   NodeDef* n3 = gd.add_node();
366   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
367                   .Device("CPU:0")
368                   .Input(n1->name(), 0, DT_INT32)
369                   .Input(n2->name(), 0, DT_INT32)
370                   .Finalize(n3));
371 
372   NodeDef* n4 = gd.add_node();
373   TF_CHECK_OK(NodeDefBuilder("graph_3/node_7", "Const")
374                   .Attr("value", 1)
375                   .Attr("_class", {"graph_3/node_9"})
376                   .Device("CPU:0")
377                   .Finalize(n4));
378 
379   NodeDef* n5 = gd.add_node();
380   TF_CHECK_OK(NodeDefBuilder("graph_4/node_9", "Const")
381                   .Attr("value", 2)
382                   .Device("CPU:0")
383                   .Finalize(n5));
384 
385   NodeDef* n6 = gd.add_node();
386   TF_CHECK_OK(NodeDefBuilder("graph_5/node_11", "Add")
387                   .Device("CPU:0")
388                   .Input(n1->name(), 0, DT_INT32)
389                   .Input(n2->name(), 0, DT_INT32)
390                   .Finalize(n6));
391 
392   uint64 hash1 = GetHash(gd, *n3);
393   uint64 hash2 = GetHash(gd, *n6);
394 
395   EXPECT_EQ(hash1, hash2);
396   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n3, gd, n6));
397 }
398 
TEST_F(DatasetHashUtilsTest,HashNodeReversedOrder)399 TEST_F(DatasetHashUtilsTest, HashNodeReversedOrder) {
400   GraphDef gd;
401 
402   NodeDef* n1 = gd.add_node();
403   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
404                   .Attr("value", 1)
405                   .Device("CPU:0")
406                   .Finalize(n1));
407 
408   NodeDef* n2 = gd.add_node();
409   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
410                   .Attr("value", 2)
411                   .Device("CPU:0")
412                   .Finalize(n2));
413 
414   NodeDef* n3 = gd.add_node();
415   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
416                   .Device("CPU:0")
417                   .Input(n1->name(), 0, DT_INT32)
418                   .Input(n2->name(), 0, DT_INT32)
419                   .Finalize(n3));
420 
421   NodeDef* n4 = gd.add_node();
422   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
423                   .Device("CPU:0")
424                   .Input(n2->name(), 0, DT_INT32)
425                   .Input(n1->name(), 0, DT_INT32)
426                   .Finalize(n4));
427 
428   uint64 hash1 = GetHash(gd, *n3);
429   uint64 hash2 = GetHash(gd, *n4);
430   // We expect different hashes because the inputs of n3 are swapped.
431   EXPECT_NE(hash1, hash2);
432   Status s = CheckSubgraphsEqual(gd, n3, gd, n4);
433   EXPECT_NE(s.code(), error::OK);
434   EXPECT_THAT(s.error_message(), ContainsRegex("AttrValues are different"));
435 }
436 
TEST_F(DatasetHashUtilsTest,HashNodeInputPortChanged)437 TEST_F(DatasetHashUtilsTest, HashNodeInputPortChanged) {
438   GraphDef gd;
439 
440   NodeDef* n1 = gd.add_node();
441   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
442                   .Attr("value", 1)
443                   .Device("CPU:0")
444                   .Finalize(n1));
445 
446   NodeDef* n2 = gd.add_node();
447   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
448                   .Attr("value", 2)
449                   .Device("CPU:0")
450                   .Finalize(n2));
451 
452   NodeDef* n3 = gd.add_node();
453   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
454                   .Device("CPU:0")
455                   .Input(n1->name(), 0, DT_INT32)
456                   .Input(n2->name(), 0, DT_INT32)
457                   .Finalize(n3));
458 
459   NodeDef* n4 = gd.add_node();
460   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
461                   .Device("CPU:0")
462                   .Input(n1->name(), 1, DT_INT32)
463                   .Input(n2->name(), 2, DT_INT32)
464                   .Finalize(n4));
465 
466   uint64 hash1 = GetHash(gd, *n3);
467   uint64 hash2 = GetHash(gd, *n4);
468   // We expect different hashes because the input ports for nodes used by n3
469   // has changed.
470   EXPECT_NE(hash1, hash2);
471   Status s = CheckSubgraphsEqual(gd, n3, gd, n4);
472   EXPECT_NE(s.code(), error::OK);
473   EXPECT_THAT(s.error_message(), ContainsRegex("Node inputs"));
474 }
475 
TEST_F(DatasetHashUtilsTest,HashNodeSameFunctionDifferentNames)476 TEST_F(DatasetHashUtilsTest, HashNodeSameFunctionDifferentNames) {
477   GraphDef gd;
478   FunctionDefLibrary* fl1 = gd.mutable_library();
479 
480   FunctionDef* f1 = fl1->add_function();
481   *f1 = FunctionDefHelper::Create(
482       "AddAndMul", {"i: float"}, {"o: float"}, {},
483       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
484        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
485       /*ret_def=*/{{"o", "ret:z:0"}},
486       /*control_ret_def=*/{{"must_execute", "add"}});
487 
488   FunctionDef* f2 = fl1->add_function();
489   *f2 = FunctionDefHelper::Create(
490       "AddAndMul2", {"input: float"}, {"o: float"}, {},
491       {{{"add"}, "Add", {"input", "input"}, {{"T", DT_FLOAT}}},
492        {{"ret"}, "Mul", {"input", "input"}, {{"T", DT_FLOAT}}}},
493       /*ret_def=*/{{"o", "ret:z:0"}},
494       /*control_ret_def=*/{{"must_execute", "add"}});
495 
496   AttrValue a1;
497   NameAttrList* nal1 = a1.mutable_func();
498   nal1->set_name("AddAndMul");
499 
500   NodeDef* n1 = gd.add_node();
501   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
502                   .Attr("value", 1)
503                   .Device("CPU:0")
504                   .Finalize(n1));
505 
506   std::vector<NodeDefBuilder::NodeOut> func_inputs;
507   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
508   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
509 
510   NodeDef* n2 = gd.add_node();
511   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
512                   .Input(n1->name(), 0, DT_INT32)
513                   .Input(n1->name(), 0, DT_INT32)
514                   .Input(n1->name(), 0, DT_INT32)
515                   .Input(func_inputs)
516                   .Attr("body", a1)
517                   .Device("CPU:0")
518                   .Finalize(n2));
519 
520   NodeDef* n3 = gd.add_node();
521   AttrValue a2;
522   NameAttrList* nal2 = a2.mutable_func();
523   nal2->set_name("AddAndMul2");
524 
525   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
526                   .Input(n1->name(), 0, DT_INT32)
527                   .Input(n1->name(), 0, DT_INT32)
528                   .Input(n1->name(), 0, DT_INT32)
529                   .Input(func_inputs)
530                   .Attr("body", a2)
531                   .Device("CPU:0")
532                   .Finalize(n3));
533 
534   uint64 hash1 = GetHash(gd, *n2);
535   uint64 hash2 = GetHash(gd, *n3);
536   EXPECT_EQ(hash1, hash2);
537   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n2, gd, n3));
538 }
539 
TEST_F(DatasetHashUtilsTest,HashNodeSameFunctionListsDifferentNames)540 TEST_F(DatasetHashUtilsTest, HashNodeSameFunctionListsDifferentNames) {
541   GraphDef gd;
542   FunctionDefLibrary* fl1 = gd.mutable_library();
543 
544   FunctionDef* f1 = fl1->add_function();
545   *f1 = FunctionDefHelper::Create(
546       "AddAndMul", {"i: float"}, {"o: float"}, {},
547       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
548        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
549       /*ret_def=*/{{"o", "ret:z:0"}},
550       /*control_ret_def=*/{{"must_execute", "add"}});
551 
552   FunctionDef* f2 = fl1->add_function();
553   *f2 = FunctionDefHelper::Create(
554       "AddAndMul2", {"input: float"}, {"o: float"}, {},
555       {{{"add"}, "Add", {"input", "input"}, {{"T", DT_FLOAT}}},
556        {{"ret"}, "Mul", {"input", "input"}, {{"T", DT_FLOAT}}}},
557       /*ret_def=*/{{"o", "ret:z:0"}},
558       /*control_ret_def=*/{{"must_execute", "add"}});
559 
560   AttrValue a1;
561   AttrValue_ListValue* list1 = a1.mutable_list();
562   NameAttrList* nal1 = list1->add_func();
563   nal1->set_name("AddAndMul");
564 
565   NodeDef* n1 = gd.add_node();
566   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
567                   .Attr("value", 1)
568                   .Device("CPU:0")
569                   .Finalize(n1));
570 
571   std::vector<NodeDefBuilder::NodeOut> func_inputs;
572   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
573   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
574 
575   NodeDef* n2 = gd.add_node();
576   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
577                   .Input(n1->name(), 0, DT_INT32)
578                   .Input(n1->name(), 0, DT_INT32)
579                   .Input(n1->name(), 0, DT_INT32)
580                   .Input(func_inputs)
581                   .Attr("body", a1)
582                   .Device("CPU:0")
583                   .Finalize(n2));
584 
585   NodeDef* n3 = gd.add_node();
586   AttrValue a2;
587   AttrValue_ListValue* list2 = a2.mutable_list();
588   NameAttrList* nal2 = list2->add_func();
589   nal2->set_name("AddAndMul2");
590 
591   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
592                   .Input(n1->name(), 0, DT_INT32)
593                   .Input(n1->name(), 0, DT_INT32)
594                   .Input(n1->name(), 0, DT_INT32)
595                   .Input(func_inputs)
596                   .Attr("body", a2)
597                   .Device("CPU:0")
598                   .Finalize(n3));
599 
600   uint64 hash1 = GetHash(gd, *n2);
601   uint64 hash2 = GetHash(gd, *n3);
602   EXPECT_EQ(hash1, hash2);
603   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n2, gd, n3));
604 }
605 
TEST_F(DatasetHashUtilsTest,HashNodeSameFunctionsOps)606 TEST_F(DatasetHashUtilsTest, HashNodeSameFunctionsOps) {
607   GraphDef gd;
608 
609   FunctionDefLibrary* fl1 = gd.mutable_library();
610   FunctionDef* f1 = fl1->add_function();
611 
612   FunctionDef func = FunctionDefHelper::Create(
613       "AddAndMul", {"i: float"}, {"o: float"}, {},
614       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
615        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
616       /*ret_def=*/{{"o", "ret:z:0"}},
617       /*control_ret_def=*/{{"must_execute", "add"}});
618   *f1 = func;
619 
620   FunctionDef* f2 = fl1->add_function();
621   func = FunctionDefHelper::Create(
622       "AddAndMul2", {"i: float"}, {"o: float"}, {},
623       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
624        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
625       /*ret_def=*/{{"o", "ret:z:0"}},
626       /*control_ret_def=*/{{"must_execute", "add"}});
627   *f2 = func;
628   FunctionLibraryDefinition flib(OpRegistry::Global(), gd.library());
629 
630   NodeDef* n1 = gd.add_node();
631   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
632                   .Attr("value", 1)
633                   .Device("CPU:0")
634                   .Finalize(n1));
635 
636   NodeDef* n2 = gd.add_node();
637   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "AddAndMul", &flib)
638                   .Input(n1->name(), 0, DT_FLOAT)
639                   .Device("CPU:0")
640                   .Finalize(n2));
641 
642   NodeDef* n3 = gd.add_node();
643   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "AddAndMul2", &flib)
644                   .Input(n1->name(), 0, DT_FLOAT)
645                   .Device("CPU:0")
646                   .Finalize(n3));
647 
648   uint64 hash1 = GetHash(gd, *n2);
649   uint64 hash2 = GetHash(gd, *n3);
650   EXPECT_EQ(hash1, hash2);
651   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n2, gd, n3));
652 }
653 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentFunctionsOps)654 TEST_F(DatasetHashUtilsTest, HashNodeDifferentFunctionsOps) {
655   GraphDef gd;
656 
657   FunctionDefLibrary* fl1 = gd.mutable_library();
658   FunctionDef* f1 = fl1->add_function();
659 
660   FunctionDef func = FunctionDefHelper::Create(
661       "AddAndMul", {"i: float"}, {"o: float"}, {},
662       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
663        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
664       /*ret_def=*/{{"o", "ret:z:0"}},
665       /*control_ret_def=*/{{"must_execute", "add"}});
666   *f1 = func;
667 
668   FunctionDef* f2 = fl1->add_function();
669   func = FunctionDefHelper::Create(
670       "AddAndMul2", {"i: float"}, {"o: float"}, {},
671       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
672        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
673       /*ret_def=*/{{"o", "ret:z:0"}},
674       /*control_ret_def=*/{{"must_execute", "ret"}});
675   *f2 = func;
676   FunctionLibraryDefinition flib(OpRegistry::Global(), gd.library());
677 
678   NodeDef* n1 = gd.add_node();
679   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
680                   .Attr("value", 1)
681                   .Device("CPU:0")
682                   .Finalize(n1));
683 
684   NodeDef* n2 = gd.add_node();
685   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "AddAndMul", &flib)
686                   .Input(n1->name(), 0, DT_FLOAT)
687                   .Device("CPU:0")
688                   .Finalize(n2));
689 
690   NodeDef* n3 = gd.add_node();
691   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "AddAndMul2", &flib)
692                   .Input(n1->name(), 0, DT_FLOAT)
693                   .Device("CPU:0")
694                   .Finalize(n3));
695 
696   uint64 hash1 = GetHash(gd, *n2);
697   uint64 hash2 = GetHash(gd, *n3);
698   EXPECT_NE(hash1, hash2);
699   Status s = CheckSubgraphsEqual(gd, n2, gd, n3);
700   EXPECT_NE(s.code(), error::OK);
701   EXPECT_THAT(
702       s.error_message(),
703       ContainsRegex("Functions AddAndMul and AddAndMul2 are not the same"));
704 }
705 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentFunctions)706 TEST_F(DatasetHashUtilsTest, HashNodeDifferentFunctions) {
707   GraphDef gd;
708 
709   FunctionDefLibrary* fl1 = gd.mutable_library();
710   FunctionDef* f1 = fl1->add_function();
711 
712   FunctionDef func = FunctionDefHelper::Create(
713       "AddAndMul", {"i: float"}, {"o: float"}, {},
714       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
715        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
716       /*ret_def=*/{{"o", "ret:z:0"}},
717       /*control_ret_def=*/{{"must_execute", "add"}});
718   *f1 = func;
719 
720   FunctionDef* f2 = fl1->add_function();
721   func = FunctionDefHelper::Create(
722       "AddAndMul2", {"i: float"}, {"o: float"}, {},
723       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
724        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
725       /*ret_def=*/{{"o", "ret:z:0"}},
726       /*control_ret_def=*/{{"must_execute", "ret"}});
727   *f2 = func;
728 
729   AttrValue a1;
730   NameAttrList* nal1 = a1.mutable_func();
731   nal1->set_name("AddAndMul");
732 
733   NodeDef* n1 = gd.add_node();
734   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
735                   .Attr("value", 1)
736                   .Device("CPU:0")
737                   .Finalize(n1));
738 
739   std::vector<NodeDefBuilder::NodeOut> func_inputs;
740   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
741   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
742 
743   NodeDef* n2 = gd.add_node();
744   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
745                   .Input(n1->name(), 0, DT_INT32)
746                   .Input(n1->name(), 0, DT_INT32)
747                   .Input(n1->name(), 0, DT_INT32)
748                   .Input(func_inputs)
749                   .Attr("body", a1)
750                   .Device("CPU:0")
751                   .Finalize(n2));
752 
753   NodeDef* n3 = gd.add_node();
754   AttrValue a2;
755   NameAttrList* nal2 = a2.mutable_func();
756   nal2->set_name("AddAndMul2");
757 
758   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
759                   .Input(n1->name(), 0, DT_INT32)
760                   .Input(n1->name(), 0, DT_INT32)
761                   .Input(n1->name(), 0, DT_INT32)
762                   .Input(func_inputs)
763                   .Attr("body", a2)
764                   .Device("CPU:0")
765                   .Finalize(n3));
766 
767   uint64 hash1 = GetHash(gd, *n2);
768   uint64 hash2 = GetHash(gd, *n3);
769   EXPECT_NE(hash1, hash2);
770   Status s = CheckSubgraphsEqual(gd, n2, gd, n3);
771   EXPECT_NE(s.code(), error::OK);
772   EXPECT_THAT(
773       s.error_message(),
774       ContainsRegex("Functions AddAndMul and AddAndMul2 are not the same"));
775 }
776 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentFunctionLists)777 TEST_F(DatasetHashUtilsTest, HashNodeDifferentFunctionLists) {
778   GraphDef gd;
779 
780   FunctionDefLibrary* fl1 = gd.mutable_library();
781   FunctionDef* f1 = fl1->add_function();
782 
783   FunctionDef func = FunctionDefHelper::Create(
784       "AddAndMul", {"i: float"}, {"o: float"}, {},
785       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
786        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
787       /*ret_def=*/{{"o", "ret:z:0"}},
788       /*control_ret_def=*/{{"must_execute", "add"}});
789   *f1 = func;
790 
791   FunctionDef* f2 = fl1->add_function();
792   func = FunctionDefHelper::Create(
793       "AddAndMul2", {"i: float"}, {"o: float"}, {},
794       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}},
795        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
796       /*ret_def=*/{{"o", "ret:z:0"}},
797       /*control_ret_def=*/{{"must_execute", "ret"}});
798   *f2 = func;
799 
800   AttrValue a1;
801   AttrValue_ListValue* list1 = a1.mutable_list();
802   NameAttrList* nal1 = list1->add_func();
803   nal1->set_name("AddAndMul");
804 
805   NodeDef* n1 = gd.add_node();
806   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
807                   .Attr("value", 1)
808                   .Device("CPU:0")
809                   .Finalize(n1));
810 
811   std::vector<NodeDefBuilder::NodeOut> func_inputs;
812   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
813   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
814 
815   NodeDef* n2 = gd.add_node();
816   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
817                   .Input(n1->name(), 0, DT_INT32)
818                   .Input(n1->name(), 0, DT_INT32)
819                   .Input(n1->name(), 0, DT_INT32)
820                   .Input(func_inputs)
821                   .Attr("body", a1)
822                   .Device("CPU:0")
823                   .Finalize(n2));
824 
825   NodeDef* n3 = gd.add_node();
826   AttrValue a2;
827   AttrValue_ListValue* list2 = a2.mutable_list();
828   NameAttrList* nal2 = list2->add_func();
829   nal2->set_name("AddAndMul2");
830 
831   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
832                   .Input(n1->name(), 0, DT_INT32)
833                   .Input(n1->name(), 0, DT_INT32)
834                   .Input(n1->name(), 0, DT_INT32)
835                   .Input(func_inputs)
836                   .Attr("body", a2)
837                   .Device("CPU:0")
838                   .Finalize(n3));
839 
840   uint64 hash1 = GetHash(gd, *n2);
841   uint64 hash2 = GetHash(gd, *n3);
842   EXPECT_NE(hash1, hash2);
843   Status s = CheckSubgraphsEqual(gd, n2, gd, n3);
844   EXPECT_NE(s.code(), error::OK);
845   EXPECT_THAT(
846       s.error_message(),
847       ContainsRegex("Functions AddAndMul and AddAndMul2 are not the same"));
848 }
849 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentControlInputs)850 TEST_F(DatasetHashUtilsTest, HashNodeDifferentControlInputs) {
851   GraphDef gd;
852 
853   NodeDef* n1 = gd.add_node();
854   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
855                   .Attr("value", 1)
856                   .Device("CPU:0")
857                   .Finalize(n1));
858 
859   NodeDef* n2 = gd.add_node();
860   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
861                   .Attr("value", 2)
862                   .Device("CPU:0")
863                   .Finalize(n2));
864 
865   NodeDef* n3 = gd.add_node();
866   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Const")
867                   .Attr("value", 10)
868                   .Device("CPU:0")
869                   .Finalize(n3));
870 
871   NodeDef* n4 = gd.add_node();
872   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Identity")
873                   .Device("CPU:0")
874                   .Input(n1->name(), 0, DT_INT32)
875                   .ControlInput(n2->name())
876                   .Finalize(n4));
877 
878   NodeDef* n5 = gd.add_node();
879   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Identity")
880                   .Device("CPU:0")
881                   .Input(n1->name(), 0, DT_INT32)
882                   .ControlInput(n3->name())
883                   .Finalize(n5));
884 
885   // Control inputs are different between these two graphs.
886   uint64 hash1 = GetHash(gd, *n4);
887   uint64 hash2 = GetHash(gd, *n5);
888   EXPECT_NE(hash1, hash2);
889   Status s = CheckSubgraphsEqual(gd, n4, gd, n5);
890   EXPECT_NE(s.code(), error::OK);
891   EXPECT_THAT(s.error_message(),
892               ContainsRegex("Control dependencies are different"));
893 }
894 
TEST_F(DatasetHashUtilsTest,HashNodeControlInputDifferentOrdering)895 TEST_F(DatasetHashUtilsTest, HashNodeControlInputDifferentOrdering) {
896   GraphDef gd;
897 
898   NodeDef* n1 = gd.add_node();
899   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
900                   .Attr("value", 1)
901                   .Device("CPU:0")
902                   .Finalize(n1));
903 
904   NodeDef* n2 = gd.add_node();
905   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
906                   .Attr("value", 2)
907                   .Device("CPU:0")
908                   .Finalize(n2));
909 
910   NodeDef* n3 = gd.add_node();
911   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Const")
912                   .Attr("value", 10)
913                   .Device("CPU:0")
914                   .Finalize(n3));
915 
916   NodeDef* n4 = gd.add_node();
917   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Identity")
918                   .Device("CPU:0")
919                   .Input(n1->name(), 0, DT_INT32)
920                   .ControlInput(n2->name())
921                   .ControlInput(n3->name())
922                   .Finalize(n4));
923 
924   NodeDef* n5 = gd.add_node();
925   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Identity")
926                   .Device("CPU:0")
927                   .Input(n1->name(), 0, DT_INT32)
928                   .ControlInput(n3->name())
929                   .ControlInput(n2->name())
930                   .Finalize(n5));
931 
932   uint64 hash1 = GetHash(gd, *n4);
933   uint64 hash2 = GetHash(gd, *n5);
934   EXPECT_EQ(hash1, hash2);
935   TF_EXPECT_OK(CheckSubgraphsEqual(gd, n4, gd, n5));
936 }
937 
TEST_F(DatasetHashUtilsTest,HashNodeDifferentGraphSamePartialGraph)938 TEST_F(DatasetHashUtilsTest, HashNodeDifferentGraphSamePartialGraph) {
939   GraphDef gd;
940 
941   NodeDef* n1 = gd.add_node();
942   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
943                   .Attr("value", 1)
944                   .Device("CPU:0")
945                   .Finalize(n1));
946 
947   NodeDef* n2 = gd.add_node();
948   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
949                   .Attr("value", 2)
950                   .Device("CPU:0")
951                   .Finalize(n2));
952 
953   NodeDef* n3 = gd.add_node();
954 
955   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
956                   .Device("CPU:0")
957                   .Input(n1->name(), 0, DT_INT32)
958                   .Input(n2->name(), 0, DT_INT32)
959                   .Finalize(n3));
960 
961   uint64 hash1 = GetHash(gd, *n1);
962 
963   n3->Clear();
964   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Mul")
965                   .Device("CPU:0")
966                   .Input(n1->name(), 0, DT_INT32)
967                   .Input(n2->name(), 0, DT_INT32)
968                   .Finalize(n3));
969 
970   uint64 hash2 = GetHash(gd, *n1);
971 
972   EXPECT_EQ(hash1, hash2);
973 }
974 
TEST_F(DatasetHashUtilsTest,HashNodeWithManyControlDependencies)975 TEST_F(DatasetHashUtilsTest, HashNodeWithManyControlDependencies) {
976   GraphDef gd;
977   NodeDef* n;
978 
979   for (int i = 0; i < 1000; ++i) {
980     n = gd.add_node();
981     NodeDefBuilder ndb(absl::StrCat("graph_1/node_", i), "Const");
982     ndb.Attr("value", 1);
983     ndb.Device("CPU:0");
984     for (int j = 0; j < i; ++j) {
985       ndb.ControlInput(absl::StrCat("graph_1/node_", j));
986     }
987     TF_CHECK_OK(ndb.Finalize(n));
988   }
989 
990   // No checks here, because so long as this does not time out, we are OK.
991   GetHash(gd, *n);
992 }
993 
TEST_F(DatasetHashUtilsTest,HashFunctionsWithControlDependencyLoop)994 TEST_F(DatasetHashUtilsTest, HashFunctionsWithControlDependencyLoop) {
995   GraphDef gd;
996 
997   FunctionDefLibrary* fl1 = gd.mutable_library();
998   FunctionDef* f1 = fl1->add_function();
999 
1000   AttrValue a1;
1001   NameAttrList* nal1 = a1.mutable_func();
1002   nal1->set_name("AddAndMul");
1003 
1004   std::pair<string, FunctionDefHelper::AttrValueWrapper> func_attr = {
1005       "body", FunctionDefHelper::AttrValueWrapper(*nal1)};
1006 
1007   FunctionDef func = FunctionDefHelper::Create(
1008       /*function_name=*/"AddAndMul",
1009       /*in_def=*/{"i: float", "j: int32"},
1010       /*out_def=*/{"o: float"},
1011       /*attr_def=*/{},
1012       /*node_def=*/
1013       {{{"add"}, "Add", {"i", "i"}, {{"T", DT_FLOAT}}, {"ret"}},
1014        // This creates a dependency on the same function.
1015        {{"for"}, "For", {"j", "j", "j"}, {func_attr, {"T", DT_FLOAT}}, {"ret"}},
1016        {{"ret"}, "Mul", {"i", "i"}, {{"T", DT_FLOAT}}}},
1017       /*ret_def=*/{{"o", "ret:z:0"}},
1018       /*control_ret_def=*/{{"must_execute", "add"}});
1019   *f1 = func;
1020 
1021   NodeDef* n1 = gd.add_node();
1022   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
1023                   .Attr("value", 1)
1024                   .Device("CPU:0")
1025                   .Finalize(n1));
1026 
1027   std::vector<NodeDefBuilder::NodeOut> func_inputs;
1028   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
1029   func_inputs.emplace_back(n1->name(), 0, DT_FLOAT);
1030 
1031   NodeDef* n2 = gd.add_node();
1032   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "For")
1033                   .Input(n1->name(), 0, DT_INT32)
1034                   .Input(n1->name(), 0, DT_INT32)
1035                   .Input(n1->name(), 0, DT_INT32)
1036                   .Input(func_inputs)
1037                   .ControlInput("graph_1/node_2")
1038                   .Attr("body", a1)
1039                   .Device("CPU:0")
1040                   .Finalize(n2));
1041 
1042   // No checks in the test, the fact that it runs and doesn't timeout or exhaust
1043   // the stack means it is successful.
1044   GetHash(gd, *n2);
1045 }
1046 
TEST_F(DatasetHashUtilsTest,HashNodeWithControlDependencyLoop)1047 TEST_F(DatasetHashUtilsTest, HashNodeWithControlDependencyLoop) {
1048   GraphDef gd;
1049 
1050   NodeDef* n1 = gd.add_node();
1051   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
1052                   .Attr("value", 1)
1053                   .Device("CPU:0")
1054                   .ControlInput("graph_1/node_2")
1055                   .Finalize(n1));
1056 
1057   NodeDef* n2 = gd.add_node();
1058   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
1059                   .Attr("value", 2)
1060                   .Device("CPU:0")
1061                   .ControlInput("graph_1/node_1")
1062                   .Finalize(n2));
1063 
1064   NodeDef* n3 = gd.add_node();
1065   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
1066                   .Device("CPU:0")
1067                   .Input(n1->name(), 0, DT_INT32)
1068                   .Input(n2->name(), 0, DT_INT32)
1069                   .ControlInput("graph_1/node_1")
1070                   .ControlInput("graph_1/node_2")
1071                   .Finalize(n3));
1072 
1073   // No checks in the test, the fact that it runs and doesn't timeout or exhaust
1074   // the stack means it is successful.
1075   GetHash(gd, *n3);
1076 }
1077 
TEST_F(DatasetHashUtilsTest,HashNodeWithControlDependencyLoopDifferentNames)1078 TEST_F(DatasetHashUtilsTest, HashNodeWithControlDependencyLoopDifferentNames) {
1079   GraphDef gd1;
1080 
1081   NodeDef* n1 = gd1.add_node();
1082   TF_CHECK_OK(NodeDefBuilder("graph_1/node_1", "Const")
1083                   .Attr("value", 1)
1084                   .Device("CPU:0")
1085                   .ControlInput("graph_1/node_2")
1086                   .Finalize(n1));
1087 
1088   NodeDef* n2 = gd1.add_node();
1089   TF_CHECK_OK(NodeDefBuilder("graph_1/node_2", "Const")
1090                   .Attr("value", 2)
1091                   .Device("CPU:0")
1092                   .ControlInput("graph_1/node_1")
1093                   .Finalize(n2));
1094 
1095   NodeDef* n3 = gd1.add_node();
1096   TF_CHECK_OK(NodeDefBuilder("graph_1/node_3", "Add")
1097                   .Device("CPU:0")
1098                   .Input(n1->name(), 0, DT_INT32)
1099                   .Input(n2->name(), 0, DT_INT32)
1100                   .ControlInput("graph_1/node_1")
1101                   .ControlInput("graph_1/node_2")
1102                   .Finalize(n3));
1103 
1104   GraphDef gd2;
1105 
1106   NodeDef* n4 = gd2.add_node();
1107   TF_CHECK_OK(NodeDefBuilder("graph_1/node_4", "Const")
1108                   .Attr("value", 1)
1109                   .Device("CPU:0")
1110                   .ControlInput("graph_1/node_5")
1111                   .Finalize(n4));
1112 
1113   NodeDef* n5 = gd2.add_node();
1114   TF_CHECK_OK(NodeDefBuilder("graph_1/node_5", "Const")
1115                   .Attr("value", 2)
1116                   .Device("CPU:0")
1117                   .ControlInput("graph_1/node_4")
1118                   .Finalize(n5));
1119 
1120   NodeDef* n6 = gd2.add_node();
1121   TF_CHECK_OK(NodeDefBuilder("graph_1/node_6", "Add")
1122                   .Device("CPU:0")
1123                   .Input(n4->name(), 0, DT_INT32)
1124                   .Input(n5->name(), 0, DT_INT32)
1125                   .ControlInput("graph_1/node_4")
1126                   .ControlInput("graph_1/node_5")
1127                   .Finalize(n6));
1128 
1129   EXPECT_EQ(GetHash(gd1, *n3), GetHash(gd2, *n6));
1130 }
1131 
TEST_F(DatasetHashUtilsTest,HashInt32Tensor)1132 TEST_F(DatasetHashUtilsTest, HashInt32Tensor) {
1133   Tensor s1(42);
1134   Tensor s2(42);
1135   Tensor s3(43);
1136 
1137   EXPECT_EQ(GetHash(s1), GetHash(s2));
1138   EXPECT_NE(GetHash(s1), GetHash(s3));
1139 
1140   Tensor v1(DT_INT32, TensorShape({2}));
1141   v1.vec<int32>()(0) = 0;
1142   v1.vec<int32>()(1) = 1;
1143   Tensor v2(DT_INT32, TensorShape({2}));
1144   v2.vec<int32>()(0) = 0;
1145   v2.vec<int32>()(1) = 1;
1146   Tensor v3(DT_INT32, TensorShape({2}));
1147   v3.vec<int32>()(0) = 0;
1148   v3.vec<int32>()(1) = 2;
1149 
1150   EXPECT_EQ(GetHash(v1), GetHash(v2));
1151   EXPECT_NE(GetHash(v1), GetHash(v3));
1152 }
1153 
TEST_F(DatasetHashUtilsTest,HashStringTensor)1154 TEST_F(DatasetHashUtilsTest, HashStringTensor) {
1155   Tensor s1("hello");
1156   Tensor s2("hello");
1157   Tensor s3("world");
1158 
1159   EXPECT_EQ(GetHash(s1), GetHash(s2));
1160   EXPECT_NE(GetHash(s1), GetHash(s3));
1161 
1162   Tensor v1(DT_STRING, TensorShape({2}));
1163   v1.vec<tstring>()(0) = "hello";
1164   v1.vec<tstring>()(1) = "world";
1165   Tensor v2(DT_STRING, TensorShape({2}));
1166   v2.vec<tstring>()(0) = "hello";
1167   v2.vec<tstring>()(1) = "world";
1168   Tensor v3(DT_STRING, TensorShape({2}));
1169   v3.vec<tstring>()(0) = "hello";
1170   v3.vec<tstring>()(1) = "universe";
1171 
1172   EXPECT_EQ(GetHash(v1), GetHash(v2));
1173   EXPECT_NE(GetHash(v1), GetHash(v3));
1174 }
1175 
1176 }  // namespace
1177 }  // namespace data
1178 }  // namespace tensorflow
1179