summaryrefslogtreecommitdiff
path: root/nn/runtime
diff options
context:
space:
mode:
authorViet Dang <vddang@google.com>2019-12-04 16:18:08 +0000
committerViet Dang <vddang@google.com>2020-01-09 15:04:38 +0000
commitf388cca00f9f6056f911a954843e9cd8adabef56 (patch)
tree806b1eab0320727f94ed53907bd48a40c033e5b6 /nn/runtime
parent2ff6fe9a732a56873eec9f2ac3fd45a43c145a4d (diff)
downloadml-f388cca00f9f6056f911a954843e9cd8adabef56.tar.gz
Implements Quantized LSTM op for R.
Also adds support for TENSOR_QUANT8_ASYMM_SIGNED in Test Generator. Bug: 144841609 Bug: 145916330 Test: NeuralNetworksTest_static Change-Id: I14b0d284b1945833d532cbaa33c66e4d77afd8b7
Diffstat (limited to 'nn/runtime')
-rw-r--r--nn/runtime/NeuralNetworks.cpp1
-rw-r--r--nn/runtime/include/NeuralNetworks.h131
-rw-r--r--nn/runtime/test/TestValidateOperations.cpp120
-rw-r--r--nn/runtime/test/generated/spec_V1_3/qlstm.example.cpp380
-rw-r--r--nn/runtime/test/specs/V1_3/qlstm.mod.py178
5 files changed, 810 insertions, 0 deletions
diff --git a/nn/runtime/NeuralNetworks.cpp b/nn/runtime/NeuralNetworks.cpp
index 3542c24b1..5ab55dbf7 100644
--- a/nn/runtime/NeuralNetworks.cpp
+++ b/nn/runtime/NeuralNetworks.cpp
@@ -188,6 +188,7 @@ static_assert(ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM == 92,
"ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM has changed");
static_assert(ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN == 93,
"ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN has changed");
+static_assert(ANEURALNETWORKS_QUANTIZED_LSTM == 95, "ANEURALNETWORKS_QUANTIZED_LSTM has changed");
static_assert(ANEURALNETWORKS_OEM_OPERATION == 10000, "ANEURALNETWORKS_OEM_OPERATION has changed");
diff --git a/nn/runtime/include/NeuralNetworks.h b/nn/runtime/include/NeuralNetworks.h
index 3bc646e2d..b2f72c47d 100644
--- a/nn/runtime/include/NeuralNetworks.h
+++ b/nn/runtime/include/NeuralNetworks.h
@@ -5084,6 +5084,137 @@ typedef enum {
* Available since API level 29.
*/
ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR = 94,
+
+ /**
+ * Quantized version of {@link ANEURALNETWORKS_LSTM}.
+ *
+ * The input and the output use asymmetric quantized types, while the rest
+ * use symmetric ones.
+ *
+ * Inputs:
+ * * 0: The input to the LSTM cell.
+ * Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+ * Shape: [batchSize, inputSize]
+ * * 1: The input-to-input weights. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, inputSize]
+ * * 2: The input-to-forget weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, inputSize]
+ * * 3: The input-to-cell weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, inputSize]
+ * * 4: The input-to-output weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, inputSize]
+ * * 5: The recurrent-to-input weights. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, outputSize]
+ * * 6: The recurrent-to-forget weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, outputSize]
+ * * 7: The recurrent-to-cell weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, outputSize]
+ * * 8: The recurrent-to-output weights.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [numUnits, outputSize]
+ * * 9: The cell-to-input weights (for peephole). Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 10: The cell-to-forget weights (for peephole). Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 11: The cell-to-output weights (for peephole). Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 12: The input gate bias. Quantized with scale being the
+ * product of input and weights scales and zeroPoint equal to 0.
+ * Optional.
+ * Type: {@link OperandType::TENSOR_INT32}
+ * Shape: [numUnits]
+ * * 13: The forget gate bias. Quantized with scale being the
+ * product of input and weights scales and zeroPoint equal to 0.
+ * Type: {@link OperandType::TENSOR_INT32}
+ * Shape: [numUnits]
+ * * 14: The cell bias. Quantized with scale being the
+ * product of input and weights scales and zeroPoint equal to 0.
+ * Type: {@link OperandType::TENSOR_INT32}
+ * Shape: [numUnits]
+ * * 15: The output gate bias. Quantized with scale being the
+ * product of input and weights scales and zeroPoint equal to 0.
+ * Type: {@link OperandType::TENSOR_INT32}
+ * Shape: [numUnits]
+ * * 16: The projection weights. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+ * Shape: [outputSize, numUnits]
+ * * 17: The projection bias. Quantized with scale being the
+ * product of input and weights scales and zeroPoint equal to 0.
+ * Optional.
+ * Type: {@link OperandType::TENSOR_INT32}
+ * Shape: [outputSize]
+ * * 18: The output from the previous time step.
+ * Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+ * Shape: [batchSize, outputSize]
+ * * 19: The cell state from the previous time step.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [batchSize, numUnits]
+ * * 20: The input layer normalization weights. Used to rescale
+ * normalized inputs to activation at input gate. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 21: The forget layer normalization weights. Used to
+ * rescale normalized inputs to activation at forget gate. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 22: The cell layer normalization weights. Used to rescale
+ * normalized inputs to activation at cell gate. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 23: The output layer normalization weights. Used to
+ * rescale normalized inputs to activation at output gate. Optional.
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [numUnits]
+ * * 24: The cell clip. If provided the cell state is clipped
+ * by this value prior to the cell output activation. Optional.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 25: The projection clip. If provided and projection is enabled,
+ * this is used for clipping the projected values. Optional.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 26: The scale of the intermediate result of matmul,
+ * i.e. input to layer normalization, at input gate.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 27: The scale of the intermediate result of matmul,
+ * i.e. input to layer normalization, at forget gate.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 28: The scale of the intermediate result of matmul,
+ * i.e. input to layer normalization, at cell gate.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 29: The scale of the intermediate result of matmul,
+ * i.e. input to layer normalization, at output gate.
+ * Type: {@link OperandType::FLOAT32}.
+ * * 30: The zero point of the hidden state, i.e. input to
+ * projection.
+ * Type: {@link OperandType::INT32}.
+ * * 31: The scale of the hidden state, i.e. input to
+ * projection.
+ * Type: {@link OperandType::FLOAT32}.
+ *
+ * Outputs:
+ * * 0: The output state (out).
+ * Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+ * Shape: [batchSize, outputSize]
+ * * 1: The cell state (out).
+ * Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+ * Shape: [batchSize, numUnits]
+ * * 2: The output. This is effectively the same as the current
+ * "output state (out)" value.
+ * Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+ * Shape: [batchSize, outputSize]
+ *
+ * Available since API level 30.
+ */
+ ANEURALNETWORKS_QUANTIZED_LSTM = 95,
} OperationCode;
/**
diff --git a/nn/runtime/test/TestValidateOperations.cpp b/nn/runtime/test/TestValidateOperations.cpp
index 0a259a2b4..0de593ef7 100644
--- a/nn/runtime/test/TestValidateOperations.cpp
+++ b/nn/runtime/test/TestValidateOperations.cpp
@@ -3520,4 +3520,124 @@ TEST(OperationValidationTest, RESIZE_NEAREST_NEIGHBOR_quant8_signed) {
resizeNearestNeighborTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, ANEURALNETWORKS_FLOAT32);
}
+TEST(OperationValidationTest, QUANTIZED_LSTM) {
+ uint32_t oneDimensional[1] = {5};
+ uint32_t twoDimensional[2] = {5, 5};
+
+ ANeuralNetworksOperandType quant8AsymSignedTensor2D = {
+ .type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED,
+ .dimensionCount = 2,
+ .dimensions = twoDimensional,
+ .scale = 0.0078125,
+ .zeroPoint = 0,
+ };
+ ANeuralNetworksOperandType quant8SymTensor2D = {
+ .type = ANEURALNETWORKS_TENSOR_QUANT8_SYMM,
+ .dimensionCount = 2,
+ .dimensions = twoDimensional,
+ .scale = 0.0078125,
+ .zeroPoint = 0,
+ };
+ ANeuralNetworksOperandType quant16SymTensor1D = {
+ .type = ANEURALNETWORKS_TENSOR_QUANT16_SYMM,
+ .dimensionCount = 1,
+ .dimensions = oneDimensional,
+ .scale = 1.0,
+ .zeroPoint = 0,
+ };
+ ANeuralNetworksOperandType quant16SymTensor2D = {
+ .type = ANEURALNETWORKS_TENSOR_QUANT16_SYMM,
+ .dimensionCount = 2,
+ .dimensions = twoDimensional,
+ .scale = 1.0,
+ .zeroPoint = 0,
+ };
+ ANeuralNetworksOperandType int32Tensor1D = {
+ .type = ANEURALNETWORKS_TENSOR_INT32,
+ .dimensionCount = 1,
+ .dimensions = oneDimensional,
+ .scale = 4.65661e-08,
+ .zeroPoint = 0,
+ };
+ ANeuralNetworksOperandType int32Scalar = {
+ .type = ANEURALNETWORKS_INT32,
+ };
+ ANeuralNetworksOperandType float32Scalar = {
+ .type = ANEURALNETWORKS_FLOAT32,
+ };
+
+ ANeuralNetworksOperandType input = quant8AsymSignedTensor2D;
+ ANeuralNetworksOperandType input_to_input_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType input_to_forget_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType input_to_cell_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType input_to_output_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType recurrent_to_input_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType recurrent_to_forget_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType recurrent_to_cell_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType recurrent_to_output_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType cell_to_input_weights = quant16SymTensor2D;
+ ANeuralNetworksOperandType cell_to_forget_weights = quant16SymTensor2D;
+ ANeuralNetworksOperandType cell_to_output_weights = quant16SymTensor2D;
+ ANeuralNetworksOperandType input_gate_bias = int32Tensor1D;
+ ANeuralNetworksOperandType forget_gate_bias = int32Tensor1D;
+ ANeuralNetworksOperandType cell_gate_bias = int32Tensor1D;
+ ANeuralNetworksOperandType output_gate_bias = int32Tensor1D;
+ ANeuralNetworksOperandType projection_weights = quant8SymTensor2D;
+ ANeuralNetworksOperandType projection_bias = int32Tensor1D;
+ ANeuralNetworksOperandType output_state_in = quant8AsymSignedTensor2D;
+ ANeuralNetworksOperandType cell_state_in = quant16SymTensor2D;
+ ANeuralNetworksOperandType input_layer_norm_weights = quant16SymTensor1D;
+ ANeuralNetworksOperandType forget_layer_norm_weights = quant16SymTensor1D;
+ ANeuralNetworksOperandType cell_layer_norm_weights = quant16SymTensor1D;
+ ANeuralNetworksOperandType output_layer_norm_weights = quant16SymTensor1D;
+ ANeuralNetworksOperandType cell_clip = float32Scalar;
+ ANeuralNetworksOperandType projection_clip = float32Scalar;
+ ANeuralNetworksOperandType input_intermediate_scale = float32Scalar;
+ ANeuralNetworksOperandType forget_intermediate_scale = float32Scalar;
+ ANeuralNetworksOperandType cell_intermediate_scale = float32Scalar;
+ ANeuralNetworksOperandType output_intermediate_scale = float32Scalar;
+ ANeuralNetworksOperandType hidden_state_zero_point = int32Scalar;
+ ANeuralNetworksOperandType hidden_state_scale = float32Scalar;
+
+ ANeuralNetworksOperandType output_state_out = quant8AsymSignedTensor2D;
+ ANeuralNetworksOperandType cell_state_out = quant16SymTensor2D;
+ ANeuralNetworksOperandType output = quant8AsymSignedTensor2D;
+
+ OperationTestBase test(ANEURALNETWORKS_QUANTIZED_LSTM,
+ {input,
+ input_to_input_weights,
+ input_to_forget_weights,
+ input_to_cell_weights,
+ input_to_output_weights,
+ recurrent_to_input_weights,
+ recurrent_to_forget_weights,
+ recurrent_to_cell_weights,
+ recurrent_to_output_weights,
+ cell_to_input_weights,
+ cell_to_forget_weights,
+ cell_to_output_weights,
+ input_gate_bias,
+ forget_gate_bias,
+ cell_gate_bias,
+ output_gate_bias,
+ projection_weights,
+ projection_bias,
+ output_state_in,
+ cell_state_in,
+ input_layer_norm_weights,
+ forget_layer_norm_weights,
+ cell_layer_norm_weights,
+ output_layer_norm_weights,
+ cell_clip,
+ projection_clip,
+ input_intermediate_scale,
+ forget_intermediate_scale,
+ cell_intermediate_scale,
+ output_intermediate_scale,
+ hidden_state_zero_point,
+ hidden_state_scale},
+ {output_state_out, cell_state_out, output});
+ test.testOpsValidations();
+}
+
} // end namespace
diff --git a/nn/runtime/test/generated/spec_V1_3/qlstm.example.cpp b/nn/runtime/test/generated/spec_V1_3/qlstm.example.cpp
new file mode 100644
index 000000000..e678fcd65
--- /dev/null
+++ b/nn/runtime/test/generated/spec_V1_3/qlstm.example.cpp
@@ -0,0 +1,380 @@
+// Generated from qlstm.mod.py
+// DO NOT EDIT
+// clang-format off
+#include "TestHarness.h"
+using namespace test_helper;
+
+namespace generated_tests::qlstm {
+
+const TestModel& get_test_model() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .inputIndexes = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23},
+ .isRelaxed = false,
+ .minSupportedVersion = TestHalVersion::V1_3,
+ .operands = {{
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({90, 102, 13, 26, 38, 102, 13, 26, 51, 64}),
+ .dimensions = {2, 5},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.0078125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({64, 77, 89, -102, -115, 13, 25, 38, -51, 64, -102, 89, -77, 64, -51, -64, -51, -38, -25, -13}),
+ .dimensions = {4, 5},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-77, -13, 38, 25, 115, -64, -25, -51, 38, -102, -51, 38, -64, -51, -77, 38, -51, -77, -64, -64}),
+ .dimensions = {4, 5},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-51, -38, -25, -13, -64, 64, -25, -38, -25, -77, 77, -13, -51, -38, -89, 89, -115, -64, 102, 77}),
+ .dimensions = {4, 5},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-102, -51, -25, -115, -13, -89, 38, -38, -102, -25, 77, -25, 51, -89, -38, -64, 13, 64, -77, -51}),
+ .dimensions = {4, 5},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-25, -38, 51, 13, -64, 115, -25, -38, -89, 6, -25, -77}),
+ .dimensions = {4, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-64, -38, -64, -25, 77, 51, 115, 38, -13, 25, 64, 25}),
+ .dimensions = {4, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-38, 25, 13, -38, 102, -10, -25, 38, 102, -77, -13, 25}),
+ .dimensions = {4, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({38, -13, 13, -25, -64, -89, -25, -77, -13, -51, -89, -25}),
+ .dimensions = {4, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00784314f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({0, 0, 0, 0}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 1.0f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({0, 0, 0, 0}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 1.0f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({0, 0, 0, 0}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 1.0f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({644245, 3221226, 4724464, 8160438}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 4.65661e-08f,
+ .type = TestOperandType::TENSOR_INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({2147484, -6442451, -4294968, 2147484}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 4.65661e-08f,
+ .type = TestOperandType::TENSOR_INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({-1073742, 15461883, 5368709, 1717987}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 4.65661e-08f,
+ .type = TestOperandType::TENSOR_INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({1073742, -214748, 4294968, 2147484}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 4.65661e-08f,
+ .type = TestOperandType::TENSOR_INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({-25, 51, 3, -51, 25, 127, 77, 20, 18, 51, -102, 51}),
+ .dimensions = {3,4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.00392157f,
+ .type = TestOperandType::TENSOR_QUANT8_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0, 0, 0}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::TENSOR_INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({0, 0, 0, 0, 0, 0}),
+ .dimensions = {2, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05176e-05f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({0, 0, 0, 0, 0, 0, 0, 0}),
+ .dimensions = {2, 4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05176e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({3277, 6553, 9830, 16384}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05182e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({6553, 6553, 13107, 9830}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05182e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({22937, 6553, 9830, 26214}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05182e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({19660, 6553, 6553, 16384}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 3.05182e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.0f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.0f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.007059f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.007812f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.007059f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.007812f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::INT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<float>({0.007f}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::FLOAT32,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({127, 127, -108, -67, 127, 127}),
+ .dimensions = {2, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 3.05176e-05f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int16_t>({-14650, 8939, 5771, 6715, -11843, 7847, 1508, 12939}),
+ .dimensions = {2, 4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 3.05176e-05f,
+ .type = TestOperandType::TENSOR_QUANT16_SYMM,
+ .zeroPoint = 0
+ }, {
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int8_t>({127, 127, -108, -67, 127, 127}),
+ .dimensions = {2, 3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::MODEL_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 3.05176e-05f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
+ .outputs = {32, 33, 34},
+ .type = TestOperationType::QUANTIZED_LSTM
+ }},
+ .outputIndexes = {32, 33, 34}
+ };
+ return model;
+}
+
+const auto dummy_test_model = TestModelManager::get().add("qlstm", get_test_model());
+
+} // namespace generated_tests::qlstm
+
diff --git a/nn/runtime/test/specs/V1_3/qlstm.mod.py b/nn/runtime/test/specs/V1_3/qlstm.mod.py
new file mode 100644
index 000000000..c00c61400
--- /dev/null
+++ b/nn/runtime/test/specs/V1_3/qlstm.mod.py
@@ -0,0 +1,178 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+#
+
+# Test for QUANTIZED_LSTM op.
+import copy
+
+model = Model()
+
+batch_size = 2
+input_size = 5
+num_units = 4
+output_size = 3
+
+input = Input("input",
+ ("TENSOR_QUANT8_ASYMM_SIGNED", "{%d, %d}" % (batch_size, input_size), 0.0078125, 0))
+
+input_to_input_weights = Input("input_to_input_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, input_size), 0.00784314, 0))
+input_to_forget_weights = Input("input_to_forget_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, input_size), 0.00784314, 0))
+input_to_cell_weights = Input("input_to_cell_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, input_size), 0.00784314, 0))
+input_to_output_weights = Input("input_to_output_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, input_size), 0.00784314, 0))
+
+recurrent_to_input_weights = Input("recurrent_to_intput_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, output_size),
+ 0.00784314, 0))
+recurrent_to_forget_weights = Input("recurrent_to_forget_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, output_size),
+ 0.00784314, 0))
+recurrent_to_cell_weights = Input("recurrent_to_cell_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, output_size),
+ 0.00784314, 0))
+recurrent_to_output_weights = Input("recurrent_to_output_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d, %d}" % (num_units, output_size),
+ 0.00784314, 0))
+
+cell_to_input_weights = Input("cell_to_input_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % (num_units), 1.0, 0))
+cell_to_forget_weights = Input("cell_to_forget_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % (num_units), 1.0, 0))
+cell_to_output_weights = Input("cell_to_output_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % (num_units), 1.0, 0))
+
+input_gate_bias = Input("input_gate_bias",
+ ("TENSOR_INT32", "{%d}" % (num_units), 4.65661e-08, 0))
+forget_gate_bias = Input("forget_gate_bias",
+ ("TENSOR_INT32", "{%d}" % (num_units), 4.65661e-08, 0))
+cell_gate_bias = Input("cell_gate_bias",
+ ("TENSOR_INT32", "{%d}" % (num_units), 4.65661e-08, 0))
+output_gate_bias = Input("output_gate_bias",
+ ("TENSOR_INT32", "{%d}" % (num_units), 4.65661e-08, 0))
+
+projection_weights = Input("projection_weights",
+ ("TENSOR_QUANT8_SYMM", "{%d,%d}" % (output_size, num_units), 0.00392157, 0))
+projection_bias = Input("projection_bias", "TENSOR_INT32", "{%d}" % (output_size))
+
+output_state_in = Input("output_state_in",
+ ("TENSOR_QUANT8_ASYMM_SIGNED", "{%d, %d}" % (batch_size, output_size),
+ 3.05176e-05, 0))
+cell_state_in = Input("cell_state_in",
+ ("TENSOR_QUANT16_SYMM", "{%d, %d}" % (batch_size, num_units), 3.05176e-05, 0))
+
+input_layer_norm_weights = Input("input_layer_norm_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % num_units, 3.05182e-05, 0))
+forget_layer_norm_weights = Input("forget_layer_norm_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % num_units, 3.05182e-05, 0))
+cell_layer_norm_weights = Input("cell_layer_norm_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % num_units, 3.05182e-05, 0))
+output_layer_norm_weights = Input("output_layer_norm_weights",
+ ("TENSOR_QUANT16_SYMM", "{%d}" % num_units, 3.05182e-05, 0))
+
+cell_clip = Float32Scalar("cell_clip", 0.)
+projection_clip = Float32Scalar("projection_clip", 0.)
+
+input_intermediate_scale = Float32Scalar("input_intermediate_scale", 0.007059)
+forget_intermediate_scale = Float32Scalar("forget_intermediate_scale", 0.007812)
+cell_intermediate_scale = Float32Scalar("cell_intermediate_scale", 0.007059)
+output_intermediate_scale = Float32Scalar("output_intermediate_scale", 0.007812)
+hidden_state_zero_point = Int32Scalar("hidden_state_zero_point", 0)
+hidden_state_scale = Float32Scalar("hidden_state_scale", 0.007)
+
+output_state_out = Output("output_state_out",
+ ("TENSOR_QUANT8_ASYMM_SIGNED", "{%d, %d}" % (batch_size, output_size),
+ 3.05176e-05, 0))
+cell_state_out = Output("cell_state_out",
+ ("TENSOR_QUANT16_SYMM", "{%d, %d}" % (batch_size, num_units), 3.05176e-05, 0))
+output = Output("output",
+ ("TENSOR_QUANT8_ASYMM_SIGNED", "{%d, %d}" % (batch_size, output_size),
+ 3.05176e-05, 0))
+
+model = model.Operation(
+ "QUANTIZED_LSTM", input, input_to_input_weights, input_to_forget_weights,
+ input_to_cell_weights, input_to_output_weights, recurrent_to_input_weights,
+ recurrent_to_forget_weights, recurrent_to_cell_weights,
+ recurrent_to_output_weights, cell_to_input_weights, cell_to_forget_weights,
+ cell_to_output_weights, input_gate_bias, forget_gate_bias, cell_gate_bias,
+ output_gate_bias, projection_weights, projection_bias, output_state_in,
+ cell_state_in, input_layer_norm_weights, forget_layer_norm_weights,
+ cell_layer_norm_weights, output_layer_norm_weights, cell_clip, projection_clip,
+ input_intermediate_scale, forget_intermediate_scale, cell_intermediate_scale,
+ output_intermediate_scale, hidden_state_zero_point, hidden_state_scale).To([output_state_out,
+ cell_state_out, output])
+
+# Example 1. Input in operand 0,
+input0 = {
+ input_to_input_weights: [
+ 64, 77, 89, -102, -115, 13, 25, 38, -51, 64, -102, 89, -77, 64, -51, -64, -51, -38, -25, -13
+ ],
+ input_to_forget_weights: [
+ -77, -13, 38, 25, 115, -64, -25, -51, 38, -102, -51, 38, -64, -51, -77, 38, -51, -77, -64, -64
+ ],
+ input_to_cell_weights: [
+ -51, -38, -25, -13, -64, 64, -25, -38, -25, -77, 77, -13, -51, -38, -89, 89, -115, -64, 102, 77
+ ],
+ input_to_output_weights: [
+ -102, -51, -25, -115, -13, -89, 38, -38, -102, -25, 77, -25, 51, -89, -38, -64, 13, 64, -77, -51
+ ],
+ input_gate_bias: [644245, 3221226, 4724464, 8160438],
+ forget_gate_bias: [2147484, -6442451, -4294968, 2147484],
+ cell_gate_bias: [-1073742, 15461883, 5368709, 1717987],
+ output_gate_bias: [1073742, -214748, 4294968, 2147484],
+ recurrent_to_input_weights: [
+ -25, -38, 51, 13, -64, 115, -25, -38, -89, 6, -25, -77
+ ],
+ recurrent_to_forget_weights: [
+ -64, -38, -64, -25, 77, 51, 115, 38, -13, 25, 64, 25
+ ],
+ recurrent_to_cell_weights: [
+ -38, 25, 13, -38, 102, -10, -25, 38, 102, -77, -13, 25
+ ],
+ recurrent_to_output_weights: [
+ 38, -13, 13, -25, -64, -89, -25, -77, -13, -51, -89, -25
+ ],
+ projection_weights: [
+ -25, 51, 3, -51, 25, 127, 77, 20, 18, 51, -102, 51
+ ],
+ projection_bias: [ 0 for _ in range(output_size) ],
+ input_layer_norm_weights: [3277, 6553, 9830, 16384],
+ forget_layer_norm_weights: [6553, 6553, 13107, 9830],
+ cell_layer_norm_weights: [22937, 6553, 9830, 26214],
+ output_layer_norm_weights: [19660, 6553, 6553, 16384],
+}
+
+test_input = [90, 102, 13, 26, 38, 102, 13, 26, 51, 64]
+
+golden_output = [
+ 127, 127, -108, -67, 127, 127
+]
+
+output0 = {
+ output_state_out: golden_output,
+ cell_state_out: [-14650, 8939, 5771, 6715, -11843, 7847, 1508, 12939],
+ output: golden_output,
+}
+
+input0[input] = test_input
+input0[output_state_in] = [ 0 for _ in range(batch_size * output_size) ]
+input0[cell_state_in] = [ 0 for _ in range(batch_size * num_units) ]
+input0[cell_to_input_weights] = [0 for _ in range(num_units) ]
+input0[cell_to_forget_weights] = [0 for _ in range(num_units) ]
+input0[cell_to_output_weights] = [0 for _ in range(num_units) ]
+
+Example((input0, output0))