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