• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 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/framework/function_testlib.h"
17 
18 #include "tensorflow/core/framework/function.h"
19 #include "tensorflow/core/framework/node_def.pb.h"
20 #include "tensorflow/core/framework/tensor_testutil.h"
21 #include "tensorflow/core/framework/versions.pb.h"
22 #include "tensorflow/core/lib/core/threadpool.h"
23 #include "tensorflow/core/public/version.h"
24 
25 namespace tensorflow {
26 namespace test {
27 namespace function {
28 
29 typedef FunctionDefHelper FDH;
30 
GDef(gtl::ArraySlice<NodeDef> nodes,gtl::ArraySlice<FunctionDef> funcs)31 GraphDef GDef(gtl::ArraySlice<NodeDef> nodes,
32               gtl::ArraySlice<FunctionDef> funcs) {
33   GraphDef g;
34   VersionDef* versions = g.mutable_versions();
35   versions->set_producer(TF_GRAPH_DEF_VERSION);
36   versions->set_min_consumer(TF_GRAPH_DEF_VERSION_MIN_CONSUMER);
37   for (const auto& n : nodes) {
38     *(g.add_node()) = n;
39   }
40   auto lib = g.mutable_library();
41   for (const auto& f : funcs) {
42     *(lib->add_function()) = f;
43   }
44   return g;
45 }
46 
47 // Helper to construct a NodeDef.
NDef(StringPiece name,StringPiece op,gtl::ArraySlice<string> inputs,gtl::ArraySlice<std::pair<string,FDH::AttrValueWrapper>> attrs,const string & device)48 NodeDef NDef(StringPiece name, StringPiece op, gtl::ArraySlice<string> inputs,
49              gtl::ArraySlice<std::pair<string, FDH::AttrValueWrapper>> attrs,
50              const string& device) {
51   NodeDef n;
52   n.set_name(string(name));
53   n.set_op(string(op));
54   for (const auto& in : inputs) n.add_input(in);
55   n.set_device(device);
56   for (const auto& na : attrs)
57     n.mutable_attr()->insert({na.first, na.second.proto});
58   return n;
59 }
60 
NonZero()61 FunctionDef NonZero() {
62   return FDH::Define(
63       // Name
64       "NonZero",
65       // Args
66       {"x:T"},
67       // Return values
68       {"y:T"},
69       // Attr def
70       {"T:{float, double, int32, int64, string}"},
71       // Nodes
72       {
73           {{"y"}, "Identity", {"x"}, {{"T", "$T"}}},
74       });
75 }
76 
IsZero()77 FunctionDef IsZero() {
78   const Tensor kZero = test::AsScalar<int64_t>(0);
79   return FDH::Define(
80       // Name
81       "IsZero",
82       // Args
83       {"x: T"},
84       // Return values
85       {"equal: bool"},
86       // Attr def
87       {"T:{float, double, int32, int64, string}"},
88       {
89           {{"zero"}, "Const", {}, {{"value", kZero}, {"dtype", DT_INT64}}},
90           {{"cast"}, "Cast", {"zero"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
91           {{"equal"}, "Equal", {"x", "cast"}, {{"T", "$T"}}},
92       });
93 }
94 
RandomUniform()95 FunctionDef RandomUniform() {
96   const Tensor kZero = test::AsScalar<int64_t>(0);
97 
98   return FDH::Define(
99       // Name
100       "RandomUniformFn",
101       // Args
102       {"x: T"},
103       // Return values
104       {"random_uniform: int64"},
105       // Attr def
106       {"T:{float, double, int32, int64, string}"},
107       // NodeDef
108       {{{"random_uniform/shape"},
109         "Const",
110         {},
111         {{"value", kZero}, {"dtype", DT_INT64}}},
112        {{"random_uniform"},
113         "RandomUniform",
114         {"random_uniform/shape"},
115         {{"T", DT_INT32},
116          {"dtype", DT_FLOAT},
117          {"seed", 87654321},
118          {"seed2", 42}}}});
119 }
120 
XTimesTwo()121 FunctionDef XTimesTwo() {
122   const Tensor kTwo = test::AsScalar<int64_t>(2);
123   return FDH::Define(
124       // Name
125       "XTimesTwo",
126       // Args
127       {"x: T"},
128       // Return values
129       {"y: T"},
130       // Attr def
131       {"T: {float, double, int32, int64}"},
132       // Nodes
133       {
134           {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
135           {{"scale"}, "Cast", {"two"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
136           {{"y"}, "Mul", {"x", "scale"}, {{"T", "$T"}}},
137       });
138 }
139 
TwoDeviceMult()140 FunctionDef TwoDeviceMult() {
141   const Tensor kTwo = test::AsScalar<int64_t>(2);
142   const Tensor kThree = test::AsScalar<int64_t>(3);
143   return FDH::Create(
144       // Name
145       "TwoDeviceMult",
146       // Args
147       {"x: T"},
148       // Return values
149       {"y_cpu: T", "y_gpu: T"},
150       // Attr def
151       {"T: {float, double, int32, int64}"},
152       // Nodes
153       {
154           {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
155           {{"num_3"}, "Const", {}, {{"value", kThree}, {"dtype", DT_INT64}}},
156           {{"factor_2"},
157            "Cast",
158            {"num_2:output:0"},
159            {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
160           {{"factor_3"},
161            "Cast",
162            {"num_3:output:0"},
163            {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
164           {{"y_cpu"},
165            "Mul",
166            {"x", "factor_2:y:0"},
167            {{"T", "$T"}},
168            {},
169            "/device:CPU:0"},
170           {{"y_gpu"},
171            "Mul",
172            {"x", "factor_3:y:0"},
173            {{"T", "$T"}},
174            {},
175            "/device:GPU:0"},
176       },
177       {{"y_cpu", "y_cpu:z:0"}, {"y_gpu", "y_gpu:z:0"}});
178 }
179 
TwoDeviceInputOutput()180 FunctionDef TwoDeviceInputOutput() {
181   const Tensor kTwo = test::AsScalar<float>(2);
182   const Tensor kThree = test::AsScalar<float>(3);
183   return FDH::Create(
184       // Name
185       "TwoDeviceInputOutput",
186       // Args
187       {"x1: T", "x2: T"},
188       // Return values
189       {"y_cpu: T", "y_gpu: T"},
190       // Attr def
191       {"T: {float}"},
192       // Nodes
193       {
194           {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
195           {{"num_3"}, "Const", {}, {{"value", kThree}, {"dtype", DT_FLOAT}}},
196           {{"y_cpu"},
197            "Mul",
198            {"x1", "num_2:output:0"},
199            {{"T", "$T"}},
200            {},
201            "/device:CPU:0"},
202           {{"y_gpu"},
203            "Mul",
204            {"x2", "num_3:output:0"},
205            {{"T", "$T"}},
206            {},
207            "/device:GPU:0"},
208       },
209       {{"y_cpu", "y_cpu:z:0"}, {"y_gpu", "y_gpu:z:0"}});
210 }
211 
FuncWithListInput()212 FunctionDef FuncWithListInput() {
213   const Tensor kTwo = test::AsScalar<float>(2);
214   return FDH::Create(
215       // Name
216       "FuncWithListInput",
217       // Args
218       {"x1: N * T"},
219       // Return values
220       {},
221       // Attr def
222       {"T: {float}", "N: int >= 1"},
223       // Nodes
224       {
225           {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
226       },
227       {});
228 }
229 
FuncWithListOutput()230 FunctionDef FuncWithListOutput() {
231   const Tensor kTwo = test::AsScalar<float>(2);
232   return FDH::Create(
233       // Name
234       "FuncWithListOutput",
235       // Args
236       {},
237       // Return values
238       {"y: N * T"},
239       // Attr def
240       {"T: {float}", "N: int >= 1"},
241       // Nodes
242       {
243           {{"num_2"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
244       },
245       {{"y", "num_2:output:0"}});
246 }
247 
XAddX()248 FunctionDef XAddX() {
249   return FDH::Define(
250       // Name
251       "XAddX",
252       // Args
253       {"x: T"},
254       // Return values
255       {"y: T"},
256       // Attr def
257       {"T: {float, double, int32, int64}"},
258       // Nodes
259       {
260           {{"y"}, "Add", {"x", "x"}, {{"T", "$T"}}},
261       });
262 }
263 
XAddY()264 FunctionDef XAddY() {
265   return FDH::Define(
266       // Name
267       "XAddY",
268       // Args
269       {"x: T", "y: T"},
270       // Return values
271       {"z: T"},
272       // Attr def
273       {"T: {float, double, int32, int64}"},
274       // Nodes
275       {
276           {{"z"}, "Add", {"x", "y"}, {{"T", "$T"}}},
277       });
278 }
279 
XTimesTwoInt32()280 FunctionDef XTimesTwoInt32() {
281   const Tensor kTwo = test::AsScalar<int64_t>(2);
282   return FDH::Define(
283       // Name
284       "XTimesTwoInt32",
285       // Args
286       {"x: int32"},
287       // Return values
288       {"y: int32"}, {},
289       // Nodes
290       {
291           {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_INT64}}},
292           {{"scale"},
293            "Cast",
294            {"two"},
295            {{"SrcT", DT_INT64}, {"DstT", DT_INT32}}},
296           {{"y"}, "Mul", {"x", "scale"}, {{"T", DT_INT32}}},
297       });
298 }
299 
XTimesFour()300 FunctionDef XTimesFour() {
301   return FDH::Create(
302       // Name
303       "XTimesFour",
304       // Args
305       {"x: T"},
306       // Return values
307       {"y: T"},
308       // Attr def
309       {"T: {float, double, int32, int64}"},
310       // Nodes
311       {
312           {{"x2"}, "XTimesTwo", {"x"}, {{"T", "$T"}}},
313           {{"y"}, "XTimesTwo", {"x2:y:0"}, {{"T", "$T"}}},
314       },
315       {{"y", "y:y:0"}});
316 }
317 
XTimes16()318 FunctionDef XTimes16() {
319   return FDH::Create(
320       // Name
321       "XTimes16",
322       // Args
323       {"x: T"},
324       // Return values
325       {"y: T"},
326       // Attr def
327       {"T: {float, double, int32, int64}"},
328       // Nodes
329       {
330           {{"x4"}, "XTimesFour", {"x"}, {{"T", "$T"}}},
331           {{"y"}, "XTimesFour", {"x4:y:0"}, {{"T", "$T"}}},
332       },
333       {{"y", "y:y:0"}});
334 }
335 
WXPlusB()336 FunctionDef WXPlusB() {
337   return FDH::Define(
338       // Name
339       "WXPlusB",
340       // Args
341       {"w: T", "x: T", "b: T"},
342       // Return values
343       {"y: T"},
344       // Attr def
345       {"T: {float, double}"},
346       // Nodes
347       {{{"mm"},
348         "MatMul",
349         {"w", "x"},
350         {{"T", "$T"}, {"transpose_a", false}, {"transpose_b", false}}},
351        {{"y"}, "Add", {"mm", "b"}, {{"T", "$T"}}}});
352 }
353 
Swap()354 FunctionDef Swap() {
355   return FDH::Define(
356       // Name
357       "Swap",
358       // Args
359       {"i0: T", "i1: T"},
360       // Return values
361       {"o0: T", "o1: T"},
362       // Attr def
363       {"T: {float, double, resource}"},
364       // Nodes
365       {{{"o0"}, "Identity", {"i1"}, {{"T", "$T"}}},
366        {{"o1"}, "Identity", {"i0"}, {{"T", "$T"}}}});
367 }
368 
EmptyBodySwap()369 FunctionDef EmptyBodySwap() {
370   return FDH::Create(
371       // Name
372       "EmptyBodySwap",
373       // Args
374       {"i0: T", "i1: T"},
375       // Return values
376       {"o0: T", "o1: T"},
377       // Attr def
378       {"T: {float, double, resource}"},
379       // Nodes
380       {},
381       // Output mapping
382       {{"o0", "i1"}, {"o1", "i0"}});
383 }
384 
ResourceOutput()385 FunctionDef ResourceOutput() {
386   const Tensor kTwo = test::AsScalar<float>(2);
387   return FDH::Create(
388       // Name
389       "ResourceOutput",
390       // Args
391       {"x: float", "y: resource"},
392       // Return values
393       {"y_out: resource", "two_x: float"},
394       // Attr def
395       {},
396       // Nodes
397       {
398           {{"two"}, "Const", {}, {{"value", kTwo}, {"dtype", DT_FLOAT}}},
399           {{"mul"}, "Mul", {"x", "two:output:0"}, {{"T", DT_FLOAT}}, {}},
400       },
401       {{"y_out", "y"}, {"two_x", "mul:z:0"}});
402 }
403 
ResourceIdentity()404 FunctionDef ResourceIdentity() {
405   return FDH::Create(
406       // Name
407       "ResourceIdentity",
408       // Args
409       {"x: resource"},
410       // Return values
411       {"y: resource"},
412       // Attr def
413       {},
414       // Nodes
415       {},
416       // Output mapping
417       {{"y", "x"}});
418 }
419 
ReadResourceVariable()420 FunctionDef ReadResourceVariable() {
421   return FDH::Create(
422       // Name
423       "ReadResourceVariable",
424       // Args
425       {"x: resource"},
426       // Return values
427       {"y: float"},
428       // Attr def
429       {},
430       // Nodes
431       {
432           {{"read"}, "ReadVariableOp", {"x"}, {{"dtype", DT_FLOAT}}, {}},
433       },
434       {{"y", "read:value:0"}});
435 }
436 
ControlFlow()437 FunctionDef ControlFlow() {
438   return FDH::Create(
439       // Name
440       "ControlFlow",
441       // Args
442       {"i: float"},
443       // Return values
444       {"o: float"},
445       // Attr def
446       {},
447       // Nodes
448       {{{"enter"}, "Enter", {"i"}, {{"T", DT_FLOAT}, {"frame_name", "while"}}}},
449       // Output mapping
450       {{"o", "enter:output"}});
451 }
452 
InvalidControlFlow()453 FunctionDef InvalidControlFlow() {
454   return FDH::Create(
455       // Name
456       "InvalidControlFlow",
457       // Args
458       {"i: int32"},
459       // Return values
460       {"o: int32"},
461       // Attr def
462       {},
463       // Nodes
464       {{{"enter"}, "Enter", {"i"}, {{"T", DT_INT32}, {"frame_name", "while"}}},
465        {{"add"}, "Add", {"enter:output", "i"}, {{"T", DT_INT32}}}},
466       // Output mapping
467       {{"o", "add:z"}});
468 }
469 
LessThanOrEqualToN(int64_t N)470 FunctionDef LessThanOrEqualToN(int64_t N) {
471   const Tensor kN = test::AsScalar<int64_t>(N);
472   return FDH::Define(
473       // Name
474       "LessThanOrEqualToN",
475       // Args
476       {"x: T"},
477       // Return values
478       {"z: bool"},
479       // Attr def
480       {"T: {float, double, int32, int64}"},
481       // Nodes
482       {
483           {{"N"}, "Const", {}, {{"value", kN}, {"dtype", DT_INT64}}},
484           {{"y"}, "Cast", {"N"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
485           {{"z"}, "LessEqual", {"x", "y"}, {{"T", "$T"}}},
486       });
487 }
488 
XPlusOneXTimesY()489 FunctionDef XPlusOneXTimesY() {
490   const Tensor kOne = test::AsScalar<int64_t>(1);
491   return FDH::Define(
492       // Name
493       "XPlusOneXTimesY",
494       // Args
495       {"x: T", "y: T"},
496       // Return values
497       {"s: T", "t: T"},
498       // Attr def
499       {"T: {float, double, int32, int64}"},
500       // Nodes
501       {{{"one"}, "Const", {}, {{"value", kOne}, {"dtype", DT_INT64}}},
502        {{"increment"}, "Cast", {"one"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
503        {{"s"}, "Add", {"x", "increment"}, {{"T", "$T"}}},
504        {{"t"}, "Mul", {"x", "y"}, {{"T", "$T"}}}});
505 }
506 
XYXLessThanOrEqualToN(int64_t N)507 FunctionDef XYXLessThanOrEqualToN(int64_t N) {
508   const Tensor kN = test::AsScalar<int64_t>(N);
509   return FDH::Define(
510       // Name
511       "XYXLessThanOrEqualToN",
512       // Args
513       {"x: T", "y: T"},
514       // Return values
515       {"z: bool"},
516       // Attr def
517       {"T: {float, double, int32, int64}"},
518       // Nodes
519       {
520           {{"N"}, "Const", {}, {{"value", kN}, {"dtype", DT_INT64}}},
521           {{"N1"}, "Cast", {"N"}, {{"SrcT", DT_INT64}, {"DstT", "$T"}}},
522           {{"z"}, "LessEqual", {"x", "N1"}, {{"T", "$T"}}},
523       });
524 }
525 
RandomUniformLess()526 FunctionDef RandomUniformLess() {
527   const Tensor kZero = test::AsScalar<int32>(0);
528   const Tensor kOne = test::AsScalar<int32>(1);
529   const Tensor k005 = test::AsScalar<float>(0.05);
530 
531   return FDH::Define(
532       // Name
533       "RandomUniformLess",
534       // Args
535       {"arg0: int64"},
536       // Return values
537       {"strided_slice: bool"},
538       // Attr def
539       {"T:{float, double, int32, int64, string}"},
540       {{{"random_uniform/shape"},
541         "Const",
542         {},
543         {{"value", kZero}, {"dtype", DT_INT32}}},
544 
545        {{"random_uniform/RandomUniform"},
546         "RandomUniform",
547         {"random_uniform/shape"},
548         {{"T", DT_INT32}, {"Tout", DT_FLOAT}, {"seed", 0}, {"seed2", 0}}},
549 
550        {{"Less/y"}, "Const", {}, {{"value", k005}, {"dtype", DT_FLOAT}}},
551 
552        {{"Less"},
553         "Less",
554         {"random_uniform/RandomUniform", "Less/y"},
555         {{"T", DT_FLOAT}}},
556 
557        {{"strided_slice/stack"},
558         "Const",
559         {},
560         {{"value", kZero}, {"dtype", DT_INT32}}},
561 
562        {{"strided_slice/stack_1"},
563         "Const",
564         {},
565         {{"value", kOne}, {"dtype", DT_INT32}}},
566 
567        {{"strided_slice/stack_2"},
568         "Const",
569         {},
570         {{"value", kOne}, {"dtype", DT_INT32}}},
571 
572        {{"strided_slice"},
573         "StridedSlice",
574         {"Less", "strided_slice/stack", "strided_slice/stack_1",
575          "strided_slice/stack_2"},
576         {{"Index", DT_INT32},
577          {"T", DT_BOOL},
578          {"begin_mask", 0},
579          {"ellipsis_mask", 0},
580          {"end_mask", 0},
581          {"new_axis_mask", 0},
582          {"shrink_axis_mask", 0}}}});
583 }
584 
MakeRangeDataset()585 FunctionDef MakeRangeDataset() {
586   return FDH::Define(
587       /*name=*/"MakeRangeDataset",
588       /*arg_def=*/{"start: int64", "stop: int64", "step: int64"},
589       /*ret_def=*/{"y:variant"},
590       /*attr_def=*/
591       {"output_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
592       /*node_def=*/
593       {{/*ret=*/{"y"},
594         /*op=*/"RangeDataset",
595         /*arg=*/{"start", "stop", "step"},
596         /*attr=*/
597         {{"output_types", "$output_types"},
598          {"output_shapes", "$output_shapes"}}}});
599 }
600 
MakeBatchDataset()601 FunctionDef MakeBatchDataset() {
602   return FDH::Define(
603       /*name=*/"MakeBatchDataset",
604       /*arg_def=*/
605       {"input_dataset: variant", "batch_size: int64", "drop_remainder: bool"},
606       /*ret_def=*/{"y: variant"},
607       /*attr_def=*/
608       {"parallel_copy: bool = false", "output_types: list(type) >= 1",
609        "output_shapes: list(shape) >= 1"},
610       /*node_def=*/
611       {{/*ret=*/{"y"},
612         /*op=*/"BatchDatasetV2",
613         /*arg=*/{"input_dataset", "batch_size", "drop_remainder"},
614         /*attr=*/
615         {{"parallel_copy", "$parallel_copy"},
616          {"output_types", "$output_types"},
617          {"output_shapes", "$output_shapes"}}}});
618 }
619 
MakeMapDataset(bool has_other_args)620 FunctionDef MakeMapDataset(bool has_other_args) {
621   std::vector<string> args = {"input_dataset: variant"};
622   std::vector<string> inputs = {"input_dataset"};
623   if (has_other_args) {
624     args.emplace_back("other_arguments: Targuments");
625     inputs.emplace_back("other_arguments");
626   }
627 
628   return FDH::Define(
629       /*name=*/"MakeMapDataset",
630       /*arg_def=*/args,
631       /*ret_def=*/
632       {"y: variant"},
633       /*attr_def=*/
634       {"f: func", "Targuments: list(type) >= 0",
635        "output_types: list(type) >= 1", "output_shapes: list(shape) >= 1",
636        "use_inter_op_parallelism: bool = true",
637        "preserve_cardinality: bool = false"},
638       /*node_def=*/
639       {{/*ret=*/{"y"},
640         /*op=*/"MapDataset",
641         /*arg=*/inputs,
642         /*attr=*/
643         {{"f", "$f"},
644          {"Targuments", "$Targuments"},
645          {"output_types", "$output_types"},
646          {"output_shapes", "$output_shapes"},
647          {"use_inter_op_parallelism", "$use_inter_op_parallelism"},
648          {"preserve_cardinality", "$preserve_cardinality"}}}});
649 }
650 
MakeTakeDataset()651 FunctionDef MakeTakeDataset() {
652   return FDH::Define(
653       // Name
654       "TakeDataset",
655       // Args
656       {"input_dataset: variant", "count: int64"},
657       // Return values
658       {"y:variant"},
659       // Attr def
660       {"output_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
661       // Nodes
662       {{{"y"},
663         "TakeDataset",
664         {"input_dataset", "count"},
665         {{"output_types", "$output_types"},
666          {"output_shapes", "$output_shapes"}}}});
667 }
668 
MakeTensorSliceDataset()669 FunctionDef MakeTensorSliceDataset() {
670   return FDH::Define(
671       // Name
672       "MakeTensorSliceDataset",
673       // Args
674       {"x: Toutput_types"},
675       // Return values
676       {"y: variant"},
677       // Attr def
678       {"Toutput_types: list(type) >= 1", "output_shapes: list(shape) >= 1"},
679       // Nodes
680       {{{"y"},
681         "TensorSliceDataset",
682         {"x"},
683         {{"Toutput_types", "$Toutput_types"},
684          {"output_shapes", "$output_shapes"}}}});
685 }
686 
Unique()687 FunctionDef Unique() {
688   return FDH::Create(
689       // Name
690       "GetUnique",
691       // Args
692       {"x:T"},
693       // Return values
694       {"y:T", "idx: out_idx"},
695       // Attr def
696       {"T: type", "out_idx: {int32, int64} = DT_INT32"},
697       // Nodes
698       {
699           {{"result"}, "Unique", {"x"}, {{"T", "$T"}, {"out_idx", "$out_idx"}}},
700       },
701       {{"y", "result:y:0"}, {"idx", "result:idx:0"}});
702 }
703 
FunctionTestSchedClosure(std::function<void ()> fn)704 void FunctionTestSchedClosure(std::function<void()> fn) {
705   static thread::ThreadPool* w =
706       new thread::ThreadPool(Env::Default(), "Test", 8);
707   w->Schedule(std::move(fn));
708 }
709 
710 }  // end namespace function
711 }  // end namespace test
712 }  // end namespace tensorflow
713