// // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #include "NeonWorkloadFactoryHelper.hpp" #include #include #include #include #include #include #include BOOST_AUTO_TEST_SUITE(CreateWorkloadNeon) namespace { boost::test_tools::predicate_result CompareIAclTensorHandleShape(IAclTensorHandle* tensorHandle, std::initializer_list expectedDimensions) { return CompareTensorHandleShape(tensorHandle, expectedDimensions); } bool TestNeonTensorHandleInfo(armnn::IAclTensorHandle* handle, const armnn::TensorInfo& expectedInfo) { using namespace armnn::armcomputetensorutils; const arm_compute::ITensorInfo* handleInfo = handle->GetTensor().info(); const arm_compute::TensorInfo expectedAclInfo = BuildArmComputeTensorInfo(expectedInfo); if (handleInfo->data_type() != expectedAclInfo.data_type()) { return false; } if (handleInfo->num_dimensions() != expectedAclInfo.num_dimensions()) { return false; } if (handleInfo->quantization_info() != expectedAclInfo.quantization_info()) { return false; } for (std::size_t d = 0; d < expectedAclInfo.num_dimensions(); ++d) { if (handleInfo->dimension(d) != expectedAclInfo.dimension(d)) { return false; } } return true; } } // namespace template static void NeonCreateActivationWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateActivationWorkloadTest(factory, graph); // Checks that inputs/outputs are as we expect them (see definition of CreateActivationWorkloadTest). ActivationQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({1, 1}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({1, 1}, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateActivationFloat16Workload) { NeonCreateActivationWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateActivationFloatWorkload) { NeonCreateActivationWorkloadTest(); } template static void NeonCreateElementwiseWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateElementwiseWorkloadTest(factory, graph); DescriptorType queueDescriptor = workload->GetData(); auto inputHandle1 = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto inputHandle2 = boost::polymorphic_downcast(queueDescriptor.m_Inputs[1]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle1, TensorInfo({2, 3}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle2, TensorInfo({2, 3}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({2, 3}, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateAdditionFloat16Workload) { NeonCreateElementwiseWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateAdditionFloatWorkload) { NeonCreateElementwiseWorkloadTest(); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateSubtractionFloat16Workload) { NeonCreateElementwiseWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateSubtractionFloatWorkload) { NeonCreateElementwiseWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateSubtractionUint8Workload) { NeonCreateElementwiseWorkloadTest(); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateMultiplicationFloat16Workload) { NeonCreateElementwiseWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateMultiplicationFloatWorkload) { NeonCreateElementwiseWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateMultiplicationUint8Workload) { NeonCreateElementwiseWorkloadTest(); } template static void NeonCreateBatchNormalizationWorkloadTest(DataLayout dataLayout) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateBatchNormalizationWorkloadTest (factory, graph, dataLayout); // Checks that outputs and inputs are as we expect them (see definition of CreateBatchNormalizationWorkloadTest). BatchNormalizationQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 4, 4} : TensorShape{2, 4, 4, 3}; TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 4, 4} : TensorShape{2, 4, 4, 3}; BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateBatchNormalizationFloat16NchwWorkload) { NeonCreateBatchNormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateBatchNormalizationFloat16NhwcWorkload) { NeonCreateBatchNormalizationWorkloadTest(DataLayout::NHWC); } #endif BOOST_AUTO_TEST_CASE(CreateBatchNormalizationFloatNchwWorkload) { NeonCreateBatchNormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateBatchNormalizationFloatNhwcWorkload) { NeonCreateBatchNormalizationWorkloadTest(DataLayout::NHWC); } template static void NeonCreateConvolution2dWorkloadTest(DataLayout dataLayout = DataLayout::NCHW) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateConvolution2dWorkloadTest(factory, graph, dataLayout); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3}; TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2}; // Checks that outputs and inputs are as we expect them (see definition of CreateConvolution2dWorkloadTest). Convolution2dQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateConvolution2dFloat16NchwWorkload) { NeonCreateConvolution2dWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateConvolution2dFloat16NhwcWorkload) { NeonCreateConvolution2dWorkloadTest(DataLayout::NHWC); } #endif BOOST_AUTO_TEST_CASE(CreateConvolution2dFloatNchwWorkload) { NeonCreateConvolution2dWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateConvolution2dFloatNhwcWorkload) { NeonCreateConvolution2dWorkloadTest(DataLayout::NHWC); } template static void NeonCreateDepthWiseConvolutionWorkloadTest(DataLayout dataLayout) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateDepthwiseConvolution2dWorkloadTest(factory, graph, dataLayout); // Checks that inputs/outputs are as we expect them (see definition of CreateNormalizationWorkloadTest). DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? std::initializer_list({ 2, 2, 5, 5 }) : std::initializer_list({ 2, 5, 5, 2 }); TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? std::initializer_list({ 2, 2, 5, 5 }) : std::initializer_list({ 2, 5, 5, 2 }); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } BOOST_AUTO_TEST_CASE(CreateDepthWiseConvolution2dFloat32NhwcWorkload) { NeonCreateDepthWiseConvolutionWorkloadTest(DataLayout::NHWC); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateDepthWiseConvolution2dFloat16NhwcWorkload) { NeonCreateDepthWiseConvolutionWorkloadTest(DataLayout::NHWC); } #endif template static void NeonCreateFullyConnectedWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateFullyConnectedWorkloadTest(factory, graph); // Checks that outputs and inputs are as we expect them (see definition of CreateFullyConnectedWorkloadTest). FullyConnectedQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({3, 1, 4, 5}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({3, 7}, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateFullyConnectedFloat16Workload) { NeonCreateFullyConnectedWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateFullyConnectedFloatWorkload) { NeonCreateFullyConnectedWorkloadTest(); } template static void NeonCreateNormalizationWorkloadTest(DataLayout dataLayout) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateNormalizationWorkloadTest(factory, graph, dataLayout); // Checks that outputs and inputs are as we expect them (see definition of CreateNormalizationWorkloadTest). NormalizationQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 5, 5, 1} : TensorShape{3, 1, 5, 5}; TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 5, 5, 1} : TensorShape{3, 1, 5, 5}; BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateNormalizationFloat16NchwWorkload) { NeonCreateNormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateNormalizationFloat16NhwcWorkload) { NeonCreateNormalizationWorkloadTest(DataLayout::NHWC); } #endif BOOST_AUTO_TEST_CASE(CreateNormalizationFloatNchwWorkload) { NeonCreateNormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateNormalizationFloatNhwcWorkload) { NeonCreateNormalizationWorkloadTest(DataLayout::NHWC); } template static void NeonCreatePooling2dWorkloadTest(DataLayout dataLayout = DataLayout::NCHW) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreatePooling2dWorkloadTest(factory, graph, dataLayout); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2}; TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2}; // Checks that outputs and inputs are as we expect them (see definition of CreatePooling2dWorkloadTest). Pooling2dQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreatePooling2dFloat16Workload) { NeonCreatePooling2dWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreatePooling2dFloatNchwWorkload) { NeonCreatePooling2dWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreatePooling2dFloatNhwcWorkload) { NeonCreatePooling2dWorkloadTest(DataLayout::NHWC); } BOOST_AUTO_TEST_CASE(CreatePooling2dUint8NchwWorkload) { NeonCreatePooling2dWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreatePooling2dUint8NhwcWorkload) { NeonCreatePooling2dWorkloadTest(DataLayout::NHWC); } static void NeonCreatePreluWorkloadTest(const armnn::TensorShape& inputShape, const armnn::TensorShape& alphaShape, const armnn::TensorShape& outputShape, armnn::DataType dataType) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreatePreluWorkloadTest(factory, graph, inputShape, alphaShape, outputShape, dataType); // Checks that outputs and inputs are as we expect them (see definition of CreateReshapeWorkloadTest). PreluQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto alphaHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[1]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, dataType))); BOOST_TEST(TestNeonTensorHandleInfo(alphaHandle, TensorInfo(alphaShape, dataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, dataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreatePreluFloat16Workload) { NeonCreatePreluWorkloadTest({ 1, 4, 1, 2 }, { 5, 4, 3, 1 }, { 5, 4, 3, 2 }, DataType::Float16); } #endif BOOST_AUTO_TEST_CASE(CreatePreluFloatWorkload) { NeonCreatePreluWorkloadTest({ 1, 4, 1, 2 }, { 5, 4, 3, 1 }, { 5, 4, 3, 2 }, DataType::Float32); } BOOST_AUTO_TEST_CASE(CreatePreluUint8Workload) { NeonCreatePreluWorkloadTest({ 1, 4, 1, 2 }, { 5, 4, 3, 1 }, { 5, 4, 3, 2 }, DataType::QuantisedAsymm8); } template static void NeonCreateReshapeWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateReshapeWorkloadTest(factory, graph); // Checks that outputs and inputs are as we expect them (see definition of CreateReshapeWorkloadTest). ReshapeQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({4, 1}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({1, 4}, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateReshapeFloat16Workload) { NeonCreateReshapeWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateReshapeFloatWorkload) { NeonCreateReshapeWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateReshapeUint8Workload) { NeonCreateReshapeWorkloadTest(); } template static void NeonCreateResizeWorkloadTest(DataLayout dataLayout) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateResizeBilinearWorkloadTest(factory, graph, dataLayout); auto queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); switch (dataLayout) { case DataLayout::NHWC: BOOST_TEST(CompareIAclTensorHandleShape(inputHandle, { 2, 4, 4, 3 })); BOOST_TEST(CompareIAclTensorHandleShape(outputHandle, { 2, 2, 2, 3 })); break; case DataLayout::NCHW: default: BOOST_TEST(CompareIAclTensorHandleShape(inputHandle, { 2, 3, 4, 4 })); BOOST_TEST(CompareIAclTensorHandleShape(outputHandle, { 2, 3, 2, 2 })); } } BOOST_AUTO_TEST_CASE(CreateResizeFloat32NchwWorkload) { NeonCreateResizeWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateResizeUint8NchwWorkload) { NeonCreateResizeWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateResizeFloat32NhwcWorkload) { NeonCreateResizeWorkloadTest(DataLayout::NHWC); } BOOST_AUTO_TEST_CASE(CreateResizeUint8NhwcWorkload) { NeonCreateResizeWorkloadTest(DataLayout::NHWC); } template static void NeonCreateSoftmaxWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateSoftmaxWorkloadTest(factory, graph); // Checks that outputs and inputs are as we expect them (see definition of CreateSoftmaxWorkloadTest). SoftmaxQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({4, 1}, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({4, 1}, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateSoftmaxFloat16Workload) { NeonCreateSoftmaxWorkloadTest(); } #endif BOOST_AUTO_TEST_CASE(CreateSoftmaxFloatWorkload) { NeonCreateSoftmaxWorkloadTest(); } template static void NeonSpaceToDepthWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateSpaceToDepthWorkloadTest(factory, graph); SpaceToDepthQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({ 1, 2, 2, 1 }, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({ 1, 1, 1, 4 }, DataType))); } BOOST_AUTO_TEST_CASE(CreateSpaceToDepthFloat32Workload) { NeonSpaceToDepthWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateSpaceToDepthFloat16Workload) { NeonSpaceToDepthWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateSpaceToDepthQAsymm8Workload) { NeonSpaceToDepthWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateSpaceToDepthQSymm16Workload) { NeonSpaceToDepthWorkloadTest(); } BOOST_AUTO_TEST_CASE(CreateSplitterWorkload) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateSplitterWorkloadTest(factory, graph); // Checks that outputs are as we expect them (see definition of CreateSplitterWorkloadTest). SplitterQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({5, 7, 7}, DataType::Float32))); auto outputHandle0 = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle0, TensorInfo({1, 7, 7}, DataType::Float32))); auto outputHandle1 = boost::polymorphic_downcast(queueDescriptor.m_Outputs[1]); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle1, TensorInfo({2, 7, 7}, DataType::Float32))); auto outputHandle2 = boost::polymorphic_downcast(queueDescriptor.m_Outputs[2]); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle2, TensorInfo({2, 7, 7}, DataType::Float32))); } BOOST_AUTO_TEST_CASE(CreateSplitterConcat) { // Tests that it is possible to decide which output of the splitter layer // should be lined to which input of the concat layer. // We tested that is is possible to specify 0th output // of the splitter to be the 1st input to the concat, and the 1st output of the splitter to be 0th input // of the concat. Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workloads = CreateSplitterConcatWorkloadTest(factory, graph); auto wlSplitter = std::move(workloads.first); auto wlConcat = std::move(workloads.second); //Checks that the index of inputs/outputs matches what we declared on InputDescriptor construction. armnn::IAclTensorHandle* sOut0 = dynamic_cast(wlSplitter->GetData().m_Outputs[0]); armnn::IAclTensorHandle* sOut1 = dynamic_cast(wlSplitter->GetData().m_Outputs[1]); armnn::IAclTensorHandle* mIn0 = dynamic_cast(wlConcat->GetData().m_Inputs[0]); armnn::IAclTensorHandle* mIn1 = dynamic_cast(wlConcat->GetData().m_Inputs[1]); BOOST_TEST(sOut0); BOOST_TEST(sOut1); BOOST_TEST(mIn0); BOOST_TEST(mIn1); bool validDataPointers = (sOut0 == mIn1) && (sOut1 == mIn0); BOOST_TEST(validDataPointers); } BOOST_AUTO_TEST_CASE(CreateSingleOutputMultipleInputs) { // Tests that it is possible to assign multiple (two) different layers to each of the outputs of a splitter layer. // We created a splitter with two outputs. That each of those outputs is used by two different activation layers Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); std::unique_ptr wlSplitter; std::unique_ptr wlActiv0_0; std::unique_ptr wlActiv0_1; std::unique_ptr wlActiv1_0; std::unique_ptr wlActiv1_1; CreateSplitterMultipleInputsOneOutputWorkloadTest(factory, graph, wlSplitter, wlActiv0_0, wlActiv0_1, wlActiv1_0, wlActiv1_1); armnn::IAclTensorHandle* sOut0 = dynamic_cast(wlSplitter->GetData().m_Outputs[0]); armnn::IAclTensorHandle* sOut1 = dynamic_cast(wlSplitter->GetData().m_Outputs[1]); armnn::IAclTensorHandle* activ0_0Im = dynamic_cast(wlActiv0_0->GetData().m_Inputs[0]); armnn::IAclTensorHandle* activ0_1Im = dynamic_cast(wlActiv0_1->GetData().m_Inputs[0]); armnn::IAclTensorHandle* activ1_0Im = dynamic_cast(wlActiv1_0->GetData().m_Inputs[0]); armnn::IAclTensorHandle* activ1_1Im = dynamic_cast(wlActiv1_1->GetData().m_Inputs[0]); BOOST_TEST(sOut0); BOOST_TEST(sOut1); BOOST_TEST(activ0_0Im); BOOST_TEST(activ0_1Im); BOOST_TEST(activ1_0Im); BOOST_TEST(activ1_1Im); bool validDataPointers = (sOut0 == activ0_0Im) && (sOut0 == activ0_1Im) && (sOut1 == activ1_0Im) && (sOut1 == activ1_1Im); BOOST_TEST(validDataPointers); } #if defined(ARMCOMPUTEREF_ENABLED) // This test unit needs the reference backend, it's not available if the reference backend is not built BOOST_AUTO_TEST_CASE(CreateMemCopyWorkloadsNeon) { NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); CreateMemCopyWorkloads(factory); } #endif template static void NeonCreateL2NormalizationWorkloadTest(DataLayout dataLayout) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateL2NormalizationWorkloadTest(factory, graph, dataLayout); // Checks that inputs/outputs are as we expect them (see definition of CreateNormalizationWorkloadTest). L2NormalizationQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 }; TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 }; BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC BOOST_AUTO_TEST_CASE(CreateL2NormalizationFloat16NchwWorkload) { NeonCreateL2NormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateL2NormalizationFloat16NhwcWorkload) { NeonCreateL2NormalizationWorkloadTest(DataLayout::NHWC); } #endif BOOST_AUTO_TEST_CASE(CreateL2NormalizationNchwWorkload) { NeonCreateL2NormalizationWorkloadTest(DataLayout::NCHW); } BOOST_AUTO_TEST_CASE(CreateL2NormalizationNhwcWorkload) { NeonCreateL2NormalizationWorkloadTest(DataLayout::NHWC); } template static void NeonCreateLstmWorkloadTest() { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateLstmWorkloadTest(factory, graph); LstmQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[1]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo({ 2, 2 }, DataType::Float32))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo({ 2, 4 }, DataType::Float32))); } BOOST_AUTO_TEST_CASE(CreateLSTMWorkloadFloatWorkload) { NeonCreateLstmWorkloadTest(); } template static void NeonCreateConcatWorkloadTest(std::initializer_list outputShape, unsigned int concatAxis) { Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateConcatWorkloadTest(factory, graph, outputShape, concatAxis); ConcatQueueDescriptor queueDescriptor = workload->GetData(); auto inputHandle0 = boost::polymorphic_downcast(queueDescriptor.m_Inputs[0]); auto inputHandle1 = boost::polymorphic_downcast(queueDescriptor.m_Inputs[1]); auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle0, TensorInfo({ 2, 3, 2, 5 }, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle1, TensorInfo({ 2, 3, 2, 5 }, DataType))); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } BOOST_AUTO_TEST_CASE(CreateConcatDim0Float32Workload) { NeonCreateConcatWorkloadTest({ 4, 3, 2, 5 }, 0); } BOOST_AUTO_TEST_CASE(CreateConcatDim1Float32Workload) { NeonCreateConcatWorkloadTest({ 2, 6, 2, 5 }, 1); } BOOST_AUTO_TEST_CASE(CreateConcatDim3Float32Workload) { NeonCreateConcatWorkloadTest({ 2, 3, 2, 10 }, 3); } BOOST_AUTO_TEST_CASE(CreateConcatDim0Uint8Workload) { NeonCreateConcatWorkloadTest({ 4, 3, 2, 5 }, 0); } BOOST_AUTO_TEST_CASE(CreateConcatDim1Uint8Workload) { NeonCreateConcatWorkloadTest({ 2, 6, 2, 5 }, 1); } BOOST_AUTO_TEST_CASE(CreateConcatDim3Uint8Workload) { NeonCreateConcatWorkloadTest({ 2, 3, 2, 10 }, 3); } template static void NeonCreateStackWorkloadTest(const std::initializer_list& inputShape, const std::initializer_list& outputShape, unsigned int axis, unsigned int numInputs) { armnn::Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateStackWorkloadTest(factory, graph, TensorShape(inputShape), TensorShape(outputShape), axis, numInputs); // Check inputs and output are as expected StackQueueDescriptor queueDescriptor = workload->GetData(); for (unsigned int i = 0; i < numInputs; ++i) { auto inputHandle = boost::polymorphic_downcast(queueDescriptor.m_Inputs[i]); BOOST_TEST(TestNeonTensorHandleInfo(inputHandle, TensorInfo(inputShape, DataType))); } auto outputHandle = boost::polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST(TestNeonTensorHandleInfo(outputHandle, TensorInfo(outputShape, DataType))); } BOOST_AUTO_TEST_CASE(CreateStackFloat32Workload) { NeonCreateStackWorkloadTest({ 3, 4, 5 }, { 3, 4, 2, 5 }, 2, 2); } BOOST_AUTO_TEST_CASE(CreateStackUint8Workload) { NeonCreateStackWorkloadTest({ 3, 4, 5 }, { 3, 4, 2, 5 }, 2, 2); } template static void NeonCreateQuantizedLstmWorkloadTest() { using boost::polymorphic_downcast; Graph graph; NeonWorkloadFactory factory = NeonWorkloadFactoryHelper::GetFactory(NeonWorkloadFactoryHelper::GetMemoryManager()); auto workload = CreateQuantizedLstmWorkloadTest(factory, graph); QuantizedLstmQueueDescriptor queueDescriptor = workload->GetData(); IAclTensorHandle* inputHandle = polymorphic_downcast(queueDescriptor.m_Inputs[0]); BOOST_TEST((inputHandle->GetShape() == TensorShape({2, 2}))); BOOST_TEST((inputHandle->GetDataType() == arm_compute::DataType::QASYMM8)); IAclTensorHandle* cellStateInHandle = polymorphic_downcast(queueDescriptor.m_Inputs[1]); BOOST_TEST((cellStateInHandle->GetShape() == TensorShape({2, 4}))); BOOST_TEST((cellStateInHandle->GetDataType() == arm_compute::DataType::QSYMM16)); IAclTensorHandle* outputStateInHandle = polymorphic_downcast(queueDescriptor.m_Inputs[2]); BOOST_TEST((outputStateInHandle->GetShape() == TensorShape({2, 4}))); BOOST_TEST((outputStateInHandle->GetDataType() == arm_compute::DataType::QASYMM8)); IAclTensorHandle* cellStateOutHandle = polymorphic_downcast(queueDescriptor.m_Outputs[0]); BOOST_TEST((cellStateOutHandle->GetShape() == TensorShape({2, 4}))); BOOST_TEST((cellStateOutHandle->GetDataType() == arm_compute::DataType::QSYMM16)); IAclTensorHandle* outputStateOutHandle = polymorphic_downcast(queueDescriptor.m_Outputs[1]); BOOST_TEST((outputStateOutHandle->GetShape() == TensorShape({2, 4}))); BOOST_TEST((outputStateOutHandle->GetDataType() == arm_compute::DataType::QASYMM8)); } BOOST_AUTO_TEST_CASE(CreateQuantizedLstmWorkload) { NeonCreateQuantizedLstmWorkloadTest(); } BOOST_AUTO_TEST_SUITE_END()