Rolling back until discussion about builtin ops schema issue is discussed. PiperOrigin-RevId: 322891511 Change-Id: Idf64195f89855cf6201cef9d5fd0ec24db403ec1
2491 lines
77 KiB
C++
2491 lines
77 KiB
C++
/* Copyright 2017 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_TOCO_MODEL_H_
|
|
#define TENSORFLOW_LITE_TOCO_MODEL_H_
|
|
|
|
#include <complex>
|
|
#include <functional>
|
|
#include <initializer_list>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "absl/types/optional.h"
|
|
#include "tensorflow/core/platform/logging.h"
|
|
#include "tensorflow/lite/toco/model_flags.pb.h"
|
|
#include "tensorflow/lite/toco/runtime/types.h"
|
|
#include "tensorflow/lite/toco/toco_port.h"
|
|
#include "tensorflow/lite/toco/toco_types.h"
|
|
|
|
namespace toco {
|
|
|
|
using tflite::QuantizationParams;
|
|
|
|
enum class OperatorType : uint8 {
|
|
kNone,
|
|
// General-purpose neural network operators.
|
|
kAdd,
|
|
kAddN,
|
|
kAveragePool,
|
|
kBatchMatMul,
|
|
kBatchNormalization,
|
|
kCeil,
|
|
kConv,
|
|
kConcatenation,
|
|
kCos,
|
|
kDepthwiseConv,
|
|
kDepthToSpace,
|
|
kSpaceToDepth,
|
|
kDequantize,
|
|
kDiv,
|
|
kExp,
|
|
kExpandDims,
|
|
kFill,
|
|
kFloorDiv,
|
|
kFloorMod,
|
|
kFullyConnected,
|
|
kL2Normalization,
|
|
kL2Pool,
|
|
kLstmCell,
|
|
kUnidirectionalSequenceLstm,
|
|
kLocalResponseNormalization,
|
|
kLog,
|
|
kLogistic,
|
|
kMaxPool,
|
|
kFakeQuant,
|
|
kMul,
|
|
kOneHot,
|
|
kRandomUniform,
|
|
kRange,
|
|
kRank,
|
|
kRelu,
|
|
kRelu1,
|
|
kRelu6,
|
|
kPRelu,
|
|
kHardSwish,
|
|
kSoftmax,
|
|
kLogSoftmax,
|
|
kSub,
|
|
kTanh,
|
|
kTransposeConv,
|
|
kCast,
|
|
kFloor,
|
|
kRound,
|
|
kGather,
|
|
kResizeBilinear,
|
|
kSin,
|
|
kSpaceToBatchND,
|
|
kPack,
|
|
kBatchToSpaceND,
|
|
kPad,
|
|
kPadV2,
|
|
kReduceProd, // Reduction product
|
|
kStridedSlice,
|
|
kSlice,
|
|
kSqueeze,
|
|
kMean,
|
|
kArgMax,
|
|
// The SVDF Op is a decomposition of a densely connected Op into
|
|
// low rank filters. For details:
|
|
// https://research.google.com/pubs/pub43813.html
|
|
kSvdf,
|
|
// Special operators used for importing TensorFlow nodes.
|
|
// The general intent is to have some graph transformation either
|
|
// drop them or rewrite them as general-purpose operators.
|
|
kAll,
|
|
kAssert,
|
|
kConcat,
|
|
kConcatV2,
|
|
kGreater,
|
|
kGreaterEqual,
|
|
kIdentity,
|
|
kLess,
|
|
kLessEqual,
|
|
kReduceMax, // Reduction Max
|
|
kMaximum, // Element-wise Maximum
|
|
kReduceMin, // Reduction Min
|
|
kMinimum, // Element-wise Minimum
|
|
kMatMul,
|
|
kMerge,
|
|
kNeg,
|
|
kReshape,
|
|
kRsqrt,
|
|
kShape,
|
|
kSplit,
|
|
kSplitV,
|
|
kSqrt,
|
|
kSquare,
|
|
kSquaredDifference,
|
|
kSum,
|
|
kSwitch,
|
|
kTile,
|
|
kTranspose,
|
|
kTopK_V2,
|
|
kDynamicPartition,
|
|
kDynamicStitch,
|
|
// An unsupported TF operation. It's only needed to be able to represent TF
|
|
// graph internally and is expected to be dropped by graph transformations.
|
|
kUnsupported,
|
|
// Finally, TensorFlow uses different conventions for axes ordering,
|
|
// see AxesOrder, and this cannot always be resolved at the time of importing
|
|
// nodes, as TensorFlow parameters may be constant-expression subgraphs
|
|
// instead of being given as plain constant arrays. So we need to insert
|
|
// special nodes in the graph to shuffle axes.
|
|
kReorderAxes,
|
|
kSegmentSum,
|
|
kSelect,
|
|
kSelectV2,
|
|
kSparseToDense,
|
|
kEqual,
|
|
kNotEqual,
|
|
kPow,
|
|
kArgMin,
|
|
kAny,
|
|
kLogicalAnd,
|
|
kLogicalNot,
|
|
kLogicalOr,
|
|
kCTCBeamSearchDecoder,
|
|
kUnpack,
|
|
kZerosLike,
|
|
kResizeNearestNeighbor,
|
|
kLeakyRelu,
|
|
kAbs,
|
|
kMirrorPad,
|
|
kUnique,
|
|
kUnidirectionalSequenceRnn,
|
|
kBidirectionalSequenceLstm,
|
|
kReverseV2,
|
|
kBidirectionalSequenceRnn,
|
|
kGatherNd,
|
|
kWhere,
|
|
kElu,
|
|
kReverseSequence,
|
|
kMatrixDiag,
|
|
kMatrixSetDiag,
|
|
kMatrixDiagV2,
|
|
kMatrixSetDiagV2,
|
|
kMatrixDiagV3,
|
|
kMatrixSetDiagV3,
|
|
kScatterNd,
|
|
// Debugging operators.
|
|
kNumericVerify
|
|
};
|
|
|
|
// Helper to deal with TensorFlow arrays using a different ordering of
|
|
// dimensions
|
|
// ("axes") than our own.
|
|
// TODO(benoitjacob): Ultimately, we shouldn't have any "ordering" of axes,
|
|
// we should have associative arrays mapping symbolic axes identifiers (like
|
|
// "output_depth") to dimensions. We would then not need this anymore.
|
|
enum class AxesOrder {
|
|
kOneAxis, // one-dimensional array, one unique axis.
|
|
kCR, // column-major matrix storage order. Our standard.
|
|
kRC, // row-major matrix storage order. TensorFlow default.
|
|
kOHWI, // Our standard for conv weights
|
|
kHWIO, // TensorFlow conv weights
|
|
k1HWO, // Our standard for DepthwiseConv weights
|
|
kHWIM, // TensorFlow DepthwiseConv weights
|
|
kNHWC, // TensorFlow activations
|
|
kHWOI, // TensorFlow back-prop conv weights
|
|
};
|
|
|
|
// The type of the scalars in an array.
|
|
// Note that the type does not by itself tell whether the values in the array
|
|
// are non-quantized (can be accessed directly) or quantized (must be
|
|
// interpreted in conjunction with QuantizationParams).
|
|
//
|
|
// In practice though:
|
|
// float values are never quantized
|
|
// uint8 values are always quantized
|
|
// int32 values are sometimes quantized (depending on whether
|
|
// QuantizationParams are present).
|
|
// complex values are never quantized
|
|
// other types are never quantized at the moment.
|
|
//
|
|
// kNone means that we don't know the data type yet, or that we don't care
|
|
// because we'll be dropping the array anyway (e.g. some exotic array types
|
|
// may be involved only in debug-only subgraphs that we may not be interested
|
|
// in actually supporting).
|
|
enum class ArrayDataType : uint8 {
|
|
kNone, // 0
|
|
kBool,
|
|
kFloat,
|
|
kInt8,
|
|
kUint8,
|
|
kInt16, // 5
|
|
kUint16,
|
|
kInt32,
|
|
kUint32,
|
|
kInt64,
|
|
kUint64, // 10
|
|
kString,
|
|
kComplex64,
|
|
kFloat16,
|
|
kFloat64,
|
|
kComplex128,
|
|
};
|
|
|
|
// Compile-time logic to map ArrayDataType to the corresponding C++ scalar type
|
|
template <ArrayDataType A>
|
|
struct DataTypeImpl {};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kNone> {
|
|
typedef int Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kBool> {
|
|
typedef bool Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kFloat> {
|
|
typedef float Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kInt8> {
|
|
typedef int8 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kUint8> {
|
|
typedef uint8 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kInt16> {
|
|
typedef int16 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kUint16> {
|
|
typedef uint16 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kInt32> {
|
|
typedef int32 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kUint32> {
|
|
typedef uint32 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kInt64> {
|
|
typedef int64 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kUint64> {
|
|
typedef uint64 Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kString> {
|
|
typedef std::string Type;
|
|
};
|
|
template <>
|
|
struct DataTypeImpl<ArrayDataType::kComplex64> {
|
|
typedef std::complex<float> Type;
|
|
};
|
|
|
|
template <ArrayDataType A>
|
|
using DataType = typename DataTypeImpl<A>::Type;
|
|
|
|
// Base class for type-specific buffer types.
|
|
struct GenericBuffer {
|
|
// Non-default-constructible: only ArrayDataType-specific subclass
|
|
// objects may be constructed.
|
|
GenericBuffer() = delete;
|
|
// Non-copyable-or-movable: we should only store pointers-to-Buffer
|
|
// in containers, not Operators themselves, so there should be no
|
|
// copy or move.
|
|
GenericBuffer(const GenericBuffer&) = delete;
|
|
GenericBuffer(const GenericBuffer&&) = delete;
|
|
|
|
// We need a virtual destructor so we can store pointers-to-Buffer
|
|
// in containers and have the containers call the right subclass destructor.
|
|
virtual ~GenericBuffer() {}
|
|
|
|
virtual int Length() const = 0;
|
|
|
|
const ArrayDataType type;
|
|
|
|
protected:
|
|
// Constructor used by subclasses for specific ArrayDataType's.
|
|
explicit GenericBuffer(ArrayDataType t) : type(t) {}
|
|
};
|
|
|
|
// Type-specific buffer, containing type-specific storage.
|
|
template <ArrayDataType A>
|
|
struct Buffer : GenericBuffer {
|
|
Buffer() : GenericBuffer(A) {}
|
|
|
|
int Length() const override { return data.size(); }
|
|
|
|
std::vector<DataType<A>> data;
|
|
};
|
|
|
|
class Shape {
|
|
public:
|
|
// For Shape, we stick to half-way encapsulation for now:
|
|
// we hide the raw dims_ member, but expose it raw by accessors
|
|
// because from some brainstorming, it's not at all easy to
|
|
// anticipate which flavor of more hermetic encapsulation would
|
|
// actually buy us future-proof-ness without being needlessly
|
|
// cumbersome.
|
|
Shape() {}
|
|
Shape(std::initializer_list<int> dim_list) : dims_(dim_list) {}
|
|
|
|
void ReplaceDims(std::initializer_list<int> dim_list) {
|
|
dims_ = std::vector<int>(dim_list);
|
|
}
|
|
|
|
const std::vector<int>& dims() const { return dims_; }
|
|
std::vector<int>* mutable_dims() { return &dims_; }
|
|
const int dimensions_count() const { return dims_.size(); }
|
|
|
|
// We still have that one convenience accessor to avoid
|
|
// the awkward double bracket issue: shape.dims()[i].
|
|
int dims(int i) const {
|
|
// Always check for out-of-bounds accesses, even in optimized builds where
|
|
// standard assertions are disabled. Out-of-bounds access here is a common
|
|
// occurrence.
|
|
CHECK_GE(i, 0);
|
|
CHECK_GT(dims_.size(), i);
|
|
return dims_[i];
|
|
}
|
|
|
|
bool operator==(const Shape& comp) const {
|
|
return (this->dims_ == comp.dims());
|
|
}
|
|
|
|
bool operator!=(const Shape& comp) const { return !((*this) == comp); }
|
|
|
|
private:
|
|
std::vector<int> dims_;
|
|
};
|
|
|
|
// Base class for all operator classes.
|
|
struct Operator {
|
|
// Non-default-constructible: only OperatorType-specific subclass
|
|
// objects may be constructed.
|
|
Operator() = delete;
|
|
// Non-copyable-or-movable: we should only store pointers-to-Operator
|
|
// in containers, not Operators themselves, so there should be no
|
|
// copy or move.
|
|
Operator(const Operator&) = delete;
|
|
Operator(const Operator&&) = delete;
|
|
|
|
// We need a virtual destructor so we can store pointers-to-Operator
|
|
// in containers and have the containers call the right subclass destructor.
|
|
virtual ~Operator() {}
|
|
|
|
// The specific type of operator. Corresponds 1:1 to subclasses.
|
|
const OperatorType type;
|
|
|
|
// The activation function that may be fused into this operator,
|
|
// or None if no activation function is fused.
|
|
FusedActivationFunctionType fused_activation_function;
|
|
|
|
// Input arrays: either activation arrays or constant array parameters.
|
|
// We refer to them by their name, not by their address; the mapping of
|
|
// names to addresses is given by the Model, which owns both Operator's and
|
|
// Array's. Thus, an Operator on its own doesn't contain much information,
|
|
// it is meant to be used in conjunction with the Model that owns it.
|
|
std::vector<std::string> inputs;
|
|
|
|
// Output activation arrays. Same comments as for inputs apply here too.
|
|
std::vector<std::string> outputs;
|
|
|
|
// If true, the operator has more outputs than are listed in the 'outputs'
|
|
// member. These need to be resolved by some graph transformation.
|
|
// This flag is only here to indicate that an operator should not be
|
|
// discarded as unused, even if from its 'outputs' member alone it
|
|
// looks unused.
|
|
bool unresolved_outputs = false;
|
|
|
|
// A serialized tensorflow::NodeDef string.
|
|
// The field is filled only when importing from TensorFlow.
|
|
// It's guaranteed to be filled for `TensorFlowUnsupportedOperator`.
|
|
// It's not guaranteed to be filled for other ops. Ops created by graph
|
|
// transformations won't have TensorFlow NodeDef.
|
|
std::string tensorflow_node_def;
|
|
|
|
protected:
|
|
// Constructor used by subclasses for specific OperatorType's.
|
|
explicit Operator(OperatorType t)
|
|
: type(t),
|
|
fused_activation_function(FusedActivationFunctionType::kNone) {}
|
|
};
|
|
|
|
// Padding types for Conv-like operators. This is how padding is typically
|
|
// specified in model files. But for inference, we will need to resolve this
|
|
// to a FixedPadding, see below.
|
|
enum class PaddingType { kNone, kSame, kValid };
|
|
|
|
// Padding as resolved for a specific layer shape, as needed for inference.
|
|
// For a given layer shape, a given padding type will resolve to a choice of
|
|
// a number of padding rows and columns, which we call the padding height and
|
|
// width respectively.
|
|
struct FixedPadding {
|
|
int width = 0;
|
|
int height = 0;
|
|
};
|
|
|
|
// "Universal" padding struct containing both a generic PaddingType (as
|
|
// represented in a model file), and a FixedPadding (as needed for inference).
|
|
// The latter is resolved during the PropagateFixedSizes pass.
|
|
struct Padding {
|
|
FixedPadding& GetOrCreateFixedPadding() {
|
|
if (!fixed) {
|
|
FixedPadding* ptr = new FixedPadding;
|
|
fixed = std::unique_ptr<FixedPadding>(ptr);
|
|
}
|
|
return *fixed;
|
|
}
|
|
|
|
Padding() : type(PaddingType::kNone) {}
|
|
PaddingType type;
|
|
std::unique_ptr<FixedPadding> fixed;
|
|
};
|
|
|
|
// "Convolutional" layer, as represented in model files.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
// inputs[1]: required: the Conv weights
|
|
// inputs[2]: optional: the bias vector, specifying the biases for each output
|
|
// channel.
|
|
//
|
|
// Outputs:
|
|
// outputs[0]: required: the output activations array
|
|
// outputs[1]: optional: the intermediate array of im2col-replicated input
|
|
// activations. Present when targeting implementations
|
|
// of Conv layers as Im2col+GEMM.
|
|
//
|
|
// TensorFlow equivalent: Conv2D
|
|
struct ConvOperator : Operator {
|
|
ConvOperator() : Operator(OperatorType::kConv) {}
|
|
Padding padding;
|
|
int stride_width = 0;
|
|
int stride_height = 0;
|
|
// A dilation_rate of 0 is invalid and this field is an optional attribute.
|
|
// Thus initializing it to 1 to allow default conv behavior when the
|
|
// attribute is not present.
|
|
int dilation_width_factor = 1;
|
|
int dilation_height_factor = 1;
|
|
};
|
|
|
|
// CTCBeamSearchDecoder operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the logits.
|
|
// inputs[1]: required: sequence length.
|
|
// inputs[2]: optional: beam width.
|
|
// inputs[3]: optional: top paths.
|
|
// inputs[4]: optional: merge repeated.
|
|
//
|
|
// Outputs:
|
|
// outputs[0]: decoded.
|
|
// outputs[1]: log probability.
|
|
//
|
|
// TensorFlow equivalent: CTCBeamSearchDecoder
|
|
struct CTCBeamSearchDecoderOperator : Operator {
|
|
CTCBeamSearchDecoderOperator()
|
|
: Operator(OperatorType::kCTCBeamSearchDecoder) {}
|
|
int beam_width;
|
|
int top_paths;
|
|
bool merge_repeated = true;
|
|
};
|
|
|
|
// Depthwise-separable convolution operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
// inputs[1]: required: the DepthwiseConv weights
|
|
// inputs[2]: optional: the bias vector, specifying the biases for each output
|
|
// channel.
|
|
//
|
|
// TensorFlow equivalent: DepthwiseConv2dNative
|
|
struct DepthwiseConvOperator : Operator {
|
|
DepthwiseConvOperator() : Operator(OperatorType::kDepthwiseConv) {}
|
|
Padding padding;
|
|
int stride_height = 0;
|
|
int stride_width = 0;
|
|
int depth_multiplier = 0;
|
|
// A dilation_rate of 0 is invalid and this field is an optional attribute.
|
|
// Thus initializing it to 1 to allow default conv behavior when the
|
|
// attribute is not present.
|
|
int dilation_width_factor = 1;
|
|
int dilation_height_factor = 1;
|
|
};
|
|
|
|
// Depth-to-space transform operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
//
|
|
// TensorFlow equivalent: DepthToSpace
|
|
struct DepthToSpaceOperator : Operator {
|
|
DepthToSpaceOperator() : Operator(OperatorType::kDepthToSpace) {}
|
|
int block_size = 0;
|
|
};
|
|
|
|
// Space-to-depth transform operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
//
|
|
// TensorFlow equivalent: SpaceToDepth
|
|
struct SpaceToDepthOperator : Operator {
|
|
SpaceToDepthOperator() : Operator(OperatorType::kSpaceToDepth) {}
|
|
int block_size = 0;
|
|
};
|
|
|
|
// Fully-connected operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
// inputs[1]: required: the FullyConnected weights
|
|
// inputs[2]: optional: the bias vector, specifying the biases for each output
|
|
// channel.
|
|
//
|
|
// TensorFlow equivalent: a pair consisting of a Reshape node reshaping the
|
|
// input activations as a matrix, followed by a MatMul node.
|
|
struct FullyConnectedOperator : Operator {
|
|
FullyConnectedOperator() : Operator(OperatorType::kFullyConnected) {}
|
|
FullyConnectedWeightsFormat weights_format =
|
|
FullyConnectedWeightsFormat::kDefault;
|
|
|
|
// `keep_num_dims` is supported in the FullyConnected kernel version 5, but
|
|
// it's never supported by Toco.
|
|
bool keep_num_dims = false;
|
|
};
|
|
|
|
// Dequantization operator, converting a quantized array of integers with
|
|
// quantization parameters specifying how these integers correspond to real
|
|
// numbers
|
|
// (see QuantizationParams) to an output activations array of floating-point
|
|
// values.
|
|
//
|
|
// In floating-point image models, there is typically a Dequantization operator
|
|
// at the very beginning, converting the input image RGB data, consisting of
|
|
// uint8 integer values, to floating-point input activations. That is where
|
|
// image model parameters such as "mean_value" and "std_value" are typically
|
|
// handled.
|
|
//
|
|
// This is the only operator type that converts from quantized to
|
|
// floating-point,
|
|
// and there is at the moment no operator type at all to convert from
|
|
// floating-point
|
|
// to quantized. Every other operator does either float->float or
|
|
// quantized->quantized.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input quantized activations array
|
|
//
|
|
// TensorFlow equivalent: Dequantize
|
|
struct DequantizeOperator : Operator {
|
|
DequantizeOperator() : Operator(OperatorType::kDequantize) {}
|
|
};
|
|
|
|
// Numeric verification operator, converting a quantized array of integers with
|
|
// quantization parameters specifying how these integers correspond to real
|
|
// numbers
|
|
// (see QuantizationParams) and verify them with an array of floating-point
|
|
// values.
|
|
|
|
// Inputs:
|
|
// inputs[0]: required: the input quantized activations array
|
|
// inputs[1]: required: the input reference activations array
|
|
//
|
|
// TensorFlow equivalent: Dequantize
|
|
struct NumericVerifyOperator : Operator {
|
|
NumericVerifyOperator() : Operator(OperatorType::kNumericVerify) {}
|
|
};
|
|
|
|
// Batch-normalization operator.
|
|
//
|
|
// We only support batch-normalization using pre-learned moments, so this is
|
|
// just
|
|
// computing (input - mean) * multiplier + offset. As such, this can be
|
|
// expressed as a combination of Add and Mul nodes, and indeed this is how
|
|
// we break it down during tooling for the purpose of fusing it into
|
|
// other operators.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
// inputs[1]: required: the learned mean array
|
|
// inputs[2]: required: the learned multiplier array
|
|
// inputs[3]: required: the learned offset array
|
|
//
|
|
// TensorFlow equivalent: a combination of Add and Mul nodes
|
|
struct BatchNormalizationOperator : Operator {
|
|
BatchNormalizationOperator()
|
|
: Operator(OperatorType::kBatchNormalization),
|
|
global_normalization(false) {}
|
|
bool global_normalization;
|
|
};
|
|
|
|
// L2-normalization operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input activations array
|
|
//
|
|
// TensorFlow equivalent: none. In TensorFlow, L2 normalization is implemented
|
|
// by a sub-graph of operators implementing L2-normalization
|
|
// from lower-level arithmetic nodes; during tooling, we identify such
|
|
// sub-graphs
|
|
// and replace them by L2NormalizationOperator's. See IdentifyL2Normalization.
|
|
struct L2NormalizationOperator : Operator {
|
|
L2NormalizationOperator() : Operator(OperatorType::kL2Normalization) {}
|
|
};
|
|
|
|
// LSTM Cell operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input data array
|
|
// inputs[1]: required: the previous output activations array
|
|
// inputs[2]: required: the learned weights array
|
|
// inputs[3]: required: the learned biases array
|
|
// inputs[4]: required: the previous output state
|
|
// outputs[0]: required: the output activations array
|
|
// outputs[1]: required: the new state array
|
|
//
|
|
// TensorFlow equivalent: none. In TensorFlow, an LSTM is implemented
|
|
// with a sub-graph of lower-level arithmetic nodes; during tooling, we identify
|
|
// such sub-graphs and replace them with LstmCells. See IdentifyLstmCell().
|
|
struct LstmCellOperator : Operator {
|
|
enum Inputs {
|
|
DATA_INPUT = 0,
|
|
PREV_ACTIV_INPUT = 1,
|
|
WEIGHTS_INPUT = 2,
|
|
BIASES_INPUT = 3,
|
|
PREV_STATE_INPUT = 4,
|
|
NUM_INPUTS = 5
|
|
};
|
|
enum Outputs {
|
|
ACTIV_OUTPUT = 0,
|
|
STATE_OUTPUT = 1,
|
|
CONCAT_TEMP = 2,
|
|
ACTIV_TEMP = 3,
|
|
NUM_OUTPUTS = 4
|
|
};
|
|
enum KernelType {
|
|
KERNEL_BASIC = 0,
|
|
KERNEL_FULL = 1,
|
|
};
|
|
|
|
LstmCellOperator()
|
|
: Operator(OperatorType::kLstmCell), kernel_type(KERNEL_BASIC) {}
|
|
|
|
KernelType kernel_type;
|
|
};
|
|
|
|
struct UnidirectionalSequenceLstmOperator : Operator {
|
|
UnidirectionalSequenceLstmOperator()
|
|
: Operator(OperatorType::kUnidirectionalSequenceLstm) {}
|
|
};
|
|
|
|
struct BidirectionalSequenceLstmOperator : Operator {
|
|
BidirectionalSequenceLstmOperator()
|
|
: Operator(OperatorType::kBidirectionalSequenceLstm) {}
|
|
bool merge_outputs;
|
|
};
|
|
|
|
struct BidirectionalSequenceRnnOperator : Operator {
|
|
BidirectionalSequenceRnnOperator()
|
|
: Operator(OperatorType::kBidirectionalSequenceRnn) {}
|
|
bool merge_outputs;
|
|
};
|
|
|
|
// Element-wise multiplication operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Mul
|
|
struct MulOperator : Operator {
|
|
MulOperator() : Operator(OperatorType::kMul) {}
|
|
};
|
|
|
|
// Element-wise Abs operator:
|
|
// x -> abs(x)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: abs
|
|
struct AbsOperator : Operator {
|
|
AbsOperator() : Operator(OperatorType::kAbs) {}
|
|
};
|
|
|
|
// Element-wise HardSwish operator:
|
|
// x -> x * relu6(x+3)/6
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: hard_swish
|
|
struct HardSwishOperator : Operator {
|
|
HardSwishOperator() : Operator(OperatorType::kHardSwish) {}
|
|
};
|
|
|
|
// Elu
|
|
// f(x) -> exp(x) - 1 for x < 0, x for x >= 0.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Elu
|
|
struct EluOperator : Operator {
|
|
EluOperator() : Operator(OperatorType::kElu) {}
|
|
};
|
|
|
|
// Element-wise Relu operator:
|
|
// x -> max(0, x)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Relu
|
|
struct ReluOperator : Operator {
|
|
ReluOperator() : Operator(OperatorType::kRelu) {}
|
|
};
|
|
|
|
// Element-wise Relu1 operator:
|
|
// x -> min(max(x, -1), 1)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: none. We can construct the operator with Minimum
|
|
// and Maximum operations
|
|
struct Relu1Operator : Operator {
|
|
Relu1Operator() : Operator(OperatorType::kRelu1) {}
|
|
};
|
|
|
|
// Element-wise Relu6 operator:
|
|
// x -> max(0, min(6, x))
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Relu6
|
|
struct Relu6Operator : Operator {
|
|
Relu6Operator() : Operator(OperatorType::kRelu6) {}
|
|
};
|
|
|
|
// PRelu
|
|
// f(x) = alpha * x for x < 0, f(x) = x for x >= 0.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the alpha array
|
|
//
|
|
// Equivalent to keras.layers.PReLU.
|
|
struct PReluOperator : Operator {
|
|
PReluOperator() : Operator(OperatorType::kPRelu) {}
|
|
};
|
|
|
|
// LeakyRelu
|
|
// x -> max(x, alpha * x)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: LeakyRelu
|
|
struct LeakyReluOperator : Operator {
|
|
LeakyReluOperator() : Operator(OperatorType::kLeakyRelu) {}
|
|
|
|
float alpha = 0.2f; // 0.2 matches the default value for the TF op attribute.
|
|
};
|
|
|
|
// Element-wise Logistic operator:
|
|
// x -> Logistic(x) = 1 / (1 + exp(-x))
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Sigmoid
|
|
struct LogisticOperator : Operator {
|
|
LogisticOperator() : Operator(OperatorType::kLogistic) {}
|
|
};
|
|
|
|
// Element-wise natural log operator:
|
|
// x -> ln(x)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Log
|
|
struct LogOperator : Operator {
|
|
LogOperator() : Operator(OperatorType::kLog) {}
|
|
};
|
|
|
|
// Element-wise Tanh operator:
|
|
// x -> Tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Tanh
|
|
struct TanhOperator : Operator {
|
|
TanhOperator() : Operator(OperatorType::kTanh) {}
|
|
};
|
|
|
|
// Element-wise Sin operator:
|
|
// x -> Sin(x) = sin(x)
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Sin
|
|
struct SinOperator : Operator {
|
|
SinOperator() : Operator(OperatorType::kSin) {}
|
|
};
|
|
|
|
// Element-wise addition operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Add
|
|
struct AddOperator : Operator {
|
|
AddOperator() : Operator(OperatorType::kAdd) {}
|
|
};
|
|
|
|
// Element-wise addition operator for N inputs.
|
|
//
|
|
// Inputs:
|
|
// inputs[i]: The i-th array to add together to form the output.
|
|
//
|
|
// TensorFlow equivalent: AddN
|
|
struct AddNOperator : Operator {
|
|
AddNOperator() : Operator(OperatorType::kAddN) {}
|
|
};
|
|
|
|
// Concatenation operator: concatenates its inputs
|
|
// along the axis.
|
|
//
|
|
// Inputs: this operator accepts any number >= 1 of inputs.
|
|
// inputs[i]: the i-th array to concatenate.
|
|
//
|
|
// TensorFlow equivalent: Concat.
|
|
struct ConcatenationOperator : Operator {
|
|
ConcatenationOperator() : Operator(OperatorType::kConcatenation) {}
|
|
int axis = 0;
|
|
};
|
|
|
|
// Reordering dimensions. Used only during tooling to transform graphs from
|
|
// the TensorFlow format.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: none. This is only useful to convert between formats.
|
|
struct ReorderAxesOperator : Operator {
|
|
ReorderAxesOperator() : Operator(OperatorType::kReorderAxes) {}
|
|
AxesOrder input_axes_order;
|
|
AxesOrder output_axes_order;
|
|
};
|
|
|
|
// Average-pooling operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: AveragePool
|
|
struct AveragePoolOperator : Operator {
|
|
AveragePoolOperator() : Operator(OperatorType::kAveragePool) {}
|
|
Padding padding;
|
|
int stride_height = 0;
|
|
int stride_width = 0;
|
|
int kheight = 0;
|
|
int kwidth = 0;
|
|
};
|
|
|
|
// Local response normalization operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: LRN
|
|
struct LocalResponseNormalizationOperator : Operator {
|
|
LocalResponseNormalizationOperator()
|
|
: Operator(OperatorType::kLocalResponseNormalization) {}
|
|
|
|
int range = 0;
|
|
float bias = 0.f;
|
|
float alpha = 0.f;
|
|
float beta = 0.f;
|
|
};
|
|
|
|
// Max-pooling operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: MaxPool
|
|
struct MaxPoolOperator : Operator {
|
|
MaxPoolOperator() : Operator(OperatorType::kMaxPool) {}
|
|
Padding padding;
|
|
int stride_height = 0;
|
|
int stride_width = 0;
|
|
int kheight = 0;
|
|
int kwidth = 0;
|
|
};
|
|
|
|
// L2-pooling operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: none. Can be shimmed by squaring+avgpool+sqrt.
|
|
struct L2PoolOperator : Operator {
|
|
L2PoolOperator() : Operator(OperatorType::kL2Pool) {}
|
|
Padding padding;
|
|
int stride_height = 0;
|
|
int stride_width = 0;
|
|
int kheight = 0;
|
|
int kwidth = 0;
|
|
};
|
|
|
|
// The expected [min, max] range of values in a given array.
|
|
// Used for quantization only.
|
|
// This information typically comes from special nodes found in quantized
|
|
// models, see FakeQuantOperator, and is used during quantization to resolve
|
|
// actual quantization parameters (see QuantizationParams).
|
|
struct MinMax {
|
|
double min = 0.;
|
|
double max = 0.;
|
|
};
|
|
|
|
inline bool operator==(const MinMax& m1, const MinMax& m2) {
|
|
return m1.min == m2.min && m1.max == m2.max;
|
|
}
|
|
|
|
inline bool operator!=(const MinMax& m1, const MinMax& m2) {
|
|
return m1.min != m2.min || m1.max != m2.max;
|
|
}
|
|
|
|
// Fake-quantization operator. This does two things:
|
|
// - Annotate its input and output arrays with MinMax information,
|
|
// - Arithmetic-wise, this operator rounds incoming activation values
|
|
// to the nearest representable value on the scale of 256
|
|
// values from the min to the max value dictated by its MinMax info.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: optional: the 'min' value, if it has not yet been resolved
|
|
// to a constant.
|
|
// inputs[2]: optional: the 'max' value, if it has not yet been resolved
|
|
// to a constant.
|
|
//
|
|
// TensorFlow equivalent: FakeQuantWithMinMaxVars, FakeQuantWithMinMaxArgs.
|
|
struct FakeQuantOperator : Operator {
|
|
FakeQuantOperator() : Operator(OperatorType::kFakeQuant) {}
|
|
std::unique_ptr<MinMax> minmax;
|
|
int num_bits = 8;
|
|
bool narrow_range = false;
|
|
};
|
|
|
|
// Element-wise division operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Div
|
|
struct DivOperator : Operator {
|
|
DivOperator() : Operator(OperatorType::kDiv) {}
|
|
};
|
|
|
|
// Element-wise identity (x->x) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Identity
|
|
struct TensorFlowIdentityOperator : Operator {
|
|
TensorFlowIdentityOperator() : Operator(OperatorType::kIdentity) {}
|
|
};
|
|
|
|
// Batch matrix multiplication operator. This comes from a tf.matmul where one
|
|
// of the operands has rank 3 or more.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side matrix
|
|
// inputs[1]: required: the right-hand side matrix
|
|
//
|
|
// TensorFlow equivalent: MatMul
|
|
struct BatchMatMulOperator : Operator {
|
|
BatchMatMulOperator() : Operator(OperatorType::kBatchMatMul) {}
|
|
bool adj_x = false;
|
|
bool adj_y = false;
|
|
};
|
|
|
|
// General matrix multiplication operator. We don't want to support general
|
|
// matrix multiplication at inference time, so we resolve it during tooling
|
|
// to more specific operator types, namely, FullyConnected.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side matrix
|
|
// inputs[1]: required: the right-hand side matrix
|
|
//
|
|
// TensorFlow equivalent: MatMul
|
|
struct TensorFlowMatMulOperator : Operator {
|
|
TensorFlowMatMulOperator() : Operator(OperatorType::kMatMul) {}
|
|
bool transpose_a = false;
|
|
bool transpose_b = false;
|
|
};
|
|
|
|
// Padding operator. Pads a tensor with zeros.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the padding array
|
|
//
|
|
// This operation pads a `input` with zeros according to the `paddings` you
|
|
// specify. `paddings` is an integer tensor with shape `[Dn, 2]`, where n is the
|
|
// rank of `input`. For each dimension D of `input`, `paddings[D, 0]` indicates
|
|
// how many zeros to add before the contents of `input` in that dimension, and
|
|
// `paddings[D, 1]` indicates how many zeros to add after the contents of
|
|
// `input` in that dimension.
|
|
//
|
|
// TensorFlow equivalent: Pad
|
|
struct PadOperator : Operator {
|
|
PadOperator() : Operator(OperatorType::kPad) {}
|
|
|
|
std::vector<int> left_padding;
|
|
std::vector<int> right_padding;
|
|
};
|
|
|
|
// PaddingV2 operator. Pads a tensor with the given constant value.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the padding array
|
|
// inputs[2]: required: the scalar constant_values
|
|
//
|
|
// This operation pads input according to the paddings and constant_values you
|
|
// specify. paddings is an integer tensor with shape [Dn, 2], where n is the
|
|
// rank of input. For each dimension D of input, paddings[D, 0] indicates how
|
|
// many padding values to add before the contents of input in that dimension,
|
|
// and paddings[D, 1] indicates how many padding values to add after the
|
|
// contents of input in that dimension. constant_values is a scalar tensor of
|
|
// the same type as input that indicates the value to use for padding input.
|
|
//
|
|
// TensorFlow equivalent: PadV2
|
|
struct PadV2Operator : Operator {
|
|
PadV2Operator() : Operator(OperatorType::kPadV2) {}
|
|
|
|
std::vector<int> left_padding;
|
|
std::vector<int> right_padding;
|
|
};
|
|
|
|
// Strided slice operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the begin array
|
|
// inputs[2]: required: the end array
|
|
// inputs[3]: optional: the strides array
|
|
//
|
|
// TensorFlow equivalent: StridedSlice
|
|
struct StridedSliceOperator : Operator {
|
|
StridedSliceOperator() : Operator(OperatorType::kStridedSlice) {}
|
|
|
|
std::vector<int> start_indices;
|
|
std::vector<int> stop_indices;
|
|
std::vector<int> strides;
|
|
|
|
int begin_mask;
|
|
int ellipsis_mask;
|
|
int end_mask;
|
|
int new_axis_mask;
|
|
int shrink_axis_mask;
|
|
|
|
StridedSliceOperator(const StridedSliceOperator& other)
|
|
: Operator(OperatorType::kStridedSlice) {
|
|
inputs = other.inputs;
|
|
outputs = other.outputs;
|
|
|
|
start_indices = other.start_indices;
|
|
stop_indices = other.stop_indices;
|
|
strides = other.strides;
|
|
|
|
begin_mask = other.begin_mask;
|
|
ellipsis_mask = other.ellipsis_mask;
|
|
end_mask = other.end_mask;
|
|
new_axis_mask = other.new_axis_mask;
|
|
shrink_axis_mask = other.shrink_axis_mask;
|
|
}
|
|
|
|
void PadIndices(int dim_count) {
|
|
// Add indices and mask bits to fully include extra dimensions
|
|
CHECK_GE(dim_count, start_indices.size());
|
|
CHECK_EQ(start_indices.size(), stop_indices.size());
|
|
CHECK_EQ(stop_indices.size(), strides.size());
|
|
|
|
for (int i = start_indices.size(); i < dim_count; i++) {
|
|
start_indices.push_back(0);
|
|
stop_indices.push_back(0);
|
|
strides.push_back(1);
|
|
begin_mask |= 1 << i;
|
|
end_mask |= 1 << i;
|
|
}
|
|
}
|
|
|
|
void ReverseIndices() {
|
|
CHECK_EQ(start_indices.size(), stop_indices.size());
|
|
CHECK_EQ(stop_indices.size(), strides.size());
|
|
|
|
std::reverse(start_indices.begin(), start_indices.end());
|
|
std::reverse(stop_indices.begin(), stop_indices.end());
|
|
std::reverse(strides.begin(), strides.end());
|
|
|
|
begin_mask = toco::port::ReverseBits32(static_cast<uint32>(begin_mask)) >>
|
|
(32 - start_indices.size());
|
|
ellipsis_mask =
|
|
toco::port::ReverseBits32(static_cast<uint32>(ellipsis_mask)) >>
|
|
(32 - start_indices.size());
|
|
end_mask = toco::port::ReverseBits32(static_cast<uint32>(end_mask)) >>
|
|
(32 - start_indices.size());
|
|
new_axis_mask =
|
|
toco::port::ReverseBits32(static_cast<uint32>(new_axis_mask)) >>
|
|
(32 - start_indices.size());
|
|
shrink_axis_mask =
|
|
toco::port::ReverseBits32(static_cast<uint32>(shrink_axis_mask)) >>
|
|
(32 - start_indices.size());
|
|
}
|
|
};
|
|
|
|
// Reshaping operator, reshaping its input array to a two-dimensional shape
|
|
// (a "matrix"). This is used in the TensorFlow format, in conjunction with
|
|
// MatMul nodes, to implement fully-connected layers.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: optional: the output tensor shape
|
|
//
|
|
// TensorFlow equivalent: Reshape --- except that we only support a special case
|
|
// here, where the output shape is a matrix (2D) shape.
|
|
struct TensorFlowReshapeOperator : Operator {
|
|
TensorFlowReshapeOperator() : Operator(OperatorType::kReshape) {}
|
|
std::vector<int> shape;
|
|
};
|
|
|
|
// Removes dimensions of size 1 from the shape of a tensor.
|
|
// https://www.tensorflow.org/api_docs/python/tf/squeeze
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Squeeze
|
|
struct SqueezeOperator : Operator {
|
|
SqueezeOperator() : Operator(OperatorType::kSqueeze) {}
|
|
|
|
std::vector<int> squeeze_dims;
|
|
};
|
|
|
|
// Inputs:
|
|
// inputs[0]: required: the output shape
|
|
// inputs[1]: required: the weights
|
|
// inputs[2]: required: the input activations array
|
|
// inputs[3]: optional: the bias vector, specifying the biases for each output
|
|
// channel.
|
|
// NOTE: The input activations is NOT the first input.
|
|
//
|
|
//
|
|
// Outputs:
|
|
// outputs[0]: required: the output activations array
|
|
//
|
|
// TensorFlow equivalent: Conv2DBackpropInput
|
|
struct TransposeConvOperator : Operator {
|
|
enum Inputs {
|
|
OUTPUT_SHAPE = 0,
|
|
WEIGHTS = 1,
|
|
DATA_INPUT = 2,
|
|
BIAS = 3,
|
|
};
|
|
|
|
TransposeConvOperator() : Operator(OperatorType::kTransposeConv) {}
|
|
Padding padding;
|
|
int stride_width = 0;
|
|
int stride_height = 0;
|
|
// Dilation is possible with transpose convolution, but Tensorflow does not
|
|
// currently support it, so we omit it.
|
|
};
|
|
|
|
// Given a tensor input, this operation calculates element-wise exponential
|
|
// (y = e^x).
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: input tensor
|
|
//
|
|
// TensorFlow equivalent: Exp
|
|
struct ExpOperator : Operator {
|
|
ExpOperator() : Operator(OperatorType::kExp) {}
|
|
};
|
|
|
|
// Given a tensor input, this operation calculates element-wise exponential
|
|
// (y = cos(x)).
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: input tensor
|
|
//
|
|
// TensorFlow equivalent: Cos
|
|
struct CosOperator : Operator {
|
|
CosOperator() : Operator(OperatorType::kCos) {}
|
|
};
|
|
|
|
// Given a tensor input, this operation inserts a dimension of 1 at the
|
|
// dimension index axis of input's shape. The dimension index axis starts at
|
|
// zero; if you specify a negative number for axis it is counted backward from
|
|
// the end.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: input tensor
|
|
// inputs[1]: required: 0-D (scalar). Specifies the dimension index at which
|
|
// to expand the shape of input
|
|
//
|
|
// TensorFlow equivalent: ExpandDims
|
|
struct ExpandDimsOperator : Operator {
|
|
ExpandDimsOperator() : Operator(OperatorType::kExpandDims) {}
|
|
};
|
|
|
|
// Creates a tensor of shape dims and fills it with the given scalar value.
|
|
// Output type will be the same as the given scalar value.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: 1-D (int32) - the shape of the output tensor
|
|
// inputs[1]: required: 0-D (scalar) - value to fill the tensor with
|
|
//
|
|
// TensorFlow equivalent: Fill
|
|
struct FillOperator : Operator {
|
|
FillOperator() : Operator(OperatorType::kFill) {}
|
|
};
|
|
|
|
// Element-wise floor division operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: FloorDiv
|
|
struct FloorDivOperator : Operator {
|
|
FloorDivOperator() : Operator(OperatorType::kFloorDiv) {}
|
|
};
|
|
|
|
// Element-wise floor mod operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: FloorMod
|
|
struct FloorModOperator : Operator {
|
|
FloorModOperator() : Operator(OperatorType::kFloorMod) {}
|
|
};
|
|
|
|
struct RandomUniformOperator : Operator {
|
|
RandomUniformOperator() : Operator(OperatorType::kRandomUniform) {}
|
|
ArrayDataType dtype = ArrayDataType::kNone;
|
|
int64 seed;
|
|
int64 seed2;
|
|
};
|
|
|
|
// Creates a sequence of numbers that begins at start and extends by increments
|
|
// of delta up to but not including limit.
|
|
//
|
|
// The dtype of the resulting tensor is inferred from the inputs unless it is
|
|
// provided explicitly.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the start
|
|
// inputs[1]: required: the limit
|
|
// inputs[2]: required: the delta
|
|
//
|
|
// TensorFlow equivalent: Range
|
|
struct RangeOperator : Operator {
|
|
RangeOperator() : Operator(OperatorType::kRange) {}
|
|
ArrayDataType dtype = ArrayDataType::kNone;
|
|
};
|
|
|
|
// Rank operator. Extracts the rank of the tensor.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// This operation outputs a 0-D int32 Tensor representing the rank of input.
|
|
//
|
|
// TensorFlow equivalent: Rank.
|
|
struct TensorFlowRankOperator : Operator {
|
|
TensorFlowRankOperator() : Operator(OperatorType::kRank) {}
|
|
ArrayDataType output_data_type = ArrayDataType::kInt32;
|
|
};
|
|
|
|
// Element-wise negation (-x) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Neg
|
|
struct NegOperator : Operator {
|
|
NegOperator() : Operator(OperatorType::kNeg) {}
|
|
};
|
|
|
|
// Element-wise select operator choosing elements from inputs[1] or input[2]
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: boolean mask per index
|
|
// inputs[1]: required: tensor of values if true
|
|
// inputs[2]: required: tensor of values if false
|
|
//
|
|
// TensorFlow equivalent: Select
|
|
struct SelectOperator : Operator {
|
|
SelectOperator() : Operator(OperatorType::kSelect) {}
|
|
};
|
|
|
|
// Element-wise reciprocal-square-root (x^-0.5) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Rsqrt
|
|
struct TensorFlowRsqrtOperator : Operator {
|
|
TensorFlowRsqrtOperator() : Operator(OperatorType::kRsqrt) {}
|
|
};
|
|
|
|
// Stacks a list of rank-R tensors into one rank-(R+1) tensor.
|
|
//
|
|
// Packs the list of tensors in values into a tensor with rank one higher than
|
|
// each tensor in values, by packing them along the axis dimension. Given a list
|
|
// of length N of tensors of shape (A, B, C);.
|
|
//
|
|
// Inputs: this operator accepts any number >= 1 of inputs.
|
|
// inputs[i]: the i-th array to merge.
|
|
//
|
|
// TensorFlow equivalent: Pack
|
|
struct PackOperator : Operator {
|
|
PackOperator() : Operator(OperatorType::kPack) {}
|
|
int values_count;
|
|
int axis = 0;
|
|
ArrayDataType dtype = ArrayDataType::kNone;
|
|
};
|
|
|
|
// Shape operator. Extracts the shape of the tensor.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// This operation outputs a 1-D integer tensor representing the shape of
|
|
// the input.
|
|
//
|
|
// TensorFlow equivalent: Shape.
|
|
struct TensorFlowShapeOperator : Operator {
|
|
TensorFlowShapeOperator() : Operator(OperatorType::kShape) {}
|
|
ArrayDataType output_data_type = ArrayDataType::kInt32;
|
|
};
|
|
|
|
// Element-wise square-root (x^0.5) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Sqrt
|
|
struct TensorFlowSqrtOperator : Operator {
|
|
TensorFlowSqrtOperator() : Operator(OperatorType::kSqrt) {}
|
|
};
|
|
|
|
// Element-wise square (x*x) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Square
|
|
struct TensorFlowSquareOperator : Operator {
|
|
TensorFlowSquareOperator() : Operator(OperatorType::kSquare) {}
|
|
};
|
|
|
|
// Element-wise squared difference ((x-y)*(x-y)) operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: SquaredDifference
|
|
struct SquaredDifferenceOperator : Operator {
|
|
SquaredDifferenceOperator() : Operator(OperatorType::kSquaredDifference) {}
|
|
};
|
|
|
|
// Transposes a tensor.
|
|
//
|
|
// By default, this operation performs a regular matrix transpose on 2-D input
|
|
// tensors.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Transpose
|
|
struct TransposeOperator : Operator {
|
|
TransposeOperator() : Operator(OperatorType::kTranspose) {}
|
|
std::vector<int> perm;
|
|
};
|
|
|
|
// Element-wise subtraction operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Sub
|
|
struct SubOperator : Operator {
|
|
SubOperator() : Operator(OperatorType::kSub) {}
|
|
};
|
|
|
|
// Sum reduction: computes the sum of all of entries across the axes.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Sum
|
|
struct TensorFlowSumOperator : Operator {
|
|
TensorFlowSumOperator() : Operator(OperatorType::kSum) {}
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// Prod reduction: computes the product of all of entries across the axes.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Prod
|
|
struct TensorFlowProdOperator : Operator {
|
|
TensorFlowProdOperator() : Operator(OperatorType::kReduceProd) {}
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// TensorFlow Tile equivalent. Refer to TensorFlow documentation for details.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: int array with length of rank(input[0])
|
|
struct TensorFlowTileOperator : Operator {
|
|
TensorFlowTileOperator() : Operator(OperatorType::kTile) {}
|
|
};
|
|
|
|
// TensorFlow Slice equivalent. Refer to TensorFlow documentation for details.
|
|
struct SliceOperator : Operator {
|
|
SliceOperator() : Operator(OperatorType::kSlice) {}
|
|
|
|
std::vector<int> begin;
|
|
std::vector<int> size;
|
|
};
|
|
|
|
// TensorFlow Split equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
struct TensorFlowSplitOperator : Operator {
|
|
TensorFlowSplitOperator() : Operator(OperatorType::kSplit) {}
|
|
int num_split = 0;
|
|
};
|
|
|
|
// TensorFlow SplitV equivalent. Refer to TensorFlow documentation for details.
|
|
struct TensorFlowSplitVOperator : Operator {
|
|
TensorFlowSplitVOperator() : Operator(OperatorType::kSplitV) {}
|
|
int num_split = 0;
|
|
};
|
|
|
|
// TensorFlow Concat equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Concretely, once the concat dim becomes known, if it is the depth
|
|
// dimension then we can change this op into a DepthConcatenation op.
|
|
// Otherwise, we hope for some other graph transformation to drop this node.
|
|
struct TensorFlowConcatOperator : Operator {
|
|
TensorFlowConcatOperator() : Operator(OperatorType::kConcat) {}
|
|
};
|
|
|
|
// TensorFlow ConcatV2 equivalent. Refer to TensorFlow documentation for
|
|
// details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Concretely, once the concat dim becomes known, if it is the depth
|
|
// dimension then we can change this op into a DepthConcatenation op.
|
|
// Otherwise, we hope for some other graph transformation to drop this node.
|
|
struct TensorFlowConcatV2Operator : Operator {
|
|
TensorFlowConcatV2Operator() : Operator(OperatorType::kConcatV2) {}
|
|
};
|
|
|
|
// TensorFlow Merge equivalent. Refer to TensorFlow documentation for details.
|
|
//
|
|
// Inputs: this operator accepts any number >= 1 of inputs.
|
|
// inputs[i]: the i-th array to merge.
|
|
//
|
|
// It is expected that graph transformations will drop all but exactly one
|
|
// of the inputs, at which point the Merge node will be equivalent to an
|
|
// Identity node forwarding the remaining input.
|
|
//
|
|
// Note: We do not currently support runtime control flow: we only support
|
|
// control flow that can be resolved at tooling time (independently of input
|
|
// activations).
|
|
struct TensorFlowMergeOperator : Operator {
|
|
TensorFlowMergeOperator() : Operator(OperatorType::kMerge) {}
|
|
};
|
|
|
|
// TensorFlow Switch equivalent. Refer to TensorFlow documentation for details.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the boolean predicate, given as an array of size 1
|
|
// and of type kBool, will determine which output gets selected.
|
|
//
|
|
// Outputs: a TensorFlow Switch node always has exactly two outputs. Depending
|
|
// on the boolean value that the input predicate resolves to (see note below),
|
|
// one or the other of the outputs will be 'selected': the input array will be
|
|
// forwarded to the 'selected output' as if by a Identity node, while the other
|
|
// output will be discarded, and any graph edge connecting that discarded output
|
|
// will be dropped. The rule for selecting outputs is as follows:
|
|
// outputs[0] will be selected if the input predicate resolves to 'true'.
|
|
// outputs[1] will be selected if the input predicate resolves to 'false'.
|
|
//
|
|
// Note: We do not currently support runtime control flow: we only support
|
|
// control flow that can be resolved at tooling time (independently of input
|
|
// activations).
|
|
struct TensorFlowSwitchOperator : Operator {
|
|
TensorFlowSwitchOperator() : Operator(OperatorType::kSwitch) {}
|
|
};
|
|
|
|
// TensorFlow All equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowAllOperator : Operator {
|
|
TensorFlowAllOperator() : Operator(OperatorType::kAll) {}
|
|
};
|
|
|
|
// TensorFlow Assert equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, we just drop Assert nodes.
|
|
struct TensorFlowAssertOperator : Operator {
|
|
TensorFlowAssertOperator() : Operator(OperatorType::kAssert) {}
|
|
};
|
|
|
|
// TensorFlow Less equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowLessOperator : Operator {
|
|
TensorFlowLessOperator() : Operator(OperatorType::kLess) {}
|
|
};
|
|
|
|
// TensorFlow LessEqual equivalent. Refer to TensorFlow documentation for
|
|
// details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowLessEqualOperator : Operator {
|
|
TensorFlowLessEqualOperator() : Operator(OperatorType::kLessEqual) {}
|
|
};
|
|
|
|
// TensorFlow Less equivalent. Refer to TensorFlow documentation for details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowGreaterOperator : Operator {
|
|
TensorFlowGreaterOperator() : Operator(OperatorType::kGreater) {}
|
|
};
|
|
|
|
// TensorFlow GreaterEqual equivalent. Refer to TensorFlow documentation for
|
|
// details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowGreaterEqualOperator : Operator {
|
|
TensorFlowGreaterEqualOperator() : Operator(OperatorType::kGreaterEqual) {}
|
|
};
|
|
|
|
// TensorFlow Equal equivalent. Refer to TensorFlow documentation for
|
|
// details.
|
|
// Not fully supported, just a placeholder to handle TensorFlow graphs and
|
|
// support graph transformations to other operator types by matching sub-graphs.
|
|
// Typically, this is only used as an input to an Assert node, so can be
|
|
// removed as an unused node as we drop Assert nodes.
|
|
struct TensorFlowEqualOperator : Operator {
|
|
TensorFlowEqualOperator() : Operator(OperatorType::kEqual) {}
|
|
};
|
|
|
|
// TensorFlow Not Equal equivalent. Refer to TensorFlow documentation for
|
|
// details.
|
|
struct TensorFlowNotEqualOperator : Operator {
|
|
TensorFlowNotEqualOperator() : Operator(OperatorType::kNotEqual) {}
|
|
};
|
|
|
|
// Max reduction: computes the max of all of entries across the axes.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Max
|
|
struct TensorFlowMaxOperator : Operator {
|
|
TensorFlowMaxOperator() : Operator(OperatorType::kReduceMax) {}
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// Min reduction: computes the min of all of entries across the axes.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Min
|
|
struct TensorFlowMinOperator : Operator {
|
|
TensorFlowMinOperator() : Operator(OperatorType::kReduceMin) {}
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// Element-wise maximum operator. Currently it only supports scalar as
|
|
// the second operand.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Maximum
|
|
struct TensorFlowMaximumOperator : Operator {
|
|
TensorFlowMaximumOperator() : Operator(OperatorType::kMaximum) {}
|
|
};
|
|
|
|
// Element-wise minimum operator. Currently it only supports scalar as
|
|
// the second operand.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the left-hand side array
|
|
// inputs[1]: required: the right-hand side array
|
|
//
|
|
// TensorFlow equivalent: Minimum
|
|
struct TensorFlowMinimumOperator : Operator {
|
|
TensorFlowMinimumOperator() : Operator(OperatorType::kMinimum) {}
|
|
};
|
|
|
|
// General TF operation, unsupported by tf.mini. Expected to be dropped by
|
|
// graph transformations.
|
|
struct TensorFlowUnsupportedOperator : Operator {
|
|
TensorFlowUnsupportedOperator() : Operator(OperatorType::kUnsupported) {}
|
|
|
|
// The original TF operation type. Used for diagnostic purposes.
|
|
std::string tensorflow_op;
|
|
// A boolean indicating if the unsupported op should be treated as quantized.
|
|
bool quantized = false;
|
|
// A boolean indicating if the unsupported op output should allow float values
|
|
// in quantized mode.
|
|
bool support_output_type_float_in_quantized_op = false;
|
|
// Output data types
|
|
std::vector<ArrayDataType> output_data_types;
|
|
// Output shapes.
|
|
std::vector<Shape> output_shapes;
|
|
};
|
|
|
|
// Softmax activation function.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Softmax
|
|
struct SoftmaxOperator : Operator {
|
|
SoftmaxOperator() : Operator(OperatorType::kSoftmax) {}
|
|
float beta = 0.f;
|
|
};
|
|
|
|
// LogSoftmax activation function.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the logits input array
|
|
//
|
|
// TensorFlow equivalent: LogSoftmax
|
|
struct LogSoftmaxOperator : Operator {
|
|
LogSoftmaxOperator() : Operator(OperatorType::kLogSoftmax) {}
|
|
|
|
// LogSoftmax can in principal have very large negative output, depending on
|
|
// the input size. However, input x_i that is less than x_max-10 is
|
|
// accumulated as exp(x_i-x_max), which is truncated to zero.
|
|
//
|
|
// Since we effectively disregard smallish inputs in the normalizing factor,
|
|
// we also drop them in the output (set to minimum output), and in doing so
|
|
// make better use of the quantization range / resolution.
|
|
static constexpr float kOutputRangeMin = -16.0;
|
|
};
|
|
|
|
// Cast operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Cast
|
|
struct CastOperator : Operator {
|
|
CastOperator() : Operator(OperatorType::kCast) {}
|
|
ArrayDataType src_data_type = ArrayDataType::kNone;
|
|
ArrayDataType dst_data_type = ArrayDataType::kNone;
|
|
};
|
|
|
|
// Floor operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Floor
|
|
struct FloorOperator : Operator {
|
|
FloorOperator() : Operator(OperatorType::kFloor) {}
|
|
};
|
|
|
|
// Ceil operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Ceil
|
|
struct CeilOperator : Operator {
|
|
CeilOperator() : Operator(OperatorType::kCeil) {}
|
|
};
|
|
|
|
// Round operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Round
|
|
struct RoundOperator : Operator {
|
|
RoundOperator() : Operator(OperatorType::kRound) {}
|
|
};
|
|
|
|
// Gather operator. It gathers slices from params according to indices.
|
|
// Only 1-D indices are supported at the moment.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the params array
|
|
// inputs[1]: required: the indices to gather
|
|
// inputs[2]: optional: axis
|
|
//
|
|
// TensorFlow equivalent: Gather
|
|
struct GatherOperator : Operator {
|
|
GatherOperator() : Operator(OperatorType::kGather) {}
|
|
// Axis is populated explicitly or implicitly from the axis input by
|
|
// ResolveGatherAttributes. An empty axis indicates that the axis has not yet
|
|
// be resolved.
|
|
absl::optional<int> axis;
|
|
|
|
// This field is not used by the standard TF Lite export but it is still need
|
|
// for legacy Gather implementations.
|
|
int input_rank = 0;
|
|
};
|
|
|
|
// GatherNd operator. It gathers slices from params according to indices.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the params array
|
|
// inputs[1]: required: the indices to gather
|
|
//
|
|
// TensorFlow equivalent: GatherNd
|
|
struct GatherNdOperator : Operator {
|
|
GatherNdOperator() : Operator(OperatorType::kGatherNd) {}
|
|
};
|
|
|
|
// ArgMax operator. It returns the index of the maximum value along axis.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input tensor
|
|
// inputs[1]: optional: 0-D (scalar) axis
|
|
//
|
|
// TensorFlow equivalent: ArgMax
|
|
struct ArgMaxOperator : Operator {
|
|
ArgMaxOperator() : Operator(OperatorType::kArgMax) {}
|
|
ArrayDataType output_data_type = ArrayDataType::kInt64;
|
|
};
|
|
|
|
// ArgMin operator. It returns the index of the minimum value along axis.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input tensor
|
|
// inputs[1]: optional: 0-D (scalar) axis
|
|
//
|
|
// TensorFlow equivalent: ArgMin
|
|
struct ArgMinOperator : Operator {
|
|
ArgMinOperator() : Operator(OperatorType::kArgMin) {}
|
|
ArrayDataType output_data_type = ArrayDataType::kInt64;
|
|
};
|
|
|
|
// ResizeBilinear operator. It resizes input images with bilinear interpolation.
|
|
// It does not support align_corners at the moment.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the new image size
|
|
//
|
|
// TensorFlow equivalent: ResizeBilinear
|
|
struct ResizeBilinearOperator : Operator {
|
|
ResizeBilinearOperator() : Operator(OperatorType::kResizeBilinear) {}
|
|
|
|
bool align_corners = false;
|
|
bool half_pixel_centers = false;
|
|
};
|
|
|
|
// ResizeNearestNeighborOperator operator. It resizes input images with nearest
|
|
// neighbor interpolation. It does not support align_corners at the moment.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the new image size
|
|
//
|
|
// TensorFlow equivalent: ResizeNearestNeighbor
|
|
struct ResizeNearestNeighborOperator : Operator {
|
|
ResizeNearestNeighborOperator()
|
|
: Operator(OperatorType::kResizeNearestNeighbor) {}
|
|
|
|
bool align_corners = false;
|
|
bool half_pixel_centers = false;
|
|
};
|
|
|
|
// SpaceToBatchND operator. It divides spatial dimensions into a grid of
|
|
// blocks and interleaves these blocks with the batch dimension. Currently,
|
|
// only 2-d blocks are supported.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the block shape
|
|
// inputs[2]: required: the paddings
|
|
//
|
|
// TensorFlow equivalent: SpaceToBatchND
|
|
struct SpaceToBatchNDOperator : Operator {
|
|
SpaceToBatchNDOperator() : Operator(OperatorType::kSpaceToBatchND) {}
|
|
|
|
std::vector<int> block_shape;
|
|
std::vector<int> before_paddings;
|
|
std::vector<int> after_paddings;
|
|
};
|
|
|
|
// BatchToSpaceND operator. Rearranges data from batch into blocks of
|
|
// spatial data. Currently, only 2-d blocks are supported.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: the block shape
|
|
// inputs[2]: required: the crops
|
|
//
|
|
// TensorFlow equivalent: BatchToSpaceND
|
|
struct BatchToSpaceNDOperator : Operator {
|
|
BatchToSpaceNDOperator() : Operator(OperatorType::kBatchToSpaceND) {}
|
|
|
|
std::vector<int> block_shape;
|
|
std::vector<int> before_crops;
|
|
std::vector<int> after_crops;
|
|
};
|
|
|
|
// Mean operator.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Mean
|
|
struct MeanOperator : Operator {
|
|
MeanOperator() : Operator(OperatorType::kMean) {}
|
|
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// Svdf operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
// inputs[1]: required: weights_feature
|
|
// inputs[2]: required: weights_time
|
|
// inputs[3]: optional: bias
|
|
struct SvdfOperator : Operator {
|
|
SvdfOperator() : Operator(OperatorType::kSvdf) {}
|
|
int rank;
|
|
};
|
|
|
|
// TopKV2 operator.
|
|
//
|
|
// Inputs:
|
|
// input tensor and top_k scalar.
|
|
struct TopKV2Operator : Operator {
|
|
TopKV2Operator() : Operator(OperatorType::kTopK_V2) {}
|
|
};
|
|
|
|
// DynamicPartition operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: data.
|
|
// inputs[1]: required: partitions.
|
|
//
|
|
// TensorFlow equivalent: DynamicPartition
|
|
struct DynamicPartitionOperator : Operator {
|
|
DynamicPartitionOperator() : Operator(OperatorType::kDynamicPartition) {}
|
|
int num_partitions;
|
|
};
|
|
|
|
// DynamicStitch operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0,N): required: indices.
|
|
// inputs[N,2N): required: data.
|
|
//
|
|
// TensorFlow equivalent: DynamicStitch/ParallelDynamicStitch
|
|
struct DynamicStitchOperator : Operator {
|
|
DynamicStitchOperator() : Operator(OperatorType::kDynamicStitch) {}
|
|
int num_partitions;
|
|
};
|
|
|
|
// SparseToDense operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: sparse_indices.
|
|
// Inputs[1]: required: output_shape.
|
|
// Inputs[2]: required: sparse_values.
|
|
//
|
|
// TensorFlow equivalent: SparseToDense.
|
|
struct SparseToDenseOperator : Operator {
|
|
SparseToDenseOperator() : Operator(OperatorType::kSparseToDense) {}
|
|
bool validate_indices;
|
|
};
|
|
|
|
// Pow operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A tensor.
|
|
// Inputs[1]: required: A tensor.
|
|
//
|
|
// TensorFlow equivalent: Pow.
|
|
struct PowOperator : Operator {
|
|
PowOperator() : Operator(OperatorType::kPow) {}
|
|
};
|
|
|
|
// Any operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A boolean input tensor.
|
|
// Inputs[1]: required: reduction_indices.
|
|
//
|
|
// TensorFlow equivalent: tf.reduce_any.
|
|
struct TensorFlowAnyOperator : Operator {
|
|
TensorFlowAnyOperator() : Operator(OperatorType::kAny) {}
|
|
std::vector<int> axis;
|
|
bool keep_dims = false;
|
|
};
|
|
|
|
// LogicalAnd operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A boolean tensor.
|
|
// Inputs[1]: required: A boolean tensor.
|
|
//
|
|
// TensorFlow equivalent: tf.logical_and.
|
|
struct LogicalAndOperator : Operator {
|
|
LogicalAndOperator() : Operator(OperatorType::kLogicalAnd) {}
|
|
};
|
|
|
|
// LogicalNot operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A boolean tensor.
|
|
//
|
|
// TensorFlow equivalent: tf.logical_not.
|
|
struct LogicalNotOperator : Operator {
|
|
LogicalNotOperator() : Operator(OperatorType::kLogicalNot) {}
|
|
};
|
|
|
|
// OneHot operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: indices.
|
|
// Inputs[1]: required: depth.
|
|
// Inputs[2]: required: on_value.
|
|
// Inputs[3]: required: off_value.
|
|
//
|
|
// TensorFlow equivalent: OneHot.
|
|
struct OneHotOperator : Operator {
|
|
enum Inputs {
|
|
INDICES_INPUT = 0,
|
|
DEPTH_INPUT = 1,
|
|
ON_VALUE_INPUT = 2,
|
|
OFF_VALUE_INPUT = 3,
|
|
};
|
|
|
|
OneHotOperator() : Operator(OperatorType::kOneHot) {}
|
|
int axis = -1;
|
|
};
|
|
|
|
// LogicalOr operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A Bool tensor.
|
|
// Inputs[1]: required: A Bool tensor.
|
|
//
|
|
// TensorFlow equivalent: LogicalOr.
|
|
struct LogicalOrOperator : Operator {
|
|
LogicalOrOperator() : Operator(OperatorType::kLogicalOr) {}
|
|
};
|
|
|
|
// Unpack operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: A boolean input tensor.
|
|
// Inputs[1]: required: reduction_indices.
|
|
//
|
|
// TensorFlow equivalent: tf.unstack.
|
|
struct UnpackOperator : Operator {
|
|
UnpackOperator() : Operator(OperatorType::kUnpack) {}
|
|
int num;
|
|
int axis;
|
|
ArrayDataType dtype = ArrayDataType::kNone;
|
|
};
|
|
|
|
// ZerosLike operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: tf.zeros_like
|
|
struct TensorFlowZerosLikeOperator : Operator {
|
|
TensorFlowZerosLikeOperator() : Operator(OperatorType::kZerosLike) {}
|
|
};
|
|
|
|
// ReverseV2 operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: the input array.
|
|
//
|
|
// TensorFlow equivalent: ReverseV2.
|
|
struct ReverseV2Operator : Operator {
|
|
ReverseV2Operator() : Operator(OperatorType::kReverseV2) {}
|
|
};
|
|
|
|
enum class MirrorPadMode { kNone, kSymmetric, kReflect };
|
|
|
|
// MirrorPad Operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: input tensor to be padded.
|
|
// Inputs[1]: required: 2 Column matrix specifying padding sizes. The number of
|
|
// rows must be the same as the rank of the input.
|
|
// Inputs[2]: required: REFLECT or SYMMETRIC.
|
|
//
|
|
// TensorFlow equivalent: MirrorPad.
|
|
struct MirrorPadOperator : Operator {
|
|
MirrorPadOperator() : Operator(OperatorType::kMirrorPad) {}
|
|
// mode is either SYMMETRIC or REFLECT.
|
|
MirrorPadMode mode;
|
|
};
|
|
|
|
// ReverseSequence operator:
|
|
//
|
|
// Inputs:
|
|
// Inputs[0]: required: the input array.
|
|
// Inputs[1]: required: the lengths of the elements to be reversed.
|
|
//
|
|
// TensorFlow equivalent: tf.reverse_sequence.
|
|
struct ReverseSequenceOperator : Operator {
|
|
ReverseSequenceOperator() : Operator(OperatorType::kReverseSequence) {}
|
|
int seq_dim;
|
|
int batch_dim = 0;
|
|
};
|
|
|
|
// Unique Operator:
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: the input array
|
|
//
|
|
// TensorFlow equivalent: Unique
|
|
struct UniqueOperator : Operator {
|
|
UniqueOperator() : Operator(OperatorType::kUnique) {}
|
|
ArrayDataType idx_out_type = ArrayDataType::kInt32;
|
|
};
|
|
|
|
struct UnidirectionalSequenceRnnOperator : Operator {
|
|
UnidirectionalSequenceRnnOperator()
|
|
: Operator(OperatorType::kUnidirectionalSequenceRnn) {}
|
|
bool time_major;
|
|
FusedActivationFunctionType fused_activation_function;
|
|
};
|
|
|
|
// Where Operator:
|
|
// Return the coordinates of the true values in condition tensor in row-major
|
|
// order.
|
|
//
|
|
// Inputs:
|
|
// inputs[0]: required: boolean condition tensor
|
|
//
|
|
// TensorFlow equivalent: Where
|
|
struct WhereOperator : Operator {
|
|
WhereOperator() : Operator(OperatorType::kWhere) {}
|
|
};
|
|
|
|
// Matrix Diag Operator:
|
|
// Construct a batched diagonal tensor with given batched diagonal values.
|
|
// Inputs: A tensor of values that will be on the diagonal of the returned
|
|
// tensor.
|
|
struct MatrixDiagOperator : Operator {
|
|
MatrixDiagOperator() : Operator(OperatorType::kMatrixDiag) {}
|
|
};
|
|
|
|
// Matrix Diag Operator V2:
|
|
// Construct a batched diagonal tensor with given batched diagonal values.
|
|
// Not fully supported, contains 4 extra inputs compared to MatrixDiag. Behave
|
|
// like MatrixDiag when default parameters are used.
|
|
struct MatrixDiagV2Operator : Operator {
|
|
MatrixDiagV2Operator() : Operator(OperatorType::kMatrixDiagV2) {}
|
|
};
|
|
|
|
// Matrix Diag Operator V3:
|
|
// Construct a batched diagonal tensor with given batched diagonal values.
|
|
// Not fully supported, contains 5 extra inputs compared to MatrixDiag. Behave
|
|
// like MatrixDiag when default parameters are used.
|
|
// V3 is only different from V2 because it has an extra attribute (align) which
|
|
// controls the alignment of diagonals in the band matrix (compact) format.
|
|
// The alignment in V2 contradicts with the default alignment in V3 so V2 is
|
|
// skipped. (It has never been, and should never be, exposed in the public API.)
|
|
struct MatrixDiagV3Operator : Operator {
|
|
MatrixDiagV3Operator() : Operator(OperatorType::kMatrixDiagV3) {}
|
|
};
|
|
|
|
// Matrix Set Diag Operator:
|
|
// Construct a batched diagonal tensor with given input and diagonal values.
|
|
// Input is a rank (k+1) tensor of values.
|
|
// diagonal is a rank (k) tensor of values that will be on the diagonal
|
|
// of the returned output. Output is rank k+1.
|
|
// tensor.
|
|
struct MatrixSetDiagOperator : Operator {
|
|
MatrixSetDiagOperator() : Operator(OperatorType::kMatrixSetDiag) {}
|
|
};
|
|
|
|
// Matrix Set Diag Operator V2:
|
|
// Construct a batched diagonal tensor with given input and diagonal values.
|
|
// Not fully supported, contains 1 extra inputs compared to MatrixSetDiag.
|
|
// Behave like MatrixSetDiag when default parameters are used.
|
|
struct MatrixSetDiagV2Operator : Operator {
|
|
MatrixSetDiagV2Operator() : Operator(OperatorType::kMatrixSetDiagV2) {}
|
|
};
|
|
|
|
// Matrix Set Diag Operator V3:
|
|
// Construct a batched diagonal tensor with given input and diagonal values.
|
|
// Not fully supported, contains 2 extra inputs compared to MatrixSetDiag.
|
|
// Behave like MatrixSetDiag when default parameters are used.
|
|
// V3 is only different from V2 because it has an extra attribute (align) which
|
|
// controls the alignment of diagonals in the band matrix (compact) format.
|
|
// The alignment in V2 contradicts with the default alignment in V3 so V2 is
|
|
// skipped. (It has never been, and should never be, exposed in the public API.)
|
|
struct MatrixSetDiagV3Operator : Operator {
|
|
MatrixSetDiagV3Operator() : Operator(OperatorType::kMatrixSetDiagV3) {}
|
|
};
|
|
|
|
struct ScatterNdOperator : Operator {
|
|
ScatterNdOperator() : Operator(OperatorType::kScatterNd) {}
|
|
};
|
|
|
|
struct SegmentSumOperator : Operator {
|
|
SegmentSumOperator() : Operator(OperatorType::kSegmentSum) {}
|
|
};
|
|
|
|
// Alloc's are used for transient arrays only. An Alloc specifies which interval
|
|
// of the "transient_data" workspace buffer passed to inference functions, is to
|
|
// be used for the transient array at hand. The 'start' and 'end' values are
|
|
// offsets from the start of the workspace buffer, expressed in bytes.
|
|
struct Alloc {
|
|
int64 start = 0;
|
|
int64 end = 0;
|
|
};
|
|
|
|
inline bool operator<(const Alloc& a, const Alloc& b) {
|
|
return a.start < b.start;
|
|
}
|
|
|
|
// Array represents an array (either a constant parameter array or an
|
|
// activations array) in a Model.
|
|
struct Array {
|
|
template <ArrayDataType A>
|
|
const Buffer<A>& GetBuffer() const {
|
|
DCHECK(buffer);
|
|
DCHECK(buffer->type == A);
|
|
return *static_cast<const Buffer<A>*>(buffer.get());
|
|
}
|
|
template <ArrayDataType A>
|
|
Buffer<A>& GetMutableBuffer() {
|
|
if (!buffer) {
|
|
Buffer<A>* ptr = new Buffer<A>;
|
|
buffer = std::unique_ptr<GenericBuffer>(ptr);
|
|
}
|
|
DCHECK(buffer);
|
|
DCHECK(buffer->type == A);
|
|
return *static_cast<Buffer<A>*>(buffer.get());
|
|
}
|
|
Alloc& GetOrCreateAlloc() {
|
|
if (!alloc) {
|
|
alloc = std::unique_ptr<Alloc>(new Alloc);
|
|
}
|
|
return *alloc;
|
|
}
|
|
MinMax& GetOrCreateMinMax() {
|
|
if (!minmax) {
|
|
minmax = std::unique_ptr<MinMax>(new MinMax);
|
|
}
|
|
return *minmax;
|
|
}
|
|
MinMax& GetMinMax() const {
|
|
DCHECK(minmax);
|
|
return *minmax;
|
|
}
|
|
QuantizationParams& GetOrCreateQuantizationParams() {
|
|
if (!quantization_params) {
|
|
quantization_params =
|
|
std::unique_ptr<QuantizationParams>(new QuantizationParams);
|
|
}
|
|
return *quantization_params;
|
|
}
|
|
QuantizationParams& GetQuantizationParams() const {
|
|
DCHECK(quantization_params);
|
|
return *quantization_params;
|
|
}
|
|
|
|
// The data type of the actual elements of this array, that is:
|
|
// - If there is a buffer (see 'buffer' member), it must be of the same
|
|
// type.
|
|
// - If there is no buffer, meaning that this is a runtime (i.e. activations)
|
|
// array, then this specifies the type of elements that there will be
|
|
// at runtime.
|
|
//
|
|
// Note that this only specifies the storage type of elements; this does
|
|
// not specify whether these are to be treated as 'real' or 'quantized'
|
|
// values.
|
|
// That is decided by whether the 'quantization_params' member is null.
|
|
ArrayDataType data_type = ArrayDataType::kNone;
|
|
// The final value that data_type should have at the end of graph
|
|
// transformations
|
|
ArrayDataType final_data_type = ArrayDataType::kNone;
|
|
// The dimensions of this array --- this specifies both sizes and strides
|
|
// (the storage layout).
|
|
//
|
|
// Issues with shape handling that remain include:
|
|
// - No way to distinguish between 0-dimensional dims and missing dims.
|
|
// - No way to describe dims that may be runtime-variable.
|
|
// - Addressing of dims by integer index differs in different graph formats
|
|
// (TensorFlow vs. other frameworks vs. what we have informally grown
|
|
// within toco).
|
|
// This is currently quite messy; see ReorderAxesOperator which is how we
|
|
// bridge some of these discrepancies at the moment. This is overdue for
|
|
// a redesign; I'm thinking that it would be nice to have more flexible
|
|
// dims that allow mapping 1:1, cleanly, dims as they are in various
|
|
// formats,
|
|
// then explicitly convert between different conventions.
|
|
|
|
// Proto-style accessors
|
|
bool has_shape() const { return array_shape != nullptr; }
|
|
const Shape& shape() const {
|
|
CHECK(has_shape());
|
|
return *array_shape;
|
|
}
|
|
Shape* mutable_shape() {
|
|
if (!array_shape) {
|
|
array_shape.reset(new Shape);
|
|
}
|
|
return array_shape.get();
|
|
}
|
|
void copy_shape(const Shape& src_shape) { *mutable_shape() = src_shape; }
|
|
void clear_shape() { array_shape = nullptr; }
|
|
|
|
// The constant buffer backing this array. This is non-null if and only if
|
|
// this is a constant parameter array. Conversely, this is null for
|
|
// activations arrays.
|
|
//
|
|
// Note that this buffer is pure storage. In the case of quantized values,
|
|
// it only stores the quantized values, it does not know by itself about the
|
|
// quantization parameters necessary to interprete these values, that is
|
|
// in the separate 'quantization_params' field. In fact, this 'buffer' field
|
|
// does no even know whether values are quantized. It only has a data_type,
|
|
// which must equal the 'data_type' member here, and which only describes
|
|
// the storage type of element, does not tell whether they are quantized i.e.
|
|
// whether they are to be interpreted with quantization_params.
|
|
std::unique_ptr<GenericBuffer> buffer;
|
|
// Only for activation arrays (i.e. when 'buffer' is null).
|
|
// Only for code generation.
|
|
//
|
|
// Describes the allocation of this array within the workspace buffer
|
|
// allocated
|
|
// for all transient arrays.
|
|
std::unique_ptr<Alloc> alloc;
|
|
// Describes the [min, max] range of values
|
|
// to be assumed when determining quantization_params.
|
|
//
|
|
// Only used for quantization. In fact, only used for determining
|
|
// quantization_params.
|
|
//
|
|
// Used for both constant arrays (those having a 'buffer') and non-constant
|
|
// arrays (activations). Indeed, it is important to use the same min-max range
|
|
// as was used during training, even if that min-max range is slightly wrong
|
|
// w.r.t. actual buffer elements. Doing otherwise would defeat the point of
|
|
// re-training for quantization.
|
|
std::unique_ptr<MinMax> minmax;
|
|
// Quantization parameters. The non-null-ness of this pointer is what
|
|
// defines whether this array is quantized or not.
|
|
//
|
|
// If this is non-null, then these quantization parameters are to be used
|
|
// to assign a meaning as real numbers to the elements of this array.
|
|
std::unique_ptr<QuantizationParams> quantization_params;
|
|
// narrow_range is a detail of how toco handles FakeQuant operators with
|
|
// narrow_range, see
|
|
// https://www.tensorflow.org/api_docs/python/tf/fake_quant_with_min_max_vars
|
|
//
|
|
// For more context about what that is useful for, see the big comment in
|
|
// graph_transformations/ensure_uint8_weights_safe_for_fast_int8_kernels.cc
|
|
//
|
|
// The narrow_range flag applies only to quantized arrays, and changes
|
|
// their quantization in the following way when it is set to 'true':
|
|
// 1. The computation of {zero_point, scale} from {min, max} needs to be
|
|
// amended so that the real min value will get quantized to
|
|
// (min_quantized_value + 1) instead of just (min_quantized_value).
|
|
// E.g. for uint8 quantization, the real min value should get quantized to
|
|
// the uint8 value 1, not 0.
|
|
// 2. Quantized values should get clamped to the interval
|
|
// [min_quantized_value + 1, max_value]. Equivalently, the
|
|
// min_quantized_value should get nudged to (min_quantized_value + 1).
|
|
// The reason why 1. does not imply 2. is that real values may not belong to
|
|
// the stated [min, max] interval. Concretely, weights recorded at the last
|
|
// learning step may not fall in the [min, max] interval recorded over
|
|
// previous learning steps, as the values evolve across learning steps.
|
|
//
|
|
// Rationale why this is directly a field on Array:
|
|
// - This can't be just a field on FakeQuantOperator, because
|
|
// FakeQuantOperators are gone (DropFakeQuant) before we get to using that
|
|
// information (Quantize). We need a place to store that bit in the interim.
|
|
// - This can't be in QuantizationParams because we need to record this
|
|
// ahead of quantization, and QuantizationParams are only created during
|
|
// quantization.
|
|
// - This could be in MinMax, but that would be an abuse of what MinMax is
|
|
// about, and would break existing code that assumes that a MinMax is just
|
|
// a min and a max. Unlike MinMax which is agnostic as to the quantized
|
|
// data type, narrow_range refers to values in the quantized data type.
|
|
bool narrow_range = false;
|
|
|
|
private:
|
|
std::unique_ptr<Shape> array_shape;
|
|
};
|
|
|
|
// Our Model struct, represents an entire model (our "top-level" struct).
|
|
// Owns everything.
|
|
class Model {
|
|
public:
|
|
using ArrayMap = std::unordered_map<std::string, std::unique_ptr<Array>>;
|
|
|
|
bool HasArray(const std::string& name) const {
|
|
return arrays.count(name) > 0;
|
|
}
|
|
Array& GetArray(const std::string& name) const {
|
|
DCHECK(HasArray(name)) << "Array not found: " << name;
|
|
return *arrays.at(name);
|
|
}
|
|
Array& GetOrCreateArray(const std::string& name) {
|
|
// Make sure name is not used by an optional array
|
|
DCHECK(!optional_arrays.count(name));
|
|
if (!HasArray(name)) {
|
|
Array* ptr = new Array;
|
|
arrays[name] = std::unique_ptr<Array>(ptr);
|
|
}
|
|
Array& result = GetArray(name);
|
|
return result;
|
|
}
|
|
void CreateOptionalArray(const std::string& name) {
|
|
DCHECK(!arrays.count(name) && !optional_arrays.count(name));
|
|
optional_arrays.insert(name);
|
|
}
|
|
bool IsOptionalArray(const std::string& name) const {
|
|
return optional_arrays.count(name);
|
|
}
|
|
|
|
// Note that this invalidates all array iterators.
|
|
void EraseArray(const std::string& name) { arrays.erase(name); }
|
|
void EraseArrays(std::function<bool(const std::string&)> discardable) {
|
|
for (auto it = arrays.begin(); it != arrays.end();) {
|
|
if (discardable(it->first)) {
|
|
it = arrays.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
const ArrayMap& GetArrayMap() const { return arrays; }
|
|
ArrayMap& GetMutableArrayMap() { return arrays; }
|
|
|
|
int64 ArithmeticOpsCount() const { return ops_count; }
|
|
|
|
void AddInvalidInputArray(std::string invalid_input_array) {
|
|
invalid_input_arrays_.insert(invalid_input_array);
|
|
}
|
|
|
|
const std::unordered_set<std::string>& GetInvalidInputArrays() const {
|
|
return invalid_input_arrays_;
|
|
}
|
|
|
|
// Optional arrays are used for optional tensors,
|
|
// these tensors do not have data, but with reserved names as op inputs.
|
|
std::set<std::string> optional_arrays;
|
|
|
|
// The list of operators. Notice how it's a list of unique_ptr's, implying
|
|
// that the Model is what owns Operator's and keeps them alive.
|
|
std::vector<std::unique_ptr<Operator>> operators;
|
|
|
|
// Generic flags, a place where we combine information passed to us via
|
|
// command-line parameters (e.g. --input_width=N) with information that
|
|
// we may or may not find in the input model file.
|
|
ModelFlags flags;
|
|
// For code-generation only: required size of the transient_data buffer
|
|
std::size_t transient_data_size = 0;
|
|
// For code-generation only: required alignment of the transient_data buffer
|
|
std::size_t transient_data_alignment = 0;
|
|
// Arithmetic operations performed in the model.
|
|
int64 ops_count = 0;
|
|
|
|
private:
|
|
// The associative array mapping names to Array's.
|
|
// Notice how it's a container of unique_ptr's, implying
|
|
// that the Model is what owns Array's and keeps them alive.
|
|
// The Operator's refer to these Array's by their name strings, not by their
|
|
// addresses. See Operator::inputs, Operator::outputs.
|
|
std::unordered_map<std::string, std::unique_ptr<Array>> arrays;
|
|
|
|
// Invalid input arrays.
|
|
std::unordered_set<std::string> invalid_input_arrays_;
|
|
};
|
|
|
|
// OperatorSignature contains the information required to making versioning
|
|
// decisions.
|
|
struct OperatorSignature {
|
|
// The operator.
|
|
const Operator* op;
|
|
|
|
// The model in which the operator resides.
|
|
const Model* model;
|
|
};
|
|
} // namespace toco
|
|
|
|
#endif // TENSORFLOW_LITE_TOCO_MODEL_H_
|