TODO(cwhipkey): When an inference context inside function has requested_input_tensor(i) or requested_input_tensor_as_partial_shape(i) set when input(i) is an _Arg op, then this request should propagate to context, and vice versa. NOTE: Recursive user-defined functions are not supported. Maybe we won't support recursive functions at all in TF, because of other maintainability issues.
| 156 | // Maybe we won't support recursive functions at all in TF, because of |
| 157 | // other maintainability issues. |
| 158 | Status ShapeRefiner::InferShapesForFunction( |
| 159 | const FunctionDef* function_def, AttrSlice attributes, |
| 160 | bool keep_nested_shapes, ExtendedInferenceContext* outer_context) { |
| 161 | const Graph* graph; |
| 162 | auto it = functions_.find(function_def); |
| 163 | if (it != functions_.end()) { |
| 164 | graph = it->second.get(); |
| 165 | } else { |
| 166 | InstantiationResult result; |
| 167 | TF_RETURN_IF_ERROR(InstantiateFunction( |
| 168 | *function_def, attributes, |
| 169 | [this](const string& op, const OpDef** sig) { |
| 170 | return this->function_library_->LookUpOpDef(op, sig); |
| 171 | }, |
| 172 | &result)); |
| 173 | |
| 174 | Graph* new_graph = new Graph(function_library_); |
| 175 | GraphConstructorOptions options; |
| 176 | options.allow_internal_ops = true; |
| 177 | TF_RETURN_IF_ERROR( |
| 178 | ConvertNodeDefsToGraph(options, result.nodes, new_graph)); |
| 179 | functions_[function_def].reset(new_graph); |
| 180 | graph = new_graph; |
| 181 | } |
| 182 | |
| 183 | std::unordered_set<const Node*> function_nodes; |
| 184 | Status inference_status = Status::OK(); |
| 185 | { |
| 186 | auto node_shape_inference_lambda = [this, &outer_context, &function_nodes, |
| 187 | &inference_status](const Node* node) { |
| 188 | if (!inference_status.ok()) return; |
| 189 | inference_status = InferShapesForFunctionSubNode( |
| 190 | node, this, outer_context->get_context()); |
| 191 | function_nodes.insert(node); |
| 192 | }; |
| 193 | |
| 194 | // Calls inference lambda for each node after visiting all predecessors. |
| 195 | // Ensures that we are adding nodes to ShapeRefiner in the topological |
| 196 | // order. |
| 197 | ReverseDFS(*graph, {}, node_shape_inference_lambda); |
| 198 | } |
| 199 | |
| 200 | if (keep_nested_shapes && inference_status.ok()) { |
| 201 | // Fill the nested inferences map. |
| 202 | // |
| 203 | // The materialized function graph has extra nodes for arguments and |
| 204 | // return values, which are not explicitly listed in the FunctionDef, |
| 205 | // we filter out these special nodes here to not expose the implementation |
| 206 | // details and keep only inferences for the nodes listed in the FunctionDef. |
| 207 | std::unordered_map<string, const NodeDef*> user_defined_nodes; |
| 208 | for (const auto& node_def : function_def->node_def()) { |
| 209 | user_defined_nodes[node_def.name()] = &node_def; |
| 210 | } |
| 211 | |
| 212 | std::unordered_map<string, std::unique_ptr<ExtendedInferenceContext>> |
| 213 | nested_inferences; |
| 214 | for (const Node* node : function_nodes) { |
| 215 | const string& node_name = node->name(); |
nothing calls this directly
no test coverage detected