/* * 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. */ #include "HalInterfaces.h" #include "Manager.h" #include "NeuralNetworks.h" #include "NeuralNetworksExtensions.h" #include "NeuralNetworksWrapperExtensions.h" #include "TestNeuralNetworksWrapper.h" #include "TypeManager.h" #include "Utils.h" #include "ValidateHal.h" #include #include "FibonacciDriver.h" #include "FibonacciExtension.h" namespace android { namespace nn { namespace { using ::android::nn::test_wrapper::ExtensionModel; using ::android::nn::test_wrapper::ExtensionOperandParams; using ::android::nn::test_wrapper::ExtensionOperandType; using ::android::nn::test_wrapper::Type; class FibonacciExtensionTest : public ::testing::Test { protected: virtual void SetUp() { if (DeviceManager::get()->getUseCpuOnly()) { // This test requires the use a custom driver. GTEST_SKIP(); } // Real world extension tests should run against actual hardware // implementations, but there is no hardware supporting the test // extension. Hence the sample software driver. DeviceManager::get()->forTest_registerDevice(sample_driver::FibonacciDriver::kDriverName, new sample_driver::FibonacciDriver()); // Discover extensions provided by registered devices. TypeManager::get()->forTest_reset(); uint32_t numDevices = 0; ASSERT_EQ(ANeuralNetworks_getDeviceCount(&numDevices), ANEURALNETWORKS_NO_ERROR); ANeuralNetworksDevice* fibonacciDevice = nullptr; ANeuralNetworksDevice* cpuDevice = nullptr; for (uint32_t i = 0; i < numDevices; i++) { ANeuralNetworksDevice* device = nullptr; EXPECT_EQ(ANeuralNetworks_getDevice(i, &device), ANEURALNETWORKS_NO_ERROR); bool supportsFibonacciExtension; ASSERT_EQ(ANeuralNetworksDevice_getExtensionSupport( device, TEST_VENDOR_FIBONACCI_EXTENSION_NAME, &supportsFibonacciExtension), ANEURALNETWORKS_NO_ERROR); if (supportsFibonacciExtension) { ASSERT_EQ(fibonacciDevice, nullptr) << "Found multiple Fibonacci drivers"; fibonacciDevice = device; } else if (DeviceManager::get()->forTest_isCpuDevice(device)) { ASSERT_EQ(cpuDevice, nullptr) << "Found multiple CPU drivers"; cpuDevice = device; } } ASSERT_NE(fibonacciDevice, nullptr) << "Expecting Fibonacci driver to be available"; ASSERT_NE(cpuDevice, nullptr) << "Expecting CPU driver to be available"; mDevices = {fibonacciDevice, cpuDevice}; } virtual void TearDown() { if (mExecution) { ANeuralNetworksExecution_free(mExecution); } if (mCompilation) { ANeuralNetworksCompilation_free(mCompilation); } DeviceManager::get()->forTest_reInitializeDeviceList(); TypeManager::get()->forTest_reset(); } void checkSupportedOperations(const std::vector& expected) { const uint32_t kMaxNumberOperations = 256; EXPECT_LE(expected.size(), kMaxNumberOperations); bool supported[kMaxNumberOperations] = {false}; EXPECT_EQ(ANeuralNetworksModel_getSupportedOperationsForDevices( mModel.getHandle(), mDevices.data(), mDevices.size(), supported), ANEURALNETWORKS_NO_ERROR); for (size_t i = 0; i < expected.size(); ++i) { SCOPED_TRACE(::testing::Message() << "i = " << i); EXPECT_EQ(supported[i], expected[i]); } } void prepareForExecution() { ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksExecution_create(mCompilation, &mExecution), ANEURALNETWORKS_NO_ERROR); } std::vector mDevices; ANeuralNetworksExecution* mExecution = nullptr; ANeuralNetworksCompilation* mCompilation = nullptr; ExtensionModel mModel; }; void addNopOperation(ExtensionModel* model, ExtensionOperandType inputType, uint32_t input, uint32_t output) { // Our NOP operation is ADD, which has no extension type support. ASSERT_EQ(inputType.operandType.type, ANEURALNETWORKS_TENSOR_FLOAT32); ASSERT_EQ(inputType.dimensions.size(), 1u); uint32_t inputZeros = model->addOperand(&inputType); uint32_t inputSize = inputType.dimensions[0]; uint32_t inputLength = sizeof(float) * inputSize; const float kZeros[100] = {}; ASSERT_GE(sizeof(kZeros), inputLength); model->setOperandValue(inputZeros, &kZeros, inputLength); ExtensionOperandType scalarType(Type::INT32, {}); uint32_t activation = model->addOperand(&scalarType); int32_t kNoActivation = ANEURALNETWORKS_FUSED_NONE; model->setOperandValue(activation, &kNoActivation, sizeof(kNoActivation)); model->addOperation(ANEURALNETWORKS_ADD, {input, inputZeros, activation}, {output}); } void createModel(ExtensionModel* model, ExtensionOperandType inputType, ExtensionOperandType outputType, bool addNopOperations) { uint32_t fibonacciInput = model->addOperand(&inputType); uint32_t fibonacciOutput = model->addOperand(&outputType); uint32_t modelInput = addNopOperations ? model->addOperand(&inputType) : fibonacciInput; uint32_t modelOutput = addNopOperations ? model->addOperand(&outputType) : fibonacciOutput; if (addNopOperations) { addNopOperation(model, inputType, modelInput, fibonacciInput); } model->addOperation(model->getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_FIBONACCI), {fibonacciInput}, {fibonacciOutput}); if (addNopOperations) { addNopOperation(model, outputType, fibonacciOutput, modelOutput); } model->identifyInputsAndOutputs({modelInput}, {modelOutput}); model->finish(); ASSERT_TRUE(model->isValid()); } TEST_F(FibonacciExtensionTest, ModelWithExtensionOperandTypes) { constexpr uint32_t N = 10; constexpr double scale = 0.5; constexpr int64_t zeroPoint = 10; ExtensionOperandType inputType( static_cast(mModel.getExtensionOperandType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_INT64)), {}); ExtensionOperandType outputType( static_cast(mModel.getExtensionOperandType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_TENSOR_QUANT64_ASYMM)), {N}, ExtensionOperandParams(TestVendorQuant64AsymmParams{ .scale = scale, .zeroPoint = zeroPoint, })); createModel(&mModel, inputType, outputType, /*addNopOperations=*/false); checkSupportedOperations({true}); prepareForExecution(); int64_t input = N; EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)), ANEURALNETWORKS_NO_ERROR); int64_t output[N] = {}; EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_NO_ERROR); EXPECT_EQ(output[0], 1 / scale + zeroPoint); EXPECT_EQ(output[1], 1 / scale + zeroPoint); EXPECT_EQ(output[2], 2 / scale + zeroPoint); EXPECT_EQ(output[3], 3 / scale + zeroPoint); EXPECT_EQ(output[4], 5 / scale + zeroPoint); EXPECT_EQ(output[5], 8 / scale + zeroPoint); EXPECT_EQ(output[6], 13 / scale + zeroPoint); EXPECT_EQ(output[7], 21 / scale + zeroPoint); EXPECT_EQ(output[8], 34 / scale + zeroPoint); EXPECT_EQ(output[9], 55 / scale + zeroPoint); } TEST_F(FibonacciExtensionTest, ModelWithTemporaries) { constexpr uint32_t N = 10; ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {N}); createModel(&mModel, inputType, outputType, /*addNopOperations=*/true); checkSupportedOperations({true, true, true}); prepareForExecution(); float input[] = {N}; EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)), ANEURALNETWORKS_NO_ERROR); float output[N] = {}; EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_NO_ERROR); EXPECT_EQ(output[0], 1); EXPECT_EQ(output[1], 1); EXPECT_EQ(output[2], 2); EXPECT_EQ(output[3], 3); EXPECT_EQ(output[4], 5); EXPECT_EQ(output[5], 8); EXPECT_EQ(output[6], 13); EXPECT_EQ(output[7], 21); EXPECT_EQ(output[8], 34); EXPECT_EQ(output[9], 55); } TEST_F(FibonacciExtensionTest, InvalidInputType) { ExtensionOperandType inputType(Type::TENSOR_INT32, {1}); // Unsupported type. ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1}); createModel(&mModel, inputType, outputType, /*addNopOperations=*/false); checkSupportedOperations({false}); // The driver reports that it doesn't support the operation. ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA); } TEST_F(FibonacciExtensionTest, InvalidOutputType) { ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_INT32, {1}); // Unsupported type. createModel(&mModel, inputType, outputType, /*addNopOperations=*/false); checkSupportedOperations({false}); // The driver reports that it doesn't support the operation. ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA); } TEST_F(FibonacciExtensionTest, InvalidInputValue) { ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1}); createModel(&mModel, inputType, outputType, /*addNopOperations=*/false); checkSupportedOperations({true}); prepareForExecution(); float input[] = {-1}; // Invalid input value. EXPECT_EQ(ANeuralNetworksExecution_setInput(mExecution, 0, nullptr, &input, sizeof(input)), ANEURALNETWORKS_NO_ERROR); float output[1] = {}; EXPECT_EQ(ANeuralNetworksExecution_setOutput(mExecution, 0, nullptr, &output, sizeof(output)), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksExecution_compute(mExecution), ANEURALNETWORKS_OP_FAILED); } TEST_F(FibonacciExtensionTest, InvalidNumInputs) { ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1}); uint32_t input1 = mModel.addOperand(&inputType); uint32_t input2 = mModel.addOperand(&inputType); // Extra input. uint32_t output = mModel.addOperand(&outputType); mModel.addOperation(mModel.getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_FIBONACCI), {input1, input2}, {output}); mModel.identifyInputsAndOutputs({input1, input2}, {output}); mModel.finish(); ASSERT_TRUE(mModel.isValid()); checkSupportedOperations({false}); ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA); } TEST_F(FibonacciExtensionTest, InvalidNumOutputs) { ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1}); uint32_t input = mModel.addOperand(&inputType); uint32_t output1 = mModel.addOperand(&outputType); uint32_t output2 = mModel.addOperand(&outputType); // Extra output. mModel.addOperation(mModel.getExtensionOperationType(TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_FIBONACCI), {input}, {output1, output2}); mModel.identifyInputsAndOutputs({input}, {output1, output2}); mModel.finish(); ASSERT_TRUE(mModel.isValid()); checkSupportedOperations({false}); ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA); } TEST_F(FibonacciExtensionTest, InvalidOperation) { ExtensionOperandType inputType(Type::TENSOR_FLOAT32, {1}); ExtensionOperandType outputType(Type::TENSOR_FLOAT32, {1}); uint32_t input = mModel.addOperand(&inputType); uint32_t output = mModel.addOperand(&outputType); mModel.addOperation(mModel.getExtensionOperationType( TEST_VENDOR_FIBONACCI_EXTENSION_NAME, TEST_VENDOR_FIBONACCI + 1), // This operation should not exist. {input}, {output}); mModel.identifyInputsAndOutputs({input}, {output}); mModel.finish(); ASSERT_TRUE(mModel.isValid()); checkSupportedOperations({false}); ASSERT_EQ(ANeuralNetworksCompilation_createForDevices(mModel.getHandle(), mDevices.data(), mDevices.size(), &mCompilation), ANEURALNETWORKS_NO_ERROR); ASSERT_EQ(ANeuralNetworksCompilation_finish(mCompilation), ANEURALNETWORKS_BAD_DATA); } } // namespace } // namespace nn } // namespace android