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