Home | History | Annotate | Download | only in framework
      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