aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/core/grappler/costs
diff options
context:
space:
mode:
authorGravatar A. Unique TensorFlower <gardener@tensorflow.org>2018-07-10 09:44:04 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-07-10 09:49:29 -0700
commit89e172f6ae5a52f8ceca6b4690331e1dce89d456 (patch)
treec942f2b89a8bf6b4d464e09da9b3ce263a0eb6a7 /tensorflow/core/grappler/costs
parent0c7e26bcf14afa88e882bfb8eeff682d268f52e7 (diff)
Function shape inference for Grappler.
PiperOrigin-RevId: 203962286
Diffstat (limited to 'tensorflow/core/grappler/costs')
-rw-r--r--tensorflow/core/grappler/costs/BUILD1
-rw-r--r--tensorflow/core/grappler/costs/graph_properties.cc173
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_test.cc248
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt117
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt251
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt251
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt317
-rw-r--r--tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt597
8 files changed, 1938 insertions, 17 deletions
diff --git a/tensorflow/core/grappler/costs/BUILD b/tensorflow/core/grappler/costs/BUILD
index b054068299..f3dc2c2091 100644
--- a/tensorflow/core/grappler/costs/BUILD
+++ b/tensorflow/core/grappler/costs/BUILD
@@ -41,6 +41,7 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
":utils",
+ "//tensorflow/core/grappler/utils:functions",
"//tensorflow/core/grappler/utils:topological_sort",
"//tensorflow/core/grappler:graph_view",
"//tensorflow/core/grappler:op_types",
diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc
index 0c02876ac5..83a8326e79 100644
--- a/tensorflow/core/grappler/costs/graph_properties.cc
+++ b/tensorflow/core/grappler/costs/graph_properties.cc
@@ -28,6 +28,7 @@ limitations under the License.
#include "tensorflow/core/grappler/graph_view.h"
#include "tensorflow/core/grappler/op_types.h"
#include "tensorflow/core/grappler/utils.h"
+#include "tensorflow/core/grappler/utils/functions.h"
#include "tensorflow/core/grappler/utils/topological_sort.h"
#include "tensorflow/core/lib/strings/str_util.h"
@@ -422,11 +423,108 @@ class SymbolicShapeRefiner {
return it->second.inference_context.get();
}
- // Forward the shapes from the function's fanin to the function body,
- // then call PropagateShapes.
- // Returns an error if 'node' is not a function node.
- Status UpdateFunction(const NodeDef* node, bool* refined) {
- return UpdateNode(node, refined);
+ // Forward the shapes from the function input nodes to
+ // the argument nodes (which are Placeholder nodes), then
+ // perform shape inference on the function body.
+ //
+ // Propagate shape information of final function body node
+ // to function node `node`.
+ //
+ // In the event of an error, UpdateNode will simply set `node`'s
+ // output shape to be Unknown.
+ Status UpdateFunction(const NodeDef* node) {
+ auto it = fun_to_grappler_function_item_.find(node->op());
+ if (it == fun_to_grappler_function_item_.end()) {
+ return errors::InvalidArgument(
+ node->op(), " was not previously added to SymbolicShapeRefiner.");
+ }
+
+ GrapplerFunctionItem& grappler_function_item = it->second;
+ GraphView gv(&grappler_function_item.graph);
+
+ // Forward shapes from function input nodes to argument nodes.
+ for (int i = 0; i < grappler_function_item.inputs().size(); ++i) {
+ auto& fun_input = grappler_function_item.input(i);
+ if (fun_input.placeholders.size() > 1) {
+ // TODO(jmdecker): Handle case with multiple input placeholders
+ return errors::Unimplemented(
+ "Input arguments with multiple placeholders are not yet "
+ "supported.");
+ }
+ NodeDef* fun_node = gv.GetNode(fun_input.input_name);
+ const string& input = node->input(i);
+ const string& node_name = NodeName(input);
+
+ if (IsControlInput(input)) {
+ return errors::FailedPrecondition(
+ "Function inputs should not contain control nodes.");
+ }
+
+ NodeDef* input_node = graph_.GetNode(node_name);
+ if (input_node == nullptr) {
+ return errors::FailedPrecondition(node_name,
+ " was not found in the graph.");
+ }
+
+ InferenceContext* input_inference_context = GetContext(input_node);
+ if (input_inference_context == nullptr) {
+ return errors::FailedPrecondition(
+ "Inference context has not been created for ", node_name);
+ }
+
+ int output_port_num = NodePosition(input);
+ AttrValue attr_output_shape;
+ TensorShapeProto proto;
+ const auto& handle = input_inference_context->output(output_port_num);
+ input_inference_context->ShapeHandleToProto(handle, &proto);
+ *attr_output_shape.mutable_shape() = proto;
+ (*fun_node->mutable_attr())["shape"] = attr_output_shape;
+ }
+
+ // Perform inference on function body.
+ GraphProperties gp(grappler_function_item);
+ TF_RETURN_IF_ERROR(gp.InferStatically(true));
+
+ // Add return nodes for output shapes.
+ auto ic = GetContext(node);
+ int output = 0;
+ for (auto const& out_arg : grappler_function_item.outputs()) {
+ if (out_arg.output_tensors.size() > 1) {
+ // TODO(jmdecker): Handle case of multiple output tensors
+ return errors::Unimplemented(
+ "Output arguments with multiple output tensors are not yet "
+ "supported.");
+ }
+
+ string out_tensor = out_arg.output_tensors[0];
+ auto out_tensor_pieces = str_util::Split(out_tensor, ",");
+ string node_name = out_tensor_pieces[0];
+ int port_id;
+
+ // Check if port_id was included in out_tensor
+ if (out_tensor_pieces.size() <= 1) {
+ port_id = 0;
+ } else if (!strings::safe_strto32(out_tensor_pieces[1], &port_id)) {
+ return errors::FailedPrecondition(
+ "Failed string to integer conversion for ", out_tensor_pieces[1]);
+ }
+
+ const NodeDef* retnode = gv.GetNode(node_name);
+ if (retnode == nullptr) {
+ return errors::FailedPrecondition("Unable to find return node ",
+ node_name, " for ", node->name());
+ }
+
+ auto output_properties = gp.GetOutputProperties(retnode->name());
+ auto const& outprop = output_properties[port_id];
+ const TensorShapeProto& shape = outprop.shape();
+ ShapeHandle out;
+ TF_RETURN_IF_ERROR(ic->MakeShapeFromShapeProto(shape, &out));
+ ic->set_output(output, out);
+ output++;
+ }
+
+ return Status::OK();
}
Status UpdateNode(const NodeDef* node, bool* refined) {
@@ -436,6 +534,7 @@ class SymbolicShapeRefiner {
node_context = CHECK_NOTNULL(GetNodeContext(node));
*refined = true;
}
+
// Check if the shapes of the nodes in the fan-in of this node have changed,
// and if they have, update the node input shapes.
InferenceContext* inference_context = node_context->inference_context.get();
@@ -455,7 +554,8 @@ class SymbolicShapeRefiner {
if (c == nullptr) {
return errors::FailedPrecondition(
"Input ", dst_input, " ('", input->name(), "') for '",
- node->name(), "' was not previously added to ShapeRefiner.");
+ node->name(),
+ "' was not previously added to SymbolicShapeRefiner.");
}
if (IsConstant(*input)) {
@@ -565,6 +665,21 @@ class SymbolicShapeRefiner {
node_context->inference_context->set_input_tensors_as_shapes(
input_tensors_as_shapes);
+ // Properly handle function nodes.
+ if (node_context->op_data && node_context->op_data->is_function_op) {
+ // TODO(jmdecker): Detect if the input shapes have changed for this
+ // function. Note that when we hit a function call node, refined will be
+ // true, as the updates to the call node will have changed, even if it's
+ // the same function being called twice with the same input shapes.
+ // Example: simple_function.pbtxt
+ if (UpdateFunction(node).ok()) {
+ return Status::OK();
+ } else {
+ VLOG(1) << "UpdateFunction failed for " << node->op()
+ << ". Defaulting to ShapeUnknown.";
+ }
+ }
+
// Update the shapes of the outputs.
return InferShapes(*node, node_context);
}
@@ -681,7 +796,39 @@ class SymbolicShapeRefiner {
return true;
}
- Status AddFunction(const NodeDef* node) { return Status::OK(); }
+ Status AddFunction(const NodeDef* function_node) {
+ auto it = fun_to_grappler_function_item_.find(function_node->op());
+ if (it != fun_to_grappler_function_item_.end()) {
+ return Status::OK();
+ }
+
+ const FunctionDef* function_def =
+ CHECK_NOTNULL(function_library_.Find(function_node->op()));
+
+ GrapplerFunctionItem grappler_function_item;
+ TF_RETURN_IF_ERROR(MakeGrapplerFunctionItem(
+ *function_def, function_library_, &grappler_function_item));
+
+ if (grappler_function_item.inputs().size() > function_node->input_size()) {
+ return errors::FailedPrecondition(
+ "Function input size should be smaller than node input size.");
+ }
+
+ for (int i = grappler_function_item.inputs().size();
+ i < function_node->input_size(); ++i) {
+ const string& input = function_node->input(i);
+ if (!IsControlInput(input)) {
+ return errors::FailedPrecondition(
+ "Found regular input (", input,
+ ") instead of control nodes for node ", function_node->name());
+ }
+ }
+
+ fun_to_grappler_function_item_[function_def->signature().name()] =
+ grappler_function_item;
+
+ return Status::OK();
+ }
Status AddNode(const NodeDef* node) {
NodeContext& node_ctx = node_to_context_[node];
@@ -911,6 +1058,8 @@ class SymbolicShapeRefiner {
std::unordered_map<const NodeDef*, NodeContext> node_to_context_;
std::unordered_map<ShapeId, ShapeHandle, HashShapeId> unknown_shapes_;
std::unordered_map<DimId, DimensionHandle, HashDimId> unknown_dims_;
+ std::unordered_map<string, GrapplerFunctionItem>
+ fun_to_grappler_function_item_;
FunctionLibraryDefinition function_library_;
const std::unordered_map<string, std::unordered_set<int>>& fed_ports_;
};
@@ -1082,13 +1231,9 @@ Status GraphProperties::UpdateShapes(
// Set shapes and types of Queue ops, if needed.
TF_RETURN_IF_ERROR(UpdateQueue(n, shape_refiner, new_shapes));
} else {
- auto c = shape_refiner->GetNodeContext(n);
- if (c && c->op_data && c->op_data->is_function_op) {
- TF_RETURN_IF_ERROR(shape_refiner->UpdateFunction(n, new_shapes));
- } else {
- // Rely on regular TF shape refinement for all the other nodes.
- TF_RETURN_IF_ERROR(shape_refiner->UpdateNode(n, new_shapes));
- }
+ // Rely on regular TF shape refinement for all the other nodes.
+ // UpdateNode calls UpdateFunction if a function node is detected.
+ TF_RETURN_IF_ERROR(shape_refiner->UpdateNode(n, new_shapes));
}
return Status::OK();
}
diff --git a/tensorflow/core/grappler/costs/graph_properties_test.cc b/tensorflow/core/grappler/costs/graph_properties_test.cc
index aa787ae620..1be19d291a 100644
--- a/tensorflow/core/grappler/costs/graph_properties_test.cc
+++ b/tensorflow/core/grappler/costs/graph_properties_test.cc
@@ -783,7 +783,7 @@ TEST_F(GraphPropertiesTest, InferRestoreOpShape_WithTwoNodesShareSameOutput) {
EXPECT_EQ("float: [128,256]", PropToString(prop));
}
-TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) {
+TEST_F(GraphPropertiesTest, SimpleFunctionStaticShapeInference) {
// Test graph produced in python using:
/*
@function.Defun(*[tf.float32] * 2, noinline=True)
@@ -796,7 +796,6 @@ TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) {
z = MyAdd(x, y)
z = MyAdd(x, z)
*/
- // Check that the shape inference code infers what it can.
GrapplerItem item;
string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
"simple_function.pbtxt");
@@ -806,15 +805,258 @@ TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) {
const auto out_props = properties.GetOutputProperties("MyAdd_55e046a8");
const OpInfo::TensorProperties& out_prop = out_props[0];
EXPECT_EQ(DT_FLOAT, out_prop.dtype());
- EXPECT_TRUE(out_prop.shape().unknown_rank());
+ EXPECT_FALSE(out_prop.shape().unknown_rank());
+ EXPECT_EQ(2, out_prop.shape().dim_size());
+ EXPECT_EQ(1, out_prop.shape().dim(0).size());
+ EXPECT_EQ(2, out_prop.shape().dim(1).size());
const auto in_props = properties.GetInputProperties("MyAdd_55e046a8");
+ EXPECT_EQ(2, in_props.size());
+
+ const OpInfo::TensorProperties& in_prop = in_props[0];
+ EXPECT_EQ(DT_FLOAT, in_prop.dtype());
+ EXPECT_FALSE(in_prop.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop.shape().dim_size());
+ EXPECT_EQ(1, in_prop.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop.shape().dim(1).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_FALSE(in_prop1.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop1.shape().dim(1).size());
+}
+
+TEST_F(GraphPropertiesTest, LargeFunctionStaticShapeInference) {
+ GrapplerItem item;
+ string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
+ "large_function_graph.pbtxt");
+ TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph));
+ GraphProperties properties(item);
+ TF_CHECK_OK(properties.InferStatically(false));
+
+ const auto out_props = properties.GetOutputProperties("y0");
+ EXPECT_EQ(2, out_props.size());
+
+ const OpInfo::TensorProperties& out_prop0 = out_props[0];
+ EXPECT_EQ(DT_FLOAT, out_prop0.dtype());
+ EXPECT_EQ(4, out_prop0.shape().dim_size());
+ EXPECT_EQ(128, out_prop0.shape().dim(0).size());
+ EXPECT_EQ(112, out_prop0.shape().dim(1).size());
+ EXPECT_EQ(112, out_prop0.shape().dim(2).size());
+ EXPECT_EQ(64, out_prop0.shape().dim(3).size());
+
+ const OpInfo::TensorProperties& out_prop1 = out_props[1];
+ EXPECT_EQ(DT_FLOAT, out_prop1.dtype());
+ EXPECT_EQ(128, out_prop1.shape().dim(0).size());
+ EXPECT_EQ(112, out_prop1.shape().dim(1).size());
+ EXPECT_EQ(112, out_prop1.shape().dim(2).size());
+ EXPECT_EQ(24, out_prop1.shape().dim(3).size());
+
+ const auto in_props = properties.GetInputProperties("y0");
+ EXPECT_EQ(4, in_props.size());
+
+ const OpInfo::TensorProperties& in_prop0 = in_props[0];
+ EXPECT_EQ(DT_FLOAT, in_prop0.dtype());
+ EXPECT_EQ(1, in_prop0.shape().dim_size());
+ EXPECT_EQ(64, in_prop0.shape().dim(0).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_EQ(4, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(1, in_prop1.shape().dim(1).size());
+ EXPECT_EQ(24, in_prop1.shape().dim(2).size());
+ EXPECT_EQ(64, in_prop1.shape().dim(3).size());
+
+ const OpInfo::TensorProperties& in_prop2 = in_props[2];
+ EXPECT_EQ(DT_FLOAT, in_prop2.dtype());
+ EXPECT_EQ(4, in_prop2.shape().dim_size());
+ EXPECT_EQ(128, in_prop2.shape().dim(0).size());
+ EXPECT_EQ(224, in_prop2.shape().dim(1).size());
+ EXPECT_EQ(224, in_prop2.shape().dim(2).size());
+ EXPECT_EQ(3, in_prop2.shape().dim(3).size());
+
+ const OpInfo::TensorProperties& in_prop3 = in_props[3];
+ EXPECT_EQ(DT_FLOAT, in_prop3.dtype());
+ EXPECT_EQ(4, in_prop3.shape().dim_size());
+ EXPECT_EQ(7, in_prop3.shape().dim(0).size());
+ EXPECT_EQ(7, in_prop3.shape().dim(1).size());
+ EXPECT_EQ(3, in_prop3.shape().dim(2).size());
+ EXPECT_EQ(8, in_prop3.shape().dim(3).size());
+}
+
+TEST_F(GraphPropertiesTest, FunctionWithErrorStaticShapeInference) {
+ GrapplerItem item;
+ string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
+ "function_error.pbtxt");
+ TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph));
+ GraphProperties properties(item);
+ TF_CHECK_OK(properties.InferStatically(false));
+
+ const auto out_props = properties.GetOutputProperties("MyAdd_yabA4wXEdM4");
+ EXPECT_EQ(1, out_props.size());
+
+ const OpInfo::TensorProperties& out_prop = out_props[0];
+ EXPECT_EQ(DT_FLOAT, out_prop.dtype());
+ EXPECT_TRUE(out_prop.shape().unknown_rank());
+
+ const auto in_props = properties.GetInputProperties("MyAdd_yabA4wXEdM4");
+ EXPECT_EQ(2, in_props.size());
+
const OpInfo::TensorProperties& in_prop = in_props[0];
EXPECT_EQ(DT_FLOAT, in_prop.dtype());
EXPECT_FALSE(in_prop.shape().unknown_rank());
EXPECT_EQ(2, in_prop.shape().dim_size());
EXPECT_EQ(1, in_prop.shape().dim(0).size());
EXPECT_EQ(2, in_prop.shape().dim(1).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_FALSE(in_prop1.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop1.shape().dim(1).size());
+}
+
+TEST_F(GraphPropertiesTest, FunctionSwitchStaticShapeInference) {
+ // Test graph produced in python using:
+ /*
+ @function.Defun(*[tf.float32] * 2, noinline=True)
+ def MyAdd(x, y):
+ return tf.add(x, y)
+
+ with tf.Graph().as_default():
+ x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ z = tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ z2 = MyAdd(tf.case([(tf.less(0, 1), x)], default=y), z)
+ */
+ GrapplerItem item;
+ string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
+ "function_switch.pbtxt");
+ TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph));
+ GraphProperties properties(item);
+ TF_CHECK_OK(properties.InferStatically(false));
+ const auto out_props = properties.GetOutputProperties("MyAdd_MPaeanipb7o");
+ const OpInfo::TensorProperties& out_prop = out_props[0];
+ EXPECT_EQ(DT_FLOAT, out_prop.dtype());
+ EXPECT_FALSE(out_prop.shape().unknown_rank());
+ EXPECT_EQ(2, out_prop.shape().dim_size());
+ EXPECT_EQ(1, out_prop.shape().dim(0).size());
+ EXPECT_EQ(2, out_prop.shape().dim(1).size());
+
+ const auto in_props = properties.GetInputProperties("MyAdd_MPaeanipb7o");
+ EXPECT_EQ(2, in_props.size());
+
+ const OpInfo::TensorProperties& in_prop = in_props[0];
+ EXPECT_EQ(DT_FLOAT, in_prop.dtype());
+ EXPECT_FALSE(in_prop.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop.shape().dim_size());
+ EXPECT_EQ(1, in_prop.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop.shape().dim(1).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_FALSE(in_prop1.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop1.shape().dim(1).size());
+}
+
+TEST_F(GraphPropertiesTest, FunctionSwitch2StaticShapeInference) {
+ // Test graph produced in python using:
+ /*
+ @function.Defun(*[tf.float32] * 2, noinline=True)
+ def MyAdd(x, y):
+ return tf.add(x, y)
+
+ with tf.Graph().as_default():
+ x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ z = tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ z2 = MyAdd(tf.case([(tf.less(1, 0), x)], default=y), z)
+ */
+ GrapplerItem item;
+ string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
+ "function_switch_2.pbtxt");
+ TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph));
+ GraphProperties properties(item);
+ TF_CHECK_OK(properties.InferStatically(false));
+ const auto out_props = properties.GetOutputProperties("MyAdd_MPaeanipb7o");
+ const OpInfo::TensorProperties& out_prop = out_props[0];
+ EXPECT_EQ(DT_FLOAT, out_prop.dtype());
+ EXPECT_FALSE(out_prop.shape().unknown_rank());
+ EXPECT_EQ(2, out_prop.shape().dim_size());
+ EXPECT_EQ(1, out_prop.shape().dim(0).size());
+ EXPECT_EQ(2, out_prop.shape().dim(1).size());
+
+ const auto in_props = properties.GetInputProperties("MyAdd_MPaeanipb7o");
+ EXPECT_EQ(2, in_props.size());
+
+ const OpInfo::TensorProperties& in_prop = in_props[0];
+ EXPECT_EQ(DT_FLOAT, in_prop.dtype());
+ EXPECT_FALSE(in_prop.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop.shape().dim_size());
+ EXPECT_EQ(1, in_prop.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop.shape().dim(1).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_FALSE(in_prop1.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop1.shape().dim(1).size());
+}
+
+TEST_F(GraphPropertiesTest, FunctionSwitchShapesStaticShapeInference) {
+ // Test graph produced in python using:
+ /*
+ @function.Defun(*[tf.float32] * 2, noinline=True)
+ def MyAdd(x, y):
+ a = tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ b = tf.constant(2.0, shape=[1, 3], dtype=tf.float32)
+ c = tf.add(x, a)
+ d = tf.add(y, b)
+ return c
+
+ with tf.Graph().as_default():
+ x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32)
+ z = tf.constant(2.0, shape=[1, 3], dtype=tf.float32)
+ z2 = MyAdd(tf.case([(tf.less(1, 0), x)], default=y), z)
+ */
+ GrapplerItem item;
+ string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath,
+ "function_switch_shapes.pbtxt");
+ TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph));
+ GraphProperties properties(item);
+ TF_CHECK_OK(properties.InferStatically(false));
+ const auto out_props = properties.GetOutputProperties("MyAdd_lEKAAnIwI5I");
+ const OpInfo::TensorProperties& out_prop = out_props[0];
+ EXPECT_EQ(DT_FLOAT, out_prop.dtype());
+ EXPECT_FALSE(out_prop.shape().unknown_rank());
+ EXPECT_EQ(2, out_prop.shape().dim_size());
+ EXPECT_EQ(1, out_prop.shape().dim(0).size());
+ EXPECT_EQ(2, out_prop.shape().dim(1).size());
+
+ const auto in_props = properties.GetInputProperties("MyAdd_lEKAAnIwI5I");
+ EXPECT_EQ(2, in_props.size());
+
+ const OpInfo::TensorProperties& in_prop = in_props[0];
+ EXPECT_EQ(DT_FLOAT, in_prop.dtype());
+ EXPECT_FALSE(in_prop.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop.shape().dim_size());
+ EXPECT_EQ(1, in_prop.shape().dim(0).size());
+ EXPECT_EQ(2, in_prop.shape().dim(1).size());
+
+ const OpInfo::TensorProperties& in_prop1 = in_props[1];
+ EXPECT_EQ(DT_FLOAT, in_prop1.dtype());
+ EXPECT_FALSE(in_prop1.shape().unknown_rank());
+ EXPECT_EQ(2, in_prop1.shape().dim_size());
+ EXPECT_EQ(1, in_prop1.shape().dim(0).size());
+ EXPECT_EQ(3, in_prop1.shape().dim(1).size());
}
TEST_F(GraphPropertiesTest, SymbolicShapes) {
diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt
new file mode 100644
index 0000000000..c3f0a6c95d
--- /dev/null
+++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt
@@ -0,0 +1,117 @@
+node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "Const_1"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "MyAdd_yabA4wXEdM4"
+ op: "MyAdd_yabA4wXEdM4"
+ input: "Const"
+ input: "Const_1"
+}
+library {
+ function {
+ signature {
+ name: "MyAdd_yabA4wXEdM4"
+ input_arg {
+ name: "x"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "y"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "add_1"
+ type: DT_FLOAT
+ }
+ }
+ node_def {
+ name: "Add"
+ op: "Add"
+ input: "x"
+ input: "Add:z:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ node_def {
+ name: "Add_1"
+ op: "Add"
+ input: "Add:z:0"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ ret {
+ key: "add_1"
+ value: "Add_1:z:0"
+ }
+ attr {
+ key: "_noinline"
+ value {
+ b: true
+ }
+ }
+ }
+}
+versions {
+ producer: 26
+ min_consumer: 12
+}
diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt
new file mode 100644
index 0000000000..d6d856ce41
--- /dev/null
+++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt
@@ -0,0 +1,251 @@
+node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "Less/x"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 0
+ }
+ }
+ }
+}
+node {
+ name: "Less/y"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 1
+ }
+ }
+ }
+}
+node {
+ name: "Less"
+ op: "Less"
+ input: "Less/x"
+ input: "Less/y"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "case/cond/Switch"
+ op: "Switch"
+ input: "Less"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_t"
+ op: "Identity"
+ input: "case/cond/Switch:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_f"
+ op: "Identity"
+ input: "case/cond/Switch"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/pred_id"
+ op: "Identity"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/Const"
+ op: "Const"
+ input: "^case/cond/switch_t"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Const_1"
+ op: "Const"
+ input: "^case/cond/switch_f"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Merge"
+ op: "Merge"
+ input: "case/cond/Const_1"
+ input: "case/cond/Const"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "MyAdd_MPaeanipb7o"
+ op: "MyAdd_MPaeanipb7o"
+ input: "case/cond/Merge"
+ input: "Const"
+}
+library {
+ function {
+ signature {
+ name: "MyAdd_MPaeanipb7o"
+ input_arg {
+ name: "x"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "y"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "Add"
+ type: DT_FLOAT
+ }
+ }
+ node_def {
+ name: "Add"
+ op: "Add"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ ret {
+ key: "Add"
+ value: "Add:z:0"
+ }
+ attr {
+ key: "_noinline"
+ value {
+ b: true
+ }
+ }
+ }
+}
+versions {
+ producer: 26
+ min_consumer: 12
+}
diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt
new file mode 100644
index 0000000000..e57d9d7076
--- /dev/null
+++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt
@@ -0,0 +1,251 @@
+node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "Less/x"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 1
+ }
+ }
+ }
+}
+node {
+ name: "Less/y"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 0
+ }
+ }
+ }
+}
+node {
+ name: "Less"
+ op: "Less"
+ input: "Less/x"
+ input: "Less/y"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "case/cond/Switch"
+ op: "Switch"
+ input: "Less"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_t"
+ op: "Identity"
+ input: "case/cond/Switch:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_f"
+ op: "Identity"
+ input: "case/cond/Switch"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/pred_id"
+ op: "Identity"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/Const"
+ op: "Const"
+ input: "^case/cond/switch_t"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Const_1"
+ op: "Const"
+ input: "^case/cond/switch_f"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Merge"
+ op: "Merge"
+ input: "case/cond/Const_1"
+ input: "case/cond/Const"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "MyAdd_MPaeanipb7o"
+ op: "MyAdd_MPaeanipb7o"
+ input: "case/cond/Merge"
+ input: "Const"
+}
+library {
+ function {
+ signature {
+ name: "MyAdd_MPaeanipb7o"
+ input_arg {
+ name: "x"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "y"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "Add"
+ type: DT_FLOAT
+ }
+ }
+ node_def {
+ name: "Add"
+ op: "Add"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ ret {
+ key: "Add"
+ value: "Add:z:0"
+ }
+ attr {
+ key: "_noinline"
+ value {
+ b: true
+ }
+ }
+ }
+}
+versions {
+ producer: 26
+ min_consumer: 12
+}
diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt
new file mode 100644
index 0000000000..e9afa91886
--- /dev/null
+++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt
@@ -0,0 +1,317 @@
+node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 3
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "Less/x"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 1
+ }
+ }
+ }
+}
+node {
+ name: "Less/y"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 0
+ }
+ }
+ }
+}
+node {
+ name: "Less"
+ op: "Less"
+ input: "Less/x"
+ input: "Less/y"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "case/cond/Switch"
+ op: "Switch"
+ input: "Less"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_t"
+ op: "Identity"
+ input: "case/cond/Switch:1"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/switch_f"
+ op: "Identity"
+ input: "case/cond/Switch"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/pred_id"
+ op: "Identity"
+ input: "Less"
+ attr {
+ key: "T"
+ value {
+ type: DT_BOOL
+ }
+ }
+}
+node {
+ name: "case/cond/Const"
+ op: "Const"
+ input: "^case/cond/switch_t"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Const_1"
+ op: "Const"
+ input: "^case/cond/switch_f"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+}
+node {
+ name: "case/cond/Merge"
+ op: "Merge"
+ input: "case/cond/Const_1"
+ input: "case/cond/Const"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "MyAdd_lEKAAnIwI5I"
+ op: "MyAdd_lEKAAnIwI5I"
+ input: "case/cond/Merge"
+ input: "Const"
+}
+library {
+ function {
+ signature {
+ name: "MyAdd_lEKAAnIwI5I"
+ input_arg {
+ name: "x"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "y"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "Add"
+ type: DT_FLOAT
+ }
+ }
+ node_def {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+ }
+ node_def {
+ name: "Const_1"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 3
+ }
+ }
+ float_val: 2.0
+ }
+ }
+ }
+ }
+ node_def {
+ name: "Add"
+ op: "Add"
+ input: "x"
+ input: "Const:output:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ node_def {
+ name: "Add_1"
+ op: "Add"
+ input: "y"
+ input: "Const_1:output:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ ret {
+ key: "Add"
+ value: "Add:z:0"
+ }
+ attr {
+ key: "_noinline"
+ value {
+ b: true
+ }
+ }
+ }
+}
+versions {
+ producer: 26
+ min_consumer: 12
+}
diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt
new file mode 100644
index 0000000000..415c347a1d
--- /dev/null
+++ b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt
@@ -0,0 +1,597 @@
+node {
+ name: "Const/Const"
+ op: "Const"
+ device: "/cpu:0"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 1
+ }
+ }
+ int_val: 64
+ }
+ }
+ }
+}
+node {
+ name: "input_0_0"
+ op: "RandomUniform"
+ input: "Const/Const"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "seed"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "seed2"
+ value {
+ i: 0
+ }
+ }
+}
+node {
+ name: "Const_1/Const"
+ op: "Const"
+ device: "/cpu:0"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 4
+ }
+ }
+ tensor_content: "\001\000\000\000\001\000\000\000\030\000\000\000@\000\000\000"
+ }
+ }
+ }
+}
+node {
+ name: "input_1_0"
+ op: "RandomUniform"
+ input: "Const_1/Const"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "seed"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "seed2"
+ value {
+ i: 0
+ }
+ }
+}
+node {
+ name: "Const_2/Const"
+ op: "Const"
+ device: "/cpu:0"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 4
+ }
+ }
+ tensor_content: "\200\000\000\000\340\000\000\000\340\000\000\000\003\000\000\000"
+ }
+ }
+ }
+}
+node {
+ name: "input_2_0"
+ op: "RandomUniform"
+ input: "Const_2/Const"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "seed"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "seed2"
+ value {
+ i: 0
+ }
+ }
+}
+node {
+ name: "Const_3/Const"
+ op: "Const"
+ device: "/cpu:0"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 4
+ }
+ }
+ tensor_content: "\007\000\000\000\007\000\000\000\003\000\000\000\010\000\000\000"
+ }
+ }
+ }
+}
+node {
+ name: "input_3_0"
+ op: "RandomUniform"
+ input: "Const_3/Const"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "seed"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "seed2"
+ value {
+ i: 0
+ }
+ }
+}
+node {
+ name: "y0"
+ op: "BiasAddx1_Conv2Dx1_DepthwiseConv2dNativex1_Relux1_95"
+ input: "input_0_0"
+ input: "input_1_0"
+ input: "input_2_0"
+ input: "input_3_0"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+}
+node {
+ name: "shape"
+ op: "Shape"
+ input: "y0"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "zeros"
+ op: "ZerosLike"
+ input: "shape"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "ones"
+ op: "OnesLike"
+ input: "shape"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "slice_0"
+ op: "Slice"
+ input: "y0"
+ input: "zeros"
+ input: "ones"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "Index"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "identity_0"
+ op: "Identity"
+ input: "slice_0"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "shape_1"
+ op: "Shape"
+ input: "y0:1"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "out_type"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "zeros_1"
+ op: "ZerosLike"
+ input: "shape_1"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "ones_1"
+ op: "OnesLike"
+ input: "shape_1"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+node {
+ name: "slice_1"
+ op: "Slice"
+ input: "y0:1"
+ input: "zeros_1"
+ input: "ones_1"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "Index"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "identity_1"
+ op: "Identity"
+ input: "slice_1"
+ input: "^input_0_0"
+ input: "^input_1_0"
+ input: "^input_2_0"
+ input: "^input_3_0"
+ device: "/cpu:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+library {
+ function {
+ signature {
+ name: "BiasAddx1_Conv2Dx1_DepthwiseConv2dNativex1_Relux1_95"
+ input_arg {
+ name: "InceptionV2/Conv2d_1a_7x7/biases/read"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "InceptionV2/Conv2d_1a_7x7/pointwise_weights/read"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "random_uniform"
+ type: DT_FLOAT
+ }
+ input_arg {
+ name: "InceptionV2/Conv2d_1a_7x7/depthwise_weights/read"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu"
+ type: DT_FLOAT
+ }
+ output_arg {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise"
+ type: DT_FLOAT
+ }
+ }
+ node_def {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/BiasAdd"
+ op: "BiasAdd"
+ input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d:output:0"
+ input: "InceptionV2/Conv2d_1a_7x7/biases/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ }
+ node_def {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu"
+ op: "Relu"
+ input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/BiasAdd:output:0"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ node_def {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d"
+ op: "Conv2D"
+ input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise:output:0"
+ input: "InceptionV2/Conv2d_1a_7x7/pointwise_weights/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "VALID"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "use_cudnn_on_gpu"
+ value {
+ b: true
+ }
+ }
+ }
+ node_def {
+ name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise"
+ op: "DepthwiseConv2dNative"
+ input: "random_uniform"
+ input: "InceptionV2/Conv2d_1a_7x7/depthwise_weights/read"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "SAME"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 2
+ i: 2
+ i: 1
+ }
+ }
+ }
+ }
+ ret {
+ key: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu"
+ value: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu:activations:0"
+ }
+ ret {
+ key: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise"
+ value: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise:output:0"
+ }
+ attr {
+ key: "_noinline"
+ value {
+ b: true
+ }
+ }
+ }
+}
+versions {
+ producer: 26
+ min_consumer: 12
+}