1 /* Copyright 2016 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 #ifndef TENSORFLOW_CC_FRAMEWORK_SCOPE_H_ 17 #define TENSORFLOW_CC_FRAMEWORK_SCOPE_H_ 18 19 #include <memory> 20 #include <string> 21 #include <unordered_map> 22 #include <unordered_set> 23 #include <vector> 24 25 #include "absl/strings/str_cat.h" 26 #include "tensorflow/cc/framework/ops.h" 27 #include "tensorflow/core/common_runtime/graph_constructor.h" 28 #include "tensorflow/core/lib/core/status.h" 29 #include "tensorflow/core/lib/gtl/array_slice.h" 30 31 namespace tensorflow { 32 33 class Graph; 34 class GraphDef; 35 class NodeBuilder; 36 struct CompositeOpScopes; 37 38 /// @addtogroup core 39 /// @{ 40 41 /// A `Scope` object represents a set of related TensorFlow ops that have the 42 /// same properties such as a common name prefix. 43 /// 44 /// A Scope object is a container for TensorFlow Op properties. Op constructors 45 /// get a Scope object as a mandatory first argument and the constructed op 46 /// acquires the properties in the object. 47 /// 48 /// A simple example: 49 /// 50 /// using namespace ops; 51 /// Scope root = Scope::NewRootScope(); 52 /// auto c1 = Const(root, { {1, 1} }); 53 /// auto m = MatMul(root, c1, { {41}, {1} }); 54 /// GraphDef gdef; 55 /// Status s = root.ToGraphDef(&gdef); 56 /// if (!s.ok()) { ... } 57 /// 58 /// Scope hierarchy: 59 /// 60 /// The Scope class provides various With<> functions that create a new scope. 61 /// The new scope typically has one property changed while other properties are 62 /// inherited from the parent scope. 63 /// NewSubScope(name) method appends `name` to the prefix of names for ops 64 /// created within the scope, and WithOpName() changes the suffix which 65 /// otherwise defaults to the type of the op. 66 /// 67 /// Name examples: 68 /// 69 /// Scope root = Scope::NewRootScope(); 70 /// Scope linear = root.NewSubScope("linear"); 71 /// // W will be named "linear/W" 72 /// auto W = Variable(linear.WithOpName("W"), 73 /// {2, 2}, DT_FLOAT); 74 /// // b will be named "linear/b_3" 75 /// int idx = 3; 76 /// auto b = Variable(linear.WithOpName("b_", idx), 77 /// {2}, DT_FLOAT); 78 /// auto x = Const(linear, {...}); // name: "linear/Const" 79 /// auto m = MatMul(linear, x, W); // name: "linear/MatMul" 80 /// auto r = BiasAdd(linear, m, b); // name: "linear/BiasAdd" 81 /// 82 /// Scope lifetime: 83 /// 84 /// A new scope is created by calling Scope::NewRootScope. This creates some 85 /// resources that are shared by all the child scopes that inherit from this 86 /// scope, directly or transitively. For instance, a new scope creates a new 87 /// Graph object to which operations are added when the new scope or its 88 /// children are used by an Op constructor. The new scope also has a Status 89 /// object which will be used to indicate errors by Op-constructor functions 90 /// called on any child scope. The Op-constructor functions have to check the 91 /// scope's status by calling the ok() method before proceeding to construct the 92 /// op. 93 /// 94 /// Thread safety: 95 /// 96 /// A `Scope` object is NOT thread-safe. Threads cannot concurrently call 97 /// op-constructor functions on the same `Scope` object. 98 class Scope { 99 public: 100 Scope(const Scope& other); 101 ~Scope(); 102 Scope& operator=(const Scope& other); 103 104 // The following functions are for users making graphs. They return brand new 105 // scopes, or scopes derived from an existing scope object. 106 107 /// Return a new scope. 108 /// This creates a new graph and all operations constructed in this graph 109 /// should use the returned object as the "root" scope. 110 static Scope NewRootScope(); 111 112 /// Return a new scope. Ops created with this scope will have 113 /// `name/child_scope_name` as the prefix. The actual name will be unique 114 /// in the current scope. All other properties are inherited from the current 115 /// scope. If `child_scope_name` is empty, the `/` is elided. 116 Scope NewSubScope(const string& child_scope_name) const; 117 118 /// Return a new scope. All ops created within the returned scope will have 119 /// names of the form `name/StrCat(fragments...)[_suffix]` 120 template <typename... Ty> WithOpName(Ty...fragments)121 Scope WithOpName(Ty... fragments) const { 122 return WithOpNameImpl(absl::StrCat(fragments...)); 123 } 124 125 /// Return a new scope. All ops created within the returned scope will have as 126 /// control dependencies the union of operations in the control_deps vector 127 /// and the control dependencies of the current scope. 128 Scope WithControlDependencies( 129 const gtl::ArraySlice<Operation>& control_deps) const; 130 /// Same as above, but convenient to add control dependency on the operation 131 /// producing the control_dep output. 132 Scope WithControlDependencies(const Output& control_dep) const; 133 134 /// Return a new scope. All ops created within the returned scope will have no 135 /// control dependencies on other operations. 136 Scope WithNoControlDependencies() const; 137 138 /// Return a new scope. All ops created within the returned scope will have 139 /// the device field set to 'device'. 140 Scope WithDevice(const string& device) const; 141 142 /// Returns a new scope. All ops created within the returned scope will have 143 /// their assigned device set to `assigned_device`. 144 Scope WithAssignedDevice(const string& assigned_device) const; 145 146 /// Returns a new scope. All ops created within the returned scope will have 147 /// their _XlaCluster attribute set to `xla_cluster`. 148 Scope WithXlaCluster(const string& xla_cluster) const; 149 150 /// Return a new scope. All ops created within the returned scope will be 151 /// co-located on the device where op is placed. 152 /// NOTE: This function is intended to be use internal libraries only for 153 /// controlling placement of ops on to devices. Public use is not encouraged 154 /// because the implementation of device placement is subject to change. 155 Scope ColocateWith(const Operation& op) const; 156 /// Convenience function for above. ColocateWith(const Output & out)157 Scope ColocateWith(const Output& out) const { return ColocateWith(out.op()); } 158 /// Clear all colocation constraints. 159 Scope ClearColocation() const; 160 161 /// Return a new scope. The op-constructor functions taking the returned scope 162 /// as the scope argument will exit as soon as an error is detected, instead 163 /// of setting the status on the scope. 164 Scope ExitOnError() const; 165 166 /// Return a new scope. All ops created with the new scope will have 167 /// kernel_label as the value for their '_kernel' attribute; 168 Scope WithKernelLabel(const string& kernel_label) const; 169 170 // The following functions are for scope object consumers. 171 172 /// Return a unique name, using default_name if an op name has not been 173 /// specified. 174 string GetUniqueNameForOp(const string& default_name) const; 175 176 /// Update the status on this scope. 177 /// Note: The status object is shared between all children of this scope. 178 /// If the resulting status is not Status::OK() and exit_on_error_ is set on 179 /// this scope, this function exits by calling LOG(FATAL). 180 void UpdateStatus(const Status& s) const; 181 182 // START_SKIP_DOXYGEN 183 184 /// Update the builder with properties accumulated in this scope. Does not set 185 /// status(). 186 // TODO(skyewm): NodeBuilder is not part of public API 187 void UpdateBuilder(NodeBuilder* builder) const; 188 // END_SKIP_DOXYGEN 189 190 CompositeOpScopes GetCompositeOpScopes(const string& composite_op_name) const; 191 192 bool ok() const; 193 194 // TODO(skyewm): Graph is not part of public API 195 Graph* graph() const; 196 197 // TODO(skyewm): Graph is not part of public API 198 std::shared_ptr<Graph> graph_as_shared_ptr() const; 199 200 Status status() const; 201 202 /// If status() is Status::OK(), convert the Graph object stored in this scope 203 /// to a GraphDef proto and return Status::OK(). Otherwise, return the error 204 /// status as is without performing GraphDef conversion. 205 Status ToGraphDef(GraphDef* gdef) const; 206 207 // START_SKIP_DOXYGEN 208 209 /// If status() is Status::OK(), construct a Graph object using `opts` as the 210 /// GraphConstructorOptions, and return Status::OK if graph construction was 211 /// successful. Otherwise, return the error status. 212 // TODO(josh11b, keveman): Make this faster; right now it converts 213 // Graph->GraphDef->Graph. This cleans up the graph (e.g. adds 214 // edges from the source and to the sink node, resolves back edges 215 // by name), and makes sure the resulting graph is valid. 216 Status ToGraph( 217 Graph* g, GraphConstructorOptions opts = GraphConstructorOptions{}) const; 218 219 // Calls AddNode() using this scope's ShapeRefiner. This exists in the public 220 // API to prevent custom op wrappers from needing access to shape_refiner.h or 221 // scope_internal.h. 222 // TODO(skyewm): remove this from public API 223 Status DoShapeInference(Node* node) const; 224 225 // Creates a new root scope that causes all DoShapeInference() calls to return 226 // Status::OK() (on the returned scope and any subscopes). Used for testing. 227 // TODO(skyewm): fix tests that still require this and eventually remove, or 228 // at least remove from public API 229 static Scope DisabledShapeInferenceScope(); 230 // END_SKIP_DOXYGEN 231 232 const std::vector<Operation>& control_deps() const; 233 234 // START_SKIP_DOXYGEN 235 class Impl; impl()236 Impl* impl() { return impl_.get(); } impl()237 const Impl* impl() const { return impl_.get(); } 238 // END_SKIP_DOXYGEN 239 240 private: 241 Scope WithOpNameImpl(const string& op_name) const; 242 243 friend class InternalScope; 244 std::unique_ptr<Impl> impl_; 245 explicit Scope(Impl*); 246 }; 247 248 /// A helper struct to hold the scopes that would be used by a function 249 /// constructing a composite op. 250 struct CompositeOpScopes { 251 /// Scope to be used for creating the local ops (primitive or other composite 252 /// ops). 253 Scope child; 254 /// Scope to be used for creating the last op. 255 Scope last; 256 }; 257 258 // Creates a node of the given operation, with the given inputs, and assigns the 259 // result to output. This does not support the ability to add additional 260 // attributes. 261 Status CreateOutputWithScope(string op_name, 262 absl::Span<const ::tensorflow::Input> inputs, 263 const Scope& scope, Output* output); 264 /// @} 265 266 } // namespace tensorflow 267 268 #endif // TENSORFLOW_CC_FRAMEWORK_SCOPE_H_ 269