| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| |
| #pragma once |
| |
| #include "TestUtils.hpp" |
| |
| #include <armnn/ArmNN.hpp> |
| |
| #include <Graph.hpp> |
| #include <layers/BatchToSpaceNdLayer.hpp> |
| #include <layers/SpaceToDepthLayer.hpp> |
| #include <layers/PreluLayer.hpp> |
| #include <layers/StackLayer.hpp> |
| |
| #include <boost/algorithm/string.hpp> |
| #include <boost/test/unit_test.hpp> |
| |
| void BatchToSpaceInferOutputShapeTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::BatchToSpaceNdDescriptor descriptor; |
| descriptor.m_BlockShape = {2, 2}; |
| descriptor.m_Crops = {{0, 0}, {2, 0}}; |
| descriptor.m_DataLayout = armnn::DataLayout::NHWC; |
| |
| armnn::BatchToSpaceNdLayer* const batchToSpaceLayer = |
| graph.AddLayer<armnn::BatchToSpaceNdLayer>(descriptor, "batchToSpace"); |
| |
| std::vector<armnn::TensorShape> shapes; |
| const std::vector<unsigned int> theDimSizes = {8, 1, 3, 1}; |
| armnn::TensorShape shape(4, theDimSizes.data()); |
| shapes.push_back(shape); |
| |
| const std::vector<unsigned int> expectedDimSizes = {2, 2, 4, 1}; |
| armnn::TensorShape expectedShape(4, expectedDimSizes.data()); |
| |
| BOOST_CHECK(expectedShape == batchToSpaceLayer->InferOutputShapes(shapes).at(0)); |
| } |
| |
| void SpaceToDepthInferOutputShapeTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::SpaceToDepthDescriptor descriptor; |
| descriptor.m_BlockSize = 2; |
| descriptor.m_DataLayout = armnn::DataLayout::NHWC; |
| |
| armnn::SpaceToDepthLayer* const spaceToDepthLayer = |
| graph.AddLayer<armnn::SpaceToDepthLayer>(descriptor, "spaceToDepth"); |
| |
| std::vector<armnn::TensorShape> shapes; |
| const std::vector<unsigned int> dimSizes{ 1, 16, 8, 3 }; |
| armnn::TensorShape shape(4, dimSizes.data()); |
| shapes.push_back(shape); |
| |
| const std::vector<unsigned int> expectedDimSizes{ 1, 8, 4, 12 }; |
| armnn::TensorShape expectedShape(4, expectedDimSizes.data()); |
| |
| BOOST_CHECK(expectedShape == spaceToDepthLayer->InferOutputShapes(shapes).at(0)); |
| } |
| |
| void PreluInferOutputShapeImpl(const std::vector<armnn::TensorShape>& inputShapes, |
| std::vector<armnn::TensorShape>& outputShapes) |
| { |
| armnn::Graph graph; |
| armnn::PreluLayer* const preluLayer = graph.AddLayer<armnn::PreluLayer>("prelu"); |
| outputShapes = preluLayer->InferOutputShapes(inputShapes); |
| } |
| |
| void PreluInferOutputShapeSameDimsTest() |
| { |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 5, 1, 1, 7 }, // Input shape |
| { 5, 4, 3, 1 } // Alpha shape |
| }; |
| |
| const std::vector<armnn::TensorShape> expectedOutputShapes |
| { |
| { 5, 4, 3, 7 } // Output shape |
| }; |
| |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(PreluInferOutputShapeImpl(inputShapes, outputShapes)); |
| |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] == expectedOutputShapes[0]); |
| } |
| |
| void PreluInferOutputShapeInputBiggerTest() |
| { |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 4, 1, 4, 8 }, // Input shape |
| { 5, 4, 1 } // Alpha shape |
| }; |
| |
| const std::vector<armnn::TensorShape> expectedOutputShapes |
| { |
| { 4, 5, 4, 8 } // Output shape |
| }; |
| |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(PreluInferOutputShapeImpl(inputShapes, outputShapes)); |
| |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] == expectedOutputShapes[0]); |
| } |
| |
| void PreluInferOutputShapeAlphaBiggerTest() |
| { |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 4, 1, 2 }, // Input shape |
| { 5, 4, 3, 1 } // Alpha shape |
| }; |
| |
| const std::vector<armnn::TensorShape> expectedOutputShapes |
| { |
| { 5, 4, 3, 2 } // Output shape |
| }; |
| |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(PreluInferOutputShapeImpl(inputShapes, outputShapes)); |
| |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] == expectedOutputShapes[0]); |
| } |
| |
| void PreluInferOutputShapeNoMatchTest() |
| { |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 4, 1, 2 }, // Input shape |
| { 5, 4, 3, 1 } // Alpha shape |
| }; |
| |
| const std::vector<armnn::TensorShape> expectedOutputShapes |
| { |
| { 5, 7, 3, 2 } // Output shape |
| }; |
| |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(PreluInferOutputShapeImpl(inputShapes, outputShapes)); |
| |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] != expectedOutputShapes[0]); |
| } |
| |
| void CreatePreluLayerHelper(armnn::Graph& graph, |
| const armnn::TensorShape& inputShape, |
| const armnn::TensorShape& alphaShape, |
| const armnn::TensorShape& outputShape) |
| { |
| // Creates the PReLU layer |
| armnn::Layer* const preluLayer = graph.AddLayer<armnn::PreluLayer>("prelu"); |
| |
| // Creates extra layers |
| armnn::Layer* const input = graph.AddLayer<armnn::InputLayer> (0, "input"); |
| armnn::Layer* const alpha = graph.AddLayer<armnn::InputLayer> (1, "alpha"); |
| armnn::Layer* const output = graph.AddLayer<armnn::OutputLayer>(0, "output"); |
| |
| // Connects up |
| armnn::TensorInfo inputTensorInfo (inputShape, armnn::DataType::Float32); |
| armnn::TensorInfo alphaTensorInfo (alphaShape, armnn::DataType::Float32); |
| armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); |
| Connect(input, preluLayer, inputTensorInfo, 0, 0); |
| Connect(alpha, preluLayer, alphaTensorInfo, 0, 1); |
| Connect(preluLayer, output, outputTensorInfo, 0, 0); |
| } |
| |
| void PreluValidateTensorShapesFromInputsMatchTest() |
| { |
| armnn::Graph graph; |
| |
| // Creates the PReLU layer |
| CreatePreluLayerHelper(graph, { 1, 4, 1, 2 }, { 5, 4, 3, 1 }, { 5, 4, 3, 2 }); |
| |
| // Graph::InferTensorInfos calls Layer::ValidateTensorShapesFromInputs |
| BOOST_CHECK_NO_THROW(graph.InferTensorInfos()); |
| } |
| |
| void PreluValidateTensorShapesFromInputsNoMatchTest() |
| { |
| armnn::Graph graph; |
| |
| // Creates the PReLU layer |
| CreatePreluLayerHelper(graph, { 1, 4, 1, 2 }, { 5, 4, 3, 1 }, { 5, 7, 3, 2 }); |
| |
| // Graph::InferTensorInfos calls Layer::ValidateTensorShapesFromInputs |
| BOOST_CHECK_THROW(graph.InferTensorInfos(), armnn::LayerValidationException); |
| } |
| |
| void StackInferOutputShapeImpl(const armnn::StackDescriptor descriptor, |
| const std::vector<armnn::TensorShape>& inputShapes, |
| std::vector<armnn::TensorShape>& outputShapes) |
| { |
| armnn::Graph graph; |
| armnn::StackLayer* const stackLayer = graph.AddLayer<armnn::StackLayer>(descriptor, "stack"); |
| outputShapes = stackLayer->InferOutputShapes(inputShapes); |
| } |
| |
| void StackInferOutputShapeFromInputsMatchTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::StackDescriptor descriptor; |
| descriptor.m_Axis = 1; |
| descriptor.m_NumInputs = 3; |
| descriptor.m_InputShape = armnn::TensorShape |
| ( |
| { 4, 2 } // Defined input shape |
| ); |
| |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 4, 2 }, // Actual input shapes |
| { 4, 2 }, |
| { 4, 2 } |
| }; |
| |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(StackInferOutputShapeImpl(descriptor, inputShapes, outputShapes)); |
| |
| armnn::TensorShape expectedOutputShape |
| ( |
| { 4, 3, 2 } |
| ); |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] == expectedOutputShape); |
| } |
| |
| void StackInferOutputShapeFromInputsNoMatchTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::StackDescriptor descriptor; |
| descriptor.m_Axis = 1; |
| descriptor.m_NumInputs = 3; |
| descriptor.m_InputShape = armnn::TensorShape |
| ( |
| { 4, 2 } // Defined input shape |
| ); |
| |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 4, 2 }, // Actual input shapes |
| { 4, 5 }, // Incorrectly shaped input tensor |
| { 4, 2 } |
| }; |
| |
| // Output shape is inferred from the descriptor, so should still be correct despite mismatching input shapes |
| std::vector<armnn::TensorShape> outputShapes; |
| BOOST_CHECK_NO_THROW(StackInferOutputShapeImpl(descriptor, inputShapes, outputShapes)); |
| |
| armnn::TensorShape expectedOutputShape |
| ( |
| { 4, 3, 2 } |
| ); |
| BOOST_CHECK(outputShapes.size() == 1); |
| BOOST_CHECK(outputShapes[0] == expectedOutputShape); |
| } |
| |
| void CreateStackLayerHelper(armnn::Graph& graph, |
| const armnn::StackDescriptor& descriptor, |
| const std::vector<armnn::TensorShape>& inputShapes, |
| const armnn::TensorShape& outputShape) |
| { |
| // Creates the Stack layer |
| armnn::Layer* const stackLayer = graph.AddLayer<armnn::StackLayer>(descriptor, "stack"); |
| |
| // Creates extra layers |
| std::vector<armnn::Layer*> inputs; |
| for (unsigned int i=0; i<inputShapes.size(); ++i) |
| { |
| inputs.push_back(graph.AddLayer<armnn::InputLayer>(static_cast<int>(i), "input")); |
| } |
| armnn::Layer* const output = graph.AddLayer<armnn::OutputLayer>(0, "output"); |
| |
| // Connects up |
| std::vector<armnn::TensorInfo> inputTensorInfos; |
| for (unsigned int i=0; i<inputs.size(); ++i) |
| { |
| inputTensorInfos.push_back(armnn::TensorInfo(inputShapes[i], armnn::DataType::Float32)); |
| } |
| armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); |
| |
| for (unsigned int i=0; i<inputs.size(); ++i) |
| { |
| Connect(inputs[i], stackLayer, inputTensorInfos[i], 0, i); |
| } |
| Connect(stackLayer, output, outputTensorInfo, 0, 0); |
| } |
| |
| void StackValidateTensorShapesFromInputsMatchTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::StackDescriptor descriptor; |
| descriptor.m_Axis = 0; |
| descriptor.m_NumInputs = 3; |
| descriptor.m_InputShape = armnn::TensorShape |
| ( |
| { 2, 5 } // Defined input shape |
| ); |
| |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 2, 5 }, // Actual input shapes |
| { 2, 5 }, |
| { 2, 5 } |
| }; |
| |
| // Creates the Stack layer |
| CreateStackLayerHelper(graph, descriptor, inputShapes, { 3, 2, 5 }); |
| |
| // Graph::InferTensorInfos calls Layer::ValidateTensorShapesFromInputs |
| BOOST_CHECK_NO_THROW(graph.InferTensorInfos()); |
| } |
| |
| void StackValidateTensorShapesFromInputsNoMatchTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::StackDescriptor descriptor; |
| descriptor.m_Axis = 0; |
| descriptor.m_NumInputs = 3; |
| descriptor.m_InputShape = armnn::TensorShape |
| ( |
| { 2, 5 } // Defined input shape |
| ); |
| |
| const std::vector<armnn::TensorShape> inputShapes |
| { |
| { 2, 5 }, // Actual input shapes |
| { 2, 2 }, // Incorrectly shaped input tensor |
| { 2, 5 } |
| }; |
| |
| // Creates the Stack layer |
| CreateStackLayerHelper(graph, descriptor, inputShapes, { 3, 2, 5 }); |
| |
| // Graph::InferTensorInfos calls Layer::ValidateTensorShapesFromInputs |
| BOOST_CHECK_THROW(graph.InferTensorInfos(), armnn::LayerValidationException); |
| } |
| |
| void Convolution2dInferOutputShapeTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::Convolution2dDescriptor descriptor; |
| descriptor.m_DilationX = 2; |
| descriptor.m_DilationY = 2; |
| descriptor.m_PadTop = 1; |
| descriptor.m_PadBottom = 1; |
| descriptor.m_PadLeft = 1; |
| descriptor.m_PadRight = 1; |
| descriptor.m_StrideX = 3; |
| descriptor.m_StrideY = 3; |
| descriptor.m_DataLayout = armnn::DataLayout::NCHW; |
| |
| armnn::Convolution2dLayer* const convolution2dLayer = |
| graph.AddLayer<armnn::Convolution2dLayer>(descriptor, "convolution2d"); |
| |
| std::vector<armnn::TensorShape> shapes; |
| const std::vector<unsigned int> inputSize = {1, 2, 10, 10}; |
| armnn::TensorShape inputShape(4, inputSize.data()); |
| shapes.push_back(inputShape); |
| |
| const std::vector<unsigned int> filterSize = { 1, 2, 2, 2}; |
| armnn::TensorShape filterShape(4, filterSize.data()); |
| shapes.push_back(filterShape); |
| |
| const std::vector<unsigned int> expectedOutputSizes = {1, 1, 4, 4}; |
| armnn::TensorShape expectedOutputShape(4, expectedOutputSizes.data()); |
| |
| BOOST_CHECK(expectedOutputShape == convolution2dLayer->InferOutputShapes(shapes).at(0)); |
| } |
| |
| void TransposeConvolution2dInferOutputShapeTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::TransposeConvolution2dDescriptor descriptor; |
| descriptor.m_PadTop = 0; |
| descriptor.m_PadBottom = 1; |
| descriptor.m_PadLeft = 0; |
| descriptor.m_PadRight = 1; |
| descriptor.m_StrideX = 2; |
| descriptor.m_StrideY = 2; |
| descriptor.m_DataLayout = armnn::DataLayout::NCHW; |
| |
| armnn::TransposeConvolution2dLayer* const transposeConvolution2dLayer = |
| graph.AddLayer<armnn::TransposeConvolution2dLayer>(descriptor, "TransposeConvolution2d"); |
| |
| std::vector<armnn::TensorShape> shapes; |
| const std::vector<unsigned int> inputSize = {1, 2, 3, 3}; |
| armnn::TensorShape inputShape(4, inputSize.data()); |
| shapes.push_back(inputShape); |
| |
| const std::vector<unsigned int> filterSize = { 1, 2, 3, 3}; |
| armnn::TensorShape filterShape(4, filterSize.data()); |
| shapes.push_back(filterShape); |
| |
| const std::vector<unsigned int> expectedOutputSizes = {1, 2, 6, 6}; |
| armnn::TensorShape expectedOutputShape(4, expectedOutputSizes.data()); |
| |
| BOOST_CHECK(expectedOutputShape == transposeConvolution2dLayer->InferOutputShapes(shapes).at(0)); |
| } |
| |
| void DepthwiseConvolution2dInferOutputShapeTest() |
| { |
| armnn::Graph graph; |
| |
| armnn::DepthwiseConvolution2dDescriptor descriptor; |
| descriptor.m_DilationX = 3; |
| descriptor.m_DilationY = 3; |
| descriptor.m_PadTop = 1; |
| descriptor.m_PadBottom = 2; |
| descriptor.m_PadLeft = 1; |
| descriptor.m_PadRight = 2; |
| descriptor.m_StrideX = 2; |
| descriptor.m_StrideY = 2; |
| descriptor.m_DataLayout = armnn::DataLayout::NCHW; |
| |
| armnn::DepthwiseConvolution2dLayer* const depthwiseConvolution2dLayer = |
| graph.AddLayer<armnn::DepthwiseConvolution2dLayer>(descriptor, "DepthwiseConvolution2d"); |
| |
| std::vector<armnn::TensorShape> shapes; |
| const std::vector<unsigned int> inputSize = {1, 2, 10, 10}; |
| armnn::TensorShape inputShape(4, inputSize.data()); |
| shapes.push_back(inputShape); |
| |
| const std::vector<unsigned int> filterSize = { 1, 2, 3, 3}; |
| armnn::TensorShape filterShape(4, filterSize.data()); |
| shapes.push_back(filterShape); |
| |
| const std::vector<unsigned int> expectedOutputSizes = {1, 2, 4, 4}; |
| armnn::TensorShape expectedOutputShape(4, expectedOutputSizes.data()); |
| |
| BOOST_CHECK(expectedOutputShape == depthwiseConvolution2dLayer->InferOutputShapes(shapes).at(0)); |
| } |