/* * Copyright (C) 2018 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. */ #include "NeuralNetworksWrapper.h" #include "NeuralNetworksOEM.h" #include using namespace android::nn::wrapper; namespace { static const int32_t kAvailableOperandCodes[] = { ANEURALNETWORKS_FLOAT32, ANEURALNETWORKS_INT32, ANEURALNETWORKS_UINT32, ANEURALNETWORKS_TENSOR_FLOAT32, ANEURALNETWORKS_TENSOR_INT32, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, ANEURALNETWORKS_TENSOR_OEM_BYTE }; class OperationTestBase { public: OperationTestBase(ANeuralNetworksOperationType opCode, std::vector validInputs, std::vector validOutputs) : mOpCode(opCode), mValidInputs(std::move(validInputs)), mValidOutputs(std::move(validOutputs)) {} // Add each operand separately and add the operation using these operands. // This function does not cover the cases that a operand used mutiple times. int32_t addOperation(const std::vector& inputs, const std::vector& outputs) { ANeuralNetworksModel* model = nullptr; ANeuralNetworksModel_create(&model); uint32_t opIdx = 0; std::vector inputIds; std::vector outputIds; for (uint32_t i = 0; i < inputs.size(); i++) { ANeuralNetworksModel_addOperand(model, &inputs[i]); inputIds.push_back(opIdx++); } for (uint32_t i = 0; i < outputs.size(); i++) { ANeuralNetworksModel_addOperand(model, &outputs[i]); outputIds.push_back(opIdx++); } int32_t result = ANeuralNetworksModel_addOperation(model, mOpCode, static_cast(inputIds.size()), inputIds.data(), static_cast(outputIds.size()), outputIds.data()); ANeuralNetworksModel_free(model); return result; } bool testMutatingInputOperandCode() { for (uint32_t i = 0; i < mValidInputs.size(); i++) { // LSH_PROJECTION's second argument is allowed to have any type. // This is the only operation that currently has a type that can be // anything independent from any other type. Changing the operand // type to any other type will result in a valid model for // LSH_PROJECTION. If this is the case, skip the test. if (mOpCode == ANEURALNETWORKS_LSH_PROJECTION && i == 1) { continue; } ANeuralNetworksOperandType newType = mValidInputs[i]; int32_t originalOperandCode = mValidInputs[i].type; for (int32_t newOperandCode : kAvailableOperandCodes) { if (newOperandCode == originalOperandCode) { continue; } newType.type = newOperandCode; std::vector inputs = mValidInputs; inputs[i] = newType; int32_t result = addOperation(inputs, mValidOutputs); if (ANEURALNETWORKS_NO_ERROR == result) { return false; } } } return true; } bool testMutatingOutputOperandCode() { for (uint32_t i = 0; i < mValidOutputs.size(); i++) { // LSH_PROJECTION's second argument is allowed to have any type. // This is the only operation that currently has a type that can be // anything independent from any other type. Changing the operand // type to any other type will result in a valid model for // LSH_PROJECTION. If this is the case, skip the test. if (mOpCode == ANEURALNETWORKS_LSH_PROJECTION && i == 1) { continue; } ANeuralNetworksOperandType newType = mValidOutputs[i]; int32_t originalOperandCode = mValidOutputs[i].type; for (int32_t newOperandCode : kAvailableOperandCodes) { if (newOperandCode == originalOperandCode) { continue; } newType.type = newOperandCode; std::vector outputs = mValidOutputs; outputs[i] = newType; int32_t result = addOperation(mValidInputs, outputs); if (ANEURALNETWORKS_NO_ERROR == result) { return false; } } } return true; } bool testMutatingInputOperandCounts() { std::vector inputs = mValidInputs; for (uint32_t i = 0; i < 5; i++) { inputs.push_back(inputs[0]); if (ANEURALNETWORKS_NO_ERROR == addOperation(inputs, mValidOutputs)) { return false; } } return true; } bool testMutatingOutputOperandCounts() { std::vector outputs = mValidOutputs; for (int i = 0; i < 5; i++) { outputs.push_back(outputs[0]); if (ANEURALNETWORKS_NO_ERROR == addOperation(mValidInputs, outputs)) { return false; } } return true; } private: ANeuralNetworksOperationType mOpCode; // The dimensions in the ANeuralNetworksOperandType must outlive the test object. std::vector mValidInputs; std::vector mValidOutputs; }; TEST(OperationValidationTest, DEQUANTIZE_float32) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 1.0f, .zeroPoint = 0}; ANeuralNetworksOperandType output = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; OperationTestBase dequantizeTest(ANEURALNETWORKS_DEQUANTIZE, {input}, {output}); EXPECT_TRUE(dequantizeTest.testMutatingInputOperandCode()); EXPECT_TRUE(dequantizeTest.testMutatingInputOperandCounts()); EXPECT_TRUE(dequantizeTest.testMutatingOutputOperandCode()); EXPECT_TRUE(dequantizeTest.testMutatingOutputOperandCounts()); } void simpleMathOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input1 = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input1.scale = 0.5f; } ANeuralNetworksOperandType input2 = input1; ANeuralNetworksOperandType output = input1; ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; OperationTestBase simpleMathTest(operationCode, {input1, input2, activation}, {output}); EXPECT_TRUE(simpleMathTest.testMutatingInputOperandCode()); EXPECT_TRUE(simpleMathTest.testMutatingInputOperandCounts()); EXPECT_TRUE(simpleMathTest.testMutatingOutputOperandCode()); EXPECT_TRUE(simpleMathTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, ADD_float32) { simpleMathOpTest(ANEURALNETWORKS_ADD, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, MUL_float32) { simpleMathOpTest(ANEURALNETWORKS_MUL, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, SUB_float32) { simpleMathOpTest(ANEURALNETWORKS_SUB, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, DIV_float32) { simpleMathOpTest(ANEURALNETWORKS_DIV, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, ADD_quant8) { simpleMathOpTest(ANEURALNETWORKS_ADD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, MUL_quant8) { simpleMathOpTest(ANEURALNETWORKS_MUL, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void activationOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } ANeuralNetworksOperandType output = input; OperationTestBase activationTest(operationCode, {input}, {output}); EXPECT_TRUE(activationTest.testMutatingInputOperandCode()); EXPECT_TRUE(activationTest.testMutatingInputOperandCounts()); EXPECT_TRUE(activationTest.testMutatingOutputOperandCode()); EXPECT_TRUE(activationTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, L2_NORMALIZATION_float32) { activationOpTest(ANEURALNETWORKS_L2_NORMALIZATION, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, FLOOR_float32) { activationOpTest(ANEURALNETWORKS_FLOOR, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, TANH_float32) { activationOpTest(ANEURALNETWORKS_TANH, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, RELU_float32) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, RELU1_float32) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, RELU6_float32) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, LOGISTIC_float32) { activationOpTest(ANEURALNETWORKS_LOGISTIC, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, RELU_quant8) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, RELU1_quant8) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, RELU6_quant8) { activationOpTest(ANEURALNETWORKS_RELU, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, LOGISTIC_quant8) { activationOpTest(ANEURALNETWORKS_LOGISTIC, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void softmaxOpTest(int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } ANeuralNetworksOperandType output = input; ANeuralNetworksOperandType beta = {.type = ANEURALNETWORKS_FLOAT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; OperationTestBase softmaxTest(ANEURALNETWORKS_SOFTMAX, {input, beta}, {output}); EXPECT_TRUE(softmaxTest.testMutatingInputOperandCode()); EXPECT_TRUE(softmaxTest.testMutatingInputOperandCounts()); EXPECT_TRUE(softmaxTest.testMutatingOutputOperandCode()); EXPECT_TRUE(softmaxTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, SOFTMAX_float32) { softmaxOpTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, SOFTMAX_quant8) { softmaxOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void poolingOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 4, 4, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } ANeuralNetworksOperandType output = input; ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType padLeft = scalar; ANeuralNetworksOperandType padRight = scalar; ANeuralNetworksOperandType padTop = scalar; ANeuralNetworksOperandType padBottom = scalar; ANeuralNetworksOperandType strideWidth = scalar; ANeuralNetworksOperandType strideHeight = scalar; ANeuralNetworksOperandType filterWidth = scalar; ANeuralNetworksOperandType filterHeight = scalar; ANeuralNetworksOperandType activation = scalar; OperationTestBase explicitPoolingTest(operationCode, {input, padLeft, padRight, padTop, padBottom, strideWidth, strideHeight, filterWidth, filterHeight, activation}, {output}); EXPECT_TRUE(explicitPoolingTest.testMutatingInputOperandCode()); EXPECT_TRUE(explicitPoolingTest.testMutatingInputOperandCounts()); EXPECT_TRUE(explicitPoolingTest.testMutatingOutputOperandCode()); EXPECT_TRUE(explicitPoolingTest.testMutatingOutputOperandCounts()); ANeuralNetworksOperandType padImplicit = scalar; OperationTestBase implicitPoolingTest(operationCode, {input, padImplicit, strideWidth, strideHeight, filterWidth, filterHeight, activation}, {output}); EXPECT_TRUE(implicitPoolingTest.testMutatingInputOperandCode()); EXPECT_TRUE(implicitPoolingTest.testMutatingInputOperandCounts()); EXPECT_TRUE(implicitPoolingTest.testMutatingOutputOperandCode()); EXPECT_TRUE(implicitPoolingTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, AVERAGE_POOL_2D_float32) { poolingOpTest(ANEURALNETWORKS_AVERAGE_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, MAX_POOL_2D_float32) { poolingOpTest(ANEURALNETWORKS_MAX_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, L2_POOL_2D_float32) { poolingOpTest(ANEURALNETWORKS_L2_POOL_2D, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, AVERAGE_POOL_2D_quant8) { poolingOpTest(ANEURALNETWORKS_AVERAGE_POOL_2D, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, MAX_POOL_2D_quant8) { poolingOpTest(ANEURALNETWORKS_MAX_POOL_2D, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void spaceDepthOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } ANeuralNetworksOperandType block_size = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType output = input; OperationTestBase spaceDepthTest(operationCode, {input, block_size}, {output}); EXPECT_TRUE(spaceDepthTest.testMutatingInputOperandCode()); EXPECT_TRUE(spaceDepthTest.testMutatingInputOperandCounts()); EXPECT_TRUE(spaceDepthTest.testMutatingOutputOperandCode()); EXPECT_TRUE(spaceDepthTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, SPACE_TO_DEPTH_float32) { spaceDepthOpTest(ANEURALNETWORKS_SPACE_TO_DEPTH, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, DEPTH_TO_SPACE_float32) { spaceDepthOpTest(ANEURALNETWORKS_DEPTH_TO_SPACE, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, SPACE_TO_DEPTH_quant8) { spaceDepthOpTest(ANEURALNETWORKS_SPACE_TO_DEPTH, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, DEPTH_TO_SPACE_quant8) { spaceDepthOpTest(ANEURALNETWORKS_DEPTH_TO_SPACE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void spaceBatchOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } uint32_t blockDimensions[1] = {2}; ANeuralNetworksOperandType blockShape = {.type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = blockDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType padding = blockShape; ANeuralNetworksOperandType output = input; if (operationCode == ANEURALNETWORKS_SPACE_TO_BATCH_ND) { OperationTestBase spaceBatchTest(operationCode, {input, blockShape, padding}, {output}); EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCode()); EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCounts()); EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCode()); EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCounts()); } else { OperationTestBase spaceBatchTest(operationCode, {input, blockShape}, {output}); EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCode()); EXPECT_TRUE(spaceBatchTest.testMutatingInputOperandCounts()); EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCode()); EXPECT_TRUE(spaceBatchTest.testMutatingOutputOperandCounts()); } } TEST(OperationValidationTest, SPACE_TO_BATCH_ND_float32) { spaceBatchOpTest(ANEURALNETWORKS_SPACE_TO_BATCH_ND, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, BATCH_TO_SPACE_ND_float32) { spaceBatchOpTest(ANEURALNETWORKS_BATCH_TO_SPACE_ND, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, SPACE_TO_BATCH_ND_quant8) { spaceBatchOpTest(ANEURALNETWORKS_SPACE_TO_BATCH_ND, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, BATCH_TO_SPACE_ND_quant8) { spaceBatchOpTest(ANEURALNETWORKS_BATCH_TO_SPACE_ND, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void transposeAndSqueezeOpTest(ANeuralNetworksOperationType operationCode, int32_t operandCode) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 1.f / 256; } uint32_t blockDimensions[1] = {4}; ANeuralNetworksOperandType dims = {.type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = blockDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType output = input; OperationTestBase transposeAndSqueezeTest(operationCode, {input, dims}, {output}); EXPECT_TRUE(transposeAndSqueezeTest.testMutatingInputOperandCode()); EXPECT_TRUE(transposeAndSqueezeTest.testMutatingInputOperandCounts()); EXPECT_TRUE(transposeAndSqueezeTest.testMutatingOutputOperandCode()); EXPECT_TRUE(transposeAndSqueezeTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, TRANSPOSE_float32) { transposeAndSqueezeOpTest(ANEURALNETWORKS_TRANSPOSE, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, SQUEEZE_float32) { transposeAndSqueezeOpTest(ANEURALNETWORKS_SQUEEZE, ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, TRANSPOSE_quant8) { transposeAndSqueezeOpTest(ANEURALNETWORKS_TRANSPOSE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, SQUEEZE_quant8) { transposeAndSqueezeOpTest(ANEURALNETWORKS_SQUEEZE, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void convOpTest(int32_t operandCode) { uint32_t inputDimensions[4] = {2, 4, 4, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } ANeuralNetworksOperandType filter = input; ANeuralNetworksOperandType output = input; uint32_t biasDimensions[1] = {2}; ANeuralNetworksOperandType bias = {.type = operandCode, .dimensionCount = 1, .dimensions = biasDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { bias.type = ANEURALNETWORKS_TENSOR_INT32; bias.scale = 0.25f; } ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType padLeft = scalar; ANeuralNetworksOperandType padRight = scalar; ANeuralNetworksOperandType padTop = scalar; ANeuralNetworksOperandType padBottom = scalar; ANeuralNetworksOperandType strideWidth = scalar; ANeuralNetworksOperandType strideHeight = scalar; ANeuralNetworksOperandType activation = scalar; OperationTestBase explicitConvTest(ANEURALNETWORKS_CONV_2D, {input, filter, bias, padLeft, padRight, padTop, padBottom, strideWidth, strideHeight, activation}, {output}); EXPECT_TRUE(explicitConvTest.testMutatingInputOperandCode()); EXPECT_TRUE(explicitConvTest.testMutatingInputOperandCounts()); EXPECT_TRUE(explicitConvTest.testMutatingOutputOperandCode()); EXPECT_TRUE(explicitConvTest.testMutatingOutputOperandCounts()); ANeuralNetworksOperandType padImplicit = scalar; OperationTestBase implicitConvTest(ANEURALNETWORKS_CONV_2D, {input, filter, bias, padImplicit, strideWidth, strideHeight, activation}, {output}); EXPECT_TRUE(implicitConvTest.testMutatingInputOperandCode()); EXPECT_TRUE(implicitConvTest.testMutatingInputOperandCounts()); EXPECT_TRUE(implicitConvTest.testMutatingOutputOperandCode()); EXPECT_TRUE(implicitConvTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, CONV_2D_float32) { convOpTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, CONV_2D_quant8) { convOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void depthwiseConvOpTest(int32_t operandCode) { uint32_t inputDimensions[4] = {1, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } ANeuralNetworksOperandType filter = input; ANeuralNetworksOperandType output = input; uint32_t biasDimensions[1] = {2}; ANeuralNetworksOperandType bias = {.type = operandCode, .dimensionCount = 1, .dimensions = biasDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { bias.type = ANEURALNETWORKS_TENSOR_INT32; bias.scale = 0.25f; } ANeuralNetworksOperandType scalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType padLeft = scalar; ANeuralNetworksOperandType padRight = scalar; ANeuralNetworksOperandType padTop = scalar; ANeuralNetworksOperandType padBottom = scalar; ANeuralNetworksOperandType strideWidth = scalar; ANeuralNetworksOperandType strideHeight = scalar; ANeuralNetworksOperandType multiplier = scalar; ANeuralNetworksOperandType activation = scalar; OperationTestBase explicitDepthwiseConvTest(ANEURALNETWORKS_DEPTHWISE_CONV_2D, {input, filter, bias, padLeft, padRight, padTop, padBottom, strideWidth, strideHeight, multiplier, activation}, {output}); EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingInputOperandCode()); EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingInputOperandCounts()); EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingOutputOperandCode()); EXPECT_TRUE(explicitDepthwiseConvTest.testMutatingOutputOperandCounts()); ANeuralNetworksOperandType padImplicit = scalar; OperationTestBase implicitDepthwiseConvTest(ANEURALNETWORKS_DEPTHWISE_CONV_2D, {input, filter, bias, padImplicit, strideWidth, strideHeight, multiplier, activation}, {output}); EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingInputOperandCode()); EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingInputOperandCounts()); EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingOutputOperandCode()); EXPECT_TRUE(implicitDepthwiseConvTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, DEPTHWISE_CONV_2D_float32) { depthwiseConvOpTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, DEPTHWISE_CONV_2D_quant8) { depthwiseConvOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void fullyConnectedOpTest(int32_t operandCode) { uint32_t inputDimensions[2] = {5, 5}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 2, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } ANeuralNetworksOperandType weights = input; ANeuralNetworksOperandType output = input; uint32_t biasDimensions[1] = {5}; ANeuralNetworksOperandType bias = {.type = operandCode, .dimensionCount = 1, .dimensions = biasDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { bias.type = ANEURALNETWORKS_TENSOR_INT32; bias.scale = 0.25f; } ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; OperationTestBase fullyConnectedTest(ANEURALNETWORKS_FULLY_CONNECTED, {input, weights, bias, activation}, {output}); EXPECT_TRUE(fullyConnectedTest.testMutatingInputOperandCode()); EXPECT_TRUE(fullyConnectedTest.testMutatingInputOperandCounts()); EXPECT_TRUE(fullyConnectedTest.testMutatingOutputOperandCode()); EXPECT_TRUE(fullyConnectedTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, FULLY_CONNECTED_float32) { fullyConnectedOpTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, FULLY_CONNECTED_quant8) { fullyConnectedOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void concatenationTest(int32_t operandCode) { uint32_t inputDimensions[2] = {5, 5}; ANeuralNetworksOperandType input1 = {.type = operandCode, .dimensionCount = 2, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input1.scale = 0.5f; } ANeuralNetworksOperandType input2 = input1; ANeuralNetworksOperandType output = input1; ANeuralNetworksOperandType activation = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; OperationTestBase concat2Test(ANEURALNETWORKS_CONCATENATION, {input1, input2, activation}, {output}); EXPECT_TRUE(concat2Test.testMutatingInputOperandCode()); EXPECT_TRUE(concat2Test.testMutatingOutputOperandCode()); EXPECT_TRUE(concat2Test.testMutatingOutputOperandCounts()); OperationTestBase concat1Test(ANEURALNETWORKS_CONCATENATION, {input1, activation}, {output}); EXPECT_TRUE(concat1Test.testMutatingInputOperandCode()); EXPECT_TRUE(concat1Test.testMutatingOutputOperandCode()); EXPECT_TRUE(concat1Test.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, CONCATENATION_float32) { concatenationTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, CONCATENATION_quant8) { concatenationTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, RESIZE_BILINEAR_float32) { uint32_t inputDimensions[4] = {2, 2, 2, 2}; ANeuralNetworksOperandType input = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 4, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType height = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType width = height; ANeuralNetworksOperandType output = input; OperationTestBase resizeTest(ANEURALNETWORKS_RESIZE_BILINEAR, {input, height, width}, {output}); EXPECT_TRUE(resizeTest.testMutatingInputOperandCode()); EXPECT_TRUE(resizeTest.testMutatingInputOperandCounts()); EXPECT_TRUE(resizeTest.testMutatingOutputOperandCode()); EXPECT_TRUE(resizeTest.testMutatingOutputOperandCounts()); } void embeddingLookupTest(int32_t operandCode) { uint32_t lookupDimensions[1] = {5}; ANeuralNetworksOperandType lookup = {.type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = lookupDimensions, .scale = 0.0f, .zeroPoint = 0}; uint32_t inputDimensions[2] = {5, 5}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 2, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } ANeuralNetworksOperandType output = input; OperationTestBase embedLookupTest(ANEURALNETWORKS_EMBEDDING_LOOKUP, {lookup, input}, {output}); EXPECT_TRUE(embedLookupTest.testMutatingInputOperandCode()); EXPECT_TRUE(embedLookupTest.testMutatingInputOperandCounts()); EXPECT_TRUE(embedLookupTest.testMutatingOutputOperandCode()); EXPECT_TRUE(embedLookupTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, EMBEDDING_LOOKUP_float32) { embeddingLookupTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, EMBEDDING_LOOKUP_quant8) { embeddingLookupTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void hashtableLookupTest(int32_t operandCode) { uint32_t lookupDimensions[1] = {5}; ANeuralNetworksOperandType lookup = {.type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = lookupDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType keys = lookup; uint32_t valuesDimensions[2] = {5, 5}; ANeuralNetworksOperandType values = {.type = operandCode, .dimensionCount = 2, .dimensions = valuesDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { values.scale = 0.5f; } ANeuralNetworksOperandType output = values; ANeuralNetworksOperandType hits = lookup; hits.type = ANEURALNETWORKS_TENSOR_QUANT8_ASYMM; hits.scale = 1.0f; OperationTestBase hashLookupTest(ANEURALNETWORKS_HASHTABLE_LOOKUP, {lookup, keys, values}, {output, hits}); EXPECT_TRUE(hashLookupTest.testMutatingInputOperandCode()); EXPECT_TRUE(hashLookupTest.testMutatingInputOperandCounts()); EXPECT_TRUE(hashLookupTest.testMutatingOutputOperandCode()); EXPECT_TRUE(hashLookupTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, HASHTABLE_LOOKUP_float32) { hashtableLookupTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, HASHTABLE_LOOKUP_quant8) { hashtableLookupTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } void lshProjectionTest(int32_t operandCode) { uint32_t inputDimensions[2] = {5, 5}; ANeuralNetworksOperandType hash = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 2, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType input = hash; input.type = operandCode; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } uint32_t weightDimensions[1] = {5}; ANeuralNetworksOperandType weight = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = weightDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType type = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType output = weight; output.type = ANEURALNETWORKS_TENSOR_INT32; OperationTestBase lshProjTest(ANEURALNETWORKS_LSH_PROJECTION, {hash, input, weight, type}, {output}); EXPECT_TRUE(lshProjTest.testMutatingInputOperandCode()); EXPECT_TRUE(lshProjTest.testMutatingInputOperandCounts()); EXPECT_TRUE(lshProjTest.testMutatingOutputOperandCode()); EXPECT_TRUE(lshProjTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, LSH_PROJECTION_float32) { lshProjectionTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, LSH_PROJECTION_quant8) { lshProjectionTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } TEST(OperationValidationTest, LSTM_float32) { uint32_t oneDimensional[1] = {5}; uint32_t twoDimensional[2] = {5, 5}; ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = oneDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 2, .dimensions = twoDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType floatScalar = {.type = ANEURALNETWORKS_FLOAT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType input = floatTensor2D; ANeuralNetworksOperandType inputToInput = floatTensor2D; ANeuralNetworksOperandType inputToForget = floatTensor2D; ANeuralNetworksOperandType inputToCell = floatTensor2D; ANeuralNetworksOperandType inputToOutput = floatTensor2D; ANeuralNetworksOperandType recurrentToInput = floatTensor2D; ANeuralNetworksOperandType recurrentToForget = floatTensor2D; ANeuralNetworksOperandType recurrentToCell = floatTensor2D; ANeuralNetworksOperandType recurrentToOutput = floatTensor2D; ANeuralNetworksOperandType cellToInput = floatTensor1D; ANeuralNetworksOperandType cellToForget = floatTensor1D; ANeuralNetworksOperandType cellToOutput = floatTensor1D; ANeuralNetworksOperandType inputGateBias = floatTensor1D; ANeuralNetworksOperandType forgetGateBias = floatTensor1D; ANeuralNetworksOperandType cellBias = floatTensor1D; ANeuralNetworksOperandType outputGateBias = floatTensor1D; ANeuralNetworksOperandType projWeights = floatTensor2D; ANeuralNetworksOperandType projBias = floatTensor1D; ANeuralNetworksOperandType outputStateIn = floatTensor2D; ANeuralNetworksOperandType cellStateIn = floatTensor2D; ANeuralNetworksOperandType activation = intScalar; ANeuralNetworksOperandType clipCellState = floatScalar; ANeuralNetworksOperandType clipProjLayer = floatScalar; ANeuralNetworksOperandType scratch = floatTensor2D; ANeuralNetworksOperandType outputStateOut = floatTensor2D; ANeuralNetworksOperandType cellStateOut = floatTensor2D; ANeuralNetworksOperandType output = floatTensor2D; OperationTestBase lstmTest(ANEURALNETWORKS_LSTM, {input, inputToInput, inputToForget, inputToCell, inputToOutput, recurrentToInput, recurrentToForget, recurrentToCell, recurrentToOutput, cellToInput, cellToForget, cellToOutput, inputGateBias, forgetGateBias, cellBias, outputGateBias, projWeights, projBias, outputStateIn, cellStateIn, activation, clipCellState, clipProjLayer}, {scratch, outputStateOut, cellStateOut, output}); EXPECT_TRUE(lstmTest.testMutatingInputOperandCode()); EXPECT_TRUE(lstmTest.testMutatingInputOperandCounts()); EXPECT_TRUE(lstmTest.testMutatingOutputOperandCode()); EXPECT_TRUE(lstmTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, RNN_float32) { uint32_t oneDimensional[1] = {5}; uint32_t twoDimensional[2] = {5, 5}; ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = oneDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 2, .dimensions = twoDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType input = floatTensor2D; ANeuralNetworksOperandType weights = floatTensor2D; ANeuralNetworksOperandType recurrentWeights = floatTensor2D; ANeuralNetworksOperandType bias = floatTensor1D; ANeuralNetworksOperandType hiddenStateIn = floatTensor2D; ANeuralNetworksOperandType activation = intScalar; ANeuralNetworksOperandType hiddenStateOut = floatTensor2D; ANeuralNetworksOperandType output = floatTensor2D; OperationTestBase rnnTest(ANEURALNETWORKS_RNN, {input, weights, recurrentWeights, bias, hiddenStateIn, activation}, {hiddenStateOut, output}); EXPECT_TRUE(rnnTest.testMutatingInputOperandCode()); EXPECT_TRUE(rnnTest.testMutatingInputOperandCounts()); EXPECT_TRUE(rnnTest.testMutatingOutputOperandCode()); EXPECT_TRUE(rnnTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, SVDF_float32) { uint32_t oneDimensional[1] = {5}; uint32_t twoDimensional[2] = {5, 5}; ANeuralNetworksOperandType floatTensor1D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 1, .dimensions = oneDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType floatTensor2D = {.type = ANEURALNETWORKS_TENSOR_FLOAT32, .dimensionCount = 2, .dimensions = twoDimensional, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType intScalar = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType input = floatTensor2D; ANeuralNetworksOperandType weightsFeature = floatTensor2D; ANeuralNetworksOperandType weightsTime = floatTensor2D; ANeuralNetworksOperandType bias = floatTensor1D; ANeuralNetworksOperandType stateIn = floatTensor2D; ANeuralNetworksOperandType rank = intScalar; ANeuralNetworksOperandType activation = intScalar; ANeuralNetworksOperandType stateOut = floatTensor2D; ANeuralNetworksOperandType output = floatTensor2D; OperationTestBase svdfTest(ANEURALNETWORKS_SVDF, {input, weightsFeature, weightsTime, bias, stateIn, rank, activation}, {stateOut, output}); EXPECT_TRUE(svdfTest.testMutatingInputOperandCode()); EXPECT_TRUE(svdfTest.testMutatingInputOperandCounts()); EXPECT_TRUE(svdfTest.testMutatingOutputOperandCode()); EXPECT_TRUE(svdfTest.testMutatingOutputOperandCounts()); } void stridedSliceOpTest(int32_t operandCode) { uint32_t inputDimensions[2] = {5, 5}; ANeuralNetworksOperandType input = {.type = operandCode, .dimensionCount = 2, .dimensions = inputDimensions, .scale = 0.0f, .zeroPoint = 0}; if (operandCode == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { input.scale = 0.5f; } ANeuralNetworksOperandType output = input; uint32_t beginsDimensions[1] = {2}; ANeuralNetworksOperandType begins = {.type = ANEURALNETWORKS_TENSOR_INT32, .dimensionCount = 1, .dimensions = beginsDimensions, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType ends = begins; ANeuralNetworksOperandType strides = begins; ANeuralNetworksOperandType beginMask = {.type = ANEURALNETWORKS_INT32, .dimensionCount = 0, .dimensions = nullptr, .scale = 0.0f, .zeroPoint = 0}; ANeuralNetworksOperandType endMask = beginMask; ANeuralNetworksOperandType shrinkAxisMask = beginMask; OperationTestBase stridedSliceTest(ANEURALNETWORKS_STRIDED_SLICE, {input, begins, ends, strides, beginMask, endMask, shrinkAxisMask}, {output}); EXPECT_TRUE(stridedSliceTest.testMutatingInputOperandCode()); EXPECT_TRUE(stridedSliceTest.testMutatingInputOperandCounts()); EXPECT_TRUE(stridedSliceTest.testMutatingOutputOperandCode()); EXPECT_TRUE(stridedSliceTest.testMutatingOutputOperandCounts()); } TEST(OperationValidationTest, STRIDED_SLICE_float32) { stridedSliceOpTest(ANEURALNETWORKS_TENSOR_FLOAT32); } TEST(OperationValidationTest, STRIDED_SLICE_quant8) { stridedSliceOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM); } } // end namespace