/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #ifndef TENSORFLOW_LITE_KERNELS_SHIM_OP_KERNEL_H_ #define TENSORFLOW_LITE_KERNELS_SHIM_OP_KERNEL_H_ // This file defines a shim layer on top of TF and TFLite custom op APIs. // The goal is for a custom op to be written once and used for both runtimes // // It consists of two pieces: // * A set of *context* interfaces: // ** InvokeContext, InitContext, ShapeInferenceContext // These are passed on to the custom op implementation to read/write // tensors, etc. // // * An OpKernelShim interface: // This is what a custom op needs to implement. By using that interface the // custom op can then be easily converted to both a TF op kernel and a // TFLite op kernel. #include #include #include #include #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" #include "tensorflow/lite/kernels/shim/shape.h" #include "tensorflow/lite/kernels/shim/status_macros.h" #include "tensorflow/lite/kernels/shim/tensor_view.h" namespace tflite { namespace shim { // List of the TF custom op APIs this shim library is abstracting away. // // This enum is used as the template parameter in various places in // order to pick the correct set of types (eg. TfInvokeContext vs. // TfLiteInvokeContext) in the op implementation. enum class Runtime { kTf, kTfLite }; // TensorView or error using TensorViewOr = absl::StatusOr>; using ConstTensorViewOr = absl::StatusOr>; // Below are the interfaces for various "Context" objects to abstract away the // TF and TFLite differences. // // The interfaces are static and use the CRTP pattern instead of virtual // methods. // The attribute dictionary passed to the op using AttrValue = std::variant; // The interface for available methods during an op kernel initialization template class InitContext { public: // Read the given attribute and populate the given value. template absl::Status GetAttr(const std::string& attr_name, AttrType* value) const; protected: // Read a given attribute or return error absl::StatusOr GetAttr(const std::string& attr_name) const { return static_cast(*this).GetAttr(attr_name); } }; // The interface for available methods during an op kernel invocation template class InvokeContext { public: // Read an input tensor ConstTensorViewOr GetInput(const int idx) const { return static_cast(*this).GetInput(idx); } // Get a mutable output tensor TensorViewOr GetOutput(const int idx, const Shape& shape) const { return static_cast(*this).GetOutput(idx, shape); } // Number of input tensors int NumInputs() const { return static_cast(*this).NumInputs(); } // Number of output tensors int NumOutputs() const { return static_cast(*this).NumOutputs(); } }; // The interface for available methods during shape inference template class ShapeInferenceContext { public: // Read an input tensor shape ShapeOr GetInputShape(const int idx) const { return static_cast(*this).GetInputShape(idx); } // Set an output tensor shape absl::Status SetOutputShape(const int idx, const Shape& shape) { return static_cast(*this).SetOutputShape(idx, shape); } // Read an input tensor during shape inference ConstTensorViewOr GetInputTensor(const int idx) const { return static_cast(*this).GetInputTensor(idx); } // Number of input tensors int NumInputs() const { return static_cast(*this).NumInputs(); } // Number of output tensors int NumOutputs() const { return static_cast(*this).NumOutputs(); } // Read the given attribute and populate the given value. template absl::Status GetAttr(const std::string& attr_name, AttrType* value) const; protected: // Read a given attribute or return error absl::StatusOr GetAttr(const std::string& attr_name) const { return static_cast(*this).GetAttr(attr_name); } }; // Maps the Runtime to the correct context types. // eg. ContextTypeForRuntime --> // { TfInitContext, TfInvokeContext, TfShapreInferenceContext } template struct ContextTypeForRuntime { // * Init // * Invoke // * ShapeInference }; // A Tensorflow operation interface which is then adapted to both TF and TFLite // runtimes. // // Example usage: // // template // class MyOp : public OpKernelShim { // // // Attributes declaration // // (syntax: https://www.tensorflow.org/guide/create_op) // static std::vector Attrs(); // // // Input tensors declaration // // (syntax: https://www.tensorflow.org/guide/create_op) // static std::vector Inputs(); // // // Output tensors declaration // // (syntax: https://www.tensorflow.org/guide/create_op) // static std::vector Outputs(); // // // Initializes the op // absl::Status Init(InitContext* ctx); // // // Runs the operation // absl::Status Invoke(InvokeContext* ctx); // // // Shape inference // static absl::Status ShapeInference(ShapeInferenceContext* ctx); // // }; // // WARNING: Experimental interface, subject to change template