From 41aabbeaffb4622014e8072ce6bffdc574fb10b3 Mon Sep 17 00:00:00 2001 From: "Efimov Alexander/AI Tools Lab/./Samsung Electronics" Date: Mon, 17 Dec 2018 15:07:36 +0300 Subject: [PATCH] [nnc] Add transpose operation test for soft backend (#2647) - Test for transpose operation for soft backend - Code style refactor of testsuite - Arbitrary names for output node in tests supported Signed-off-by: Efimov Alexander --- .../nnc/unittests/soft_backend/CPPOperations.cpp | 773 +++++++++++---------- 1 file changed, 417 insertions(+), 356 deletions(-) diff --git a/contrib/nnc/unittests/soft_backend/CPPOperations.cpp b/contrib/nnc/unittests/soft_backend/CPPOperations.cpp index b94c6b6..06d6f2c 100644 --- a/contrib/nnc/unittests/soft_backend/CPPOperations.cpp +++ b/contrib/nnc/unittests/soft_backend/CPPOperations.cpp @@ -68,12 +68,13 @@ #include "core/modelIR/operations/BiasAddOp.h" #include "core/modelIR/operations/SoftmaxOp.h" #include "core/modelIR/operations/ScaleOp.h" +#include "core/modelIR/operations/SqrtOp.h" #include "core/modelIR/operations/EluOp.h" #include "core/modelIR/operations/ElementwiseOp.h" #include "core/modelIR/operations/Deconv2DOp.h" #include "core/modelIR/operations/TanhOp.h" +#include "core/modelIR/operations/TransposeOp.h" #include "core/modelIR/operations/PadOp.h" -#include "core/modelIR/operations/SqrtOp.h" // various headers #include "core/modelIR/TensorVariant.h" @@ -102,26 +103,26 @@ namespace irOps = nnc::mir::ops; NNC data types are: mir::TensorVariant, tensor::Shape, mir::Tensor */ -namespace -{ +namespace { -/** Creates graph with one operation generated by opGen function and returns this operation node*/ -mir::Operation* fillGraph(mir::Graph& g, - function& inputs)> opGen, - const vector>& inputNTensors) { +/** + * @brief Creates graph with one operation generated by opGen function and returns this operation node + */ +mir::Operation* +fillGraph(mir::Graph& g, + function& inputs)> op_gen, + const vector>& input_ntensors) { // Create inputs std::vector inputs; - int numInputs = inputNTensors.size(); - for (int i = 0; i < numInputs; ++i) - { - auto inputOp = g.create("x" + std::to_string(i), - inputNTensors[i]->getShape()); - inputs.push_back(inputOp->getOutput(0)); + int num_inputs = input_ntensors.size(); + for (int i = 0; i < num_inputs; ++i) { + auto input_op = + g.create("x" + std::to_string(i), input_ntensors[i]->getShape()); + inputs.push_back(input_op->getOutput(0)); } // Create operation - mir::Operation* op = opGen(g, inputs); + mir::Operation* op = op_gen(g, inputs); // Mark outputs g.markOutput(op); @@ -129,49 +130,53 @@ mir::Operation* fillGraph(mir::Graph& g, return op; } -/** Fills NNC Shape object with data from src container*/ -void fillNShape(mir::Shape &nshape, const vector &rawShapeData) -{ - int shapeRank = rawShapeData.size(); - nshape.resize(shapeRank); - for (int i = 0; i < shapeRank; ++i) - { - nshape.dim(i) = rawShapeData[i]; - } +/** + * @brief Fills NNC Shape object with data from src container + */ +void fillNShape(mir::Shape &nshape, const vector &raw_shape_data) { + int shape_rank = raw_shape_data.size(); + nshape.resize(shape_rank); + for (int i = 0; i < shape_rank; ++i) + nshape.dim(i) = raw_shape_data[i]; } -/** Converts NNC Shape to artifact Shape*/ -void copyAShapeFromNShape(Shape &ashape, const mir::Shape &src) -{ - int shapeRank = src.rank(); - ashape.setDims(shapeRank); - for (int i = 0; i < shapeRank; ++i) - { +/** + * @brief Converts NNC Shape to artifact Shape + */ +void copyAShapeFromNShape(Shape &ashape, const mir::Shape &src) { + int shape_rank = src.rank(); + ashape.setDims(shape_rank); + for (int i = 0; i < shape_rank; ++i) ashape[i] = src.dim(i); - } } -/** Fills NNC and artifact Shape objects with data from rawShapeData*/ -void fillShapes(mir::Shape &nshape, Shape &ashape, const vector &rawShapeData) -{ - fillNShape(nshape, rawShapeData); +/** + * @brief Fills NNC and artifact Shape objects with data from rawShapeData + */ +void fillShapes(mir::Shape &nshape, Shape &ashape, const vector &raw_shape_data) { + fillNShape(nshape, raw_shape_data); copyAShapeFromNShape(ashape, nshape); } -/** Fills NNC tensor with some determined data*/ -void fillNTensor(mir::TensorVariant &dst, float start) -{ +/** + * @brief Fills NNC tensor with some determined data + */ +void fillNTensor(mir::TensorVariant &dst, float start) { float t = start; mir::Tensor wrapper(dst); - for (mir::Index idx: mir::ShapeRange(dst.getShape())) - { + for (mir::Index idx: mir::ShapeRange(dst.getShape())) { wrapper.at(idx) = sin(t) * 2.0f; t += 1.0f; } } -mir::TensorVariant createNTensor(mir::Shape &shape, float start) -{ +/** + * @brief Creates and fills TensorVariant + * @param shape Shape of desired tensor + * @param start Seed for tensor filler + * @return Created object + */ +mir::TensorVariant createNTensor(mir::Shape &shape, float start) { shared_ptr dataBuf( new char[sizeof(float) * shape.numElements()], default_delete()); mir::TensorVariant tensor(shape, dataBuf, mir::DTYPE::FLOAT32, sizeof(float)); @@ -179,53 +184,57 @@ mir::TensorVariant createNTensor(mir::Shape &shape, float start) return tensor; } -/** Converts NNC mir::TensorVariant to artifact Tensor object*/ -void copyATensorFromNTensor(Tensor &dst, mir::TensorVariant &src) -{ +/** + * @brief Converts NNC mir::TensorVariant to artifact Tensor object + */ +void copyATensorFromNTensor(Tensor &dst, mir::TensorVariant &src) { mir::Tensor wrapper(src); - Index artIdx; + Index art_idx; int rank = src.getShape().rank(); - artIdx.setDims(rank); - for (mir::Index idx: mir::ShapeRange(src.getShape())) - { + art_idx.setDims(rank); + for (mir::Index idx: mir::ShapeRange(src.getShape())) { for (int i = 0; i < rank; ++i) - { - artIdx[i] = idx.at(i); - } - dst.at(artIdx) = wrapper.at(idx); + art_idx[i] = idx.at(i); + dst.at(art_idx) = wrapper.at(idx); } } -/** Fills NNC and artifact tensor objects with some determined data*/ -void fillTensors(unique_ptr &nTensor, Tensor &aTensor, const vector &shape, float start) -{ - Shape aShape; - mir::Shape nShape; - fillShapes(nShape, aShape, shape); - aTensor.reShape(aShape); +/** + * @brief Fills NNC and artifact tensor objects with some determined data + */ +void fillTensors(unique_ptr &ntensor, + Tensor &atensor, + const vector &shape, + float start) { + Shape ashape; + mir::Shape nshape; + fillShapes(nshape, ashape, shape); + atensor.reShape(ashape); shared_ptr dataBuf( - new char[sizeof(float) * nShape.numElements()], default_delete()); - nTensor.reset(new mir::TensorVariant(nShape, dataBuf, mir::DTYPE::FLOAT32, sizeof(float))); - fillNTensor(*nTensor, start); - copyATensorFromNTensor(aTensor, *nTensor); + new char[sizeof(float) * nshape.numElements()], default_delete()); + ntensor.reset(new mir::TensorVariant(nshape, dataBuf, mir::DTYPE::FLOAT32, sizeof(float))); + fillNTensor(*ntensor, start); + copyATensorFromNTensor(atensor, *ntensor); } -/** Run interpreter to get reference output data*/ -mir::TensorVariant getReferenceTensor(mir::Graph &g, const vector> &inputNTensors) -{ +/** + * @brief Run interpreter to get reference output data + */ +mir::TensorVariant getReferenceTensor(mir::Graph &g, + const vector> &input_ntensors, + const string& output_name) { mir::NNInterpreter interpreter; - for (int i = 0; i < static_cast(inputNTensors.size()); ++i) - { - interpreter.setInput("x" + to_string(i), *inputNTensors[i]); - } + for (int i = 0; i < static_cast(input_ntensors.size()); ++i) + interpreter.setInput("x" + to_string(i), *input_ntensors[i]); g.accept(&interpreter); - return interpreter.getResult(g.getOutput("y"))[0]; + return interpreter.getResult(g.getOutput(output_name))[0]; }; -/** Run selected operation, used to make code in tests more compact and fit getReferenceTensor format*/ +/** + * @brief Run selected operation, used to make code in tests more compact and fit getReferenceTensor format + */ template -Tensor run(Operation op, const Args &...args) -{ +Tensor run(Operation op, const Args &...args) { Tensor output; op(output, args...); return output; @@ -256,387 +265,407 @@ bool areFloatsNear(float a, float b, int32_t ulp, float eps) { return bi - ai <= ulp; } -/** Compares nnc mir::TensorVariant and artifact Tensor objects*/ -void compareResults(const mir::TensorVariant &ref_nnc_tensor, const Tensor &test_art_tensor) -{ +/** + * @brief Compares nnc mir::TensorVariant and artifact Tensor objects + * @param ref_nnc_tensor Reference tensor that interpreter produced + * @param test_art_tensor Tensor that artifact operation computed + */ +void compareResults(const mir::TensorVariant &ref_nnc_tensor, const Tensor &test_art_tensor) { + assert(ref_nnc_tensor.getElementSize() == 4L && + ref_nnc_tensor.getDataType() == mir::DTYPE::FLOAT32); + const mir::Shape &nnc_shape = ref_nnc_tensor.getShape(); const Shape &art_shape = test_art_tensor.getShape(); + + // check that reference and test shapes are equal ASSERT_EQ(nnc_shape.rank(), art_shape.getDims()); + int rank = nnc_shape.rank(); for (int i = 0; i < rank; ++i) - { ASSERT_EQ(nnc_shape.dim(i), art_shape[i]); - } + + // check that reference and test tensor contents are equal Index artifact_idx; artifact_idx.setDims(rank); - for (mir::Index nnc_idx: mir::ShapeRange(nnc_shape)) - { + for (mir::Index nnc_idx: mir::ShapeRange(nnc_shape)) { for (int i = 0; i < rank; ++i) - { artifact_idx[i] = nnc_idx.at(i); - } - assert(ref_nnc_tensor.getElementSize() == 4L && - ref_nnc_tensor.getDataType() == mir::DTYPE::FLOAT32); // Input and output data lies in range of [-10, 10], // chosen epsilon lies near the edge of float type computational precision float ref_data = mir::Tensor(ref_nnc_tensor).at(nnc_idx); float test_data = test_art_tensor.at(artifact_idx); ASSERT_TRUE(areFloatsNear(ref_data, test_data, 32, 1e-5)) - << "Tensor element " << nnc_idx << " diverged, reference: " << ref_data << " test result: " << test_data; + << "Tensor element " << nnc_idx << " diverged, reference: " + << ref_data << " test result: " << test_data; } } /** - * This function creates test graph, runs interpeter, specifies artifact operation and compares results + * @brief This function creates test graph, runs interpeter, specifies artifact operation and compares results */ template void createAndRunTestGraph( function& inputs)> opGenerator, + const std::vector& inputs)> op_generator, TestFunc artifactOperation, - const vector> &inputNTensors, - const Args &...inputATensors) -{ + const vector> &input_ntensors, + const Args &...input_atensors) { mir::Graph g; - mir::Operation *actualOperation = fillGraph(g, opGenerator, inputNTensors); + mir::Operation *actual_operation = fillGraph(g, op_generator, input_ntensors); // serialize data for soft backend operation - list inferenceSequence; - OpDescr opDescr; - opDescr._op = actualOperation; - inferenceSequence.push_back(opDescr); + list inference_sequence; + OpDescr op_descr; + op_descr._op = actual_operation; + inference_sequence.push_back(op_descr); Serializer serializer; - serializer.serialize(inferenceSequence); - assert(inferenceSequence.front()._paramStartOffset == 0); + serializer.serialize(inference_sequence); + assert(inference_sequence.front()._paramStartOffset == 0); - mir::TensorVariant referenceOutput = getReferenceTensor(g, inputNTensors); + const string& output_name = actual_operation->getName(); + mir::TensorVariant reference_output = getReferenceTensor(g, input_ntensors, output_name); - Tensor testOutput; - artifactOperation(testOutput, serializer.getBuffer().data(), inputATensors...); + Tensor test_output; + artifactOperation(test_output, serializer.getBuffer().data(), input_atensors...); - compareResults(referenceOutput, testOutput); + compareResults(reference_output, test_output); } } -TEST(cpp_operations_test, bias) -{ - vector inputShapeData{2, 3, 4, 5}; - mir::Shape weightsShape{5}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); +TEST(cpp_operations_test, bias) { + vector input_shape_data{2, 3, 4, 5}; + mir::Shape weights_shape{5}; + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); - mir::TensorVariant weights = createNTensor(weightsShape, 1.0f); - auto opGenerator = [weights](mir::Graph& g, const std::vector& inputs) { + mir::TensorVariant weights = createNTensor(weights_shape, 1.0f); + auto op_generator = [weights](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], weights); }; - createAndRunTestGraph(opGenerator, biasAdd, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, biasAdd, input_ntensors, input_atensor); } -TEST(cpp_operations_test, scale) -{ - vector inputShapeData{2, 3, 4, 5}; - mir::Shape weightsShape{5}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); +TEST(cpp_operations_test, scale) { + vector input_shape_data{2, 3, 4, 5}; + mir::Shape weights_shape{5}; + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); - mir::TensorVariant weights = createNTensor(weightsShape, 1.0f); - auto opGenerator = [weights](mir::Graph& g, const std::vector& inputs) { + mir::TensorVariant weights = createNTensor(weights_shape, 1.0f); + auto op_generator = [weights](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], weights); }; - createAndRunTestGraph(opGenerator, scale, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, scale, input_ntensors, input_atensor); } -TEST(cpp_operations_test, capped_relu) -{ +TEST(cpp_operations_test, capped_relu) { // test prerequisites // cap has this value to cut input numbers(they are in range [-1, 1]) float cap = 0.5f; - vector shapeData{2, 3, 4, 5}; - Tensor aInputTensor; - vector> inputNTensors(1); - fillTensors(inputNTensors[0], aInputTensor, shapeData, 1.0f); - auto opGenerator = [cap](mir::Graph& g, const std::vector& inputs) { + vector shape_data{2, 3, 4, 5}; + Tensor input_atensor; + vector> input_ntensors(1); + fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f); + auto op_generator = [cap](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], cap); }; - createAndRunTestGraph(opGenerator, cappedRelu, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, cappedRelu, input_ntensors, input_atensor); } -TEST(cpp_operations_test, concat) -{ - for (int numDims = 1; numDims <= 4; ++numDims) - for (int axis = 0; axis < numDims; ++axis) +TEST(cpp_operations_test, concat) { + for (int num_dims = 1; num_dims <= 4; ++num_dims) + for (int axis = 0; axis < num_dims; ++axis) { // test prerequisites - vector shape1Data{2, 3, 5, 7}; - vector shape2Data{2, 3, 5, 7}; - shape1Data.resize(numDims); - shape2Data.resize(numDims); + vector shape_data1{2, 3, 5, 7}; + vector shape_data2{2, 3, 5, 7}; + shape_data1.resize(num_dims); + shape_data2.resize(num_dims); // set different size for concatenating axis - shape2Data[axis] = 11; - vector inputATensors(2); - vector> inputNTensors(2); - fillTensors(inputNTensors[0], inputATensors[0], shape1Data, 1.0f); - fillTensors(inputNTensors[1], inputATensors[1], shape2Data, 2.0f); - auto opGenerator = [axis](mir::Graph& g, const std::vector& inputs) { + shape_data2[axis] = 11; + vector input_atensors(2); + vector> input_ntensors(2); + fillTensors(input_ntensors[0], input_atensors[0], shape_data1, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data2, 2.0f); + auto op_generator = [axis](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, axis); }; - createAndRunTestGraph(opGenerator, concat, inputNTensors, inputATensors[0], inputATensors[1]); + createAndRunTestGraph(op_generator, + concat, + input_ntensors, + input_atensors[0], + input_atensors[1]); } } TEST(cpp_operations_test, add2bc) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data1{3, 44, 5, 1}; vector shape_data2{3, 1, 5, 6}; - shape_data1.resize(numDims); - shape_data2.resize(numDims); - vector input_a_tensors(2); - vector> input_n_tensors(2); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data1, 1.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data2, 2.0f); + shape_data1.resize(num_dims); + shape_data2.resize(num_dims); + vector input_atensors(2); + vector> input_ntensors(2); + fillTensors(input_ntensors[0], input_atensors[0], shape_data1, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data2, 2.0f); auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::add); }; - createAndRunTestGraph(op_generator, ElementWise, input_n_tensors, - input_a_tensors[0], - input_a_tensors[1]); + createAndRunTestGraph(op_generator, ElementWise, input_ntensors, + input_atensors[0], + input_atensors[1]); } } TEST(cpp_operations_test, mul3bc) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data1{3, 22, 5, 1}; vector shape_data2{3, 1, 5, 6}; vector shape_data3{1, 22, 1, 6}; - shape_data1.resize(numDims); - shape_data2.resize(numDims); - shape_data3.resize(numDims); - vector input_a_tensors(3); - vector> input_n_tensors(3); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data1, 1.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data2, 2.0f); - fillTensors(input_n_tensors[2], input_a_tensors[2], shape_data3, 3.0f); + shape_data1.resize(num_dims); + shape_data2.resize(num_dims); + shape_data3.resize(num_dims); + vector input_atensors(3); + vector> input_ntensors(3); + fillTensors(input_ntensors[0], input_atensors[0], shape_data1, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data2, 2.0f); + fillTensors(input_ntensors[2], input_atensors[2], shape_data3, 3.0f); auto opGenerator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::mul); }; - createAndRunTestGraph(opGenerator, ElementWise, input_n_tensors, - input_a_tensors[0], - input_a_tensors[1], input_a_tensors[2]); + createAndRunTestGraph(opGenerator, ElementWise, input_ntensors, + input_atensors[0], + input_atensors[1], input_atensors[2]); } } TEST(cpp_operations_test, div3bc) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data1{3, 22, 5, 1}; vector shape_data2{3, 1, 5, 6}; vector shape_data3{1, 22, 1, 6}; - shape_data1.resize(numDims); - shape_data2.resize(numDims); - shape_data3.resize(numDims); - vector input_a_tensors(3); - vector> input_n_tensors(3); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data1, 5.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data2, 2.0f); - fillTensors(input_n_tensors[2], input_a_tensors[2], shape_data3, 3.0f); + shape_data1.resize(num_dims); + shape_data2.resize(num_dims); + shape_data3.resize(num_dims); + vector input_atensors(3); + vector> input_ntensors(3); + fillTensors(input_ntensors[0], input_atensors[0], shape_data1, 5.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data2, 2.0f); + fillTensors(input_ntensors[2], input_atensors[2], shape_data3, 3.0f); auto opGenerator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::div); }; createAndRunTestGraph( opGenerator, ElementWise, - input_n_tensors, - input_a_tensors[0], - input_a_tensors[1], - input_a_tensors[2] + input_ntensors, + input_atensors[0], + input_atensors[1], + input_atensors[2] ); } } - TEST(cpp_operations_test, add2) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data{2, 3, 5, 7}; - shape_data.resize(numDims); - vector input_a_tensors(2); - vector> input_n_tensors(2); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data, 1.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data, 2.0f); + shape_data.resize(num_dims); + vector input_atensors(2); + vector> input_ntensors(2); + fillTensors(input_ntensors[0], input_atensors[0], shape_data, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data, 2.0f); auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::add); }; - createAndRunTestGraph(op_generator, ElementWise, input_n_tensors, - input_a_tensors[0], - input_a_tensors[1]); + createAndRunTestGraph(op_generator, + ElementWise, + input_ntensors, + input_atensors[0], + input_atensors[1]); } } TEST(cpp_operations_test, mul3) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data{2, 3, 5, 7}; - shape_data.resize(numDims); - vector input_a_tensors(3); - vector> input_n_tensors(3); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data, 1.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data, 2.0f); - fillTensors(input_n_tensors[2], input_a_tensors[2], shape_data, 3.0f); - auto opGenerator = [](mir::Graph& g, const std::vector& inputs) { + shape_data.resize(num_dims); + vector input_atensors(3); + vector> input_ntensors(3); + fillTensors(input_ntensors[0], input_atensors[0], shape_data, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data, 2.0f); + fillTensors(input_ntensors[2], input_atensors[2], shape_data, 3.0f); + auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::mul); }; - createAndRunTestGraph(opGenerator, ElementWise, input_n_tensors, - input_a_tensors[0], - input_a_tensors[1], input_a_tensors[2]); + createAndRunTestGraph(op_generator, + ElementWise, + input_ntensors, + input_atensors[0], + input_atensors[1], + input_atensors[2]); } } TEST(cpp_operations_test, max4) { - for (int numDims = 2; numDims <= 4; ++numDims) { + for (int num_dims = 2; num_dims <= 4; ++num_dims) { // test prerequisites vector shape_data{2, 3, 5, 7}; - shape_data.resize(numDims); - vector input_a_tensors(4); - vector> input_n_tensors(4); - fillTensors(input_n_tensors[0], input_a_tensors[0], shape_data, 1.0f); - fillTensors(input_n_tensors[1], input_a_tensors[1], shape_data, 2.0f); - fillTensors(input_n_tensors[2], input_a_tensors[2], shape_data, 3.0f); - fillTensors(input_n_tensors[3], input_a_tensors[3], shape_data, 3.0f); - auto opGenerator = [](mir::Graph& g, const std::vector& inputs) { + shape_data.resize(num_dims); + vector input_atensors(4); + vector> input_ntensors(4); + fillTensors(input_ntensors[0], input_atensors[0], shape_data, 1.0f); + fillTensors(input_ntensors[1], input_atensors[1], shape_data, 2.0f); + fillTensors(input_ntensors[2], input_atensors[2], shape_data, 3.0f); + fillTensors(input_ntensors[3], input_atensors[3], shape_data, 3.0f); + auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs, mir::ops::ElementwiseOp::OpType::max); }; - createAndRunTestGraph(opGenerator, ElementWise, - input_n_tensors, input_a_tensors[0], - input_a_tensors[1], input_a_tensors[2], input_a_tensors[3]); + createAndRunTestGraph(op_generator, + ElementWise, + input_ntensors, + input_atensors[0], + input_atensors[1], + input_atensors[2], + input_atensors[3]); } } -TEST(cpp_operations_test, convTransposed2d) -{ +TEST(cpp_operations_test, convTransposed2d) { // Iterate over kernel width, kernel height, - // input channels(inputC), output channels(outputC), + // input channels(input_c), output channels(output_c), // stride width, stride height // size 3 is chosen to cover all cases, where width bigger/smaller then height and equal/not equal to 1 using iT = int32_t; - for (iT kernelH = 2; kernelH <= 4; ++kernelH) - for (iT kernelW = 2; kernelW <= 4; ++kernelW) - for (iT inputC = 1; inputC <= 3; ++inputC) - for (iT outputC = 1; outputC <= 3; ++outputC) - for (iT strideH = 1; strideH <= 3; ++strideH) - for (iT strideW = 1; strideW <= 3; ++strideW) { - vector inputShapeData{3, 9, 3, static_cast(inputC)}; // NHWC - mir::Shape kernelShape{kernelH, kernelW, outputC, inputC}; - mir::Shape strides{strideH, strideW, 1}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); + for (iT kernel_h = 2; kernel_h <= 4; ++kernel_h) + for (iT kernel_w = 2; kernel_w <= 4; ++kernel_w) + for (iT input_c = 1; input_c <= 3; ++input_c) + for (iT output_c = 1; output_c <= 3; ++output_c) + for (iT stride_h = 1; stride_h <= 3; ++stride_h) + for (iT stride_w = 1; stride_w <= 3; ++stride_w) { + vector input_shape_data{3, 9, 3, static_cast(input_c)}; // NHWC + mir::Shape kernel_shape{kernel_h, kernel_w, output_c, input_c}; + mir::Shape strides{stride_h, stride_w, 1}; + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); auto padT = mir::ops::PaddingType::Same; - mir::TensorVariant kernel = createNTensor(kernelShape, 1.0f); - auto opGenerator = [kernel, strides, padT]( + mir::TensorVariant kernel = createNTensor(kernel_shape, 1.0f); + auto op_generator = [kernel, strides, padT]( mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], kernel, strides, padT); }; - createAndRunTestGraph(opGenerator, convTransposed2d, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, convTransposed2d, input_ntensors, input_atensor); } } -TEST(cpp_operations_test, conv2d) -{ +TEST(cpp_operations_test, conv2d) { // Iterate over kernel width, kernel height, - // input channels(inputC), output channels(outputC), + // input channels(input_c), output channels(output_c), // stride width, stride height // size 3 is chosen to cover all cases, where width bigger/smaller then height and equal/not equal to 1 using iT = int32_t; - for (iT kernelH = 1; kernelH <= 3; ++kernelH) - for (iT kernelW = 1; kernelW <= 3; ++kernelW) - for (iT inputC = 1; inputC <= 3; ++inputC) - for (iT outputC = 1; outputC <= 3; ++outputC) - for (iT strideH = 1; strideH <= 3; ++strideH) - for (iT strideW = 1; strideW <= 3; ++strideW) { - vector inputShapeData{1, 5, 7, static_cast(inputC)}; // NHWC - mir::Shape kernelShape{kernelH, kernelW, inputC, outputC}; // HWCN - mir::Shape strides{strideH, strideW}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); - mir::TensorVariant kernel = createNTensor(kernelShape, 1.0f); - auto opGenerator = [kernel, strides](mir::Graph& g, + for (iT kernel_h = 1; kernel_h <= 3; ++kernel_h) + for (iT kernel_w = 1; kernel_w <= 3; ++kernel_w) + for (iT input_c = 1; input_c <= 3; ++input_c) + for (iT output_c = 1; output_c <= 3; ++output_c) + for (iT stride_h = 1; stride_h <= 3; ++stride_h) + for (iT stride_w = 1; stride_w <= 3; ++stride_w) { + vector input_shape_data{1, 5, 7, static_cast(input_c)}; // NHWC + mir::Shape kernel_shape{kernel_h, kernel_w, input_c, output_c}; // HWCN + mir::Shape strides{stride_h, stride_w}; + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); + mir::TensorVariant kernel = createNTensor(kernel_shape, 1.0f); + auto op_generator = [kernel, strides](mir::Graph& g, const std::vector& inputs) { std::vector padding{0, 0}; return g.create("y", inputs[0], kernel, strides, padding, padding); }; - createAndRunTestGraph(opGenerator, conv2d, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, conv2d, input_ntensors, input_atensor); } } -TEST(cpp_operations_tests, depthwise_conv) -{ +TEST(cpp_operations_test, depthwise_conv) { // Iterate over kernel width, kernel height, // channels // stride width, stride height // layers multiplier // size 3 is chosen to cover all cases, where width bigger/smaller then height and equal/not equal to 1 using iT = int32_t; - for (iT kernelH = 1; kernelH <= 3; ++kernelH) - for (iT kernelW = 1; kernelW <= 3; ++kernelW) + for (iT kernel_h = 1; kernel_h <= 3; ++kernel_h) + for (iT kernel_w = 1; kernel_w <= 3; ++kernel_w) for (iT channels = 1; channels <= 3; ++channels) - for (iT strideW = 1; strideW <= 3; ++strideW) - for (iT strideH = 1; strideH <= 3; ++strideH) + for (iT stride_w = 1; stride_w <= 3; ++stride_w) + for (iT stride_h = 1; stride_h <= 3; ++stride_h) for (iT multiplier = 1; multiplier <= 2; ++multiplier) { - vector inputShapeData{1, 5, 7, static_cast(channels)}; // NHWC - mir::Shape kernelShape{kernelH, kernelW, channels, multiplier}; // HWCN - mir::Shape strides{strideH, strideW}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); - mir::TensorVariant kernel = createNTensor(kernelShape, 1.0f); - auto opGenerator = [kernel, strides](mir::Graph& g, - const std::vector& inputs) { + vector input_shape_data{1, 5, 7, static_cast(channels)}; // NHWC + mir::Shape kernel_shape{kernel_h, kernel_w, channels, multiplier}; // HWCN + mir::Shape strides{stride_h, stride_w}; + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); + mir::TensorVariant kernel = createNTensor(kernel_shape, 1.0f); + auto op_generator = [kernel, strides](mir::Graph& g, + const std::vector& inputs) { std::vector padding{0, 0}; return g.create("y", inputs[0], kernel, strides, padding, padding); }; - createAndRunTestGraph(opGenerator, depthwiseConv2d, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, depthwiseConv2d, input_ntensors, input_atensor); } } -TEST(cpp_operations_test, fully_connected) -{ - vector inputShapeData{1, 13}; +TEST(cpp_operations_test, fully_connected) { + vector input_shape_data{1, 13}; mir::Shape weightsShape{13, 7}; - vector> inputNTensors(1); - Tensor aInputTensor; - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); + vector> input_ntensors(1); + Tensor input_atensor; + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); mir::TensorVariant weights = createNTensor(weightsShape, 1.0f); - auto opGenerator = [weights](mir::Graph& g, const std::vector& inputs) { + auto op_generator = [weights](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], weights); }; - createAndRunTestGraph(opGenerator, fullConnect, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, fullConnect, input_ntensors, input_atensor); } +template +static mir::Operation* createPool(mir::Graph& g, + const std::vector& inputs, + mir::Shape& window_shape, + mir::Shape& strides, + irOps::PoolOp::BorderType border) { + std::vector padding{0, 0}; + return g.create("pool", inputs[0], poolT, window_shape, strides, + padding, padding, border, + irOps::PoolOp::RoundMode::floor); +}; + template -static void genericPoolTest(Func testFunc, const vector borders) -{ +static void genericPoolTest(Func test_func, const vector borders) { // Iterate over window width, window height // channels // stride width, stride height @@ -645,85 +674,78 @@ static void genericPoolTest(Func testFunc, const vector shapeData{1, 5, 7, static_cast(channels)}; - mir::Shape windowShape{windowH, windowW}; - mir::Shape strides{strideH, strideW}; - Tensor aInputTensor; - vector> inputNTensors(1); - fillTensors(inputNTensors[0], aInputTensor, shapeData, 1.0f); - - for (auto border: borders) - { - auto opGenerator = [windowShape, strides, border](mir::Graph& g, - const std::vector& inputs) { - std::vector padding{0, 0}; - return g.create("y", inputs[0], poolT, windowShape, strides, - padding, padding, border, - irOps::PoolOp::RoundMode::floor); - }; - - createAndRunTestGraph(opGenerator, testFunc, inputNTensors, aInputTensor); + for (iT stride_h = 1; stride_h <= 3; ++stride_h) + for (iT stride_w = 1; stride_w <= 3; ++stride_w) { + vector shape_data{1, 5, 7, static_cast(channels)}; + mir::Shape window_shape{windowH, windowW}; + mir::Shape strides{stride_h, stride_w}; + Tensor input_atensor; + vector> input_ntensors(1); + fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f); + + for (auto border: borders) { + using namespace std::placeholders; + + auto op_generator = + std::bind(createPool, _1, _2, window_shape, strides, border); + + createAndRunTestGraph(op_generator, test_func, input_ntensors, input_atensor); } } }; -TEST(cpp_operations_test, maxpool) -{ - vector borderTypes{ +TEST(cpp_operations_test, maxpool) { + vector border_types = { irOps::PoolOp::BorderType::EMPTY }; - genericPoolTest(maxPool, borderTypes); + genericPoolTest(maxPool, border_types); } -TEST(cpp_operations_test, avgpool) -{ - vector borderTypes{ +TEST(cpp_operations_test, avgpool) { + vector border_types = { irOps::PoolOp::BorderType::EMPTY, irOps::PoolOp::BorderType::ZEROFILLED }; - genericPoolTest(avgPool, borderTypes); + genericPoolTest(avgPool, border_types); } -TEST(cpp_operations_test, relu) -{ +TEST(cpp_operations_test, relu) { // test prerequisites - vector shapeData{2, 3, 4, 5}; - Tensor aInputTensor; - vector> inputNTensors(1); - fillTensors(inputNTensors[0], aInputTensor, shapeData, 1.0f); - auto opGenerator = [](mir::Graph& g, const std::vector& inputs) { + vector shape_data{2, 3, 4, 5}; + Tensor input_atensor; + vector> input_ntensors(1); + fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f); + auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0]); }; - createAndRunTestGraph(opGenerator, relu, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, relu, input_ntensors, input_atensor); } TEST(cpp_operations_test, elu) { // test prerequisites vector shape_data{2, 3, 4, 5}; Tensor a_input_tensor; - vector> input_n_tensors(1); - fillTensors(input_n_tensors[0], a_input_tensor, shape_data, 1.0f); + vector> input_ntensors(1); + fillTensors(input_ntensors[0], a_input_tensor, shape_data, 1.0f); auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], 1); }; - createAndRunTestGraph(op_generator, elu, input_n_tensors, a_input_tensor); + createAndRunTestGraph(op_generator, elu, input_ntensors, a_input_tensor); } TEST(cpp_operations_test, tanh) { // test prerequisites vector shape_data{2, 3, 4, 5}; Tensor a_input_tensor; - vector> input_n_tensors(1); - fillTensors(input_n_tensors[0], a_input_tensor, shape_data, 1.0f); + vector> input_ntensors(1); + fillTensors(input_ntensors[0], a_input_tensor, shape_data, 1.0f); auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0]); }; - createAndRunTestGraph(op_generator, tanhActivation, input_n_tensors, a_input_tensor); + createAndRunTestGraph(op_generator, tanhActivation, input_ntensors, a_input_tensor); } TEST(cpp_operations_test, reduceMeanTst) { @@ -742,8 +764,8 @@ TEST(cpp_operations_test, reduceMeanTst) { for (const bool keep_dims: {true, false}) { vector input_shape_data{2, 3, 4, 5}; Tensor a_input_tensor; - vector> input_n_tensors(1); - fillTensors(input_n_tensors[0], a_input_tensor, input_shape_data, 1.0f); + vector> input_ntensors(1); + fillTensors(input_ntensors[0], a_input_tensor, input_shape_data, 1.0f); auto op_generator = [axis_list, keep_dims](mir::Graph& g, const std::vector& inputs) { auto op = g.create( @@ -752,66 +774,64 @@ TEST(cpp_operations_test, reduceMeanTst) { return op; }; - createAndRunTestGraph(op_generator, reduceMean, input_n_tensors, a_input_tensor); + createAndRunTestGraph(op_generator, reduceMean, input_ntensors, a_input_tensor); } } } -TEST(cpp_operations_test, softmax) -{ +TEST(cpp_operations_test, softmax) { // iterate over number of dimensions in tensor - for (int numDims = 1; numDims <= 4; ++numDims) { + for (int num_dims = 1; num_dims <= 4; ++num_dims) { // test prerequisites - vector shapeData{2, 3, 4, 5}; - shapeData.resize(numDims); - int axis = numDims - 1; - Tensor aInputTensor; - vector> inputNTensors(1); - fillTensors(inputNTensors[0], aInputTensor, shapeData, 1.0f); - auto opGenerator = [axis](mir::Graph& g, const std::vector& inputs) { + vector shape_data{2, 3, 4, 5}; + shape_data.resize(num_dims); + int axis = num_dims - 1; + Tensor input_atensor; + vector> input_ntensors(1); + fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f); + auto op_generator = [axis](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0], axis); }; - createAndRunTestGraph(opGenerator, softmax, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, softmax, input_ntensors, input_atensor); } } -TEST(cpp_operations_test, reshape) -{ +TEST(cpp_operations_test, reshape) { // test prerequisites - vector inputShapeData{2, 3, 4, 5}; - vector outputShapeData{1, 120}; - mir::Shape nOutputShape; - fillNShape(nOutputShape, outputShapeData); - Tensor aInputTensor; - vector> inputNTensors(1); - fillTensors(inputNTensors[0], aInputTensor, inputShapeData, 1.0f); - auto opGenerator = [nOutputShape](mir::Graph& g, const std::vector& inputs) { - return g.create("y", inputs[0], nOutputShape); + vector input_shape_data{2, 3, 4, 5}; + vector output_shape_data{1, 120}; + mir::Shape output_nshape; + fillNShape(output_nshape, output_shape_data); + Tensor input_atensor; + vector> input_ntensors(1); + fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); + auto op_generator = [output_nshape](mir::Graph& g, const std::vector& inputs) { + return g.create("y", inputs[0], output_nshape); }; - createAndRunTestGraph(opGenerator, reshape, inputNTensors, aInputTensor); + createAndRunTestGraph(op_generator, reshape, input_ntensors, input_atensor); } TEST(cpp_operations_test, sqrtTest) { // test prerequisites vector shape_data{2, 3, 4, 5}; - Tensor a_input_tensor; - vector> input_n_tensor(1); - fillTensors(input_n_tensor[0], a_input_tensor, shape_data, 1.0f); + Tensor input_atensor; + vector> input_ntensor(1); + fillTensors(input_ntensor[0], input_atensor, shape_data, 1.0f); auto op_generator = [](mir::Graph& g, const std::vector& inputs) { return g.create("y", inputs[0]); }; - createAndRunTestGraph(op_generator, sqrtFN, input_n_tensor, a_input_tensor); + createAndRunTestGraph(op_generator, sqrtFN, input_ntensor, input_atensor); } TEST(cpp_operations_test, pad) { // test on matrix 2x3 vector input_shape{2,3}; - Tensor a_input_tensor; - vector> input_n_tensor(1); - fillTensors(input_n_tensor[0], a_input_tensor, input_shape, 1.0f); + Tensor input_atensor; + vector> input_ntensor(1); + fillTensors(input_ntensor[0], input_atensor, input_shape, 1.0f); // PadOp params int num_dims = input_shape.size(); vector> paddings; @@ -820,11 +840,52 @@ TEST(cpp_operations_test, pad) { float const_value = 0.0; - mir::Scalar constant_value(reinterpret_cast(&const_value), mir::DTYPE::FLOAT32, sizeof(float)); + mir::Scalar constant_value(reinterpret_cast(&const_value), + mir::DTYPE::FLOAT32, sizeof(float)); auto op_generator = [num_dims, &paddings, &constant_value] - (mir::Graph& g, const std::vector& inputs) - { return g.create("y", inputs[0], num_dims, paddings, constant_value); }; + (mir::Graph& g, const std::vector& inputs) { + return g.create("y", inputs[0], num_dims, paddings, constant_value); + }; + + createAndRunTestGraph(op_generator, pad, input_ntensor, input_atensor); +} + +TEST(cpp_operations_test, transpose) { + // test transpose for 4 dims tensors + vector input_shape_4d{2, 3, 4, 5}; + Tensor input_atensor_4d; + vector> input_ntensor_4d(1); + fillTensors(input_ntensor_4d[0], input_atensor_4d, input_shape_4d, 1.0f); + + vector test_cases_pack_4d[] = { + {0, 1, 2, 3}, + {1, 0, 2, 3}, + {3, 2, 1, 0} + }; + for (const auto& permute: test_cases_pack_4d) { + auto op_generator = [&permute](mir::Graph& g, const std::vector& inputs) { + return g.create("transpose", inputs[0], permute); + }; + createAndRunTestGraph(op_generator, transpose, input_ntensor_4d, input_atensor_4d); + } + + // test transpose for 3 dims tensors + vector input_shape_3d{3, 4, 5}; + Tensor input_atensor_3d; + vector> input_ntensor_3d(1); + fillTensors(input_ntensor_3d[0], input_atensor_3d, input_shape_3d, 1.0f); + vector test_cases_pack_3d[] = { + {0, 1, 2}, + {1, 0, 2}, + {2, 1, 0} + }; + for (const auto& permute: test_cases_pack_3d) { + auto op_generator = [&permute](mir::Graph& g, const std::vector& inputs) { + return g.create("transpose", inputs[0], permute); + }; + createAndRunTestGraph(op_generator, transpose, input_ntensor_3d, input_atensor_3d); + } + - createAndRunTestGraph(op_generator, pad, input_n_tensor, a_input_tensor); } -- 2.7.4