• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 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 // Main abstraction controlling the tflite interpreter.
16 // See context.h for the API for defining operations (TfLiteRegistration).
17 #ifndef TENSORFLOW_CONTRIB_LITE_INTERPRETER_H_
18 #define TENSORFLOW_CONTRIB_LITE_INTERPRETER_H_
19 
20 #include <cstdio>
21 #include <cstdlib>
22 #include <vector>
23 #include "tensorflow/contrib/lite/allocation.h"
24 #include "tensorflow/contrib/lite/context.h"
25 #include "tensorflow/contrib/lite/error_reporter.h"
26 #include "tensorflow/contrib/lite/memory_planner.h"
27 
28 namespace tflite {
29 
30 // Map statically from a c++ type to a TfLiteType (used below for safe casts).
31 template <class T>
typeToTfLiteType()32 constexpr TfLiteType typeToTfLiteType() {
33   return kTfLiteNoType;
34 }
35 template <>
36 constexpr TfLiteType typeToTfLiteType<int>() {
37   return kTfLiteInt32;
38 }
39 template <>
40 constexpr TfLiteType typeToTfLiteType<int64_t>() {
41   return kTfLiteInt64;
42 }
43 template <>
44 constexpr TfLiteType typeToTfLiteType<float>() {
45   return kTfLiteFloat32;
46 }
47 template <>
48 constexpr TfLiteType typeToTfLiteType<unsigned char>() {
49   return kTfLiteUInt8;
50 }
51 
52 // Forward declare since NNAPIDelegate uses Interpreter.
53 class NNAPIDelegate;
54 
55 // An interpreter for a graph of nodes that input and output from tensors.
56 // Each node of the graph processes a set of input tensors and produces a
57 // set of output Tensors. All inputs/output tensors are referenced by index.
58 //
59 // Usage:
60 //
61 // -- Create basic model
62 // Interpreter foo(2, 1);
63 // foo.SetTensorParametersReadWrite(0, ...);
64 // foo.SetTensorParametersReadOnly(1, ...);
65 // foo.SetNodeParameters(0, ...)
66 //
67 // -- Resize input array to 1 length.
68 // foo.ResizeInputTensor(0, 1);
69 // foo.AllocateTensors();
70 // -- Install array data
71 // foo.typed_tensor<float>(0)[0] = 3;
72 // foo.Invoke();
73 // foo.typed_tensor<float>(0)[0] = 4;
74 // foo.Invoke();
75 // -- Resize input array and set data.
76 // foo.ResizeInputTensor(0, 2);
77 // foo.AllocateTensors();
78 // foo.typed_tensor<float>(0)[0] = 4;
79 // foo.typed_tensor<float>(0)[1] = 8;
80 // foo.Invoke();
81 //
82 
83 struct TfLiteIntArrayDeleter {
operatorTfLiteIntArrayDeleter84   void operator()(TfLiteIntArray* a) {
85     if (a) TfLiteIntArrayFree(a);
86   }
87 };
88 
89 class Interpreter {
90  public:
91   // Instantiate an interpreter. All errors associated with reading and
92   // processing this model will be forwarded to the error_reporter object.
93   //
94   // Note, if error_reporter is nullptr, then a default StderrReporter is
95   // used.
96   explicit Interpreter(ErrorReporter* error_reporter = DefaultErrorReporter());
97 
98   ~Interpreter();
99 
100   Interpreter(const Interpreter&) = delete;
101   Interpreter& operator=(const Interpreter&) = delete;
102 
103   // Functions to build interpreter
104 
105   // Provide a list of tensor indexes that are inputs to the model.
106   // Each index is bound check and this modifies the consistent_ flag of the
107   // interpreter.
108   TfLiteStatus SetInputs(std::vector<int> inputs);
109 
110   // Provide a list of tensor indexes that are outputs to the model
111   // Each index is bound check and this modifies the consistent_ flag of the
112   // interpreter.
113   TfLiteStatus SetOutputs(std::vector<int> outputs);
114 
115   // Adds a node with the given parameters and returns the index of the new
116   // node in `node_index` (optionally). Interpreter will take ownership of
117   // `builtin_data` and destroy it with `free`. Ownership of 'init_data'
118   // remains with the caller.
119   TfLiteStatus AddNodeWithParameters(const std::vector<int>& inputs,
120                                      const std::vector<int>& outputs,
121                                      const char* init_data,
122                                      size_t init_data_size, void* builtin_data,
123                                      const TfLiteRegistration* registration,
124                                      int* node_index = nullptr);
125 
126   // Adds `tensors_to_add` tensors, preserving pre-existing Tensor entries.
127   // The value pointed to by `first_new_tensor_index` will be set to the
128   // index of the first new tensor if `first_new_tensor_index` is non-null.
129   TfLiteStatus AddTensors(int tensors_to_add,
130                           int* first_new_tensor_index = nullptr);
131 
132   // Set description of inputs/outputs/data/fptrs for node `node_index`.
133   // This variant assumes an external buffer has been allocated of size
134   // bytes. The lifetime of buffer must be ensured to be greater or equal
135   // to Interpreter.
136   TfLiteStatus SetTensorParametersReadOnly(
137       int tensor_index, TfLiteType type, const char* name,
138       const std::vector<int>& dims, TfLiteQuantizationParams quantization,
139       const char* buffer, size_t bytes, const Allocation* allocation = nullptr);
140 
141   // Set description of inputs/outputs/data/fptrs for node `node_index`.
142   // This variant assumes an external buffer has been allocated of size
143   // bytes. The lifetime of buffer must be ensured to be greater or equal
144   // to Interpreter.
145   TfLiteStatus SetTensorParametersReadWrite(
146       int tensor_index, TfLiteType type, const char* name,
147       const std::vector<int>& dims, TfLiteQuantizationParams quantization);
148 
149   // Functions to access tensor data
150 
151   // Read only access to list of inputs.
inputs()152   const std::vector<int>& inputs() const { return inputs_; }
153 
154   // Return the name of a given input. The given index must be between 0 and
155   // inputs().size().
GetInputName(int index)156   const char* GetInputName(int index) const {
157     return context_.tensors[inputs_[index]].name;
158   }
159 
160   // Read only access to list of outputs.
outputs()161   const std::vector<int>& outputs() const { return outputs_; }
162 
163   // Return the name of a given output. The given index must be between 0 and
164   // outputs().size().
GetOutputName(int index)165   const char* GetOutputName(int index) const {
166     return context_.tensors[outputs_[index]].name;
167   }
168 
169   // Return the number of tensors in the model.
tensors_size()170   int tensors_size() const { return context_.tensors_size; }
171 
172   // Return the number of ops in the model.
nodes_size()173   int nodes_size() const { return nodes_and_registration_.size(); }
174 
175   // WARNING: Experimental interface, subject to change
execution_plan()176   const std::vector<int>& execution_plan() const { return execution_plan_; }
177 
178   // WARNING: Experimental interface, subject to change
179   // Overrides execution plan. This bounds checks indices sent in.
180   TfLiteStatus SetExecutionPlan(const std::vector<int>& new_plan);
181 
182   // Get a tensor data structure.
183   // TODO(aselle): Create a safe ArrayHandle interface to avoid exposing this
184   // read/write access to structure
tensor(int tensor_index)185   TfLiteTensor* tensor(int tensor_index) {
186     if (tensor_index >= context_.tensors_size || tensor_index < 0)
187       return nullptr;
188     return &context_.tensors[tensor_index];
189   }
190 
191   // Get a pointer to an operation and registration data structure if in bounds.
192   // TODO(aselle): Create a safe ArrayHandle interface to avoid exposing this
193   // read/write access to structure
node_and_registration(int node_index)194   const std::pair<TfLiteNode, TfLiteRegistration>* node_and_registration(
195       int node_index) {
196     if (node_index >= nodes_and_registration_.size() || node_index < 0)
197       return nullptr;
198     return &nodes_and_registration_[node_index];
199   }
200 
201   // Perform a checked cast to the appropriate tensor type.
202   template <class T>
typed_tensor(int tensor_index)203   T* typed_tensor(int tensor_index) {
204     if (TfLiteTensor* tensor_ptr = tensor(tensor_index)) {
205       if (tensor_ptr->type == typeToTfLiteType<T>()) {
206         return reinterpret_cast<T*>(tensor_ptr->data.raw);
207       }
208     }
209     return nullptr;
210   }
211 
212   // Return a pointer into the data of a given input tensor. The given index
213   // must be between 0 and inputs().size().
214   template <class T>
typed_input_tensor(int index)215   T* typed_input_tensor(int index) {
216     return typed_tensor<T>(inputs_[index]);
217   }
218 
219   // Return a pointer into the data of a given output tensor. The given index
220   // must be between 0 and outputs().size().
221   template <class T>
typed_output_tensor(int index)222   T* typed_output_tensor(int index) {
223     return typed_tensor<T>(outputs_[index]);
224   }
225 
226   // Change the dimensionality of a given tensor. Note, this is only acceptable
227   // for tensor indices that are inputs.
228   // Returns status of failure or success.
229   // TODO(aselle): Consider implementing ArraySlice equivalent to make this
230   //   more adept at accepting data without an extra copy. Use absl::ArraySlice
231   //   if our partners determine that dependency is acceptable.
232   TfLiteStatus ResizeInputTensor(int tensor_index,
233                                  const std::vector<int>& dims);
234 
235   // Update allocations for all tensors. This will redim dependent tensors using
236   // the input tensor dimensionality as given. This is relatively expensive.
237   // If you know that your sizes are not changing, you need not call this.
238 
239   // Returns status of success or failure.
240   TfLiteStatus AllocateTensors();
241 
242   // Invoke the interpreter (run the whole graph in dependency order).
243   //
244   // NOTE: It is possible that the interpreter is not in a ready state
245   // to evaluate (i.e. if a ResizeTensor() has been performed without an
246   // AllocateTensors().
247   // Returns status of success or failure.
248   TfLiteStatus Invoke();
249 
250   // Enable or disable the NN API (true to enable)
251   void UseNNAPI(bool enable);
252 
253   // Set the number of threads available to the interpreter.
254   void SetNumThreads(int num_threads);
255 
256   // Allow a delegate to look at the graph and modify the graph to handle
257   // parts of the graph themselves. After this is called, the graph may
258   // contain new nodes that replace 1 more nodes.
259   TfLiteStatus ModifyGraphWithDelegate(TfLiteDelegate* delegate);
260 
261  private:
262   // Give 'op_reg' a chance to initialize itself using the contents of
263   // 'buffer'.
OpInit(const TfLiteRegistration & op_reg,const char * buffer,size_t length)264   void* OpInit(const TfLiteRegistration& op_reg, const char* buffer,
265                size_t length) {
266     if (op_reg.init == nullptr) return nullptr;
267     return op_reg.init(&context_, buffer, length);
268   }
269 
270   // Let 'op_reg' release any memory it might have allocated via 'OpInit'.
OpFree(const TfLiteRegistration & op_reg,void * buffer)271   void OpFree(const TfLiteRegistration& op_reg, void* buffer) {
272     if (op_reg.free == nullptr) return;
273     if (buffer) {
274       op_reg.free(&context_, buffer);
275     }
276   }
277 
278   // Prepare the given 'node' for execution.
OpPrepare(const TfLiteRegistration & op_reg,TfLiteNode * node)279   TfLiteStatus OpPrepare(const TfLiteRegistration& op_reg, TfLiteNode* node) {
280     if (op_reg.prepare == nullptr) return kTfLiteOk;
281     return op_reg.prepare(&context_, node);
282   }
283 
284   // Invoke the operator represented by 'node'.
OpInvoke(const TfLiteRegistration & op_reg,TfLiteNode * node)285   TfLiteStatus OpInvoke(const TfLiteRegistration& op_reg, TfLiteNode* node) {
286     if (op_reg.invoke == nullptr) return kTfLiteError;
287     return op_reg.invoke(&context_, node);
288   }
289 
290   // Call OpPrepare() for as many ops as possible, allocating memory for their
291   // tensors. If an op containing dynamic tensors is found, preparation will be
292   // postponed until this function is called again. This allows the interpreter
293   // to wait until Invoke() to resolve the sizes of dynamic tensors.
294   TfLiteStatus PrepareOpsAndTensors();
295 
296   // Call OpPrepare() for all ops starting at 'first_node'. Stop when a
297   // dynamic tensors is found or all ops have been prepared. Fill
298   // 'last_node_prepared' with the id of the op containing dynamic tensors, or
299   // the last in the graph.
300   TfLiteStatus PrepareOpsStartingAt(int first_execution_plan_index,
301                                     int* last_execution_plan_index_prepared);
302 
303   // Tensors needed by the interpreter. Use `AddTensors` to add more blank
304   // tensor entries. Note, `tensors_.data()` needs to be synchronized to the
305   // `context_` whenever this std::vector is reallocated. Currently this
306   // only happens in `AddTensors()`.
307   std::vector<TfLiteTensor> tensors_;
308 
309   // Check if an array of tensor indices are valid with respect to the Tensor
310   // array.
311   // NOTE: this changes consistent_ to be false if indices are out of bounds.
312   TfLiteStatus CheckTensorIndices(const char* label, const int* indices,
313                                   int length);
314 
315   // Compute the number of bytes required to represent a tensor with dimensions
316   // specified by the array dims (of length dims_size). Returns the status code
317   // and bytes.
318   TfLiteStatus BytesRequired(TfLiteType type, const int* dims, int dims_size,
319                              size_t* bytes);
320 
321   // Request an tensor be resized implementation. If the given tensor is of
322   // type kTfLiteDynamic it will also be allocated new memory.
323   TfLiteStatus ResizeTensorImpl(TfLiteTensor* tensor, TfLiteIntArray* new_size);
324 
325   // Report a detailed error string (will be printed to stderr).
326   // TODO(aselle): allow user of class to provide alternative destinations.
327   void ReportErrorImpl(const char* format, va_list args);
328 
329   // Entry point for C node plugin API to request an tensor be resized.
330   static TfLiteStatus ResizeTensor(TfLiteContext* context, TfLiteTensor* tensor,
331                                    TfLiteIntArray* new_size);
332   // Entry point for C node plugin API to report an error.
333   static void ReportError(TfLiteContext* context, const char* format, ...);
334 
335   // Entry point for C node plugin API to add new tensors.
336   static TfLiteStatus AddTensors(TfLiteContext* context, int tensors_to_add,
337                                  int* first_new_tensor_index);
338 
339   // WARNING: This is an experimental API and subject to change.
340   // Entry point for C API ReplaceSubgraphsWithDelegateKernels
341   static TfLiteStatus ReplaceSubgraphsWithDelegateKernels(
342       TfLiteContext* context, TfLiteRegistration registration,
343       const TfLiteIntArray* nodes_to_replace);
344 
345   // Update the execution graph to replace some of the nodes with stub
346   // nodes. Specifically any node index that has `nodes[index]==1` will be
347   // slated for replacement with a delegate kernel specified by registration.
348   // WARNING: This is an experimental interface that is subject to change.
349   TfLiteStatus ReplaceSubgraphsWithDelegateKernels(
350       TfLiteRegistration registration, const TfLiteIntArray* nodes_to_replace);
351 
352   // WARNING: This is an experimental interface that is subject to change.
353   // Gets the internal pointer to a TensorFlow lite node by node_index.
354   TfLiteStatus GetNodeAndRegistration(int node_index, TfLiteNode** node,
355                                       TfLiteRegistration** registration);
356 
357   // WARNING: This is an experimental interface that is subject to change.
358   // Entry point for C node plugin API to get a node by index.
359   static TfLiteStatus GetNodeAndRegistration(struct TfLiteContext*,
360                                              int node_index, TfLiteNode** node,
361                                              TfLiteRegistration** registration);
362 
363   // WARNING: This is an experimental interface that is subject to change.
364   // Gets an TfLiteIntArray* representing the execution plan. The caller owns
365   // this memory and must free it with TfLiteIntArrayFree().
366   TfLiteStatus GetExecutionPlan(TfLiteIntArray** execution_plan);
367 
368   // WARNING: This is an experimental interface that is subject to change.
369   // Entry point for C node plugin API to get the execution plan
370   static TfLiteStatus GetExecutionPlan(struct TfLiteContext* context,
371                                        TfLiteIntArray** execution_plan);
372 
373   // A pure C data structure used to communicate with the pure C plugin
374   // interface. To avoid copying tensor metadata, this is also the definitive
375   // structure to store tensors.
376   TfLiteContext context_;
377 
378   // Node inputs/outputs are stored in TfLiteNode and TfLiteRegistration stores
379   // function pointers to actual implementation.
380   std::vector<std::pair<TfLiteNode, TfLiteRegistration>>
381       nodes_and_registration_;
382 
383   // Whether the model is consistent. That is to say if the inputs and outputs
384   // of every node and the global inputs and outputs are valid indexes into
385   // the tensor array.
386   bool consistent_ = true;
387 
388   // Whether the model is safe to invoke (if any errors occurred this
389   // will be false).
390   bool invokable_ = false;
391 
392   // Array of indices representing the tensors that are inputs to the
393   // interpreter.
394   std::vector<int> inputs_;
395 
396   // Array of indices representing the tensors that are outputs to the
397   // interpreter.
398   std::vector<int> outputs_;
399 
400   // The error reporter delegate that tflite will forward queries errors to.
401   ErrorReporter* error_reporter_;
402 
403   // Index of the next node to prepare.
404   // During Invoke(), Interpreter will allocate input tensors first, which are
405   // known to be fixed size. Then it will allocate outputs from nodes as many
406   // as possible. When there is a node that produces dynamic sized tensor.
407   // Intepreter will stop allocating tensors, set the value of next allocate
408   // node id, and execute the node to generate the output tensor before continue
409   // to allocate successors. This process repeats until all nodes are executed.
410   // NOTE: this relies on the order of nodes that is in topological order.
411   int next_execution_plan_index_to_prepare_;
412 
413   // WARNING: This is an experimental interface that is subject to change.
414   // This is a list of node indices (to index into nodes_and_registration).
415   // This represents a valid topological sort (dependency ordered) execution
416   // plan. In particular, it is valid for this ordering to contain only a
417   // subset of the node indices.
418   std::vector<int> execution_plan_;
419 
420   // In the future, we'd like a TfLiteIntArray compatible representation.
421   // TODO(aselle): replace execution_plan_ with this.
422   std::unique_ptr<TfLiteIntArray, TfLiteIntArrayDeleter> plan_cache_;
423 
424   // Whether to delegate to NN API
425   std::unique_ptr<NNAPIDelegate> nnapi_delegate_;
426 
427   std::unique_ptr<MemoryPlanner> memory_planner_;
428 };
429 
430 }  // namespace tflite
431 #endif  // TENSORFLOW_CONTRIB_LITE_INTERPRETER_H_
432