From c690d52bdd137ed6a17353aa7af35e8141ece77b Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Wed, 7 Sep 2022 19:04:21 +0900 Subject: [PATCH] Imported Upstream version 1.21.0 --- .ahub/sam/exclude.txt | 16 + .ahub/tcchecker-tca/config.yaml | 23 +- .github/workflows/check-pr-commit.yml | 7 + compiler/arser/include/arser/arser.h | 87 +- compiler/circle-eval-diff/CMakeLists.txt | 10 +- compiler/circle-eval-diff/driver/Driver.cpp | 115 +- compiler/circle-eval-diff/include/CircleEvalDiff.h | 30 +- compiler/circle-eval-diff/src/CircleEvalDiff.cpp | 217 ++- compiler/circle-eval-diff/src/InputDataLoader.cpp | 235 +++ compiler/circle-eval-diff/src/InputDataLoader.h | 98 ++ .../circle-eval-diff/src/InputDataLoader.test.cpp | 65 + compiler/circle-eval-diff/src/MetricPrinter.cpp | 479 +++++- compiler/circle-eval-diff/src/MetricPrinter.h | 127 ++ .../circle-eval-diff/src/MetricPrinter.test.cpp | 312 ++++ compiler/circle-eval-diff/src/ModuleEvalDiff.cpp | 216 --- compiler/circle-eval-diff/src/ModuleEvalDiff.h | 67 - compiler/circle-eval-diff/src/Tensor.cpp | 52 + compiler/circle-eval-diff/src/Tensor.h | 3 + compiler/circle-eval-diff/src/Tensor.test.cpp | 28 + compiler/circle-execution-plan/CMakeLists.txt | 9 + .../src/CircleExecutionPlan.cpp | 29 +- .../circle-execution-plan/src/ExecutionPlanner.cpp | 70 + .../circle-execution-plan/src/ExecutionPlanner.h | 2 + compiler/circle-inspect/driver/Driver.cpp | 2 +- compiler/circle-inspect/requires.cmake | 1 + compiler/circle-inspect/src/Dump.cpp | 20 +- compiler/circle-inspect/src/Reader.cpp | 127 -- compiler/circle-inspect/src/Reader.h | 87 -- compiler/circle-interpreter/CMakeLists.txt | 13 + compiler/circle-interpreter/requires.cmake | 6 + .../circle-interpreter/src/CircleInterpreter.cpp | 145 ++ compiler/circle-operator-test/CMakeLists.txt | 18 + compiler/circle-operator-test/README.md | 7 + compiler/circle-operator-test/requires.cmake | 2 + .../src/circle-operator.test.cpp | 248 ++++ compiler/circle-operator/CMakeLists.txt | 17 + compiler/circle-operator/README.md | 70 + compiler/circle-operator/driver/Driver.cpp | 112 ++ compiler/circle-operator/requires.cmake | 4 + compiler/circle-operator/src/Dump.cpp | 85 ++ compiler/circle-operator/src/Dump.h | 45 + compiler/circle-opselector/driver/Driver.cpp | 20 +- compiler/circle-part-value-test/CMakeLists.txt | 3 +- compiler/circle-partitioner-test/CMakeLists.txt | 3 +- compiler/circle-partitioner/CMakeLists.txt | 20 - compiler/circle-partitioner/README.md | 23 +- compiler/circle-partitioner/requires.cmake | 1 - .../circle-partitioner/src/CirclePartitioner.cpp | 83 +- .../CMakeLists.txt | 17 +- .../circle-quantizer-dredd-recipe-test/test.lst | 65 + compiler/circle-quantizer/CMakeLists.txt | 1 - compiler/circle-quantizer/requires.cmake | 1 - compiler/circle-quantizer/src/CircleQuantizer.cpp | 96 +- compiler/circle-tensordump/driver/Driver.cpp | 4 +- compiler/circle-tensordump/src/Dump.cpp | 7 +- compiler/circle-tensordump/src/Reader.cpp | 117 -- compiler/circle-tensordump/src/Reader.h | 85 -- compiler/circle-verify/src/Driver.cpp | 2 +- compiler/circle2circle-dredd-recipe-test/test.lst | 3 + compiler/circle2circle/CMakeLists.txt | 2 - compiler/circle2circle/requires.cmake | 1 - compiler/circle2circle/src/Circle2Circle.cpp | 504 ++----- compiler/circlechef/tools/file/Driver.cpp | 6 +- compiler/circlechef/tools/reverse/Driver.cpp | 6 +- compiler/circledump/CMakeLists.txt | 1 + compiler/circledump/driver/Driver.cpp | 16 +- compiler/circledump/include/circleread/Model.h | 43 - compiler/circledump/requires.cmake | 1 + compiler/circledump/src/Dump.cpp | 14 +- compiler/circledump/src/Load.cpp | 133 -- compiler/circledump/src/OpPrinter.cpp | 6 +- compiler/circledump/src/Read.cpp | 119 -- compiler/circledump/src/Read.h | 106 -- compiler/cli/CMakeLists.txt | 2 +- compiler/coco/core/src/IR/Module.cpp | 2 +- compiler/coco/generic/src/IR/Data.cpp | 3 +- compiler/common-artifacts/CMakeLists.txt | 49 +- compiler/common-artifacts/exclude.lst | 3 + .../common-artifacts/src/TestDataGenerator.cpp | 12 +- compiler/crew/CMakeLists.txt | 3 + compiler/crew/src/PConfigIni.cpp | 71 +- compiler/crew/src/PConfigIni.test.cpp | 61 +- compiler/crew/src/test_read_semicolon.ini | 2 + compiler/enco/core/src/CppGen/Host.cpp | 2 +- compiler/enco/core/src/CppGen/Subnet.cpp | 4 +- compiler/enco/core/src/Transforms/Split.cpp | 28 +- .../src/Conversion/DepthwiseConv2DConverter.cpp | 2 + compiler/kuma/src/IntervalSet.h | 1 + compiler/loco/include/loco/IR/DataTypeTraits.h | 9 + .../src/Passes/SimplifyDomainConversionPass.cpp | 12 +- compiler/luci-eval-driver/src/EvalDriver.cpp | 17 +- .../pal/cmsisnn/KernelsToBuild.lst | 2 + .../luci-interpreter/pal/cmsisnn/PALDequantize.h | 2 +- .../luci-interpreter/pal/cmsisnn/PALQuantize.h | 2 +- .../pal/cmsisnn/PALreference_ops.h | 1568 ++++++++++++++++++++ .../luci-interpreter/pal/linux/KernelsToBuild.lst | 3 + .../luci-interpreter/pal/linux/PALreference_ops.h | 22 + .../luci-interpreter/pal/mcu/KernelsToBuild.lst | 2 + compiler/luci-interpreter/pal/mcu/PALDequantize.h | 2 +- compiler/luci-interpreter/pal/mcu/PALQuantize.h | 2 +- .../luci-interpreter/pal/mcu/PALreference_ops.h | 1556 +++++++++++++++++++ compiler/luci-interpreter/src/core/KernelParams.h | 5 + compiler/luci-interpreter/src/kernels/Fill.cpp | 117 ++ compiler/luci-interpreter/src/kernels/Fill.h | 47 + .../luci-interpreter/src/kernels/Fill.test.cpp | 169 +++ .../luci-interpreter/src/kernels/MirrorPad.cpp | 2 + compiler/luci-interpreter/src/kernels/Pack.cpp | 5 +- .../luci-interpreter/src/kernels/Pack.test.cpp | 20 +- compiler/luci-interpreter/src/kernels/Pad.cpp | 2 + compiler/luci-interpreter/src/kernels/PadV2.cpp | 2 + .../luci-interpreter/src/kernels/ReduceMax.cpp | 181 +++ compiler/luci-interpreter/src/kernels/ReduceMax.h | 50 + .../src/kernels/ReduceMax.test.cpp | 103 ++ compiler/luci-interpreter/src/kernels/Shape.cpp | 70 + compiler/luci-interpreter/src/kernels/Shape.h | 46 + .../luci-interpreter/src/kernels/Shape.test.cpp | 89 ++ compiler/luci-interpreter/src/kernels/SplitV.cpp | 28 +- .../luci-interpreter/src/kernels/StridedSlice.cpp | 5 + .../luci-interpreter/src/loader/GraphLoader.cpp | 2 +- compiler/luci-interpreter/src/loader/nodes/Add.cpp | 4 +- .../luci-interpreter/src/loader/nodes/ArgMax.cpp | 4 +- .../src/loader/nodes/AveragePool2D.cpp | 4 +- .../src/loader/nodes/BatchMatMul.cpp | 4 +- .../src/loader/nodes/BatchToSpaceND.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Cast.cpp | 4 +- .../src/loader/nodes/Concatenation.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Conv2D.cpp | 4 +- .../src/loader/nodes/DepthToSpace.cpp | 4 +- .../src/loader/nodes/DepthwiseConv2D.cpp | 4 +- .../src/loader/nodes/Dequantize.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Div.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Elu.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Equal.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Exp.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Fill.cpp | 37 + .../luci-interpreter/src/loader/nodes/Floor.cpp | 4 +- .../luci-interpreter/src/loader/nodes/FloorDiv.cpp | 4 +- .../src/loader/nodes/FullyConnected.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Gather.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Greater.cpp | 4 +- .../src/loader/nodes/GreaterEqual.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/If.cpp | 4 +- .../src/loader/nodes/InstanceNorm.cpp | 4 +- .../src/loader/nodes/L2Normalize.cpp | 4 +- .../luci-interpreter/src/loader/nodes/L2Pool2D.cpp | 4 +- .../src/loader/nodes/LeakyRelu.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Less.cpp | 4 +- .../src/loader/nodes/LessEqual.cpp | 4 +- .../loader/nodes/LocalResponseNormalization.cpp | 4 +- .../src/loader/nodes/LogSoftmax.cpp | 4 +- .../src/loader/nodes/LogicalAnd.cpp | 4 +- .../src/loader/nodes/LogicalNot.cpp | 4 +- .../src/loader/nodes/LogicalOr.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Logistic.cpp | 4 +- .../src/loader/nodes/MaxPool2D.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Maximum.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Mean.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Minimum.cpp | 4 +- .../src/loader/nodes/MirrorPad.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Mul.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Neg.cpp | 4 +- .../luci-interpreter/src/loader/nodes/NotEqual.cpp | 4 +- .../luci-interpreter/src/loader/nodes/PRelu.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Pack.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Pad.cpp | 4 +- .../luci-interpreter/src/loader/nodes/PadV2.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Pow.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Quantize.cpp | 5 +- .../src/loader/nodes/ReduceMax.cpp | 55 + .../luci-interpreter/src/loader/nodes/Relu.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Relu6.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Reshape.cpp | 4 +- .../src/loader/nodes/ResizeBilinear.cpp | 4 +- .../src/loader/nodes/ResizeNearestNeighbor.cpp | 4 +- .../src/loader/nodes/ReverseV2.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Rsqrt.cpp | 4 +- .../luci-interpreter/src/loader/nodes/SVDF.cpp | 5 +- .../luci-interpreter/src/loader/nodes/Shape.cpp | 39 + .../luci-interpreter/src/loader/nodes/Slice.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Softmax.cpp | 4 +- .../src/loader/nodes/SpaceToBatchND.cpp | 4 +- .../src/loader/nodes/SpaceToDepth.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Split.cpp | 4 +- .../luci-interpreter/src/loader/nodes/SplitV.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Sqrt.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Square.cpp | 4 +- .../src/loader/nodes/SquaredDifference.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Squeeze.cpp | 4 +- .../src/loader/nodes/StridedSlice.cpp | 4 +- compiler/luci-interpreter/src/loader/nodes/Sub.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Tanh.cpp | 4 +- .../src/loader/nodes/Transpose.cpp | 4 +- .../src/loader/nodes/TransposeConv.cpp | 4 +- .../luci-interpreter/src/loader/nodes/Unpack.cpp | 4 +- .../luci-interpreter/src/loader/nodes/While.cpp | 4 +- compiler/luci-micro/CMakeLists.txt | 2 +- .../luci-micro/luci-interpreter/CMakeLists.txt | 15 + compiler/luci-micro/luci-interpreter/README.md | 158 ++ .../include/luci_interpreter/BuddyMemoryManager.h | 144 ++ .../luci_interpreter/GraphBuilderRegistry.h | 35 + .../include/luci_interpreter/Interpreter.h | 84 ++ .../include/luci_interpreter/MemoryManager.h | 37 + .../include/luci_interpreter/SimpleMemoryManager.h | 34 + .../include/luci_interpreter/StaticMemoryManager.h | 45 + .../include/luci_interpreter/TestMemoryManager.h | 47 + .../include/luci_interpreter/core/DataType.h | 36 + .../include/luci_interpreter/core/Tensor.h | 186 +++ .../pal/cmsisnn/KernelsToBuild.lst | 62 + .../luci-interpreter/pal/cmsisnn/PALArgMax.h | 33 + .../pal/cmsisnn/PALAveragePool2d.h | 124 ++ .../pal/cmsisnn/PALBatchToSpaceND.h | 37 + .../luci-interpreter/pal/cmsisnn/PALConv2d.h | 199 +++ .../luci-interpreter/pal/cmsisnn/PALDepthToSpace.h | 35 + .../pal/cmsisnn/PALDepthwiseConv2d.h | 192 +++ .../luci-interpreter/pal/cmsisnn/PALDequantize.h | 44 + .../luci-interpreter/pal/cmsisnn/PALElu.h | 33 + .../pal/cmsisnn/PALFullyConnected.h | 114 ++ .../luci-interpreter/pal/cmsisnn/PALL2Normalize.h | 34 + .../luci-interpreter/pal/cmsisnn/PALL2Pool2D.h | 33 + .../luci-interpreter/pal/cmsisnn/PALLeakyRelu.h | 32 + .../luci-interpreter/pal/cmsisnn/PALMul.h | 45 + .../luci-interpreter/pal/cmsisnn/PALNeg.h | 32 + .../luci-interpreter/pal/cmsisnn/PALQuantize.h | 44 + .../pal/cmsisnn/PALResizeBilinear.h | 37 + .../pal/cmsisnn/PALResizeNearestNeighbor.h | 37 + .../luci-interpreter/pal/cmsisnn/PALSVDF.h | 190 +++ .../luci-interpreter/pal/cmsisnn/PALSoftmax.h | 78 + .../pal/cmsisnn/PALSpaceToBatchND.h | 38 + .../luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h | 35 + .../luci-interpreter/pal/cmsisnn/PALSub.h | 35 + .../luci-interpreter/pal/cmsisnn/pal.cmake | 65 + .../luci-interpreter/pal/linux/KernelsToBuild.lst | 77 + .../luci-interpreter/pal/linux/PALArgMax.h | 33 + .../luci-interpreter/pal/linux/PALAveragePool2d.h | 73 + .../luci-interpreter/pal/linux/PALBatchMatMul.h | 67 + .../luci-interpreter/pal/linux/PALBatchToSpaceND.h | 37 + .../luci-interpreter/pal/linux/PALConv2d.h | 127 ++ .../luci-interpreter/pal/linux/PALDepthToSpace.h | 35 + .../pal/linux/PALDepthwiseConv2d.h | 91 ++ .../luci-interpreter/pal/linux/PALDequantize.h | 34 + .../luci-micro/luci-interpreter/pal/linux/PALElu.h | 31 + .../luci-interpreter/pal/linux/PALFullyConnected.h | 61 + .../luci-interpreter/pal/linux/PALGather.h | 35 + .../luci-interpreter/pal/linux/PALL2Normalize.h | 34 + .../luci-interpreter/pal/linux/PALL2Pool2D.h | 33 + .../luci-interpreter/pal/linux/PALLeakyRelu.h | 32 + .../pal/linux/PALLocalResponseNormalization.h | 34 + .../luci-interpreter/pal/linux/PALLogSoftmax.h | 47 + .../luci-micro/luci-interpreter/pal/linux/PALMul.h | 55 + .../luci-micro/luci-interpreter/pal/linux/PALNeg.h | 32 + .../luci-interpreter/pal/linux/PALQuantize.h | 44 + .../luci-interpreter/pal/linux/PALRelu.h | 39 + .../luci-interpreter/pal/linux/PALRelu6.h | 39 + .../luci-interpreter/pal/linux/PALResizeBilinear.h | 37 + .../pal/linux/PALResizeNearestNeighbor.h | 37 + .../luci-interpreter/pal/linux/PALSVDF.h | 90 ++ .../luci-interpreter/pal/linux/PALSlice.h | 33 + .../luci-interpreter/pal/linux/PALSoftmax.h | 47 + .../luci-interpreter/pal/linux/PALSpaceToBatchND.h | 38 + .../luci-interpreter/pal/linux/PALSpaceToDepth.h | 35 + .../luci-interpreter/pal/linux/PALSplit.h | 33 + .../luci-micro/luci-interpreter/pal/linux/PALSub.h | 35 + .../luci-interpreter/pal/linux/pal.cmake | 82 + .../luci-interpreter/pal/mcu/KernelsToBuild.lst | 62 + .../luci-interpreter/pal/mcu/PALArgMax.h | 33 + .../luci-interpreter/pal/mcu/PALAveragePool2d.h | 73 + .../luci-interpreter/pal/mcu/PALBatchToSpaceND.h | 37 + .../luci-interpreter/pal/mcu/PALConv2d.h | 85 ++ .../luci-interpreter/pal/mcu/PALDepthToSpace.h | 35 + .../luci-interpreter/pal/mcu/PALDepthwiseConv2d.h | 91 ++ .../luci-interpreter/pal/mcu/PALDequantize.h | 44 + .../luci-micro/luci-interpreter/pal/mcu/PALElu.h | 33 + .../luci-interpreter/pal/mcu/PALFullyConnected.h | 61 + .../luci-interpreter/pal/mcu/PALL2Normalize.h | 34 + .../luci-interpreter/pal/mcu/PALL2Pool2D.h | 33 + .../luci-interpreter/pal/mcu/PALLeakyRelu.h | 32 + .../luci-micro/luci-interpreter/pal/mcu/PALMul.h | 45 + .../luci-micro/luci-interpreter/pal/mcu/PALNeg.h | 32 + .../luci-interpreter/pal/mcu/PALQuantize.h | 44 + .../luci-interpreter/pal/mcu/PALResizeBilinear.h | 37 + .../pal/mcu/PALResizeNearestNeighbor.h | 37 + .../luci-micro/luci-interpreter/pal/mcu/PALSVDF.h | 258 ++++ .../luci-interpreter/pal/mcu/PALSoftmax.h | 62 + .../luci-interpreter/pal/mcu/PALSpaceToBatchND.h | 38 + .../luci-interpreter/pal/mcu/PALSpaceToDepth.h | 35 + .../luci-micro/luci-interpreter/pal/mcu/PALSub.h | 35 + .../luci-micro/luci-interpreter/pal/mcu/pal.cmake | 56 + .../luci-micro/luci-interpreter/requires.cmake | 1 + .../luci-interpreter/src/BuddyMemoryManager.cpp | 96 ++ .../src/BuddyMemoryManager.test.cpp | 69 + .../luci-micro/luci-interpreter/src/CMakeLists.txt | 61 + .../luci-interpreter/src/Interpreter.cpp | 145 ++ .../luci-interpreter/src/SimpleMemoryManager.cpp | 51 + .../luci-interpreter/src/StaticMemoryManager.cpp | 39 + .../luci-interpreter/src/TestMemoryManager.cpp | 45 + .../luci-interpreter/src/core/CMakeLists.txt | 19 + .../luci-interpreter/src/core/EventNotifier.h | 36 + .../luci-micro/luci-interpreter/src/core/Kernel.h | 75 + .../luci-interpreter/src/core/KernelParams.h | 228 +++ .../luci-interpreter/src/core/RuntimeGraph.cpp | 201 +++ .../luci-interpreter/src/core/RuntimeGraph.h | 71 + .../luci-interpreter/src/core/RuntimeModule.h | 60 + .../luci-interpreter/src/core/Tensor.cpp | 58 + .../luci-interpreter/src/import/CMakeLists.txt | 15 + .../src/import/GraphBuilderRegistry.cpp | 33 + .../src/import/Nodes/CircleReferencingConst.cpp | 113 ++ .../src/import/Nodes/CircleReferencingConst.h | 39 + .../luci-interpreter/src/kernels/Add.cpp | 220 +++ .../luci-micro/luci-interpreter/src/kernels/Add.h | 50 + .../luci-interpreter/src/kernels/Add.test.cpp | 357 +++++ .../luci-interpreter/src/kernels/ArgMax.cpp | 139 ++ .../luci-interpreter/src/kernels/ArgMax.h | 44 + .../luci-interpreter/src/kernels/ArgMax.test.cpp | 122 ++ .../luci-interpreter/src/kernels/AveragePool2D.cpp | 194 +++ .../luci-interpreter/src/kernels/AveragePool2D.h | 54 + .../src/kernels/AveragePool2D.test.cpp | 283 ++++ .../luci-interpreter/src/kernels/BatchMatMul.cpp | 188 +++ .../luci-interpreter/src/kernels/BatchMatMul.h | 49 + .../src/kernels/BatchMatMul.test.cpp | 272 ++++ .../src/kernels/BatchToSpaceND.cpp | 104 ++ .../luci-interpreter/src/kernels/BatchToSpaceND.h | 45 + .../src/kernels/BatchToSpaceND.test.cpp | 100 ++ .../luci-interpreter/src/kernels/BinaryOpCommon.h | 73 + .../luci-interpreter/src/kernels/CMakeLists.txt | 43 + .../luci-interpreter/src/kernels/Cast.cpp | 143 ++ .../luci-micro/luci-interpreter/src/kernels/Cast.h | 43 + .../luci-interpreter/src/kernels/Cast.test.cpp | 241 +++ .../luci-interpreter/src/kernels/Concatenation.cpp | 149 ++ .../luci-interpreter/src/kernels/Concatenation.h | 48 + .../src/kernels/Concatenation.test.cpp | 268 ++++ .../luci-interpreter/src/kernels/Conv2D.cpp | 456 ++++++ .../luci-interpreter/src/kernels/Conv2D.h | 59 + .../luci-interpreter/src/kernels/Conv2D.test.cpp | 707 +++++++++ .../luci-interpreter/src/kernels/DepthToSpace.cpp | 80 + .../luci-interpreter/src/kernels/DepthToSpace.h | 45 + .../src/kernels/DepthToSpace.test.cpp | 115 ++ .../src/kernels/DepthwiseConv2D.cpp | 451 ++++++ .../luci-interpreter/src/kernels/DepthwiseConv2D.h | 57 + .../src/kernels/DepthwiseConv2D.test.cpp | 622 ++++++++ .../luci-interpreter/src/kernels/Dequantize.cpp | 79 + .../luci-interpreter/src/kernels/Dequantize.h | 43 + .../src/kernels/Dequantize.test.cpp | 149 ++ .../luci-interpreter/src/kernels/Div.cpp | 152 ++ .../luci-micro/luci-interpreter/src/kernels/Div.h | 49 + .../luci-interpreter/src/kernels/Div.test.cpp | 230 +++ .../luci-interpreter/src/kernels/Elu.cpp | 52 + .../luci-micro/luci-interpreter/src/kernels/Elu.h | 43 + .../luci-interpreter/src/kernels/Elu.test.cpp | 81 + .../luci-interpreter/src/kernels/Equal.cpp | 142 ++ .../luci-interpreter/src/kernels/Equal.h | 54 + .../luci-interpreter/src/kernels/Equal.test.cpp | 306 ++++ .../luci-interpreter/src/kernels/Exp.cpp | 56 + .../luci-micro/luci-interpreter/src/kernels/Exp.h | 46 + .../luci-interpreter/src/kernels/Exp.test.cpp | 55 + .../luci-interpreter/src/kernels/ExpandDims.cpp | 88 ++ .../luci-interpreter/src/kernels/ExpandDims.h | 44 + .../src/kernels/ExpandDims.test.cpp | 115 ++ .../luci-interpreter/src/kernels/Fill.cpp | 117 ++ .../luci-micro/luci-interpreter/src/kernels/Fill.h | 47 + .../luci-interpreter/src/kernels/Fill.test.cpp | 169 +++ .../luci-interpreter/src/kernels/Floor.cpp | 57 + .../luci-interpreter/src/kernels/Floor.h | 45 + .../luci-interpreter/src/kernels/Floor.test.cpp | 76 + .../luci-interpreter/src/kernels/FloorDiv.cpp | 85 ++ .../luci-interpreter/src/kernels/FloorDiv.h | 46 + .../luci-interpreter/src/kernels/FloorDiv.test.cpp | 147 ++ .../src/kernels/FullyConnected.cpp | 192 +++ .../luci-interpreter/src/kernels/FullyConnected.h | 51 + .../src/kernels/FullyConnected.test.cpp | 260 ++++ .../luci-interpreter/src/kernels/Gather.cpp | 139 ++ .../luci-interpreter/src/kernels/Gather.h | 47 + .../luci-interpreter/src/kernels/Gather.test.cpp | 137 ++ .../luci-interpreter/src/kernels/Greater.cpp | 142 ++ .../luci-interpreter/src/kernels/Greater.h | 54 + .../luci-interpreter/src/kernels/Greater.test.cpp | 334 +++++ .../luci-interpreter/src/kernels/GreaterEqual.cpp | 145 ++ .../luci-interpreter/src/kernels/GreaterEqual.h | 54 + .../src/kernels/GreaterEqual.test.cpp | 333 +++++ .../luci-micro/luci-interpreter/src/kernels/If.cpp | 94 ++ .../luci-micro/luci-interpreter/src/kernels/If.h | 49 + .../luci-interpreter/src/kernels/If.test.cpp | 161 ++ .../luci-interpreter/src/kernels/InstanceNorm.cpp | 121 ++ .../luci-interpreter/src/kernels/InstanceNorm.h | 49 + .../src/kernels/InstanceNorm.test.cpp | 97 ++ .../luci-interpreter/src/kernels/L2Normalize.cpp | 75 + .../luci-interpreter/src/kernels/L2Normalize.h | 46 + .../src/kernels/L2Normalize.test.cpp | 126 ++ .../luci-interpreter/src/kernels/L2Pool2D.cpp | 88 ++ .../luci-interpreter/src/kernels/L2Pool2D.h | 49 + .../luci-interpreter/src/kernels/L2Pool2D.test.cpp | 291 ++++ .../luci-interpreter/src/kernels/LeakyRelu.cpp | 90 ++ .../luci-interpreter/src/kernels/LeakyRelu.h | 53 + .../src/kernels/LeakyRelu.test.cpp | 127 ++ .../luci-interpreter/src/kernels/Less.cpp | 142 ++ .../luci-micro/luci-interpreter/src/kernels/Less.h | 54 + .../luci-interpreter/src/kernels/Less.test.cpp | 334 +++++ .../luci-interpreter/src/kernels/LessEqual.cpp | 142 ++ .../luci-interpreter/src/kernels/LessEqual.h | 54 + .../src/kernels/LessEqual.test.cpp | 334 +++++ .../src/kernels/LocalResponseNormalization.cpp | 65 + .../src/kernels/LocalResponseNormalization.h | 44 + .../kernels/LocalResponseNormalization.test.cpp | 157 ++ .../luci-interpreter/src/kernels/LogSoftmax.cpp | 92 ++ .../luci-interpreter/src/kernels/LogSoftmax.h | 48 + .../src/kernels/LogSoftmax.test.cpp | 124 ++ .../luci-interpreter/src/kernels/LogicalAnd.cpp | 62 + .../luci-interpreter/src/kernels/LogicalAnd.h | 47 + .../src/kernels/LogicalAnd.test.cpp | 101 ++ .../luci-interpreter/src/kernels/LogicalNot.cpp | 60 + .../luci-interpreter/src/kernels/LogicalNot.h | 46 + .../src/kernels/LogicalNot.test.cpp | 78 + .../luci-interpreter/src/kernels/LogicalOr.cpp | 49 + .../luci-interpreter/src/kernels/LogicalOr.h | 44 + .../src/kernels/LogicalOr.test.cpp | 104 ++ .../luci-interpreter/src/kernels/Logistic.cpp | 94 ++ .../luci-interpreter/src/kernels/Logistic.h | 52 + .../luci-interpreter/src/kernels/Logistic.test.cpp | 148 ++ .../luci-interpreter/src/kernels/MaxPool2D.cpp | 150 ++ .../luci-interpreter/src/kernels/MaxPool2D.h | 52 + .../src/kernels/MaxPool2D.test.cpp | 139 ++ .../luci-interpreter/src/kernels/Maximum.cpp | 65 + .../luci-interpreter/src/kernels/Maximum.h | 47 + .../luci-interpreter/src/kernels/Maximum.test.cpp | 82 + .../luci-interpreter/src/kernels/Mean.cpp | 346 +++++ .../luci-micro/luci-interpreter/src/kernels/Mean.h | 55 + .../luci-interpreter/src/kernels/Mean.test.cpp | 240 +++ .../luci-interpreter/src/kernels/Minimum.cpp | 65 + .../luci-interpreter/src/kernels/Minimum.h | 47 + .../luci-interpreter/src/kernels/Minimum.test.cpp | 82 + .../luci-interpreter/src/kernels/MirrorPad.cpp | 172 +++ .../luci-interpreter/src/kernels/MirrorPad.h | 45 + .../src/kernels/MirrorPad.test.cpp | 225 +++ .../luci-interpreter/src/kernels/Mul.cpp | 150 ++ .../luci-micro/luci-interpreter/src/kernels/Mul.h | 52 + .../luci-interpreter/src/kernels/Mul.test.cpp | 292 ++++ .../luci-interpreter/src/kernels/Neg.cpp | 58 + .../luci-micro/luci-interpreter/src/kernels/Neg.h | 46 + .../luci-interpreter/src/kernels/Neg.test.cpp | 71 + .../luci-interpreter/src/kernels/NotEqual.cpp | 142 ++ .../luci-interpreter/src/kernels/NotEqual.h | 54 + .../luci-interpreter/src/kernels/NotEqual.test.cpp | 306 ++++ .../luci-interpreter/src/kernels/OneHot.cpp | 136 ++ .../luci-interpreter/src/kernels/OneHot.h | 48 + .../luci-interpreter/src/kernels/OneHot.test.cpp | 192 +++ .../luci-interpreter/src/kernels/PRelu.cpp | 211 +++ .../luci-interpreter/src/kernels/PRelu.h | 59 + .../luci-interpreter/src/kernels/PRelu.test.cpp | 397 +++++ .../luci-interpreter/src/kernels/Pack.cpp | 142 ++ .../luci-micro/luci-interpreter/src/kernels/Pack.h | 46 + .../luci-interpreter/src/kernels/Pack.test.cpp | 163 ++ .../luci-interpreter/src/kernels/Pad.cpp | 114 ++ .../luci-micro/luci-interpreter/src/kernels/Pad.h | 43 + .../luci-interpreter/src/kernels/Pad.test.cpp | 109 ++ .../luci-interpreter/src/kernels/PadV2.cpp | 108 ++ .../luci-interpreter/src/kernels/PadV2.h | 44 + .../luci-interpreter/src/kernels/PadV2.test.cpp | 90 ++ .../luci-interpreter/src/kernels/Pow.cpp | 79 + .../luci-micro/luci-interpreter/src/kernels/Pow.h | 46 + .../luci-interpreter/src/kernels/Pow.test.cpp | 140 ++ .../luci-interpreter/src/kernels/Quantize.cpp | 160 ++ .../luci-interpreter/src/kernels/Quantize.h | 43 + .../luci-interpreter/src/kernels/Quantize.test.cpp | 254 ++++ .../luci-interpreter/src/kernels/Relu.cpp | 114 ++ .../luci-micro/luci-interpreter/src/kernels/Relu.h | 51 + .../luci-interpreter/src/kernels/Relu.test.cpp | 168 +++ .../luci-interpreter/src/kernels/Relu6.cpp | 88 ++ .../luci-interpreter/src/kernels/Relu6.h | 50 + .../luci-interpreter/src/kernels/Relu6.test.cpp | 149 ++ .../luci-interpreter/src/kernels/Reshape.cpp | 90 ++ .../luci-interpreter/src/kernels/Reshape.h | 43 + .../luci-interpreter/src/kernels/Reshape.test.cpp | 82 + .../src/kernels/ResizeBilinear.cpp | 74 + .../luci-interpreter/src/kernels/ResizeBilinear.h | 45 + .../src/kernels/ResizeBilinear.test.cpp | 255 ++++ .../src/kernels/ResizeNearestNeighbor.cpp | 74 + .../src/kernels/ResizeNearestNeighbor.h | 45 + .../src/kernels/ResizeNearestNeighbor.test.cpp | 231 +++ .../luci-interpreter/src/kernels/ReverseV2.cpp | 81 + .../luci-interpreter/src/kernels/ReverseV2.h | 43 + .../src/kernels/ReverseV2.test.cpp | 71 + .../luci-interpreter/src/kernels/Rsqrt.cpp | 66 + .../luci-interpreter/src/kernels/Rsqrt.h | 46 + .../luci-interpreter/src/kernels/Rsqrt.test.cpp | 90 ++ .../luci-interpreter/src/kernels/SVDF.cpp | 241 +++ .../luci-micro/luci-interpreter/src/kernels/SVDF.h | 56 + .../luci-interpreter/src/kernels/SVDF.test.cpp | 341 +++++ .../luci-interpreter/src/kernels/Shape.cpp | 70 + .../luci-interpreter/src/kernels/Shape.h | 46 + .../luci-interpreter/src/kernels/Shape.test.cpp | 89 ++ .../luci-interpreter/src/kernels/Slice.cpp | 153 ++ .../luci-interpreter/src/kernels/Slice.h | 44 + .../luci-interpreter/src/kernels/Slice.test.cpp | 70 + .../luci-interpreter/src/kernels/Softmax.cpp | 92 ++ .../luci-interpreter/src/kernels/Softmax.h | 49 + .../luci-interpreter/src/kernels/Softmax.test.cpp | 117 ++ .../src/kernels/SpaceToBatchND.cpp | 103 ++ .../luci-interpreter/src/kernels/SpaceToBatchND.h | 45 + .../src/kernels/SpaceToBatchND.test.cpp | 123 ++ .../luci-interpreter/src/kernels/SpaceToDepth.cpp | 79 + .../luci-interpreter/src/kernels/SpaceToDepth.h | 45 + .../src/kernels/SpaceToDepth.test.cpp | 65 + .../luci-interpreter/src/kernels/Split.cpp | 81 + .../luci-interpreter/src/kernels/Split.h | 47 + .../luci-interpreter/src/kernels/Split.test.cpp | 129 ++ .../luci-interpreter/src/kernels/SplitV.cpp | 111 ++ .../luci-interpreter/src/kernels/SplitV.h | 49 + .../luci-interpreter/src/kernels/SplitV.test.cpp | 112 ++ .../luci-interpreter/src/kernels/Sqrt.cpp | 66 + .../luci-micro/luci-interpreter/src/kernels/Sqrt.h | 46 + .../luci-interpreter/src/kernels/Sqrt.test.cpp | 90 ++ .../luci-interpreter/src/kernels/Square.cpp | 66 + .../luci-interpreter/src/kernels/Square.h | 46 + .../luci-interpreter/src/kernels/Square.test.cpp | 52 + .../src/kernels/SquaredDifference.cpp | 64 + .../src/kernels/SquaredDifference.h | 47 + .../src/kernels/SquaredDifference.test.cpp | 78 + .../luci-interpreter/src/kernels/Squeeze.cpp | 86 ++ .../luci-interpreter/src/kernels/Squeeze.h | 44 + .../luci-interpreter/src/kernels/Squeeze.test.cpp | 74 + .../luci-interpreter/src/kernels/StridedSlice.cpp | 150 ++ .../luci-interpreter/src/kernels/StridedSlice.h | 47 + .../src/kernels/StridedSlice.test.cpp | 112 ++ .../luci-interpreter/src/kernels/Sub.cpp | 164 ++ .../luci-micro/luci-interpreter/src/kernels/Sub.h | 49 + .../luci-interpreter/src/kernels/Sub.test.cpp | 266 ++++ .../luci-interpreter/src/kernels/Tanh.cpp | 93 ++ .../luci-micro/luci-interpreter/src/kernels/Tanh.h | 52 + .../luci-interpreter/src/kernels/Tanh.test.cpp | 164 ++ .../luci-interpreter/src/kernels/TestUtils.cpp | 128 ++ .../luci-interpreter/src/kernels/TestUtils.h | 296 ++++ .../luci-interpreter/src/kernels/Transpose.cpp | 84 ++ .../luci-interpreter/src/kernels/Transpose.h | 44 + .../src/kernels/Transpose.test.cpp | 115 ++ .../luci-interpreter/src/kernels/TransposeConv.cpp | 351 +++++ .../luci-interpreter/src/kernels/TransposeConv.h | 65 + .../src/kernels/TransposeConv.test.cpp | 353 +++++ .../luci-interpreter/src/kernels/Unpack.cpp | 84 ++ .../luci-interpreter/src/kernels/Unpack.h | 46 + .../luci-interpreter/src/kernels/Unpack.test.cpp | 148 ++ .../luci-interpreter/src/kernels/Utils.cpp | 198 +++ .../luci-interpreter/src/kernels/Utils.h | 293 ++++ .../luci-interpreter/src/kernels/While.cpp | 116 ++ .../luci-interpreter/src/kernels/While.h | 48 + .../luci-interpreter/src/kernels/While.test.cpp | 101 ++ .../luci-interpreter/src/loader/CMakeLists.txt | 39 + .../luci-interpreter/src/loader/GraphLoader.cpp | 344 +++++ .../luci-interpreter/src/loader/GraphLoader.h | 55 + .../luci-interpreter/src/loader/KernelBuilder.cpp | 104 ++ .../luci-interpreter/src/loader/KernelBuilder.h | 52 + .../src/loader/KernelBuilder.test.cpp | 1376 +++++++++++++++++ .../src/loader/KernelBuilderHelper.cpp | 64 + .../src/loader/KernelBuilderHelper.h | 84 ++ .../luci-interpreter/src/loader/ModuleLoader.cpp | 53 + .../luci-interpreter/src/loader/ModuleLoader.h | 52 + .../luci-interpreter/src/loader/RuntimeToIR.h | 38 + .../luci-interpreter/src/loader/nodes/Add.cpp | 40 + .../luci-interpreter/src/loader/nodes/ArgMax.cpp | 39 + .../src/loader/nodes/AveragePool2D.cpp | 64 + .../src/loader/nodes/BatchMatMul.cpp | 70 + .../src/loader/nodes/BatchToSpaceND.cpp | 38 + .../luci-interpreter/src/loader/nodes/Builders.h | 37 + .../luci-interpreter/src/loader/nodes/Cast.cpp | 37 + .../src/loader/nodes/Concatenation.cpp | 42 + .../luci-interpreter/src/loader/nodes/Conv2D.cpp | 66 + .../src/loader/nodes/DepthToSpace.cpp | 39 + .../src/loader/nodes/DepthwiseConv2D.cpp | 67 + .../src/loader/nodes/Dequantize.cpp | 35 + .../luci-interpreter/src/loader/nodes/Div.cpp | 39 + .../luci-interpreter/src/loader/nodes/Elu.cpp | 35 + .../luci-interpreter/src/loader/nodes/Equal.cpp | 38 + .../luci-interpreter/src/loader/nodes/Exp.cpp | 36 + .../src/loader/nodes/ExpandDims.cpp | 37 + .../luci-interpreter/src/loader/nodes/Fill.cpp | 37 + .../luci-interpreter/src/loader/nodes/Floor.cpp | 36 + .../luci-interpreter/src/loader/nodes/FloorDiv.cpp | 37 + .../src/loader/nodes/FullyConnected.cpp | 42 + .../luci-interpreter/src/loader/nodes/Gather.cpp | 42 + .../luci-interpreter/src/loader/nodes/Greater.cpp | 37 + .../src/loader/nodes/GreaterEqual.cpp | 37 + .../luci-interpreter/src/loader/nodes/If.cpp | 47 + .../src/loader/nodes/InstanceNorm.cpp | 43 + .../src/loader/nodes/L2Normalize.cpp | 39 + .../luci-interpreter/src/loader/nodes/L2Pool2D.cpp | 44 + .../src/loader/nodes/LeakyRelu.cpp | 38 + .../luci-interpreter/src/loader/nodes/Less.cpp | 37 + .../src/loader/nodes/LessEqual.cpp | 37 + .../loader/nodes/LocalResponseNormalization.cpp | 42 + .../src/loader/nodes/LogSoftmax.cpp | 36 + .../src/loader/nodes/LogicalAnd.cpp | 37 + .../src/loader/nodes/LogicalNot.cpp | 36 + .../src/loader/nodes/LogicalOr.cpp | 37 + .../luci-interpreter/src/loader/nodes/Logistic.cpp | 36 + .../src/loader/nodes/MaxPool2D.cpp | 44 + .../luci-interpreter/src/loader/nodes/Maximum.cpp | 37 + .../luci-interpreter/src/loader/nodes/Mean.cpp | 61 + .../luci-interpreter/src/loader/nodes/Minimum.cpp | 37 + .../src/loader/nodes/MirrorPad.cpp | 40 + .../luci-interpreter/src/loader/nodes/Mul.cpp | 40 + .../luci-interpreter/src/loader/nodes/Neg.cpp | 36 + .../luci-interpreter/src/loader/nodes/NotEqual.cpp | 37 + .../luci-interpreter/src/loader/nodes/OneHot.cpp | 42 + .../luci-interpreter/src/loader/nodes/PRelu.cpp | 37 + .../luci-interpreter/src/loader/nodes/Pack.cpp | 44 + .../luci-interpreter/src/loader/nodes/Pad.cpp | 37 + .../luci-interpreter/src/loader/nodes/PadV2.cpp | 38 + .../luci-interpreter/src/loader/nodes/Pow.cpp | 38 + .../luci-interpreter/src/loader/nodes/Quantize.cpp | 36 + .../luci-interpreter/src/loader/nodes/Relu.cpp | 36 + .../luci-interpreter/src/loader/nodes/Relu6.cpp | 36 + .../luci-interpreter/src/loader/nodes/Reshape.cpp | 38 + .../src/loader/nodes/ResizeBilinear.cpp | 41 + .../src/loader/nodes/ResizeNearestNeighbor.cpp | 46 + .../src/loader/nodes/ReverseV2.cpp | 37 + .../luci-interpreter/src/loader/nodes/Rsqrt.cpp | 36 + .../luci-interpreter/src/loader/nodes/SVDF.cpp | 92 ++ .../luci-interpreter/src/loader/nodes/Shape.cpp | 39 + .../luci-interpreter/src/loader/nodes/Slice.cpp | 39 + .../luci-interpreter/src/loader/nodes/Softmax.cpp | 39 + .../src/loader/nodes/SpaceToBatchND.cpp | 39 + .../src/loader/nodes/SpaceToDepth.cpp | 39 + .../luci-interpreter/src/loader/nodes/Split.cpp | 40 + .../luci-interpreter/src/loader/nodes/SplitV.cpp | 41 + .../luci-interpreter/src/loader/nodes/Sqrt.cpp | 36 + .../luci-interpreter/src/loader/nodes/Square.cpp | 36 + .../src/loader/nodes/SquaredDifference.cpp | 37 + .../luci-interpreter/src/loader/nodes/Squeeze.cpp | 39 + .../src/loader/nodes/StridedSlice.cpp | 47 + .../luci-interpreter/src/loader/nodes/Sub.cpp | 40 + .../luci-interpreter/src/loader/nodes/Tanh.cpp | 36 + .../src/loader/nodes/Transpose.cpp | 37 + .../src/loader/nodes/TransposeConv.cpp | 55 + .../luci-interpreter/src/loader/nodes/Unpack.cpp | 42 + .../luci-interpreter/src/loader/nodes/While.cpp | 47 + compiler/luci-micro/standalone/CMakeLists.txt | 19 +- compiler/luci-pass-value-test/CMakeLists.txt | 7 + compiler/luci-pass-value-test/test.lst | 9 + compiler/luci-value-test/test.lst | 2 + .../luci/export/src/CircleBuiltinTypesExtractor.h | 4 + compiler/luci/export/src/CircleOps.lst | 1 + compiler/luci/export/src/CircleTensorExporter.cpp | 6 + compiler/luci/import/CMakeLists.txt | 1 + compiler/luci/import/include/luci/Import/Nodes.h | 1 + .../include/luci/Import/Nodes/CircleDensify.h | 37 + compiler/luci/import/include/luci/ImporterEx.h | 39 + compiler/luci/import/src/GraphBuilderRegistry.cpp | 2 +- compiler/luci/import/src/ImporterEx.cpp | 61 + compiler/luci/import/src/Nodes/CircleConst.cpp | 4 + compiler/luci/import/src/Nodes/CircleDensify.cpp | 43 + compiler/luci/lang/include/luci/IR/CircleNodes.h | 1 + compiler/luci/lang/include/luci/IR/CircleNodes.lst | 1 + .../lang/include/luci/IR/Nodes/CircleDensify.h | 40 + compiler/luci/lang/src/Nodes/CircleConst.cpp | 1 + .../luci/lang/src/Nodes/CircleDensify.test.cpp | 76 + .../luci/logex/src/CircleNodeSummaryBuilder.cpp | 1 + .../luci/logex/src/CircleNodeSummaryBuilders.cpp | 16 + .../luci/logex/src/CircleNodeSummaryBuilders.h | 5 + compiler/luci/partition/include/luci/ConnectNode.h | 219 +++ compiler/luci/partition/src/ConnectNode.cpp | 2 +- compiler/luci/partition/src/ConnectNode.h | 218 --- compiler/luci/partition/src/ConnectNode.test.h | 2 +- compiler/luci/partition/src/Nodes/CircleAbs.cpp | 2 +- .../luci/partition/src/Nodes/CircleAbs.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleAdd.cpp | 2 +- .../luci/partition/src/Nodes/CircleAdd.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleAddN.cpp | 2 +- .../luci/partition/src/Nodes/CircleAddN.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleArgMax.cpp | 2 +- .../luci/partition/src/Nodes/CircleArgMax.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleArgMin.cpp | 2 +- .../luci/partition/src/Nodes/CircleArgMin.test.cpp | 2 +- .../partition/src/Nodes/CircleAveragePool2D.cpp | 2 +- .../src/Nodes/CircleAveragePool2D.test.cpp | 2 +- .../src/Nodes/CircleBCQFullyConnected.cpp | 2 +- .../src/Nodes/CircleBCQFullyConnected.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleBCQGather.cpp | 2 +- .../partition/src/Nodes/CircleBCQGather.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleBatchMatMul.cpp | 2 +- .../partition/src/Nodes/CircleBatchMatMul.test.cpp | 2 +- .../partition/src/Nodes/CircleBatchToSpaceND.cpp | 2 +- .../src/Nodes/CircleBatchToSpaceND.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleCast.cpp | 2 +- .../luci/partition/src/Nodes/CircleCast.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleCeil.cpp | 2 +- .../luci/partition/src/Nodes/CircleCeil.test.cpp | 2 +- .../partition/src/Nodes/CircleConcatenation.cpp | 2 +- .../src/Nodes/CircleConcatenation.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleConst.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleConv2D.cpp | 2 +- .../luci/partition/src/Nodes/CircleConv2D.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleCos.cpp | 2 +- .../luci/partition/src/Nodes/CircleCos.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleCustom.cpp | 2 +- .../luci/partition/src/Nodes/CircleCustom.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleCustomOut.cpp | 2 +- .../partition/src/Nodes/CircleCustomOut.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleDensify.cpp | 38 + .../partition/src/Nodes/CircleDensify.test.cpp | 90 ++ .../partition/src/Nodes/CircleDepthToSpace.cpp | 2 +- .../src/Nodes/CircleDepthToSpace.test.cpp | 2 +- .../partition/src/Nodes/CircleDepthwiseConv2D.cpp | 2 +- .../src/Nodes/CircleDepthwiseConv2D.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleDequantize.cpp | 2 +- .../partition/src/Nodes/CircleDequantize.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleDiv.cpp | 2 +- .../luci/partition/src/Nodes/CircleDiv.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleElu.cpp | 2 +- .../luci/partition/src/Nodes/CircleElu.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleEqual.cpp | 2 +- .../luci/partition/src/Nodes/CircleEqual.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleExp.cpp | 2 +- .../luci/partition/src/Nodes/CircleExp.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleExpandDims.cpp | 2 +- .../partition/src/Nodes/CircleExpandDims.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleFakeQuant.cpp | 2 +- .../partition/src/Nodes/CircleFakeQuant.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleFill.cpp | 2 +- .../luci/partition/src/Nodes/CircleFill.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleFloor.cpp | 2 +- .../luci/partition/src/Nodes/CircleFloor.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleFloorDiv.cpp | 2 +- .../partition/src/Nodes/CircleFloorDiv.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleFloorMod.cpp | 2 +- .../partition/src/Nodes/CircleFloorMod.test.cpp | 2 +- .../partition/src/Nodes/CircleFullyConnected.cpp | 2 +- .../src/Nodes/CircleFullyConnected.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleGather.cpp | 2 +- .../luci/partition/src/Nodes/CircleGather.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleGatherNd.cpp | 2 +- .../partition/src/Nodes/CircleGatherNd.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleGreater.cpp | 2 +- .../partition/src/Nodes/CircleGreater.test.cpp | 2 +- .../partition/src/Nodes/CircleGreaterEqual.cpp | 2 +- .../src/Nodes/CircleGreaterEqual.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleIf.cpp | 2 +- .../luci/partition/src/Nodes/CircleIf.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleIfOut.cpp | 2 +- .../luci/partition/src/Nodes/CircleIfOut.test.cpp | 2 +- .../partition/src/Nodes/CircleInstanceNorm.cpp | 2 +- .../src/Nodes/CircleInstanceNorm.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleL2Normalize.cpp | 2 +- .../partition/src/Nodes/CircleL2Normalize.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleL2Pool2D.cpp | 2 +- .../partition/src/Nodes/CircleL2Pool2D.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLeakyRelu.cpp | 2 +- .../partition/src/Nodes/CircleLeakyRelu.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleLess.cpp | 2 +- .../luci/partition/src/Nodes/CircleLess.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLessEqual.cpp | 2 +- .../partition/src/Nodes/CircleLessEqual.test.cpp | 2 +- .../src/Nodes/CircleLocalResponseNormalization.cpp | 2 +- .../CircleLocalResponseNormalization.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleLog.cpp | 2 +- .../luci/partition/src/Nodes/CircleLog.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLogSoftmax.cpp | 2 +- .../partition/src/Nodes/CircleLogSoftmax.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLogicalAnd.cpp | 2 +- .../partition/src/Nodes/CircleLogicalAnd.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLogicalNot.cpp | 2 +- .../partition/src/Nodes/CircleLogicalNot.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLogicalOr.cpp | 2 +- .../partition/src/Nodes/CircleLogicalOr.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleLogistic.cpp | 2 +- .../partition/src/Nodes/CircleLogistic.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleMatrixDiag.cpp | 2 +- .../partition/src/Nodes/CircleMatrixDiag.test.cpp | 2 +- .../partition/src/Nodes/CircleMatrixSetDiag.cpp | 2 +- .../src/Nodes/CircleMatrixSetDiag.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleMaxPool2D.cpp | 2 +- .../partition/src/Nodes/CircleMaxPool2D.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleMaximum.cpp | 2 +- .../partition/src/Nodes/CircleMaximum.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleMean.cpp | 2 +- .../luci/partition/src/Nodes/CircleMean.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleMinimum.cpp | 2 +- .../partition/src/Nodes/CircleMinimum.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleMirrorPad.cpp | 2 +- .../partition/src/Nodes/CircleMirrorPad.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleMul.cpp | 2 +- .../luci/partition/src/Nodes/CircleMul.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleNeg.cpp | 2 +- .../luci/partition/src/Nodes/CircleNeg.test.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV4.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV4.test.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV4Out.cpp | 2 +- .../Nodes/CircleNonMaxSuppressionV4Out.test.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV5.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV5.test.cpp | 2 +- .../src/Nodes/CircleNonMaxSuppressionV5Out.cpp | 2 +- .../Nodes/CircleNonMaxSuppressionV5Out.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleNotEqual.cpp | 2 +- .../partition/src/Nodes/CircleNotEqual.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleOneHot.cpp | 2 +- .../luci/partition/src/Nodes/CircleOneHot.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleOutputDummy.cpp | 2 +- .../partition/src/Nodes/CircleOutputExclude.cpp | 2 +- compiler/luci/partition/src/Nodes/CirclePRelu.cpp | 2 +- .../luci/partition/src/Nodes/CirclePRelu.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CirclePack.cpp | 2 +- .../luci/partition/src/Nodes/CirclePack.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CirclePad.cpp | 2 +- .../luci/partition/src/Nodes/CirclePad.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CirclePadV2.cpp | 2 +- .../luci/partition/src/Nodes/CirclePadV2.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CirclePow.cpp | 2 +- .../luci/partition/src/Nodes/CirclePow.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleQuantize.cpp | 2 +- .../partition/src/Nodes/CircleQuantize.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRange.cpp | 2 +- .../luci/partition/src/Nodes/CircleRange.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRank.cpp | 2 +- .../luci/partition/src/Nodes/CircleRank.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReduceAny.cpp | 2 +- .../partition/src/Nodes/CircleReduceAny.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReduceMax.cpp | 2 +- .../partition/src/Nodes/CircleReduceMax.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReduceMin.cpp | 2 +- .../partition/src/Nodes/CircleReduceMin.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReduceProd.cpp | 2 +- .../partition/src/Nodes/CircleReduceProd.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRelu.cpp | 2 +- .../luci/partition/src/Nodes/CircleRelu.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRelu6.cpp | 2 +- .../luci/partition/src/Nodes/CircleRelu6.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReluN1To1.cpp | 2 +- .../partition/src/Nodes/CircleReluN1To1.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReshape.cpp | 2 +- .../partition/src/Nodes/CircleReshape.test.cpp | 2 +- .../partition/src/Nodes/CircleResizeBilinear.cpp | 2 +- .../src/Nodes/CircleResizeBilinear.test.cpp | 2 +- .../src/Nodes/CircleResizeNearestNeighbor.cpp | 2 +- .../src/Nodes/CircleResizeNearestNeighbor.test.cpp | 2 +- .../partition/src/Nodes/CircleReverseSequence.cpp | 2 +- .../src/Nodes/CircleReverseSequence.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleReverseV2.cpp | 2 +- .../partition/src/Nodes/CircleReverseV2.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRound.cpp | 2 +- .../luci/partition/src/Nodes/CircleRound.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleRsqrt.cpp | 2 +- .../luci/partition/src/Nodes/CircleRsqrt.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSVDF.cpp | 2 +- .../luci/partition/src/Nodes/CircleSVDF.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleScatterNd.cpp | 2 +- .../partition/src/Nodes/CircleScatterNd.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSegmentSum.cpp | 2 +- .../partition/src/Nodes/CircleSegmentSum.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSelect.cpp | 2 +- .../luci/partition/src/Nodes/CircleSelect.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSelectV2.cpp | 2 +- .../partition/src/Nodes/CircleSelectV2.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleShape.cpp | 2 +- .../luci/partition/src/Nodes/CircleShape.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSin.cpp | 2 +- .../luci/partition/src/Nodes/CircleSin.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSlice.cpp | 2 +- .../luci/partition/src/Nodes/CircleSlice.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSoftmax.cpp | 2 +- .../partition/src/Nodes/CircleSoftmax.test.cpp | 2 +- .../partition/src/Nodes/CircleSpaceToBatchND.cpp | 2 +- .../src/Nodes/CircleSpaceToBatchND.test.cpp | 2 +- .../partition/src/Nodes/CircleSpaceToDepth.cpp | 2 +- .../src/Nodes/CircleSpaceToDepth.test.cpp | 2 +- .../partition/src/Nodes/CircleSparseToDense.cpp | 2 +- .../src/Nodes/CircleSparseToDense.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSplit.cpp | 2 +- .../luci/partition/src/Nodes/CircleSplit.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSplitOut.cpp | 2 +- .../partition/src/Nodes/CircleSplitOut.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSplitV.cpp | 2 +- .../luci/partition/src/Nodes/CircleSplitV.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSplitVOut.cpp | 2 +- .../partition/src/Nodes/CircleSplitVOut.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSqrt.cpp | 2 +- .../luci/partition/src/Nodes/CircleSqrt.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSquare.cpp | 2 +- .../luci/partition/src/Nodes/CircleSquare.test.cpp | 2 +- .../src/Nodes/CircleSquaredDifference.cpp | 2 +- .../src/Nodes/CircleSquaredDifference.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleSqueeze.cpp | 2 +- .../partition/src/Nodes/CircleSqueeze.test.cpp | 2 +- .../partition/src/Nodes/CircleStridedSlice.cpp | 2 +- .../src/Nodes/CircleStridedSlice.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSub.cpp | 2 +- .../luci/partition/src/Nodes/CircleSub.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleSum.cpp | 2 +- .../luci/partition/src/Nodes/CircleSum.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleTanh.cpp | 2 +- .../luci/partition/src/Nodes/CircleTanh.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleTile.cpp | 2 +- .../luci/partition/src/Nodes/CircleTile.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleTopKV2.cpp | 2 +- .../luci/partition/src/Nodes/CircleTopKV2.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleTopKV2Out.cpp | 2 +- .../partition/src/Nodes/CircleTopKV2Out.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleTranspose.cpp | 2 +- .../partition/src/Nodes/CircleTranspose.test.cpp | 2 +- .../partition/src/Nodes/CircleTransposeConv.cpp | 2 +- .../src/Nodes/CircleTransposeConv.test.cpp | 2 +- .../src/Nodes/CircleUnidirectionalSequenceLSTM.cpp | 2 +- .../CircleUnidirectionalSequenceLSTM.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleUnique.cpp | 2 +- .../luci/partition/src/Nodes/CircleUnique.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleUniqueOut.cpp | 2 +- .../partition/src/Nodes/CircleUniqueOut.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleUnpack.cpp | 2 +- .../luci/partition/src/Nodes/CircleUnpack.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleUnpackOut.cpp | 2 +- .../partition/src/Nodes/CircleUnpackOut.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleVariable.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleWhere.cpp | 2 +- .../luci/partition/src/Nodes/CircleWhere.test.cpp | 2 +- compiler/luci/partition/src/Nodes/CircleWhile.cpp | 2 +- .../luci/partition/src/Nodes/CircleWhile.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleWhileOut.cpp | 2 +- .../partition/src/Nodes/CircleWhileOut.test.cpp | 2 +- .../luci/partition/src/Nodes/CircleZerosLike.cpp | 2 +- .../partition/src/Nodes/CircleZerosLike.test.cpp | 2 +- compiler/luci/partition/src/PartitionIR.cpp | 2 +- compiler/luci/partition/src/PartitionMerge.cpp | 2 +- compiler/luci/partition/src/PartitionPGroups.cpp | 2 +- compiler/luci/partition/src/PartitionPModules.cpp | 4 +- compiler/luci/pass/CMakeLists.txt | 8 + compiler/luci/pass/include/luci/CircleOptimizer.h | 3 + .../luci/pass/include/luci/Pass/FoldDensifyPass.h | 38 + .../luci/Pass/RemoveRedundantDequantizePass.h | 37 + .../luci/Pass/RemoveUnnecessaryReshapeNetPass.h | 39 + .../Pass/ReplaceNonConstFCWithBatchMatMulPass.h | 37 + .../include/luci/Pass/ResolveCustomOpSplitVPass.h | 37 + compiler/luci/pass/src/CircleOptimizer.cpp | 39 +- compiler/luci/pass/src/CircleQuantizer.cpp | 7 +- compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp | 329 +++- .../luci/pass/src/ConvertNCHWToNHWCPass.test.cpp | 525 ++++++- .../pass/src/ConvertToFakeQuantizedModelPass.cpp | 57 +- compiler/luci/pass/src/FoldDensifyPass.cpp | 180 +++ compiler/luci/pass/src/FoldDensifyPass.test.cpp | 158 ++ compiler/luci/pass/src/FoldDequantizePass.cpp | 96 +- compiler/luci/pass/src/FoldDequantizePass.test.cpp | 377 +++++ compiler/luci/pass/src/FoldSparseToDensePass.cpp | 2 + .../luci/pass/src/ForwardReshapeToUnaryOpPass.cpp | 49 + .../pass/src/ForwardReshapeToUnaryOpPass.test.cpp | 86 ++ .../pass/src/FuseAddWithFullyConnectedPass.cpp | 6 + compiler/luci/pass/src/FuseAddWithTConvPass.cpp | 20 +- .../luci/pass/src/FuseBatchNormWithTConvPass.cpp | 53 +- compiler/luci/pass/src/FuseInstanceNormPass.cpp | 186 +-- .../luci/pass/src/PropagateQParamBackwardPass.cpp | 1 + .../luci/pass/src/PropagateQParamForwardPass.cpp | 9 +- compiler/luci/pass/src/QuantizationUtils.cpp | 126 +- compiler/luci/pass/src/QuantizationUtils.h | 16 +- compiler/luci/pass/src/QuantizeActivation.cpp | 11 +- compiler/luci/pass/src/QuantizeBias.cpp | 14 + compiler/luci/pass/src/QuantizeBias.test.cpp | 189 +++ .../pass/src/QuantizeDequantizeWeightsPass.cpp | 7 +- compiler/luci/pass/src/QuantizeWeights.cpp | 1 + compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp | 91 +- .../luci/pass/src/QuantizedModelVerifier.test.cpp | 53 + .../pass/src/RemoveRedundantDequantizePass.cpp | 80 + .../src/RemoveRedundantDequantizePass.test.cpp | 114 ++ .../pass/src/RemoveUnnecessaryReshapeNetPass.cpp | 172 +++ .../src/RemoveUnnecessaryReshapeNetPass.test.cpp | 123 ++ .../src/ReplaceNonConstFCWithBatchMatMulPass.cpp | 196 +++ .../ReplaceNonConstFCWithBatchMatMulPass.test.cpp | 189 +++ .../luci/pass/src/ResolveCustomOpSplitVPass.cpp | 172 +++ .../pass/src/ResolveCustomOpSplitVPass.test.cpp | 175 +++ .../luci/pass/src/VerifyQuantizedNodeGranularity.h | 7 + compiler/luci/pass/src/VerifyQuantizedNodeType.cpp | 9 + compiler/luci/pass/src/VerifyQuantizedNodeType.h | 1 + .../pass/src/helpers/SparsityFormatConverter.cpp | 312 ++++ .../pass/src/helpers/SparsityFormatConverter.h | 129 ++ compiler/luci/requires.cmake | 1 + compiler/luci/service/src/CircleCloneNode.h | 1 + .../luci/service/src/CircleShapeInferenceRule.cpp | 103 +- .../luci/service/src/CircleTypeInferenceRule.cpp | 5 + compiler/luci/service/src/Nodes/CircleDensify.cpp | 27 + .../luci/service/src/Nodes/CircleDensify.test.cpp | 33 + .../luci/service/src/ShapeInfer_StridedSlice.cpp | 261 +++- compiler/luci/tests/test.lst | 2 + compiler/mio-circle04/include/mio_circle/Helper.h | 17 + compiler/mio-circle04/include/mio_circle/Reader.h | 101 ++ compiler/mio-circle04/src/Reader.cpp | 147 ++ compiler/mio-circle04/src/Reader.test.cpp | 60 + compiler/mio-tflite/README.md | 2 + compiler/mio-tflite260/README.md | 2 + compiler/mir/include/mir/Graph.h | 4 + compiler/mir/src/Graph.cpp | 9 +- compiler/mir2loco/src/mir2loco.test.cpp | 49 +- compiler/moco/import/src/Importer.cpp | 2 +- compiler/moco/lang/src/IR/TFNode.cpp | 1 + compiler/one-cmds/CMakeLists.txt | 8 +- compiler/one-cmds/dummy-driver/CMakeLists.txt | 27 + compiler/one-cmds/dummy-driver/src/dummy-infer.cpp | 34 + .../one-cmds/dummy-driver/src/dummy-inferV2.cpp | 34 + compiler/one-cmds/dummy-driver/src/help-infer.cpp | 42 + compiler/one-cmds/how-to-use-one-commands.txt | 5 +- compiler/one-cmds/one-build | 5 +- compiler/one-cmds/one-build.template.cfg | 1 + compiler/one-cmds/one-codegen | 2 - compiler/one-cmds/one-import-bcq | 11 +- compiler/one-cmds/one-import-onnx | 83 +- compiler/one-cmds/one-import-pytorch | 7 +- compiler/one-cmds/one-import-tf | 10 +- compiler/one-cmds/one-import-tflite | 5 +- compiler/one-cmds/one-infer | 224 +++ compiler/one-cmds/one-init | 280 ++++ compiler/one-cmds/one-optimize | 13 +- compiler/one-cmds/one-pack | 2 - compiler/one-cmds/one-partition | 130 ++ compiler/one-cmds/one-prepare-venv | 10 +- compiler/one-cmds/one-profile | 2 - compiler/one-cmds/one-quantize | 211 ++- compiler/one-cmds/onecc | 105 +- compiler/one-cmds/onecc.template.cfg | 144 +- compiler/one-cmds/onelib/CfgRunner.py | 99 ++ compiler/one-cmds/onelib/OptionBuilder.py | 95 ++ compiler/one-cmds/onelib/TopologicalSortHelper.py | 45 + compiler/one-cmds/onelib/WorkflowRunner.py | 131 ++ compiler/one-cmds/onelib/constant.py | 7 +- compiler/one-cmds/onelib/make_cmd.py | 5 + compiler/one-cmds/onnx_legalizer.py | 59 +- compiler/one-cmds/requires.cmake | 1 + compiler/one-cmds/tests/CMakeLists.txt | 12 + compiler/one-cmds/tests/OONECC_024.cfg | 2 + compiler/one-cmds/tests/one-build_008.cfg | 1 - compiler/one-cmds/tests/one-build_009.cfg | 1 - compiler/one-cmds/tests/one-import-onnx_002.test | 71 + .../one-cmds/tests/one-infer-test-post-process.py | 16 + compiler/one-cmds/tests/one-infer_001.test | 42 + compiler/one-cmds/tests/one-infer_002.test | 48 + compiler/one-cmds/tests/one-infer_003.test | 48 + compiler/one-cmds/tests/one-infer_004.test | 38 + compiler/one-cmds/tests/one-infer_005.cfg | 3 + compiler/one-cmds/tests/one-infer_005.test | 51 + compiler/one-cmds/tests/one-infer_006.test | 53 + compiler/one-cmds/tests/one-infer_neg_001.test | 39 + compiler/one-cmds/tests/one-infer_neg_002.test | 40 + compiler/one-cmds/tests/one-infer_neg_003.test | 40 + compiler/one-cmds/tests/one-infer_neg_004.test | 41 + compiler/one-cmds/tests/one-infer_neg_005.test | 54 + compiler/one-cmds/tests/one-optimize_001.test | 2 +- compiler/one-cmds/tests/one-optimize_002.test | 2 +- compiler/one-cmds/tests/one-optimize_neg_001.test | 2 +- compiler/one-cmds/tests/one-optimize_neg_002.test | 2 +- compiler/one-cmds/tests/one-optimize_neg_003.test | 2 +- compiler/one-cmds/tests/one-optimize_neg_004.test | 2 +- compiler/one-cmds/tests/one-partition_001.test | 46 + compiler/one-cmds/tests/one-partition_neg_001.test | 51 + compiler/one-cmds/tests/one-partition_neg_002.test | 47 + compiler/one-cmds/tests/one-quantize_010.test | 65 + compiler/one-cmds/tests/one-quantize_011.test | 56 + .../one-cmds/tests/one-quantize_012.qconf.json | 16 + compiler/one-cmds/tests/one-quantize_012.test | 46 + .../one-cmds/tests/one-quantize_013.qconf.json | 16 + compiler/one-cmds/tests/one-quantize_013.test | 48 + compiler/one-cmds/tests/one-quantize_014.test | 59 + compiler/one-cmds/tests/one-quantize_015.test | 45 + compiler/one-cmds/tests/one-quantize_neg_019.test | 2 +- compiler/one-cmds/tests/one-quantize_neg_020.test | 48 + compiler/one-cmds/tests/onecc_008.cfg | 1 - compiler/one-cmds/tests/onecc_009.cfg | 1 - compiler/one-cmds/tests/onecc_024.cfg | 22 + compiler/one-cmds/tests/onecc_024.test | 77 + compiler/one-cmds/tests/onecc_025.cfg | 20 + compiler/one-cmds/tests/onecc_025.test | 40 + compiler/one-cmds/tests/onecc_026.cfg | 16 + compiler/one-cmds/tests/onecc_026.test | 46 + compiler/one-cmds/tests/onecc_027.cfg | 15 + compiler/one-cmds/tests/onecc_027.test | 46 + compiler/one-cmds/tests/onecc_028.test | 42 + compiler/one-cmds/tests/onecc_028.workflow.json | 37 + compiler/one-cmds/tests/onecc_029.test | 42 + compiler/one-cmds/tests/onecc_029.workflow.json | 30 + compiler/one-cmds/tests/onecc_030.test | 48 + compiler/one-cmds/tests/onecc_030.workflow.json | 29 + compiler/one-cmds/tests/onecc_031.test | 48 + compiler/one-cmds/tests/onecc_031.workflow.json | 33 + compiler/one-cmds/tests/onecc_032.test | 48 + compiler/one-cmds/tests/onecc_032.workflow.json | 42 + compiler/one-cmds/tests/onecc_033.test | 42 + compiler/one-cmds/tests/onecc_033.workflow.json | 42 + compiler/one-cmds/tests/onecc_034.test | 48 + compiler/one-cmds/tests/onecc_034.workflow.json | 35 + compiler/one-cmds/tests/onecc_035.test | 47 + compiler/one-cmds/tests/onecc_035.workflow.json | 22 + compiler/one-cmds/tests/onecc_036.test | 47 + compiler/one-cmds/tests/onecc_036.workflow.json | 18 + compiler/one-cmds/tests/onecc_037.test | 42 + compiler/one-cmds/tests/onecc_037.workflow.json | 29 + compiler/one-cmds/tests/onecc_038.test | 42 + compiler/one-cmds/tests/onecc_038.workflow.json | 31 + compiler/one-cmds/tests/onecc_039.test | 48 + compiler/one-cmds/tests/onecc_039.workflow.json | 21 + compiler/one-cmds/tests/onecc_040.cfg | 20 + compiler/one-cmds/tests/onecc_040.test | 42 + compiler/one-cmds/tests/onecc_040.workflow.json | 10 + compiler/one-cmds/tests/onecc_041.cfg | 16 + compiler/one-cmds/tests/onecc_041.test | 58 + compiler/one-cmds/tests/onecc_041.workflow.json | 61 + compiler/one-cmds/tests/onecc_neg_009.test | 69 + compiler/one-cmds/tests/onecc_neg_010.test | 41 + compiler/one-cmds/tests/onecc_neg_011.cfg | 13 + compiler/one-cmds/tests/onecc_neg_011.test | 41 + compiler/one-cmds/tests/onecc_neg_012.cfg | 15 + compiler/one-cmds/tests/onecc_neg_012.test | 41 + compiler/one-cmds/tests/onecc_neg_013.test | 41 + compiler/one-cmds/tests/onecc_neg_014.test | 41 + .../one-cmds/tests/onecc_neg_014.workflow.json | 3 + compiler/one-cmds/tests/onecc_neg_015.test | 42 + .../one-cmds/tests/onecc_neg_015.workflow.json | 21 + compiler/one-cmds/tests/onecc_neg_016.test | 42 + .../one-cmds/tests/onecc_neg_016.workflow.json | 21 + compiler/one-cmds/tests/onecc_neg_017.test | 41 + .../one-cmds/tests/onecc_neg_017.workflow.json | 18 + compiler/one-cmds/tests/onecc_neg_018.test | 41 + .../one-cmds/tests/onecc_neg_018.workflow.json | 24 + compiler/one-cmds/tests/onecc_neg_019.test | 41 + .../one-cmds/tests/onecc_neg_019.workflow.json | 21 + compiler/one-cmds/tests/onecc_neg_020.test | 41 + .../one-cmds/tests/onecc_neg_020.workflow.json | 21 + compiler/one-cmds/tests/onecc_neg_021.test | 41 + .../one-cmds/tests/onecc_neg_021.workflow.json | 44 + compiler/one-cmds/tests/onecc_neg_022.cfg | 16 + compiler/one-cmds/tests/onecc_neg_022.test | 41 + .../one-cmds/tests/onecc_neg_022.workflow.json | 63 + compiler/one-cmds/tests/onecc_neg_023.test | 41 + .../one-cmds/tests/onecc_neg_023.workflow.json | 30 + compiler/one-cmds/tests/prepare_test_materials.sh | 14 + compiler/one-cmds/utils.py | 59 +- compiler/onnx-tools/CMakeLists.txt | 6 + .../pota-quantization-value-test/CMakeLists.txt | 4 + .../record-minmax-conversion-test/CMakeLists.txt | 4 + compiler/record-minmax/driver/Driver.cpp | 39 +- compiler/record-minmax/include/RecordFunction.h | 2 +- compiler/record-minmax/src/MinMaxObserver.cpp | 3 +- compiler/record-minmax/src/RecordMinMax.cpp | 8 +- compiler/souschef/CMakeLists.txt | 7 + compiler/souschef/include/souschef/Data/Explicit.h | 35 + compiler/souschef/include/souschef/Data/Gaussian.h | 21 + compiler/souschef/src/Explicit.cpp | 21 + compiler/souschef/src/Gaussian.cpp | 45 +- compiler/tf2circle-conversion-test/CMakeLists.txt | 4 + compiler/tf2circle-dredd-pb-test/CMakeLists.txt | 4 + compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt | 4 + compiler/tf2circle-model-test/CMakeLists.txt | 4 + compiler/tf2tflite-dredd-pb-test/CMakeLists.txt | 4 + compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt | 4 + compiler/tf2tflite-value-pb-test/CMakeLists.txt | 4 + compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt | 4 + .../tf2tfliteV2-conversion-test/CMakeLists.txt | 4 + compiler/tf2tfliteV2/tf2tfliteV2.py | 9 + compiler/tfl-inspect/CMakeLists.txt | 4 +- compiler/tfl-inspect/driver/Driver.cpp | 2 +- compiler/tfl-verify/CMakeLists.txt | 4 +- compiler/tfl-verify/src/Driver.cpp | 2 +- compiler/tflchef/CMakeLists.txt | 5 + compiler/tflchef/core/src/Convert.cpp | 222 +++ compiler/tflchef/core/src/Convert.h | 49 + compiler/tflchef/core/src/DataChef.def | 4 + compiler/tflchef/core/src/ModelChef.cpp | 167 ++- compiler/tflchef/core/src/Op/Densify.cpp | 29 + compiler/tflchef/core/src/Op/Densify.h | 46 + compiler/tflchef/core/src/OpChef.def | 1 + compiler/tflchef/core/src/OpChefs.h | 1 + compiler/tflchef/proto/tflchef.proto | 12 + compiler/tflchef/tests/make_sparse/test.recipe | 44 + compiler/tflchef/tests/make_sparse_f16/test.recipe | 54 + compiler/tflchef/tflite/CMakeLists.txt | 1 + compiler/tflchef/tflite/src/Convert.cpp | 3 +- compiler/tflchef/tflite/src/FillerHelper.cpp | 15 + compiler/tflchef/tflite/src/FillerHelper.h | 8 + compiler/tflchef/tflite/src/Op/Add.cpp | 6 +- compiler/tflchef/tflite/src/Op/Maximum.cpp | 6 +- compiler/tflchef/tflite/src/Op/Minimum.cpp | 6 +- compiler/tflchef/tflite/src/Op/Mul.cpp | 6 +- .../tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp | 2 +- .../tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp | 2 +- compiler/tflchef/tflite/src/Op/PadV2.cpp | 7 +- compiler/tflchef/tflite/src/Op/ScatterNd.cpp | 6 +- compiler/tflchef/tflite/src/Op/SegmentSum.cpp | 7 +- compiler/tflchef/tflite/src/Op/Sub.cpp | 6 +- compiler/tflchef/tflite/src/Op/{ => include}/Abs.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Add.h | 0 .../tflchef/tflite/src/Op/{ => include}/AddN.h | 0 .../tflchef/tflite/src/Op/{ => include}/ArgMax.h | 0 .../tflchef/tflite/src/Op/{ => include}/ArgMin.h | 0 .../tflite/src/Op/{ => include}/AveragePool2D.h | 0 .../tflite/src/Op/{ => include}/BatchMatMul.h | 0 .../tflite/src/Op/{ => include}/BatchToSpaceND.h | 0 .../Op/{ => include}/BidirectionalSequenceLSTM.h | 0 .../tflchef/tflite/src/Op/{ => include}/Cast.h | 0 .../tflchef/tflite/src/Op/{ => include}/Ceil.h | 0 .../tflite/src/Op/{ => include}/Concatenation.h | 0 .../tflchef/tflite/src/Op/{ => include}/Conv2D.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Cos.h | 0 .../tflite/src/Op/{ => include}/DepthToSpace.h | 0 .../tflite/src/Op/{ => include}/DepthwiseConv2D.h | 0 .../tflite/src/Op/{ => include}/Dequantize.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Div.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/ELU.h | 0 .../tflchef/tflite/src/Op/{ => include}/Equal.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Exp.h | 0 .../tflite/src/Op/{ => include}/ExpandDims.h | 0 .../tflite/src/Op/{ => include}/FakeQuant.h | 0 .../tflchef/tflite/src/Op/{ => include}/Fill.h | 0 .../tflchef/tflite/src/Op/{ => include}/Floor.h | 0 .../tflchef/tflite/src/Op/{ => include}/FloorDiv.h | 0 .../tflchef/tflite/src/Op/{ => include}/FloorMod.h | 0 .../tflite/src/Op/{ => include}/FullyConnected.h | 0 .../tflchef/tflite/src/Op/{ => include}/Gather.h | 0 .../tflchef/tflite/src/Op/{ => include}/GatherNd.h | 0 .../tflchef/tflite/src/Op/{ => include}/Greater.h | 0 .../tflite/src/Op/{ => include}/GreaterEqual.h | 0 .../tflite/src/Op/{ => include}/L2Normalize.h | 0 .../tflchef/tflite/src/Op/{ => include}/L2Pool2D.h | 0 .../tflite/src/Op/{ => include}/LeakyRelu.h | 0 .../tflchef/tflite/src/Op/{ => include}/Less.h | 0 .../tflite/src/Op/{ => include}/LessEqual.h | 0 .../Op/{ => include}/LocalResponseNormalization.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Log.h | 0 .../tflite/src/Op/{ => include}/LogSoftmax.h | 0 .../tflite/src/Op/{ => include}/LogicalAnd.h | 0 .../tflite/src/Op/{ => include}/LogicalNot.h | 0 .../tflite/src/Op/{ => include}/LogicalOr.h | 0 .../tflchef/tflite/src/Op/{ => include}/Logistic.h | 0 .../tflite/src/Op/{ => include}/MatrixDiag.h | 0 .../tflite/src/Op/{ => include}/MatrixSetDiag.h | 0 .../tflite/src/Op/{ => include}/MaxPool2D.h | 0 .../tflchef/tflite/src/Op/{ => include}/Maximum.h | 0 .../tflchef/tflite/src/Op/{ => include}/Mean.h | 0 .../tflchef/tflite/src/Op/{ => include}/Minimum.h | 0 .../tflite/src/Op/{ => include}/MirrorPad.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Mul.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Neg.h | 0 .../src/Op/{ => include}/NonMaxSuppressionV4.h | 0 .../src/Op/{ => include}/NonMaxSuppressionV5.h | 0 .../tflchef/tflite/src/Op/{ => include}/NotEqual.h | 0 .../tflchef/tflite/src/Op/{ => include}/OneHot.h | 0 .../tflchef/tflite/src/Op/{ => include}/PRelu.h | 0 .../tflchef/tflite/src/Op/{ => include}/Pack.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Pad.h | 0 .../tflchef/tflite/src/Op/{ => include}/PadV2.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Pow.h | 0 .../tflchef/tflite/src/Op/{ => include}/Quantize.h | 0 .../tflchef/tflite/src/Op/{ => include}/Range.h | 0 .../tflchef/tflite/src/Op/{ => include}/Rank.h | 0 .../tflchef/tflite/src/Op/{ => include}/ReLU.h | 0 .../tflchef/tflite/src/Op/{ => include}/ReLU6.h | 0 .../tflite/src/Op/{ => include}/ReLUN1To1.h | 0 .../tflite/src/Op/{ => include}/ReduceAny.h | 0 .../tflite/src/Op/{ => include}/ReduceMax.h | 0 .../tflite/src/Op/{ => include}/ReduceMin.h | 0 .../tflite/src/Op/{ => include}/ReduceProd.h | 0 .../tflchef/tflite/src/Op/{ => include}/Reshape.h | 0 .../tflite/src/Op/{ => include}/ResizeBilinear.h | 0 .../src/Op/{ => include}/ResizeNearestNeighbor.h | 0 .../tflite/src/Op/{ => include}/ReverseSequence.h | 0 .../tflite/src/Op/{ => include}/ReverseV2.h | 0 .../tflchef/tflite/src/Op/{ => include}/Round.h | 0 .../tflchef/tflite/src/Op/{ => include}/Rsqrt.h | 0 .../tflchef/tflite/src/Op/{ => include}/SVDF.h | 0 .../tflite/src/Op/{ => include}/ScatterNd.h | 0 .../tflite/src/Op/{ => include}/SegmentSum.h | 0 .../tflchef/tflite/src/Op/{ => include}/Select.h | 0 .../tflchef/tflite/src/Op/{ => include}/SelectV2.h | 0 .../tflchef/tflite/src/Op/{ => include}/Shape.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Sin.h | 0 .../tflchef/tflite/src/Op/{ => include}/Slice.h | 0 .../tflchef/tflite/src/Op/{ => include}/Softmax.h | 0 .../tflite/src/Op/{ => include}/SpaceToBatchND.h | 0 .../tflite/src/Op/{ => include}/SpaceToDepth.h | 0 .../tflite/src/Op/{ => include}/SparseToDense.h | 0 .../tflchef/tflite/src/Op/{ => include}/Split.h | 0 .../tflchef/tflite/src/Op/{ => include}/SplitV.h | 0 .../tflchef/tflite/src/Op/{ => include}/Sqrt.h | 0 .../tflchef/tflite/src/Op/{ => include}/Square.h | 0 .../src/Op/{ => include}/SquaredDifference.h | 0 .../tflchef/tflite/src/Op/{ => include}/Squeeze.h | 0 .../tflite/src/Op/{ => include}/StridedSlice.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Sub.h | 0 compiler/tflchef/tflite/src/Op/{ => include}/Sum.h | 0 .../tflchef/tflite/src/Op/{ => include}/Tanh.h | 0 .../tflchef/tflite/src/Op/{ => include}/Tile.h | 0 .../tflchef/tflite/src/Op/{ => include}/TopKV2.h | 0 .../tflite/src/Op/{ => include}/Transpose.h | 0 .../tflite/src/Op/{ => include}/TransposeConv.h | 0 .../Op/{ => include}/UnidirectionalSequenceLSTM.h | 0 .../tflchef/tflite/src/Op/{ => include}/Unique.h | 0 .../tflchef/tflite/src/Op/{ => include}/Unpack.h | 0 .../tflchef/tflite/src/Op/{ => include}/Where.h | 0 .../tflite/src/Op/{ => include}/ZerosLike.h | 0 compiler/tflchef/tflite/src/TFliteOpChefs.h | 220 +-- compiler/tflchef/tools/file/Driver.cpp | 6 +- compiler/tflchef/tools/reverse/Driver.cpp | 6 +- compiler/tfldump/CMakeLists.txt | 1 + compiler/tfldump/driver/Driver.cpp | 15 +- compiler/tfldump/include/tflread/Model.h | 43 - compiler/tfldump/requires.cmake | 1 + compiler/tfldump/src/Dump.cpp | 6 +- compiler/tfldump/src/Load.cpp | 133 -- compiler/tfldump/src/OpPrinter.cpp | 1 + .../tflite2circle-conversion-test/CMakeLists.txt | 4 + compiler/tflite2circle/driver/Driver.cpp | 23 +- compiler/tflite2circle/src/BuildBuiltinOptions.h | 2 + .../src/BuildBuiltinOptions/DensifyOptions.cpp | 29 + .../src/BuildBuiltinOptions/DensifyOptions.h | 31 + .../src/BuildBuiltinOptions/DequantizeOptions.cpp | 30 + .../src/BuildBuiltinOptions/DequantizeOptions.h | 31 + .../BuildBuiltinOptions/MaximumMinimumOptions.cpp | 2 - compiler/tflite2circle/src/CircleModel.cpp | 9 +- compiler/tflite2circle/src/TFLBuiltinOptions.lst | 3 +- compiler/vconone/CMakeLists.txt | 2 +- compiler/vconone/src/version.cpp | 2 +- compute/ARMComputeEx/CMakeLists.txt | 2 +- compute/cker/CMakeLists.txt | 17 + compute/cker/include/cker/CpuBackendThreadpool.h | 7 + compute/cker/include/cker/NeonTensorUtils.h | 2 +- compute/cker/include/cker/operation/Conv.h | 30 +- .../cker/include/cker/operation/DepthwiseConv.h | 1 + .../cker/include/cker/operation/reference/Conv.h | 59 +- .../reference/integer_ops/DepthwiseConvUInt8.h | 152 ++ compute/cker/include/cker/ruy/RuySupport.h | 34 +- .../{test/cker/Range.cc => cker/src/Range.test.cc} | 0 compute/ruy/include/ruy/RuySupport.h | 43 +- compute/ruy/include/ruy/operation/Conv.h | 2 +- compute/ruy/include/ruy/operation/FullyConnected.h | 2 +- compute/test/CMakeLists.txt | 17 - docs/conf.py | 2 +- docs/howto/how-to-build-runtime-tizen-gbs-rpi4.md | 18 +- docs/release/1.20/index.rst | 13 + docs/release/1.20/release-note-1.20.0.md | 34 + docs/release/1.21/index.rst | 13 + docs/release/1.21/release-note_1.21.0.md | 35 + infra/cmake/modules/IdentifyPlatform.cmake | 4 + infra/cmake/packages/AbseilConfig.cmake | 14 +- infra/cmake/packages/AbseilSourceConfig.cmake | 7 +- .../CMSISSource-5.8.0/CMSISSourceConfig.cmake | 3 +- infra/cmake/packages/CaffeSourceConfig.cmake | 3 +- infra/cmake/packages/CpuInfoSourceConfig.cmake | 4 +- infra/cmake/packages/Egl_HeadersSourceConfig.cmake | 21 + infra/cmake/packages/FarmhashSourceConfig.cmake | 3 +- .../FlatBuffersSourceConfig.cmake | 3 +- infra/cmake/packages/Fp16SourceConfig.cmake | 2 +- infra/cmake/packages/GEMMLowpSourceConfig.cmake | 3 +- infra/cmake/packages/GFlagsSourceConfig.cmake | 3 +- infra/cmake/packages/GTestSourceConfig.cmake | 3 +- infra/cmake/packages/HDF5SourceConfig.cmake | 3 +- infra/cmake/packages/JsoncppSourceConfig.cmake | 3 +- .../MbedOSSource-6.15/MbedOSSourceConfig.cmake | 3 +- infra/cmake/packages/NEON2SSESourceConfig.cmake | 8 +- .../ONNXSource-1.4.1/ONNXSourceConfig.cmake | 3 +- .../ONNXSource-1.6.0/ONNXSourceConfig.cmake | 3 +- infra/cmake/packages/OouraFFTSourceConfig.cmake | 3 +- .../packages/Opengl_HeadersSourceConfig.cmake | 21 + infra/cmake/packages/ProtobufSourceConfig.cmake | 3 +- infra/cmake/packages/Pybind11SourceConfig.cmake | 3 +- infra/cmake/packages/PytorchSourceConfig.cmake | 3 +- .../TensorFlowEigenSourceConfig.cmake | 3 +- .../TensorFlowEigenSourceConfig.cmake | 21 + .../TensorFlowEigenSourceConfigVersion.cmake | 10 + .../TensorFlowGEMMLowpSourceConfig.cmake | 3 +- .../TensorFlowGEMMLowpSourceConfig.cmake | 3 +- .../TensorFlowGEMMLowpSourceConfig.cmake | 3 +- .../TensorFlowGEMMLowpSourceConfig.cmake | 3 +- .../cmake/packages/TensorFlowGpuSourceConfig.cmake | 5 +- .../TensorFlowRuySourceConfig.cmake | 3 +- .../TensorFlowRuySourceConfig.cmake | 3 +- .../TensorFlowRuySourceConfig.cmake | 21 + .../TensorFlowRuySourceConfigVersion.cmake | 10 + .../TensorFlowSourceConfig.cmake | 3 +- .../TensorFlowSourceConfig.cmake | 3 +- .../TensorFlowSourceConfig.cmake | 3 +- .../TensorFlowSource-2.3.0-rc0Config.cmake | 3 +- .../TensorFlowSourceConfig.cmake | 3 +- .../TensorFlowSourceConfig.cmake | 3 +- .../TensorFlowSourceConfig.cmake | 3 +- infra/cmake/packages/VulkanSourceConfig.cmake | 20 + infra/command/format | 6 +- infra/command/gen-coverage-report | 4 +- infra/debian/compiler/changelog | 47 + infra/debian/compiler/docs/one-infer.1 | 46 + infra/debian/compiler/docs/one-partition.1 | 56 + infra/debian/compiler/one-compiler.install | 9 + infra/debian/compiler/one-compiler.manpages | 2 + infra/debian/runtime/changelog | 15 + infra/debian/runtime/rules | 2 +- infra/docker/bionic/Dockerfile | 2 +- infra/docker/focal/Dockerfile | 2 +- infra/nncc/CMakeLists.txt | 5 +- .../cmake/options/options_armv7em-generic.cmake | 3 + infra/nnfw/CMakeLists.txt | 6 + infra/nnfw/cmake/ApplyCompileFlags.cmake | 10 + infra/nnfw/cmake/CfgOptionFlags.cmake | 5 +- .../buildtool/config/config_aarch64-android.cmake | 3 - .../nnfw/cmake/buildtool/config/config_linux.cmake | 13 +- .../buildtool/config/config_x86_64-darwin.cmake | 3 - .../buildtool/cross/toolchain_aarch64-linux.cmake | 6 - .../buildtool/cross/toolchain_aarch64-tizen.cmake | 6 - .../buildtool/cross/toolchain_armv7l-linux.cmake | 6 - .../buildtool/cross/toolchain_armv7l-tizen.cmake | 10 - .../cmake/options/options_aarch64-android.cmake | 2 + .../nnfw/cmake/options/options_armv7l-tizen.cmake | 1 + .../nnfw/cmake/options/options_x86_64-tizen.cmake | 1 + infra/nnfw/cmake/packages/ARMComputeConfig.cmake | 8 +- infra/nnfw/cmake/packages/CpuInfoConfig.cmake | 16 +- infra/nnfw/cmake/packages/GLib2.0Config.cmake | 41 + infra/nnfw/cmake/packages/Ruy/CMakeLists.txt | 5 +- infra/nnfw/cmake/packages/RuyConfig.cmake | 17 +- .../TensorFlowLite/CMakeLists.txt | 6 + .../TensorFlowLite/CMakeLists.txt | 96 -- .../TensorFlowLiteConfig.cmake | 44 - .../TensorFlowLiteConfigVersion.cmake | 9 - .../TensorFlowLite/CMakeLists.txt | 121 ++ .../TensorFlowLiteConfig.cmake | 50 + .../TensorFlowLiteConfigVersion.cmake | 9 + infra/nnfw/config/gbs.conf | 11 +- infra/packaging/preset/20220323 | 13 +- infra/packaging/preset/20220323_windows | 14 +- infra/packaging/res/tf2nnpkg.20220323 | 2 +- infra/scripts/compiler_modules.sh | 10 +- infra/scripts/docker_build_nncc.sh | 4 +- infra/scripts/docker_build_test_x64.sh | 4 +- infra/scripts/docker_collect_nnpkg_resources.sh | 6 +- infra/scripts/test_ubuntu_runtime_mixed.sh | 4 +- infra/scripts/unittest_compiler_xml.sh | 11 +- nnpackage/examples/README.md | 7 + nnpackage/examples/v1.3.0/two_tflites/README.md | 28 + .../examples/v1.3.0/two_tflites/metadata/MANIFEST | 11 + .../v1.3.0/two_tflites/metadata/tc/expected.h5 | Bin 0 -> 1614584 bytes .../v1.3.0/two_tflites/metadata/tc/input.h5 | Bin 0 -> 611064 bytes nnpackage/examples/v1.3.0/two_tflites/mv1.0.tflite | Bin 0 -> 4276 bytes nnpackage/examples/v1.3.0/two_tflites/mv1.1.tflite | Bin 0 -> 2024 bytes nnpackage/schema/circle_schema.fbs | 173 ++- packaging/ABSEIL.tar.gz | Bin 1702946 -> 1909045 bytes packaging/CPUINFO.tar.gz | Bin 3476406 -> 136288 bytes packaging/FP16.tar.gz | Bin 71362 -> 70160 bytes packaging/RUY.tar.gz | Bin 235110 -> 0 bytes packaging/TENSORFLOW-2.8.0-RUY.tar.gz | Bin 0 -> 290633 bytes packaging/nnfw.spec | 119 +- .../Quant_InstanceNorm_000/test.qconf.json | 11 + .../Quant_InstanceNorm_000/test.recipe | 43 + .../Quant_InstanceNorm_000/test.reverse | 0 res/CircleRecipes/Quant_InstanceNorm_000/test.rule | 13 + .../Quant_InstanceNorm_001/test.qconf.json | 11 + .../Quant_InstanceNorm_001/test.recipe | 43 + .../Quant_InstanceNorm_001/test.reverse | 0 res/CircleRecipes/Quant_InstanceNorm_001/test.rule | 13 + res/TensorFlowLiteRecipes/ArgMax_004/test.recipe | 30 + res/TensorFlowLiteRecipes/ArgMax_004/test.reverse | 0 res/TensorFlowLiteRecipes/Densify_000/test.recipe | 44 + .../FullyConnected_007/test.recipe | 29 + .../FullyConnected_007/test.reverse | 0 .../FullyConnected_007/test.rule | 7 + .../Net_Densify_Add_000/test.recipe | 44 + .../Net_Densify_Dequantize_Add_000/test.recipe | 54 + .../Net_Dequantize_Add_000/test.recipe | 41 + .../Net_TConv_Add_000/test.recipe | 20 - .../Net_TConv_Add_001/test.recipe | 20 - .../Net_TConv_Add_002/test.recipe | 20 - .../Net_TConv_BN_000/test.recipe | 26 - .../Net_TConv_BN_001/test.recipe | 26 - .../Net_TConv_BN_002/test.recipe | 30 - .../Net_TConv_BN_003/test.recipe | 135 ++ .../Net_TConv_BN_003/test.rule | 7 + .../Net_TConv_BN_004/test.recipe | 149 ++ .../Net_TConv_BN_004/test.rule | 7 + .../Quant_Add_001/test.qconf.json | 11 + .../Quant_Add_001/test.recipe | 31 + res/TensorFlowLiteRecipes/Quant_Add_001/test.rule | 12 + .../Quant_Add_002/test.qconf.json | 11 + .../Quant_Add_002/test.recipe | 31 + res/TensorFlowLiteRecipes/Quant_Add_002/test.rule | 12 + .../Quant_AveragePool2D_000/test.qconf.json | 11 + .../Quant_AveragePool2D_000/test.recipe | 24 + .../Quant_AveragePool2D_000/test.reverse | 0 .../Quant_AveragePool2D_000/test.rule | 11 + .../Quant_AveragePool2D_001/test.qconf.json | 11 + .../Quant_AveragePool2D_001/test.recipe | 24 + .../Quant_AveragePool2D_001/test.reverse | 0 .../Quant_AveragePool2D_001/test.rule | 11 + .../Quant_BatchMatMul_000/test.qconf.json | 11 + .../Quant_BatchMatMul_000/test.recipe | 28 + .../Quant_BatchMatMul_000/test.reverse | 0 .../Quant_BatchMatMul_000/test.rule | 13 + .../Quant_BatchMatMul_001/test.qconf.json | 11 + .../Quant_BatchMatMul_001/test.recipe | 28 + .../Quant_BatchMatMul_001/test.reverse | 0 .../Quant_BatchMatMul_001/test.rule | 13 + .../Quant_Concatenation_000/test.qconf.json | 11 + .../Quant_Concatenation_000/test.recipe | 28 + .../Quant_Concatenation_000/test.reverse | 0 .../Quant_Concatenation_000/test.rule | 13 + .../Quant_Concatenation_001/test.qconf.json | 11 + .../Quant_Concatenation_001/test.recipe | 28 + .../Quant_Concatenation_001/test.reverse | 0 .../Quant_Concatenation_001/test.rule | 13 + .../Quant_Conv_000/test.recipe | 44 + res/TensorFlowLiteRecipes/Quant_Conv_000/test.rule | 10 + .../Quant_Conv_001/test.recipe | 44 + res/TensorFlowLiteRecipes/Quant_Conv_001/test.rule | 11 + .../Quant_Conv_002/test.recipe | 44 + res/TensorFlowLiteRecipes/Quant_Conv_002/test.rule | 13 + .../Quant_Conv_003/test.qconf.json | 11 + .../Quant_Conv_003/test.recipe | 44 + .../Quant_Conv_003/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Conv_003/test.rule | 13 + .../Quant_Conv_004/test.qconf.json | 11 + .../Quant_Conv_004/test.recipe | 44 + .../Quant_Conv_004/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Conv_004/test.rule | 13 + .../Quant_DepthwiseConv2D_000/test.qconf.json | 11 + .../Quant_DepthwiseConv2D_000/test.recipe | 49 + .../Quant_DepthwiseConv2D_000/test.reverse | 0 .../Quant_DepthwiseConv2D_000/test.rule | 13 + .../Quant_DepthwiseConv2D_001/test.qconf.json | 11 + .../Quant_DepthwiseConv2D_001/test.recipe | 49 + .../Quant_DepthwiseConv2D_001/test.reverse | 0 .../Quant_DepthwiseConv2D_001/test.rule | 13 + .../Quant_FullyConnected_000/test.qconf.json | 11 + .../Quant_FullyConnected_000/test.recipe | 55 + .../Quant_FullyConnected_000/test.reverse | 0 .../Quant_FullyConnected_000/test.rule | 13 + .../Quant_FullyConnected_001/test.qconf.json | 11 + .../Quant_FullyConnected_001/test.recipe | 55 + .../Quant_FullyConnected_001/test.reverse | 0 .../Quant_FullyConnected_001/test.rule | 13 + .../Quant_LeakyRelu_000/test.qconf.json | 11 + .../Quant_LeakyRelu_000/test.recipe | 20 + .../Quant_LeakyRelu_000/test.reverse | 0 .../Quant_LeakyRelu_000/test.rule | 11 + .../Quant_LeakyRelu_001/test.qconf.json | 11 + .../Quant_LeakyRelu_001/test.recipe | 20 + .../Quant_LeakyRelu_001/test.reverse | 0 .../Quant_LeakyRelu_001/test.rule | 11 + .../Quant_Logistic_000/test.qconf.json | 11 + .../Quant_Logistic_000/test.recipe | 17 + .../Quant_Logistic_000/test.reverse | 0 .../Quant_Logistic_000/test.rule | 11 + .../Quant_Logistic_001/test.qconf.json | 11 + .../Quant_Logistic_001/test.recipe | 17 + .../Quant_Logistic_001/test.reverse | 0 .../Quant_Logistic_001/test.rule | 11 + .../Quant_MaxPool2D_000/test.qconf.json | 11 + .../Quant_MaxPool2D_000/test.recipe | 24 + .../Quant_MaxPool2D_000/test.reverse | 0 .../Quant_MaxPool2D_000/test.rule | 11 + .../Quant_MaxPool2D_001/test.qconf.json | 11 + .../Quant_MaxPool2D_001/test.recipe | 24 + .../Quant_MaxPool2D_001/test.reverse | 0 .../Quant_MaxPool2D_001/test.rule | 11 + .../Quant_Mean_000/test.qconf.json | 11 + .../Quant_Mean_000/test.recipe | 27 + .../Quant_Mean_000/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Mean_000/test.rule | 11 + .../Quant_Mean_001/test.qconf.json | 11 + .../Quant_Mean_001/test.recipe | 27 + .../Quant_Mean_001/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Mean_001/test.rule | 11 + .../Quant_Mul_000/test.qconf.json | 11 + .../Quant_Mul_000/test.recipe | 27 + res/TensorFlowLiteRecipes/Quant_Mul_000/test.rule | 13 + .../Quant_Mul_001/test.qconf.json | 11 + .../Quant_Mul_001/test.recipe | 27 + res/TensorFlowLiteRecipes/Quant_Mul_001/test.rule | 13 + .../Quant_Neg_000/test.qconf.json | 11 + .../Quant_Neg_000/test.recipe | 17 + .../Quant_Neg_000/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Neg_000/test.rule | 11 + .../Quant_Neg_001/test.qconf.json | 11 + .../Quant_Neg_001/test.recipe | 17 + .../Quant_Neg_001/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Neg_001/test.rule | 11 + .../Quant_PRelu_000/test.qconf.json | 11 + .../Quant_PRelu_000/test.recipe | 27 + .../Quant_PRelu_000/test.reverse | 0 .../Quant_PRelu_000/test.rule | 12 + .../Quant_PRelu_001/test.qconf.json | 11 + .../Quant_PRelu_001/test.recipe | 27 + .../Quant_PRelu_001/test.reverse | 0 .../Quant_PRelu_001/test.rule | 12 + .../Quant_Pad_000/test.qconf.json | 11 + .../Quant_Pad_000/test.recipe | 30 + .../Quant_Pad_000/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Pad_000/test.rule | 11 + .../Quant_Pad_001/test.qconf.json | 11 + .../Quant_Pad_001/test.recipe | 30 + .../Quant_Pad_001/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Pad_001/test.rule | 11 + .../Quant_ReLU6_000/test.qconf.json | 11 + .../Quant_ReLU6_000/test.recipe | 17 + .../Quant_ReLU6_000/test.reverse | 0 .../Quant_ReLU6_000/test.rule | 11 + .../Quant_ReLU6_001/test.qconf.json | 11 + .../Quant_ReLU6_001/test.recipe | 17 + .../Quant_ReLU6_001/test.reverse | 0 .../Quant_ReLU6_001/test.rule | 11 + .../Quant_ReLU_000/test.qconf.json | 11 + .../Quant_ReLU_000/test.recipe | 17 + .../Quant_ReLU_000/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_ReLU_000/test.rule | 11 + .../Quant_ReLU_001/test.qconf.json | 11 + .../Quant_ReLU_001/test.recipe | 17 + .../Quant_ReLU_001/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_ReLU_001/test.rule | 11 + .../Quant_Reshape_000/test.qconf.json | 11 + .../Quant_Reshape_000/test.recipe | 20 + .../Quant_Reshape_000/test.reverse | 0 .../Quant_Reshape_000/test.rule | 11 + .../Quant_Reshape_001/test.qconf.json | 11 + .../Quant_Reshape_001/test.recipe | 20 + .../Quant_Reshape_001/test.reverse | 0 .../Quant_Reshape_001/test.rule | 11 + .../Quant_ResizeBilinear_000/test.qconf.json | 11 + .../Quant_ResizeBilinear_000/test.recipe | 30 + .../Quant_ResizeBilinear_000/test.reverse | 0 .../Quant_ResizeBilinear_000/test.rule | 11 + .../Quant_ResizeBilinear_001/test.qconf.json | 11 + .../Quant_ResizeBilinear_001/test.recipe | 30 + .../Quant_ResizeBilinear_001/test.reverse | 0 .../Quant_ResizeBilinear_001/test.rule | 11 + .../test.qconf.json | 11 + .../Quant_ResizeNearestNeighbor_000/test.recipe | 27 + .../Quant_ResizeNearestNeighbor_000/test.reverse | 0 .../Quant_ResizeNearestNeighbor_000/test.rule | 11 + .../test.qconf.json | 11 + .../Quant_ResizeNearestNeighbor_001/test.recipe | 27 + .../Quant_ResizeNearestNeighbor_001/test.reverse | 0 .../Quant_ResizeNearestNeighbor_001/test.rule | 11 + .../Quant_Slice_000/test.qconf.json | 11 + .../Quant_Slice_000/test.recipe | 37 + .../Quant_Slice_000/test.reverse | 0 .../Quant_Slice_000/test.rule | 11 + .../Quant_Slice_001/test.qconf.json | 11 + .../Quant_Slice_001/test.recipe | 37 + .../Quant_Slice_001/test.reverse | 0 .../Quant_Slice_001/test.rule | 11 + .../Quant_Softmax_000/test.qconf.json | 11 + .../Quant_Softmax_000/test.recipe | 20 + .../Quant_Softmax_000/test.reverse | 0 .../Quant_Softmax_000/test.rule | 11 + .../Quant_Softmax_001/test.qconf.json | 11 + .../Quant_Softmax_001/test.recipe | 20 + .../Quant_Softmax_001/test.reverse | 0 .../Quant_Softmax_001/test.rule | 11 + .../Quant_Tanh_000/test.qconf.json | 11 + .../Quant_Tanh_000/test.recipe | 17 + .../Quant_Tanh_000/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Tanh_000/test.rule | 11 + .../Quant_Tanh_001/test.qconf.json | 11 + .../Quant_Tanh_001/test.recipe | 17 + .../Quant_Tanh_001/test.reverse | 0 res/TensorFlowLiteRecipes/Quant_Tanh_001/test.rule | 11 + .../Quant_TransposeConv_000/test.qconf.json | 11 + .../Quant_TransposeConv_000/test.recipe | 54 + .../Quant_TransposeConv_000/test.reverse | 0 .../Quant_TransposeConv_000/test.rule | 13 + .../Quant_TransposeConv_001/test.qconf.json | 11 + .../Quant_TransposeConv_001/test.recipe | 54 + .../Quant_TransposeConv_001/test.reverse | 0 .../Quant_TransposeConv_001/test.rule | 13 + .../Quant_Transpose_000/test.qconf.json | 11 + .../Quant_Transpose_000/test.recipe | 27 + .../Quant_Transpose_000/test.reverse | 0 .../Quant_Transpose_000/test.rule | 11 + .../Quant_Transpose_001/test.qconf.json | 11 + .../Quant_Transpose_001/test.recipe | 27 + .../Quant_Transpose_001/test.reverse | 0 .../Quant_Transpose_001/test.rule | 11 + .../StridedSlice_004/test.recipe | 46 + .../StridedSlice_004/test.reverse | 0 .../examples/AddV2/__init__.py | 2 + .../examples/BatchMatMulV2/__init__.py | 2 + .../examples/Bidirectional_LSTM/__init__.py | 2 + .../examples/PadV2/__init__.py | 2 + .../examples/abs/__init__.py | 2 + .../examples/add/__init__.py | 2 + .../examples/add_n/__init__.py | 2 + .../examples/argmax/__init__.py | 2 + .../examples/argmin/__init__.py | 2 + .../examples/atrous_conv2d/__init__.py | 2 + .../examples/average_pool_2d/__init__.py | 2 + .../examples/batch_normalization/__init__.py | 2 + .../examples/batch_to_space/__init__.py | 2 + .../examples/biasadd/__init__.py | 2 + .../examples/cast/__init__.py | 2 + .../examples/ceil/__init__.py | 2 + .../examples/concat/__init__.py | 2 + .../examples/cond/__init__.py | 2 + .../examples/cond_1/__init__.py | 2 + .../examples/conv2d_1/__init__.py | 2 + .../examples/conv2d_2/__init__.py | 2 + .../examples/conv2d_transpose/__init__.py | 2 + .../examples/cos/__init__.py | 2 + .../examples/depth_to_space/__init__.py | 2 + .../examples/depthwise_conv2d_1/__init__.py | 2 + .../examples/depthwise_conv2d_2/__init__.py | 2 + .../examples/div/__init__.py | 2 + .../examples/elu/__init__.py | 2 + .../examples/exp/__init__.py | 2 + .../examples/expand_dims_00/__init__.py | 2 + .../examples/expand_dims_01/__init__.py | 2 + .../examples/expand_dims_02/__init__.py | 2 + .../examples/fill/__init__.py | 2 + .../examples/flatten/__init__.py | 2 + .../examples/floor/__init__.py | 2 + .../examples/floordiv/__init__.py | 2 + .../examples/floormod/__init__.py | 2 + .../examples/fused_batch_norm/__init__.py | 2 + .../examples/gather/__init__.py | 2 + .../examples/gather_nd/__init__.py | 2 + .../examples/greater/__init__.py | 2 + .../examples/greater_equal/__init__.py | 2 + .../examples/gru/__init__.py | 2 + .../examples/instance_norm/__init__.py | 2 + .../examples/l2_normalize/__init__.py | 2 + .../examples/leaky_relu/__init__.py | 2 + .../examples/less/__init__.py | 2 + .../examples/less_equal/__init__.py | 2 + .../local_response_normalization/__init__.py | 2 + .../examples/log/__init__.py | 2 + .../examples/log_softmax/__init__.py | 2 + .../examples/log_softmax_2/__init__.py | 2 + .../examples/logical_and/__init__.py | 2 + .../examples/logical_not/__init__.py | 2 + .../examples/logical_or/__init__.py | 2 + .../examples/lstm/__init__.py | 2 + .../examples/matmul/__init__.py | 2 + .../examples/matrix_band_part/__init__.py | 2 + .../examples/matrix_diag/__init__.py | 2 + .../examples/matrix_set_diag/__init__.py | 2 + .../examples/max_pool_with_argmax/__init__.py | 2 + .../examples/maximum/__init__.py | 2 + .../examples/minimum/__init__.py | 2 + .../examples/multiply/__init__.py | 2 + .../examples/negative/__init__.py | 2 + .../non_max_suppression_padded/__init__.py | 2 + .../non_max_suppression_padded_2/__init__.py | 2 + .../non_max_suppression_with_scores/__init__.py | 2 + .../non_max_suppression_with_scores_2/__init__.py | 2 + .../examples/not_equal/__init__.py | 2 + .../examples/one_hot/__init__.py | 2 + .../examples/pack/__init__.py | 2 + .../examples/pad-reflect/__init__.py | 2 + .../examples/pad/__init__.py | 2 + .../examples/pow/__init__.py | 2 + .../examples/prelu/__init__.py | 2 + .../examples/range/__init__.py | 2 + .../examples/rank/__init__.py | 2 + .../examples/reduce_all/__init__.py | 2 + .../examples/reduce_any/__init__.py | 2 + .../examples/reduce_max/__init__.py | 2 + .../examples/reduce_min/__init__.py | 2 + .../examples/reduce_prod/__init__.py | 2 + .../examples/relu/__init__.py | 2 + .../examples/relu6/__init__.py | 2 + .../examples/reshape/__init__.py | 2 + .../examples/resize_bilinear/__init__.py | 2 + .../examples/resize_nearest_neighbor/__init__.py | 2 + .../examples/reverse_sequence/__init__.py | 2 + .../examples/reverse_v2/__init__.py | 2 + .../examples/rnn/__init__.py | 2 + .../examples/round/__init__.py | 2 + .../examples/rsqrt/__init__.py | 2 + .../examples/scatter_nd/__init__.py | 2 + .../examples/segment_sum/__init__.py | 2 + .../examples/shape/__init__.py | 2 + .../examples/sigmoid/__init__.py | 2 + .../examples/sin/__init__.py | 2 + .../examples/slice/__init__.py | 2 + .../examples/softmax/__init__.py | 2 + .../examples/space_to_batch/__init__.py | 2 + .../examples/space_to_batch_nd/__init__.py | 2 + .../examples/space_to_depth/__init__.py | 2 + .../examples/sparse_to_dense/__init__.py | 2 + .../examples/split/__init__.py | 2 + .../examples/split_2/__init__.py | 2 + .../examples/sqrt/__init__.py | 2 + .../examples/square/__init__.py | 2 + .../examples/squared_difference/__init__.py | 2 + .../examples/squeeze_1/__init__.py | 2 + .../examples/squeeze_2/__init__.py | 2 + .../examples/strided_slice/__init__.py | 2 + .../examples/subtract/__init__.py | 2 + .../examples/sum/__init__.py | 2 + .../examples/tanh/__init__.py | 2 + .../examples/tile/__init__.py | 2 + .../examples/top_k/__init__.py | 2 + .../unidirectional_sequence_LSTM/__init__.py | 2 + .../examples/unique/__init__.py | 2 + .../examples/unstack/__init__.py | 2 + .../examples/where/__init__.py | 2 + .../examples/where_2/__init__.py | 2 + .../examples/where_v2/__init__.py | 2 + .../examples/where_v2_2/__init__.py | 2 + .../examples/while/__init__.py | 2 + .../examples/while_2/__init__.py | 2 + .../examples/while_3/__init__.py | 2 + .../examples/yuv_to_rgb/__init__.py | 2 + .../examples/zeros_like/__init__.py | 2 + runtime/contrib/android/api/build.gradle | 2 +- runtime/libs/misc/CMakeLists.txt | 19 +- .../libs/misc/examples/tensor_index_iterator.cpp | 74 - runtime/libs/misc/include/misc/EnvConfigSource.h | 41 + .../libs/misc/include/misc/GeneralConfigSource.h | 44 + runtime/libs/misc/include/misc/IConfigSource.h | 46 + runtime/libs/misc/include/misc/string_helpers.h | 2 +- runtime/libs/misc/src/EnvConfigSource.cpp | 40 + runtime/libs/misc/src/GeneralConfigSource.cpp | 40 + runtime/libs/misc/src/string_helpers.test.cpp | 81 + .../libs/misc/src/tensor/IndexEnumerator.test.cpp | 59 + .../libs/misc/src/tensor/IndexIterator.test.cpp | 61 + runtime/libs/ndarray/CMakeLists.txt | 11 +- runtime/libs/ndarray/include/ndarray/Array.h | 24 +- runtime/libs/ndarray/src/Array.test.cpp | 452 ++++++ runtime/libs/ndarray/src/ContiguousSpan.test.cpp | 198 +++ runtime/libs/ndarray/src/detail/cxx14.h | 67 - runtime/libs/ndarray/test/CMakeLists.txt | 18 - runtime/libs/ndarray/test/ndarray_test.cpp | 122 -- runtime/onert/CMakeLists.txt | 6 - runtime/onert/api/CMakeLists.txt | 1 + runtime/onert/api/include/nnfw.h | 4 +- runtime/onert/api/include/nnfw_version.h | 2 +- runtime/onert/api/src/nnfw_api.cc | 10 +- runtime/onert/api/src/nnfw_api_internal.cc | 267 ++-- runtime/onert/api/src/nnfw_api_internal.h | 29 +- runtime/onert/backend/acl_cl/Backend.h | 4 +- runtime/onert/backend/acl_neon/Backend.h | 4 +- runtime/onert/backend/cpu/CMakeLists.txt | 2 +- runtime/onert/backend/cpu/ExternalContext.h | 2 + runtime/onert/backend/cpu/KernelGenerator.cc | 10 +- runtime/onert/backend/cpu/ops/ConvolutionLayer.cc | 58 +- runtime/onert/backend/cpu/ops/ConvolutionLayer.h | 5 +- .../backend/cpu/ops/DepthwiseConvolutionLayer.cc | 64 +- .../backend/cpu/ops/DepthwiseConvolutionLayer.h | 8 +- .../backend/cpu/ops/DetectionPostProcessLayer.cc | 4 +- runtime/onert/backend/ruy/ExternalContext.h | 2 + runtime/onert/backend/ruy/KernelGenerator.cc | 10 +- runtime/onert/backend/trix/CMakeLists.txt | 2 +- runtime/onert/backend/trix/DevContext.h | 42 +- runtime/onert/backend/trix/ops/BulkLayer.cc | 105 +- runtime/onert/backend/trix/ops/BulkLayer.h | 2 +- runtime/onert/backend/xnnpack/KernelGenerator.cc | 10 +- runtime/onert/core/CMakeLists.txt | 14 +- runtime/onert/core/include/backend/ITensor.h | 1 + .../include/backend/basic/BackendContextHelpers.h | 4 +- .../onert/core/include/compiler/BackendManager.h | 13 +- runtime/onert/core/include/compiler/Compiler.h | 74 +- runtime/onert/core/include/compiler/LoweredGraph.h | 7 +- .../core/include/compiler/StaticShapeInferer.h | 78 +- runtime/onert/core/include/exec/Execution.h | 6 +- runtime/onert/core/include/exec/Executors.h | 71 + runtime/onert/core/include/exec/FunctionSequence.h | 3 +- runtime/onert/core/include/exec/IExecutor.h | 2 - runtime/onert/core/include/ir/Graph.h | 29 +- runtime/onert/core/include/ir/Index.h | 10 +- runtime/onert/core/include/ir/Layout.h | 1 + runtime/onert/core/include/ir/Model.h | 139 ++ runtime/onert/core/include/ir/NNPkg.h | 193 +++ runtime/onert/core/include/ir/Subgraphs.h | 139 -- runtime/onert/core/include/ir/TypeInfo.h | 6 +- runtime/onert/core/include/ir/operation/Bulk.h | 2 + .../core/include/util/CalculateActivationRange.h | 2 + runtime/onert/core/include/util/Config.lst | 2 +- runtime/onert/core/include/util/ConfigSource.h | 10 +- runtime/onert/core/include/util/EnvConfigSource.h | 41 - .../onert/core/include/util/GeneralConfigSource.h | 44 - runtime/onert/core/include/util/IConfigSource.h | 46 - runtime/onert/core/include/util/ObjectManager.h | 13 +- runtime/onert/core/include/util/TracingCtx.h | 26 +- .../core/src/backend/builtin/ExternalContext.h | 2 + .../core/src/backend/builtin/KernelGenerator.cc | 32 +- .../core/src/backend/builtin/KernelGenerator.h | 17 +- .../core/src/backend/builtin/kernel/IfLayer.cc | 16 +- .../core/src/backend/builtin/kernel/IfLayer.h | 7 +- .../src/backend/builtin/kernel/PermuteLayer.cc | 4 +- .../core/src/backend/builtin/kernel/PermuteLayer.h | 6 +- .../core/src/backend/builtin/kernel/WhileLayer.cc | 19 +- .../core/src/backend/builtin/kernel/WhileLayer.h | 6 +- runtime/onert/core/src/compiler/BackendManager.cc | 15 +- runtime/onert/core/src/compiler/Compiler.cc | 505 ++++--- runtime/onert/core/src/compiler/ExecutorFactory.cc | 85 +- runtime/onert/core/src/compiler/ExecutorFactory.h | 26 +- .../onert/core/src/compiler/Fp32ToFp16Converter.cc | 10 +- runtime/onert/core/src/compiler/HEScheduler.cc | 11 +- runtime/onert/core/src/compiler/HEScheduler.h | 18 +- .../onert/core/src/compiler/HEScheduler.test.cc | 572 +++++++ runtime/onert/core/src/compiler/Linear.cc | 10 +- runtime/onert/core/src/compiler/LoweredGraph.cc | 44 +- runtime/onert/core/src/compiler/ShapeValidator.cc | 667 +++++---- runtime/onert/core/src/compiler/ShapeValidator.h | 8 +- .../onert/core/src/compiler/StaticShapeInferer.cc | 648 ++++---- runtime/onert/core/src/compiler/TensorRegistries.h | 13 +- .../compiler/pass/PermutationEliminationPass.cc | 1 - .../src/compiler/pass/PermutationInsertionPass.cc | 18 +- .../pass/UnusedOperandEliminationPass.test.cc | 47 + runtime/onert/core/src/dumper/dot/DotDumper.cc | 222 +-- runtime/onert/core/src/dumper/dot/DotDumper.h | 25 +- runtime/onert/core/src/exec/DataflowExecutor.h | 17 +- runtime/onert/core/src/exec/ExecTime.cc | 6 +- runtime/onert/core/src/exec/ExecTime.test.cc | 106 ++ runtime/onert/core/src/exec/Execution.cc | 24 +- runtime/onert/core/src/exec/Execution.test.cc | 302 ++++ runtime/onert/core/src/exec/ExecutionObservee.h | 5 +- runtime/onert/core/src/exec/ExecutionObservers.cc | 14 +- runtime/onert/core/src/exec/ExecutionObservers.h | 13 +- runtime/onert/core/src/exec/ExecutorBase.cc | 5 +- runtime/onert/core/src/exec/ExecutorBase.h | 15 +- runtime/onert/core/src/exec/Executors.cc | 183 +++ runtime/onert/core/src/exec/FunctionSequence.cc | 4 +- runtime/onert/core/src/exec/JSONExecTime.cc | 4 +- runtime/onert/core/src/exec/LinearExecutor.h | 5 +- runtime/onert/core/src/exec/ParallelExecutor.h | 14 +- runtime/onert/core/src/exec/feature/MockTensor.h | 66 + .../core/src/exec/feature/nchw/Reader.test.cc | 85 ++ .../onert/core/src/exec/feature/nchw/View.test.cc | 85 ++ .../core/src/exec/feature/nhwc/Reader.test.cc | 86 ++ runtime/onert/core/src/exec/feature/nhwc/View.h | 2 +- .../onert/core/src/exec/feature/nhwc/View.test.cc | 86 ++ runtime/onert/core/src/interp/InterpExecutor.cc | 7 +- runtime/onert/core/src/interp/InterpExecutor.h | 7 +- .../onert/core/src/interp/InterpExecutor.test.cc | 355 +++++ .../src/interp/operations/BinaryArithmeticOps.cc | 10 +- runtime/onert/core/src/interp/operations/Concat.cc | 8 +- runtime/onert/core/src/interp/operations/Conv2D.cc | 10 +- .../core/src/interp/operations/DepthwiseConv2D.cc | 10 +- .../interp/operations/ElementwiseActivations.cc | 9 +- .../core/src/interp/operations/FullyConnected.cc | 8 +- runtime/onert/core/src/interp/operations/Gather.cc | 8 +- .../core/src/interp/operations/InstanceNorm.cc | 8 +- runtime/onert/core/src/interp/operations/Pad.cc | 6 +- runtime/onert/core/src/interp/operations/Pool2D.cc | 12 +- .../onert/core/src/interp/operations/Reshape.cc | 2 +- .../onert/core/src/interp/operations/Softmax.cc | 8 +- .../core/src/interp/operations/TransposeConv.cc | 8 +- runtime/onert/core/src/ir/Graph.cc | 14 +- runtime/onert/core/src/ir/Graph.test.cc | 147 ++ runtime/onert/core/src/ir/LayoutSet.test.cc | 67 + .../onert/{test/core => core/src}/ir/MockNode.h | 0 runtime/onert/core/src/ir/Operand.test.cc | 86 ++ .../onert/core/src/ir/OperandIndexSequence.test.cc | 52 + runtime/onert/core/src/ir/Operands.test.cc | 45 + runtime/onert/core/src/ir/Operation.test.cc | 98 ++ runtime/onert/core/src/ir/Operations.test.cc | 42 + runtime/onert/core/src/ir/Shape.test.cc | 58 + .../onert/core/src/ir/verifier/Verifier.test.cc | 93 ++ .../core/src/util/ChromeTracingEventWriter.cc | 6 +- runtime/onert/core/src/util/ConfigSource.cc | 25 +- runtime/onert/core/src/util/EnvConfigSource.cc | 40 - runtime/onert/core/src/util/EventCollector.cc | 2 +- runtime/onert/core/src/util/EventCollector.h | 7 +- runtime/onert/core/src/util/EventRecorder.cc | 2 +- runtime/onert/core/src/util/EventWriter.cc | 2 +- runtime/onert/core/src/util/GeneralConfigSource.cc | 45 - runtime/onert/core/src/util/Index.test.cc | 34 + runtime/onert/core/src/util/MDTableEventWriter.cc | 10 +- runtime/onert/core/src/util/ObjectManager.test.cc | 211 +++ runtime/onert/core/src/util/SNPEEventWriter.cc | 5 +- runtime/onert/core/src/util/ShapeInference.test.cc | 544 +++++++ .../frontend/base_loader/include/base_loader.h | 36 +- .../onert/frontend/circle/include/circle_loader.h | 4 +- runtime/onert/frontend/circle/src/circle_loader.cc | 16 +- runtime/onert/frontend/nnapi/execution.cc | 2 +- .../nnapi/wrapper/ANeuralNetworksCompilation.cc | 9 +- .../nnapi/wrapper/ANeuralNetworksCompilation.h | 21 +- .../nnapi/wrapper/ANeuralNetworksExecution.h | 2 +- .../frontend/nnapi/wrapper/ANeuralNetworksModel.cc | 8 +- .../frontend/nnapi/wrapper/ANeuralNetworksModel.h | 4 +- .../onert/frontend/tflite/include/tflite_loader.h | 2 +- runtime/onert/frontend/tflite/src/tflite_loader.cc | 8 +- runtime/onert/frontend/trix/CMakeLists.txt | 2 +- runtime/onert/frontend/trix/include/trix_loader.h | 2 +- runtime/onert/frontend/trix/src/trix_loader.cc | 32 +- .../onert/frontend/trix/src/trix_loader_dummy.cc | 6 +- runtime/onert/test/CMakeLists.txt | 15 - runtime/onert/test/core/compiler/HEScheduler.cc | 573 ------- .../compiler/pass/UnusedOperandEliminationPass.cc | 45 - runtime/onert/test/core/exec/ExecInstance.cc | 301 ---- runtime/onert/test/core/exec/ExecTime.test.cc | 103 -- runtime/onert/test/core/interp/ExecManager.cc | 360 ----- runtime/onert/test/core/ir/Graph.cc | 148 -- runtime/onert/test/core/ir/LayoutSet.cc | 67 - runtime/onert/test/core/ir/OperandIndexSet.cc | 52 - runtime/onert/test/core/ir/OperandSet.cc | 45 - runtime/onert/test/core/ir/OperationSet.cc | 41 - runtime/onert/test/core/ir/SetIO.cc | 99 -- runtime/onert/test/core/ir/Shape.cc | 58 - runtime/onert/test/core/ir/UseDef.cc | 85 -- runtime/onert/test/core/ir/Verifier.cc | 92 -- runtime/onert/test/core/util/Index.cc | 34 - runtime/onert/test/core/util/ObjectManager.cc | 211 --- runtime/onert/test/core/util/ShapeInference.cc | 545 ------- runtime/service/CMakeLists.txt | 1 + runtime/service/npud/CMakeLists.txt | 21 + runtime/service/npud/core/Server.cc | 65 + runtime/service/npud/core/Server.h | 55 + runtime/service/npud/core/Signal.cc | 56 + runtime/service/npud/core/Signal.h | 37 + runtime/service/npud/core/main.cc | 40 + runtime/service/npud/util/Config.lst | 22 + runtime/service/npud/util/ConfigSource.cc | 126 ++ runtime/service/npud/util/ConfigSource.h | 51 + runtime/service/npud/util/Logging.h | 88 ++ tests/nnapi/CMakeLists.txt | 5 - tests/nnfw_api/src/CircleGen.cc | 28 + tests/nnfw_api/src/CircleGen.h | 4 + tests/nnfw_api/src/GenModelTest.h | 23 +- .../{GenModelTests.cc => GenModelTests.test.cc} | 0 ...micTensor.cc => ModelTestDynamicTensor.test.cc} | 0 ...eshaping.cc => ModelTestInputReshaping.test.cc} | 0 ...{RegressionTests.cc => RegressionTests.test.cc} | 0 ...ded.cc => ValidationTestAddModelLoaded.test.cc} | 0 ...cc => ValidationTestAddSessionPrepared.test.cc} | 0 ...=> ValidationTestFourAddModelsSetInput.test.cc} | 0 ...s.cc => ValidationTestMultipleSessions.test.cc} | 0 ...on.cc => ValidationTestPipelineSession.test.cc} | 0 ...ted.cc => ValidationTestSessionCreated.test.cc} | 0 ...sion.cc => ValidationTestSingleSession.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Add.cc | 301 ---- tests/nnfw_api/src/one_op_tests/Add.test.cc | 301 ++++ .../src/one_op_tests/{AddN.cc => AddN.test.cc} | 0 tests/nnfw_api/src/one_op_tests/ArgMinMax.cc | 256 ---- tests/nnfw_api/src/one_op_tests/ArgMinMax.test.cc | 256 ++++ tests/nnfw_api/src/one_op_tests/AveragePool2D.cc | 243 --- .../src/one_op_tests/AveragePool2D.test.cc | 243 +++ .../{BatchToSpaceND.cc => BatchToSpaceND.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Cast.cc | 173 --- tests/nnfw_api/src/one_op_tests/Cast.test.cc | 201 +++ tests/nnfw_api/src/one_op_tests/Concat.cc | 244 --- tests/nnfw_api/src/one_op_tests/Concat.test.cc | 244 +++ tests/nnfw_api/src/one_op_tests/Conv2D.cc | 248 ---- tests/nnfw_api/src/one_op_tests/Conv2D.test.cc | 278 ++++ .../src/one_op_tests/{Cos.cc => Cos.test.cc} | 0 tests/nnfw_api/src/one_op_tests/DepthToSpace.cc | 89 -- .../nnfw_api/src/one_op_tests/DepthToSpace.test.cc | 89 ++ tests/nnfw_api/src/one_op_tests/DepthwiseConv2D.cc | 457 ------ .../src/one_op_tests/DepthwiseConv2D.test.cc | 502 +++++++ ...PostProcess.cc => DetectionPostProcess.test.cc} | 0 .../src/one_op_tests/{Elu.cc => Elu.test.cc} | 0 .../src/one_op_tests/{Equal.cc => Equal.test.cc} | 0 .../{ExpandDims.cc => ExpandDims.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Fill.cc | 148 -- tests/nnfw_api/src/one_op_tests/Fill.test.cc | 148 ++ .../src/one_op_tests/{Floor.cc => Floor.test.cc} | 0 .../one_op_tests/{FloorDiv.cc => FloorDiv.test.cc} | 0 .../{FullyConnected.cc => FullyConnected.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Greater.test.cc | 144 ++ .../nnfw_api/src/one_op_tests/GreaterEqual.test.cc | 145 ++ tests/nnfw_api/src/one_op_tests/If.cc | 132 -- tests/nnfw_api/src/one_op_tests/If.test.cc | 132 ++ .../{InstanceNorm.cc => InstanceNorm.test.cc} | 0 ...{L2Normalization.cc => L2Normalization.test.cc} | 0 .../{LeakyRelu.cc => LeakyRelu.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Less.test.cc | 143 ++ tests/nnfw_api/src/one_op_tests/LessEqual.test.cc | 144 ++ .../{LogSoftmax.cc => LogSoftmax.test.cc} | 0 .../src/one_op_tests/{Mean.cc => Mean.test.cc} | 0 .../src/one_op_tests/{Mul.cc => Mul.test.cc} | 0 .../src/one_op_tests/{Neg.cc => Neg.test.cc} | 0 tests/nnfw_api/src/one_op_tests/NotEqual.test.cc | 158 ++ .../src/one_op_tests/{OneHot.cc => OneHot.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Pad.cc | 172 --- tests/nnfw_api/src/one_op_tests/Pad.test.cc | 172 +++ .../src/one_op_tests/{PadV2.cc => PadV2.test.cc} | 0 .../one_op_tests/{Quantize.cc => Quantize.test.cc} | 0 .../src/one_op_tests/{Rank.cc => Rank.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Reduce.cc | 70 - tests/nnfw_api/src/one_op_tests/Reduce.test.cc | 70 + .../src/one_op_tests/{Relu.cc => Relu.test.cc} | 0 .../src/one_op_tests/{Relu6.cc => Relu6.test.cc} | 0 tests/nnfw_api/src/one_op_tests/ResizeBilinear.cc | 101 -- .../src/one_op_tests/ResizeBilinear.test.cc | 101 ++ ...stNeighbor.cc => ResizeNearestNeighbor.test.cc} | 0 .../one_op_tests/{Reverse.cc => Reverse.test.cc} | 0 .../src/one_op_tests/{Select.cc => Select.test.cc} | 0 .../src/one_op_tests/{Shape.cc => Shape.test.cc} | 0 tests/nnfw_api/src/one_op_tests/Slice.cc | 187 --- tests/nnfw_api/src/one_op_tests/Slice.test.cc | 187 +++ tests/nnfw_api/src/one_op_tests/Softmax.cc | 130 -- tests/nnfw_api/src/one_op_tests/Softmax.test.cc | 130 ++ .../src/one_op_tests/{Split.cc => Split.test.cc} | 0 .../src/one_op_tests/{Sqrt.cc => Sqrt.test.cc} | 0 .../src/one_op_tests/{Square.cc => Square.test.cc} | 0 .../{StridedSlice.cc => StridedSlice.test.cc} | 0 .../src/one_op_tests/{Sub.cc => Sub.test.cc} | 0 .../src/one_op_tests/{Tile.cc => Tile.test.cc} | 0 .../{Transpose.cc => Transpose.test.cc} | 0 tests/nnfw_api/src/one_op_tests/While.cc | 270 ---- tests/nnfw_api/src/one_op_tests/While.test.cc | 270 ++++ tests/scripts/command/nnpkg-test | 11 +- tests/scripts/command/prepare-model | 12 +- tests/tools/nnpackage_run/src/nnpackage_run.cc | 6 + tests/tools/nnpackage_run/src/rawformatter.cc | 26 +- tests/tools/tflite_vanilla_run/CMakeLists.txt | 11 +- .../tflite_vanilla_run/src/tflite_vanilla_run.cc | 7 +- tools/cross/arm/sources.list.jammy | 11 + tools/cross/arm/sources.list.xenial | 11 - tools/cross/install_rootfs.sh | 11 +- tools/nnpackage_tool/gen_golden/gen_golden.py | 4 +- tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh | 137 +- 2095 files changed, 77633 insertions(+), 12831 deletions(-) create mode 100644 compiler/circle-eval-diff/src/InputDataLoader.cpp create mode 100644 compiler/circle-eval-diff/src/InputDataLoader.h create mode 100644 compiler/circle-eval-diff/src/InputDataLoader.test.cpp delete mode 100644 compiler/circle-eval-diff/src/ModuleEvalDiff.cpp delete mode 100644 compiler/circle-eval-diff/src/ModuleEvalDiff.h delete mode 100644 compiler/circle-inspect/src/Reader.cpp delete mode 100644 compiler/circle-inspect/src/Reader.h create mode 100644 compiler/circle-interpreter/CMakeLists.txt create mode 100644 compiler/circle-interpreter/requires.cmake create mode 100644 compiler/circle-interpreter/src/CircleInterpreter.cpp create mode 100644 compiler/circle-operator-test/CMakeLists.txt create mode 100644 compiler/circle-operator-test/README.md create mode 100644 compiler/circle-operator-test/requires.cmake create mode 100644 compiler/circle-operator-test/src/circle-operator.test.cpp create mode 100644 compiler/circle-operator/CMakeLists.txt create mode 100644 compiler/circle-operator/README.md create mode 100644 compiler/circle-operator/driver/Driver.cpp create mode 100644 compiler/circle-operator/requires.cmake create mode 100644 compiler/circle-operator/src/Dump.cpp create mode 100644 compiler/circle-operator/src/Dump.h delete mode 100644 compiler/circle-tensordump/src/Reader.cpp delete mode 100644 compiler/circle-tensordump/src/Reader.h delete mode 100644 compiler/circledump/include/circleread/Model.h delete mode 100644 compiler/circledump/src/Load.cpp delete mode 100644 compiler/circledump/src/Read.cpp delete mode 100644 compiler/circledump/src/Read.h create mode 100644 compiler/crew/src/test_read_semicolon.ini create mode 100644 compiler/luci-interpreter/pal/cmsisnn/PALreference_ops.h create mode 100644 compiler/luci-interpreter/pal/linux/PALreference_ops.h create mode 100644 compiler/luci-interpreter/pal/mcu/PALreference_ops.h create mode 100644 compiler/luci-interpreter/src/kernels/Fill.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Fill.h create mode 100644 compiler/luci-interpreter/src/kernels/Fill.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/ReduceMax.cpp create mode 100644 compiler/luci-interpreter/src/kernels/ReduceMax.h create mode 100644 compiler/luci-interpreter/src/kernels/ReduceMax.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Shape.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Shape.h create mode 100644 compiler/luci-interpreter/src/kernels/Shape.test.cpp create mode 100644 compiler/luci-interpreter/src/loader/nodes/Fill.cpp create mode 100644 compiler/luci-interpreter/src/loader/nodes/ReduceMax.cpp create mode 100644 compiler/luci-interpreter/src/loader/nodes/Shape.cpp create mode 100644 compiler/luci-micro/luci-interpreter/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/README.md create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/BuddyMemoryManager.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/GraphBuilderRegistry.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/Interpreter.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/MemoryManager.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/SimpleMemoryManager.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/StaticMemoryManager.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/TestMemoryManager.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/DataType.h create mode 100644 compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALElu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALMul.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALNeg.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSub.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/cmsisnn/pal.cmake create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/KernelsToBuild.lst create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALArgMax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALAveragePool2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALBatchMatMul.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALDepthToSpace.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALDequantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALElu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALFullyConnected.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALGather.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALL2Normalize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALL2Pool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALLeakyRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALLogSoftmax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALMul.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALNeg.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALQuantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALRelu6.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALResizeBilinear.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSVDF.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSlice.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSoftmax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSplit.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/PALSub.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/linux/pal.cmake create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALArgMax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALDequantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALElu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALFullyConnected.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Normalize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALMul.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALNeg.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALQuantize.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALSVDF.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALSoftmax.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/PALSub.h create mode 100644 compiler/luci-micro/luci-interpreter/pal/mcu/pal.cmake create mode 100644 compiler/luci-micro/luci-interpreter/requires.cmake create mode 100644 compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/src/Interpreter.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/SimpleMemoryManager.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/StaticMemoryManager.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/TestMemoryManager.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/core/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/src/core/EventNotifier.h create mode 100644 compiler/luci-micro/luci-interpreter/src/core/Kernel.h create mode 100644 compiler/luci-micro/luci-interpreter/src/core/KernelParams.h create mode 100644 compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.h create mode 100644 compiler/luci-micro/luci-interpreter/src/core/RuntimeModule.h create mode 100644 compiler/luci-micro/luci-interpreter/src/core/Tensor.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/import/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/src/import/GraphBuilderRegistry.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Add.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Add.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Add.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/BinaryOpCommon.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Cast.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Cast.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Cast.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Div.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Div.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Div.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Elu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Elu.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Elu.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Equal.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Equal.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Equal.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Exp.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Exp.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Exp.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Fill.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Fill.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Fill.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Floor.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Floor.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Floor.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Gather.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Gather.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Gather.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Greater.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Greater.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Greater.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/If.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/If.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/If.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Less.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Less.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Less.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Logistic.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Logistic.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Logistic.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Maximum.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Maximum.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Maximum.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mean.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mean.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mean.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Minimum.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Minimum.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Minimum.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mul.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mul.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Mul.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Neg.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Neg.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Neg.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/OneHot.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/OneHot.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/OneHot.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PRelu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PRelu.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PRelu.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pack.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pack.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pack.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pad.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pad.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pad.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PadV2.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PadV2.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/PadV2.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pow.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pow.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Pow.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Quantize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Quantize.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Quantize.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu6.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu6.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Relu6.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Reshape.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Reshape.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Reshape.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SVDF.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SVDF.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SVDF.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Shape.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Shape.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Shape.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Slice.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Slice.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Slice.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Softmax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Softmax.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Softmax.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Split.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Split.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Split.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SplitV.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SplitV.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SplitV.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Square.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Square.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Square.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sub.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sub.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Sub.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Tanh.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Tanh.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Tanh.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Transpose.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Transpose.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Transpose.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Unpack.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Unpack.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Unpack.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Utils.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/Utils.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/While.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/While.h create mode 100644 compiler/luci-micro/luci-interpreter/src/kernels/While.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/CMakeLists.txt create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.test.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/RuntimeToIR.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Add.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Builders.h create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Cast.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Div.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Elu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Equal.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Exp.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Fill.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Floor.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Gather.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Greater.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/If.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Less.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Logistic.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Maximum.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Mean.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Minimum.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Mul.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Neg.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/OneHot.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/PRelu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Pack.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Pad.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/PadV2.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Pow.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Quantize.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu6.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Reshape.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/SVDF.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Shape.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Slice.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Softmax.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Split.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/SplitV.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Square.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Sub.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Tanh.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Transpose.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/Unpack.cpp create mode 100644 compiler/luci-micro/luci-interpreter/src/loader/nodes/While.cpp create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleDensify.h create mode 100644 compiler/luci/import/include/luci/ImporterEx.h create mode 100644 compiler/luci/import/src/ImporterEx.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleDensify.cpp create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleDensify.h create mode 100644 compiler/luci/lang/src/Nodes/CircleDensify.test.cpp create mode 100644 compiler/luci/partition/include/luci/ConnectNode.h delete mode 100644 compiler/luci/partition/src/ConnectNode.h create mode 100644 compiler/luci/partition/src/Nodes/CircleDensify.cpp create mode 100644 compiler/luci/partition/src/Nodes/CircleDensify.test.cpp create mode 100644 compiler/luci/pass/include/luci/Pass/FoldDensifyPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/RemoveRedundantDequantizePass.h create mode 100644 compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapeNetPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/ResolveCustomOpSplitVPass.h create mode 100644 compiler/luci/pass/src/FoldDensifyPass.cpp create mode 100644 compiler/luci/pass/src/FoldDensifyPass.test.cpp create mode 100644 compiler/luci/pass/src/QuantizeBias.test.cpp create mode 100644 compiler/luci/pass/src/RemoveRedundantDequantizePass.cpp create mode 100644 compiler/luci/pass/src/RemoveRedundantDequantizePass.test.cpp create mode 100644 compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.cpp create mode 100644 compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.test.cpp create mode 100644 compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp create mode 100644 compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp create mode 100644 compiler/luci/pass/src/ResolveCustomOpSplitVPass.cpp create mode 100644 compiler/luci/pass/src/ResolveCustomOpSplitVPass.test.cpp create mode 100644 compiler/luci/pass/src/helpers/SparsityFormatConverter.cpp create mode 100644 compiler/luci/pass/src/helpers/SparsityFormatConverter.h create mode 100644 compiler/luci/service/src/Nodes/CircleDensify.cpp create mode 100644 compiler/luci/service/src/Nodes/CircleDensify.test.cpp create mode 100644 compiler/mio-circle04/include/mio_circle/Reader.h create mode 100644 compiler/mio-circle04/src/Reader.cpp create mode 100644 compiler/mio-circle04/src/Reader.test.cpp create mode 100644 compiler/one-cmds/dummy-driver/src/dummy-infer.cpp create mode 100644 compiler/one-cmds/dummy-driver/src/dummy-inferV2.cpp create mode 100644 compiler/one-cmds/dummy-driver/src/help-infer.cpp create mode 100644 compiler/one-cmds/one-infer create mode 100644 compiler/one-cmds/one-init create mode 100644 compiler/one-cmds/one-partition create mode 100644 compiler/one-cmds/onelib/CfgRunner.py create mode 100644 compiler/one-cmds/onelib/OptionBuilder.py create mode 100644 compiler/one-cmds/onelib/TopologicalSortHelper.py create mode 100644 compiler/one-cmds/onelib/WorkflowRunner.py create mode 100644 compiler/one-cmds/tests/OONECC_024.cfg create mode 100644 compiler/one-cmds/tests/one-import-onnx_002.test create mode 100644 compiler/one-cmds/tests/one-infer-test-post-process.py create mode 100644 compiler/one-cmds/tests/one-infer_001.test create mode 100644 compiler/one-cmds/tests/one-infer_002.test create mode 100644 compiler/one-cmds/tests/one-infer_003.test create mode 100644 compiler/one-cmds/tests/one-infer_004.test create mode 100644 compiler/one-cmds/tests/one-infer_005.cfg create mode 100644 compiler/one-cmds/tests/one-infer_005.test create mode 100644 compiler/one-cmds/tests/one-infer_006.test create mode 100644 compiler/one-cmds/tests/one-infer_neg_001.test create mode 100644 compiler/one-cmds/tests/one-infer_neg_002.test create mode 100644 compiler/one-cmds/tests/one-infer_neg_003.test create mode 100644 compiler/one-cmds/tests/one-infer_neg_004.test create mode 100644 compiler/one-cmds/tests/one-infer_neg_005.test create mode 100644 compiler/one-cmds/tests/one-partition_001.test create mode 100644 compiler/one-cmds/tests/one-partition_neg_001.test create mode 100644 compiler/one-cmds/tests/one-partition_neg_002.test create mode 100644 compiler/one-cmds/tests/one-quantize_010.test create mode 100644 compiler/one-cmds/tests/one-quantize_011.test create mode 100644 compiler/one-cmds/tests/one-quantize_012.qconf.json create mode 100644 compiler/one-cmds/tests/one-quantize_012.test create mode 100644 compiler/one-cmds/tests/one-quantize_013.qconf.json create mode 100644 compiler/one-cmds/tests/one-quantize_013.test create mode 100644 compiler/one-cmds/tests/one-quantize_014.test create mode 100644 compiler/one-cmds/tests/one-quantize_015.test create mode 100644 compiler/one-cmds/tests/one-quantize_neg_020.test create mode 100644 compiler/one-cmds/tests/onecc_024.cfg create mode 100644 compiler/one-cmds/tests/onecc_024.test create mode 100644 compiler/one-cmds/tests/onecc_025.cfg create mode 100644 compiler/one-cmds/tests/onecc_025.test create mode 100644 compiler/one-cmds/tests/onecc_026.cfg create mode 100644 compiler/one-cmds/tests/onecc_026.test create mode 100644 compiler/one-cmds/tests/onecc_027.cfg create mode 100644 compiler/one-cmds/tests/onecc_027.test create mode 100644 compiler/one-cmds/tests/onecc_028.test create mode 100644 compiler/one-cmds/tests/onecc_028.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_029.test create mode 100644 compiler/one-cmds/tests/onecc_029.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_030.test create mode 100644 compiler/one-cmds/tests/onecc_030.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_031.test create mode 100644 compiler/one-cmds/tests/onecc_031.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_032.test create mode 100644 compiler/one-cmds/tests/onecc_032.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_033.test create mode 100644 compiler/one-cmds/tests/onecc_033.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_034.test create mode 100644 compiler/one-cmds/tests/onecc_034.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_035.test create mode 100644 compiler/one-cmds/tests/onecc_035.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_036.test create mode 100644 compiler/one-cmds/tests/onecc_036.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_037.test create mode 100644 compiler/one-cmds/tests/onecc_037.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_038.test create mode 100644 compiler/one-cmds/tests/onecc_038.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_039.test create mode 100644 compiler/one-cmds/tests/onecc_039.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_040.cfg create mode 100644 compiler/one-cmds/tests/onecc_040.test create mode 100644 compiler/one-cmds/tests/onecc_040.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_041.cfg create mode 100644 compiler/one-cmds/tests/onecc_041.test create mode 100644 compiler/one-cmds/tests/onecc_041.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_009.test create mode 100644 compiler/one-cmds/tests/onecc_neg_010.test create mode 100644 compiler/one-cmds/tests/onecc_neg_011.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_011.test create mode 100644 compiler/one-cmds/tests/onecc_neg_012.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_012.test create mode 100644 compiler/one-cmds/tests/onecc_neg_013.test create mode 100644 compiler/one-cmds/tests/onecc_neg_014.test create mode 100644 compiler/one-cmds/tests/onecc_neg_014.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_015.test create mode 100644 compiler/one-cmds/tests/onecc_neg_015.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_016.test create mode 100644 compiler/one-cmds/tests/onecc_neg_016.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_017.test create mode 100644 compiler/one-cmds/tests/onecc_neg_017.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_018.test create mode 100644 compiler/one-cmds/tests/onecc_neg_018.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_019.test create mode 100644 compiler/one-cmds/tests/onecc_neg_019.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_020.test create mode 100644 compiler/one-cmds/tests/onecc_neg_020.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_021.test create mode 100644 compiler/one-cmds/tests/onecc_neg_021.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_022.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_022.test create mode 100644 compiler/one-cmds/tests/onecc_neg_022.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_023.test create mode 100644 compiler/one-cmds/tests/onecc_neg_023.workflow.json create mode 100644 compiler/tflchef/core/src/Op/Densify.cpp create mode 100644 compiler/tflchef/core/src/Op/Densify.h create mode 100644 compiler/tflchef/tests/make_sparse/test.recipe create mode 100644 compiler/tflchef/tests/make_sparse_f16/test.recipe rename compiler/tflchef/tflite/src/Op/{ => include}/Abs.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Add.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/AddN.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ArgMax.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ArgMin.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/AveragePool2D.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/BatchMatMul.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/BatchToSpaceND.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/BidirectionalSequenceLSTM.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Cast.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Ceil.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Concatenation.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Conv2D.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Cos.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/DepthToSpace.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/DepthwiseConv2D.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Dequantize.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Div.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ELU.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Equal.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Exp.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ExpandDims.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/FakeQuant.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Fill.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Floor.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/FloorDiv.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/FloorMod.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/FullyConnected.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Gather.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/GatherNd.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Greater.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/GreaterEqual.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/L2Normalize.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/L2Pool2D.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LeakyRelu.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Less.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LessEqual.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LocalResponseNormalization.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Log.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LogSoftmax.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LogicalAnd.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LogicalNot.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/LogicalOr.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Logistic.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/MatrixDiag.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/MatrixSetDiag.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/MaxPool2D.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Maximum.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Mean.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Minimum.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/MirrorPad.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Mul.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Neg.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/NonMaxSuppressionV4.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/NonMaxSuppressionV5.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/NotEqual.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/OneHot.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/PRelu.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Pack.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Pad.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/PadV2.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Pow.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Quantize.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Range.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Rank.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReLU.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReLU6.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReLUN1To1.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReduceAny.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReduceMax.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReduceMin.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReduceProd.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Reshape.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ResizeBilinear.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ResizeNearestNeighbor.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReverseSequence.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ReverseV2.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Round.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Rsqrt.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SVDF.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ScatterNd.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SegmentSum.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Select.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SelectV2.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Shape.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Sin.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Slice.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Softmax.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SpaceToBatchND.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SpaceToDepth.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SparseToDense.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Split.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SplitV.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Sqrt.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Square.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/SquaredDifference.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Squeeze.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/StridedSlice.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Sub.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Sum.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Tanh.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Tile.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/TopKV2.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Transpose.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/TransposeConv.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/UnidirectionalSequenceLSTM.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Unique.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Unpack.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/Where.h (100%) rename compiler/tflchef/tflite/src/Op/{ => include}/ZerosLike.h (100%) delete mode 100644 compiler/tfldump/include/tflread/Model.h delete mode 100644 compiler/tfldump/src/Load.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.h create mode 100644 compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvUInt8.h rename compute/{test/cker/Range.cc => cker/src/Range.test.cc} (100%) delete mode 100644 compute/test/CMakeLists.txt create mode 100644 docs/release/1.20/index.rst create mode 100644 docs/release/1.20/release-note-1.20.0.md create mode 100644 docs/release/1.21/index.rst create mode 100644 docs/release/1.21/release-note_1.21.0.md create mode 100644 infra/cmake/packages/Egl_HeadersSourceConfig.cmake create mode 100644 infra/cmake/packages/Opengl_HeadersSourceConfig.cmake create mode 100644 infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfig.cmake create mode 100644 infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfigVersion.cmake create mode 100644 infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfig.cmake create mode 100644 infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfigVersion.cmake create mode 100644 infra/cmake/packages/VulkanSourceConfig.cmake create mode 100644 infra/debian/compiler/docs/one-infer.1 create mode 100644 infra/debian/compiler/docs/one-partition.1 create mode 100644 infra/nncc/cmake/options/options_armv7em-generic.cmake create mode 100644 infra/nnfw/cmake/packages/GLib2.0Config.cmake delete mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLite/CMakeLists.txt delete mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfig.cmake delete mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfigVersion.cmake create mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLite/CMakeLists.txt create mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfig.cmake create mode 100644 infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfigVersion.cmake create mode 100644 nnpackage/examples/v1.3.0/two_tflites/README.md create mode 100644 nnpackage/examples/v1.3.0/two_tflites/metadata/MANIFEST create mode 100644 nnpackage/examples/v1.3.0/two_tflites/metadata/tc/expected.h5 create mode 100644 nnpackage/examples/v1.3.0/two_tflites/metadata/tc/input.h5 create mode 100644 nnpackage/examples/v1.3.0/two_tflites/mv1.0.tflite create mode 100644 nnpackage/examples/v1.3.0/two_tflites/mv1.1.tflite delete mode 100644 packaging/RUY.tar.gz create mode 100644 packaging/TENSORFLOW-2.8.0-RUY.tar.gz create mode 100644 res/CircleRecipes/Quant_InstanceNorm_000/test.qconf.json create mode 100644 res/CircleRecipes/Quant_InstanceNorm_000/test.recipe create mode 100644 res/CircleRecipes/Quant_InstanceNorm_000/test.reverse create mode 100644 res/CircleRecipes/Quant_InstanceNorm_000/test.rule create mode 100644 res/CircleRecipes/Quant_InstanceNorm_001/test.qconf.json create mode 100644 res/CircleRecipes/Quant_InstanceNorm_001/test.recipe create mode 100644 res/CircleRecipes/Quant_InstanceNorm_001/test.reverse create mode 100644 res/CircleRecipes/Quant_InstanceNorm_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/ArgMax_004/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ArgMax_004/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Densify_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_007/test.recipe create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_007/test.reverse create mode 100644 res/TensorFlowLiteRecipes/FullyConnected_007/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_Densify_Add_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_Densify_Dequantize_Add_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_Dequantize_Add_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_TConv_BN_003/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_TConv_BN_003/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_TConv_BN_004/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_TConv_BN_004/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_002/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_002/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_BatchMatMul_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Concatenation_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_002/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_003/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_003/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_003/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_003/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_004/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_004/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_004/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_004/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_FullyConnected_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_LeakyRelu_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Logistic_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Neg_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Pad_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU6_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Reshape_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeBilinear_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_ResizeNearestNeighbor_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Slice_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Softmax_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Tanh_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_000/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_001/test.qconf.json create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Quant_Transpose_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/StridedSlice_004/test.recipe create mode 100644 res/TensorFlowLiteRecipes/StridedSlice_004/test.reverse delete mode 100644 runtime/libs/misc/examples/tensor_index_iterator.cpp create mode 100644 runtime/libs/misc/include/misc/EnvConfigSource.h create mode 100644 runtime/libs/misc/include/misc/GeneralConfigSource.h create mode 100644 runtime/libs/misc/include/misc/IConfigSource.h create mode 100644 runtime/libs/misc/src/EnvConfigSource.cpp create mode 100644 runtime/libs/misc/src/GeneralConfigSource.cpp create mode 100644 runtime/libs/misc/src/string_helpers.test.cpp create mode 100644 runtime/libs/misc/src/tensor/IndexEnumerator.test.cpp create mode 100644 runtime/libs/misc/src/tensor/IndexIterator.test.cpp create mode 100644 runtime/libs/ndarray/src/Array.test.cpp create mode 100644 runtime/libs/ndarray/src/ContiguousSpan.test.cpp delete mode 100644 runtime/libs/ndarray/src/detail/cxx14.h delete mode 100644 runtime/libs/ndarray/test/CMakeLists.txt delete mode 100644 runtime/libs/ndarray/test/ndarray_test.cpp create mode 100644 runtime/onert/core/include/exec/Executors.h create mode 100644 runtime/onert/core/include/ir/Model.h create mode 100644 runtime/onert/core/include/ir/NNPkg.h delete mode 100644 runtime/onert/core/include/ir/Subgraphs.h delete mode 100644 runtime/onert/core/include/util/EnvConfigSource.h delete mode 100644 runtime/onert/core/include/util/GeneralConfigSource.h delete mode 100644 runtime/onert/core/include/util/IConfigSource.h create mode 100644 runtime/onert/core/src/compiler/HEScheduler.test.cc create mode 100644 runtime/onert/core/src/compiler/pass/UnusedOperandEliminationPass.test.cc create mode 100644 runtime/onert/core/src/exec/ExecTime.test.cc create mode 100644 runtime/onert/core/src/exec/Execution.test.cc create mode 100644 runtime/onert/core/src/exec/Executors.cc create mode 100644 runtime/onert/core/src/exec/feature/MockTensor.h create mode 100644 runtime/onert/core/src/exec/feature/nchw/Reader.test.cc create mode 100644 runtime/onert/core/src/exec/feature/nchw/View.test.cc create mode 100644 runtime/onert/core/src/exec/feature/nhwc/Reader.test.cc create mode 100644 runtime/onert/core/src/exec/feature/nhwc/View.test.cc create mode 100644 runtime/onert/core/src/interp/InterpExecutor.test.cc create mode 100644 runtime/onert/core/src/ir/Graph.test.cc create mode 100644 runtime/onert/core/src/ir/LayoutSet.test.cc rename runtime/onert/{test/core => core/src}/ir/MockNode.h (100%) create mode 100644 runtime/onert/core/src/ir/Operand.test.cc create mode 100644 runtime/onert/core/src/ir/OperandIndexSequence.test.cc create mode 100644 runtime/onert/core/src/ir/Operands.test.cc create mode 100644 runtime/onert/core/src/ir/Operation.test.cc create mode 100644 runtime/onert/core/src/ir/Operations.test.cc create mode 100644 runtime/onert/core/src/ir/Shape.test.cc create mode 100644 runtime/onert/core/src/ir/verifier/Verifier.test.cc delete mode 100644 runtime/onert/core/src/util/EnvConfigSource.cc delete mode 100644 runtime/onert/core/src/util/GeneralConfigSource.cc create mode 100644 runtime/onert/core/src/util/Index.test.cc create mode 100644 runtime/onert/core/src/util/ObjectManager.test.cc create mode 100644 runtime/onert/core/src/util/ShapeInference.test.cc delete mode 100644 runtime/onert/test/CMakeLists.txt delete mode 100644 runtime/onert/test/core/compiler/HEScheduler.cc delete mode 100644 runtime/onert/test/core/compiler/pass/UnusedOperandEliminationPass.cc delete mode 100644 runtime/onert/test/core/exec/ExecInstance.cc delete mode 100644 runtime/onert/test/core/exec/ExecTime.test.cc delete mode 100644 runtime/onert/test/core/interp/ExecManager.cc delete mode 100644 runtime/onert/test/core/ir/Graph.cc delete mode 100644 runtime/onert/test/core/ir/LayoutSet.cc delete mode 100644 runtime/onert/test/core/ir/OperandIndexSet.cc delete mode 100644 runtime/onert/test/core/ir/OperandSet.cc delete mode 100644 runtime/onert/test/core/ir/OperationSet.cc delete mode 100644 runtime/onert/test/core/ir/SetIO.cc delete mode 100644 runtime/onert/test/core/ir/Shape.cc delete mode 100644 runtime/onert/test/core/ir/UseDef.cc delete mode 100644 runtime/onert/test/core/ir/Verifier.cc delete mode 100644 runtime/onert/test/core/util/Index.cc delete mode 100644 runtime/onert/test/core/util/ObjectManager.cc delete mode 100644 runtime/onert/test/core/util/ShapeInference.cc create mode 100644 runtime/service/CMakeLists.txt create mode 100644 runtime/service/npud/CMakeLists.txt create mode 100644 runtime/service/npud/core/Server.cc create mode 100644 runtime/service/npud/core/Server.h create mode 100644 runtime/service/npud/core/Signal.cc create mode 100644 runtime/service/npud/core/Signal.h create mode 100644 runtime/service/npud/core/main.cc create mode 100644 runtime/service/npud/util/Config.lst create mode 100644 runtime/service/npud/util/ConfigSource.cc create mode 100644 runtime/service/npud/util/ConfigSource.h create mode 100644 runtime/service/npud/util/Logging.h rename tests/nnfw_api/src/{GenModelTests.cc => GenModelTests.test.cc} (100%) rename tests/nnfw_api/src/{ModelTestDynamicTensor.cc => ModelTestDynamicTensor.test.cc} (100%) rename tests/nnfw_api/src/{ModelTestInputReshaping.cc => ModelTestInputReshaping.test.cc} (100%) rename tests/nnfw_api/src/{RegressionTests.cc => RegressionTests.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestAddModelLoaded.cc => ValidationTestAddModelLoaded.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestAddSessionPrepared.cc => ValidationTestAddSessionPrepared.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestFourAddModelsSetInput.cc => ValidationTestFourAddModelsSetInput.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestMultipleSessions.cc => ValidationTestMultipleSessions.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestPipelineSession.cc => ValidationTestPipelineSession.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestSessionCreated.cc => ValidationTestSessionCreated.test.cc} (100%) rename tests/nnfw_api/src/{ValidationTestSingleSession.cc => ValidationTestSingleSession.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Add.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Add.test.cc rename tests/nnfw_api/src/one_op_tests/{AddN.cc => AddN.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/ArgMinMax.cc create mode 100644 tests/nnfw_api/src/one_op_tests/ArgMinMax.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/AveragePool2D.cc create mode 100644 tests/nnfw_api/src/one_op_tests/AveragePool2D.test.cc rename tests/nnfw_api/src/one_op_tests/{BatchToSpaceND.cc => BatchToSpaceND.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Cast.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Cast.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/Concat.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Concat.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/Conv2D.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Conv2D.test.cc rename tests/nnfw_api/src/one_op_tests/{Cos.cc => Cos.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/DepthToSpace.cc create mode 100644 tests/nnfw_api/src/one_op_tests/DepthToSpace.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/DepthwiseConv2D.cc create mode 100644 tests/nnfw_api/src/one_op_tests/DepthwiseConv2D.test.cc rename tests/nnfw_api/src/one_op_tests/{DetectionPostProcess.cc => DetectionPostProcess.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Elu.cc => Elu.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Equal.cc => Equal.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{ExpandDims.cc => ExpandDims.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Fill.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Fill.test.cc rename tests/nnfw_api/src/one_op_tests/{Floor.cc => Floor.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{FloorDiv.cc => FloorDiv.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{FullyConnected.cc => FullyConnected.test.cc} (100%) create mode 100644 tests/nnfw_api/src/one_op_tests/Greater.test.cc create mode 100644 tests/nnfw_api/src/one_op_tests/GreaterEqual.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/If.cc create mode 100644 tests/nnfw_api/src/one_op_tests/If.test.cc rename tests/nnfw_api/src/one_op_tests/{InstanceNorm.cc => InstanceNorm.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{L2Normalization.cc => L2Normalization.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{LeakyRelu.cc => LeakyRelu.test.cc} (100%) create mode 100644 tests/nnfw_api/src/one_op_tests/Less.test.cc create mode 100644 tests/nnfw_api/src/one_op_tests/LessEqual.test.cc rename tests/nnfw_api/src/one_op_tests/{LogSoftmax.cc => LogSoftmax.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Mean.cc => Mean.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Mul.cc => Mul.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Neg.cc => Neg.test.cc} (100%) create mode 100644 tests/nnfw_api/src/one_op_tests/NotEqual.test.cc rename tests/nnfw_api/src/one_op_tests/{OneHot.cc => OneHot.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Pad.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Pad.test.cc rename tests/nnfw_api/src/one_op_tests/{PadV2.cc => PadV2.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Quantize.cc => Quantize.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Rank.cc => Rank.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Reduce.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Reduce.test.cc rename tests/nnfw_api/src/one_op_tests/{Relu.cc => Relu.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Relu6.cc => Relu6.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/ResizeBilinear.cc create mode 100644 tests/nnfw_api/src/one_op_tests/ResizeBilinear.test.cc rename tests/nnfw_api/src/one_op_tests/{ResizeNearestNeighbor.cc => ResizeNearestNeighbor.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Reverse.cc => Reverse.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Select.cc => Select.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Shape.cc => Shape.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/Slice.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Slice.test.cc delete mode 100644 tests/nnfw_api/src/one_op_tests/Softmax.cc create mode 100644 tests/nnfw_api/src/one_op_tests/Softmax.test.cc rename tests/nnfw_api/src/one_op_tests/{Split.cc => Split.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Sqrt.cc => Sqrt.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Square.cc => Square.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{StridedSlice.cc => StridedSlice.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Sub.cc => Sub.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Tile.cc => Tile.test.cc} (100%) rename tests/nnfw_api/src/one_op_tests/{Transpose.cc => Transpose.test.cc} (100%) delete mode 100644 tests/nnfw_api/src/one_op_tests/While.cc create mode 100644 tests/nnfw_api/src/one_op_tests/While.test.cc create mode 100644 tools/cross/arm/sources.list.jammy delete mode 100644 tools/cross/arm/sources.list.xenial diff --git a/.ahub/sam/exclude.txt b/.ahub/sam/exclude.txt index c9ba5e0..f16f84f 100644 --- a/.ahub/sam/exclude.txt +++ b/.ahub/sam/exclude.txt @@ -5,6 +5,22 @@ # Eigen /ONE/compiler/nnc/backends/soft_backend/code_snippets/eigen.def +# Frontend test tools that are needed for release package build +/ONE/compiler/circlechef +/ONE/compiler/circle-verify +/ONE/compiler/luci/tester + +# Exclude IR headers which have lots of similar patterns +# TODO remove this when refactoring is possible +/ONE/compiler/luci/lang/include/luci/IR/Nodes +/ONE/compiler/luci/import/include/luci/Import/Nodes +/ONE/compiler/loco/include/loco/IR +/ONE/compiler/tflchef/tflite/src/Op/include + +# Exclude interpreter kernels which have similar patterns +/ONE/compiler/luci-interpreter/src/kernels +/ONE/compiler/locomotiv/src/Node + # Test codes /ONE/tests diff --git a/.ahub/tcchecker-tca/config.yaml b/.ahub/tcchecker-tca/config.yaml index 95e11d0..73ec548 100644 --- a/.ahub/tcchecker-tca/config.yaml +++ b/.ahub/tcchecker-tca/config.yaml @@ -4,30 +4,23 @@ test: testCaseLanguage: CPP testFW: GTEST testCaseFolder: - - /compute/test/cker - - /runtime/onert/core/src/backend/basic - - /runtime/onert/frontend/nnapi - - /runtime/onert/test/core/compiler - - /runtime/onert/test/core/exec - - /runtime/onert/test/core/interp - - /runtime/onert/test/graph - - /runtime/onert/test/graph/operand - - /runtime/onert/test/graph/operation - - /runtime/onert/test/graph/verifier - - /runtime/onert/test/ir - - /runtime/onert/test/util - - /tests/nnfw_api/src + - /compute/cker + - /runtime/libs/misc + - /runtime/libs/ndarray + - /runtime/onert + - /tests/nnfw_api testFile: - - extension: cpp + - extension: test.cpp any: true - - extension: cc + - extension: test.cc any: true testCase: - condition: - functionName: starts: - TEST + - TYPED_TEST - excludes : - Verifier.dag_checker - graph_operand_LayoutSet.layout_set_operators diff --git a/.github/workflows/check-pr-commit.yml b/.github/workflows/check-pr-commit.yml index 38c76dc..a3f4c1c 100644 --- a/.github/workflows/check-pr-commit.yml +++ b/.github/workflows/check-pr-commit.yml @@ -5,6 +5,11 @@ on: branches: - master - release/* + types: + - opened + - synchronize + - reopened + - ready_for_review defaults: run: @@ -14,6 +19,8 @@ jobs: check-commit-message: name: Check commit message runs-on: ubuntu-20.04 + # Skip on draft, check on draft -> ready + if: github.event.pull_request.draft == false steps: - name: Checkout diff --git a/compiler/arser/include/arser/arser.h b/compiler/arser/include/arser/arser.h index 1703e42..43f99dc 100644 --- a/compiler/arser/include/arser/arser.h +++ b/compiler/arser/include/arser/arser.h @@ -303,7 +303,7 @@ private: std::string _long_name; std::string _short_name; std::vector _names; - std::string _type; + std::string _type = "string"; std::string _help_message; std::function _func; uint32_t _nargs{1}; @@ -540,16 +540,20 @@ public: /* ** print usage */ + auto print_usage_arg = [&](const arser::Argument &arg) { + stream << " "; + std::string arg_name = arser::internal::remove_dash(arg._long_name); + std::for_each(arg_name.begin(), arg_name.end(), + [&stream](const char &c) { stream << static_cast(::toupper(c)); }); + }; stream << "Usage: ./" << parser._program_name << " "; // required optional argument for (const auto &arg : parser._optional_arg_vec) { if (!arg._is_required) continue; - stream << arg._short_name << " "; - std::string arg_name = arser::internal::remove_dash(arg._long_name); - std::for_each(arg_name.begin(), arg_name.end(), - [&stream](const char &c) { stream << static_cast(::toupper(c)); }); + stream << arg._short_name; + print_usage_arg(arg); stream << " "; } // rest of the optional argument @@ -560,10 +564,7 @@ public: stream << "[" << arg._short_name; if (arg._nargs) { - stream << " "; - std::string arg_name = arser::internal::remove_dash(arg._long_name); - std::for_each(arg_name.begin(), arg_name.end(), - [&stream](const char &c) { stream << static_cast(::toupper(c)); }); + print_usage_arg(arg); } stream << "]" << " "; @@ -591,39 +592,28 @@ public: } const size_t message_width = 60; - // positional argument - if (!parser._positional_arg_vec.empty()) - { - stream << "[Positional argument]" << std::endl; - for (const auto &arg : parser._positional_arg_vec) + auto print_help_args = [&](const std::list &args, const std::string &title) { + if (!args.empty()) { - stream.width(length_of_longest_arg); - stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t"; - for (size_t i = 0; i < arg._help_message.length(); i += message_width) + stream << title << std::endl; + for (const auto &arg : args) { - if (i) - stream << std::string(length_of_longest_arg, ' ') << "\t"; - stream << arg._help_message.substr(i, message_width) << std::endl; + stream.width(length_of_longest_arg); + stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t"; + for (size_t i = 0; i < arg._help_message.length(); i += message_width) + { + if (i) + stream << std::string(length_of_longest_arg, ' ') << "\t"; + stream << arg._help_message.substr(i, message_width) << std::endl; + } } + std::cout << std::endl; } - std::cout << std::endl; - } + }; + // positional argument + print_help_args(parser._positional_arg_vec, "[Positional argument]"); // optional argument - if (!parser._optional_arg_vec.empty()) - { - stream << "[Optional argument]" << std::endl; - for (const auto &arg : parser._optional_arg_vec) - { - stream.width(length_of_longest_arg); - stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t"; - for (size_t i = 0; i < arg._help_message.length(); i += message_width) - { - if (i) - stream << std::string(length_of_longest_arg, ' ') << "\t"; - stream << arg._help_message.substr(i, message_width) << std::endl; - } - } - } + print_help_args(parser._optional_arg_vec, "[Optional argument]"); return stream; } @@ -737,6 +727,29 @@ template T Arser::get(const std::string &arg_name) return get_impl(arg_name, static_cast(nullptr)); } +class Helper +{ +public: + static void add_version(Arser &arser, const std::function &func) + { + arser.add_argument("--version") + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(func); + } + + static void add_verbose(Arser &arser) + { + arser.add_argument("-V", "--verbose") + .nargs(0) + .required(false) + .default_value(false) + .help("output additional information to stdout or stderr"); + } +}; + } // namespace arser #endif // __ARSER_H__ diff --git a/compiler/circle-eval-diff/CMakeLists.txt b/compiler/circle-eval-diff/CMakeLists.txt index 4d86f80..d5a6230 100644 --- a/compiler/circle-eval-diff/CMakeLists.txt +++ b/compiler/circle-eval-diff/CMakeLists.txt @@ -6,6 +6,7 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_executable(circle-eval-diff ${DRIVER} ${SOURCES}) target_include_directories(circle-eval-diff PRIVATE include) +target_include_directories(circle-eval-diff PRIVATE src) target_link_libraries(circle-eval-diff arser) target_link_libraries(circle-eval-diff safemain) @@ -17,6 +18,8 @@ target_link_libraries(circle-eval-diff luci_interpreter) target_link_libraries(circle-eval-diff dio_hdf5) target_link_libraries(circle-eval-diff vconone) +install(TARGETS circle-eval-diff DESTINATION bin) + if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) @@ -25,10 +28,15 @@ endif(NOT ENABLE_TEST) # Instead, we use TEST_SOURCES to specify sources uesd for tests. set(TEST_SOURCES "src/MetricPrinter.cpp" - "src/Tensor.cpp") + "src/Tensor.cpp" + "src/InputDataLoader.cpp") nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_eval_diff_test ${TESTS} ${TEST_SOURCES}) +target_include_directories(circle_eval_diff_test PRIVATE include) target_include_directories(circle_eval_diff_test PRIVATE src) target_link_libraries(circle_eval_diff_test luci_testhelper) target_link_libraries(circle_eval_diff_test nncc_coverage) +target_link_libraries(circle_eval_diff_test dio_hdf5) +target_link_libraries(circle_eval_diff_test loco) +target_link_libraries(circle_eval_diff_test luci_lang) diff --git a/compiler/circle-eval-diff/driver/Driver.cpp b/compiler/circle-eval-diff/driver/Driver.cpp index f4a12a4..7e63ec8 100644 --- a/compiler/circle-eval-diff/driver/Driver.cpp +++ b/compiler/circle-eval-diff/driver/Driver.cpp @@ -30,19 +30,15 @@ std::string to_lower_case(std::string s) return s; } -Metric to_metric(const std::string &str) -{ - if (to_lower_case(str).compare("mae") == 0) - return Metric::MAE; - - throw std::runtime_error("Unsupported metric."); -} - InputFormat to_input_format(const std::string &str) { - if (to_lower_case(str).compare("h5") == 0) + auto small_str = to_lower_case(str); + if (small_str.compare("h5") == 0) return InputFormat::H5; + if (small_str.compare("directory") == 0 || small_str.compare("dir") == 0) + return InputFormat::DIR; + throw std::runtime_error("Unsupported input format."); } @@ -58,50 +54,50 @@ int entry(const int argc, char **argv) { arser::Arser arser("Compare inference results of two circle models"); - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); + arser::Helper::add_version(arser, print_version); - arser.add_argument("--first_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("First input model filepath"); + arser.add_argument("--first_model").required(true).help("First input model filepath"); - arser.add_argument("--second_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Second input model filepath"); + arser.add_argument("--second_model").required(true).help("Second input model filepath"); arser.add_argument("--first_input_data") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .help("Input data filepath for the first model. If not given, circle-eval-diff will run with " "randomly generated data"); arser.add_argument("--second_input_data") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .help("Input data filepath for the second model. If not given, circle-eval-diff will run with " "randomly generated data"); - arser.add_argument("--metric") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .default_value("MAE") - .help("Metric for comparison (default: MAE)"); + arser.add_argument("--dump_output_with_prefix") + .help("Dump output to files. should be given as an argument. " + "Outputs are saved in ..first.output and " + "..second.output."); + + arser.add_argument("--print_mae").nargs(0).default_value(false).help("Print Mean Absolute Error"); + + arser.add_argument("--print_mape") + .nargs(0) + .default_value(false) + .help("Print Mean Absolute PercentageError"); + + arser.add_argument("--print_mpeir") + .nargs(0) + .default_value(false) + .help("Print Mean Peak Error to Interval Ratio"); + + arser.add_argument("--print_top1_match") + .nargs(0) + .default_value(false) + .help("Print Mean Top-1 Match Ratio"); + + arser.add_argument("--print_top5_match") + .nargs(0) + .default_value(false) + .help("Print Mean Top-5 Match Ratio"); + + arser.add_argument("--print_mse").nargs(0).default_value(false).help("Print Mean Squared Error"); arser.add_argument("--input_data_format") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .default_value("h5") .help("Input data format. h5/hdf5 (default) or directory"); @@ -124,6 +120,7 @@ int entry(const int argc, char **argv) std::string second_input_data_path; std::string metric; std::string input_data_format; + std::string output_prefix; if (arser["--first_input_data"]) first_input_data_path = arser.get("--first_input_data"); @@ -135,22 +132,54 @@ int entry(const int argc, char **argv) throw std::runtime_error("Input data path should be given for both first_model and " "second_model, or neither must be given."); - metric = arser.get("--metric"); + if (arser["--dump_output_with_prefix"]) + output_prefix = arser.get("--dump_output_with_prefix"); + + // Set Metrics + std::vector metrics; + if (arser["--print_mae"] and arser.get("--print_mae")) + { + metrics.emplace_back(Metric::MAE); + } + if (arser["--print_mape"] and arser.get("--print_mape")) + { + metrics.emplace_back(Metric::MAPE); + } + if (arser["--print_mpeir"] and arser.get("--print_mpeir")) + { + metrics.emplace_back(Metric::MPEIR); + } + if (arser["--print_top1_match"] and arser.get("--print_top1_match")) + { + metrics.emplace_back(Metric::MTOP1); + } + if (arser["--print_top5_match"] and arser.get("--print_top5_match")) + { + metrics.emplace_back(Metric::MTOP5); + } + if (arser["--print_mse"] and arser.get("--print_mse")) + { + metrics.emplace_back(Metric::MSE); + } + input_data_format = arser.get("--input_data_format"); auto ctx = std::make_unique(); { ctx->first_model_path = first_model_path; ctx->second_model_path = second_model_path; - ctx->metric = to_metric(metric); + ctx->first_input_data_path = first_input_data_path; + ctx->second_input_data_path = second_input_data_path; + ctx->metric = metrics; ctx->input_format = to_input_format(input_data_format); + ctx->output_prefix = output_prefix; } CircleEvalDiff ced(std::move(ctx)); ced.init(); - ced.evalDiff(first_input_data_path, second_input_data_path); + ced.evalDiff(); return EXIT_SUCCESS; } diff --git a/compiler/circle-eval-diff/include/CircleEvalDiff.h b/compiler/circle-eval-diff/include/CircleEvalDiff.h index bf6aff4..7894480 100644 --- a/compiler/circle-eval-diff/include/CircleEvalDiff.h +++ b/compiler/circle-eval-diff/include/CircleEvalDiff.h @@ -20,8 +20,12 @@ #include #include +#include "InputDataLoader.h" +#include "MetricPrinter.h" + #include #include +#include namespace circle_eval_diff { @@ -32,14 +36,12 @@ class ModuleEvalDiff; enum class Metric { Undefined, // For debugging - MAE, -}; - -enum class InputFormat -{ - Undefined, // For debugging - H5, - // TODO Implement Random, Directory + MAE, // Mean Absolute Error + MAPE, // Mean Percentage Absolute Error + MPEIR, // Mean Peak Error to Interval Ratio + MTOP1, // Mean Top-1 Match Ratio + MTOP5, // Mean Top-5 Match Ratio + MSE, // Mean Squared Error }; class CircleEvalDiff final @@ -49,8 +51,11 @@ public: { std::string first_model_path; std::string second_model_path; - Metric metric = Metric::Undefined; + std::string first_input_data_path; + std::string second_input_data_path; + std::vector metric; InputFormat input_format = InputFormat::Undefined; + std::string output_prefix; }; public: @@ -61,12 +66,13 @@ public: void init(); // Evaluate two circle models for the given input data and compare the results - void evalDiff(const std::string &first_input_data_path, - const std::string &second_input_data_path) const; + void evalDiff(void) const; private: std::unique_ptr _ctx; - std::unique_ptr _runner; + std::unique_ptr _first_module; + std::unique_ptr _second_module; + std::vector> _metrics; }; } // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/CircleEvalDiff.cpp b/compiler/circle-eval-diff/src/CircleEvalDiff.cpp index c39a113..43e026b 100644 --- a/compiler/circle-eval-diff/src/CircleEvalDiff.cpp +++ b/compiler/circle-eval-diff/src/CircleEvalDiff.cpp @@ -15,8 +15,9 @@ */ #include "CircleEvalDiff.h" -#include "ModuleEvalDiff.h" +#include "InputDataLoader.h" #include "MetricPrinter.h" +#include "Tensor.h" #include #include @@ -26,6 +27,25 @@ namespace { +bool same_shape(const luci::CircleNode *a, const luci::CircleNode *b) +{ + if (a->rank() != b->rank()) + return false; + + for (uint32_t i = 0; i < a->rank(); i++) + { + if (not(a->dim(i) == b->dim(i))) + return false; + } + + return true; +} + +bool same_dtype(const luci::CircleNode *a, const luci::CircleNode *b) +{ + return a->dtype() == b->dtype(); +} + std::unique_ptr import(const std::string &model_path) { // Load model from the file @@ -40,7 +60,12 @@ std::unique_ptr import(const std::string &model_path) throw std::runtime_error("Failed to verify circle '" + model_path + "'"); } - auto module = luci::Importer().importModule(circle::GetModel(model_data.data())); + auto circle_model = circle::GetModel(model_data.data()); + + if (not circle_model) + throw std::runtime_error("Failed to load '" + model_path + "'"); + + auto module = luci::Importer().importModule(circle_model); if (not module) throw std::runtime_error("Failed to load '" + model_path + "'"); @@ -48,50 +73,192 @@ std::unique_ptr import(const std::string &model_path) return module; } +const std::vector inputs_of(const luci::Module *module) +{ + return loco::input_nodes(module->graph()); +} + +const std::vector outputs_of(const luci::Module *module) +{ + return loco::output_nodes(module->graph()); +} + +void writeDataToFile(const std::string &filename, const char *data, size_t data_size) +{ + std::ofstream fs(filename, std::ofstream::binary); + if (fs.fail()) + throw std::runtime_error("Cannot open file \"" + filename + "\".\n"); + if (fs.write(data, data_size).fail()) + { + throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n"); + } +} + +void checkOutputs(const luci::Module *first, const luci::Module *second) +{ + const auto first_output = outputs_of(first); + const auto second_output = outputs_of(second); + + if (first_output.size() != second_output.size()) + throw std::runtime_error("Models have different output counts"); + + for (uint32_t i = 0; i < first_output.size(); i++) + { + const auto first_node = loco::must_cast(first_output[i]); + const auto second_node = loco::must_cast(second_output[i]); + + if (not same_shape(first_node, second_node)) + throw std::runtime_error("Output shape mismatch (" + first_node->name() + ", " + + second_node->name() + ")"); + + if (not same_dtype(first_node, second_node)) + throw std::runtime_error("Output dtype mismatch (" + first_node->name() + ", " + + second_node->name() + ")"); + } +} + } // namespace namespace circle_eval_diff { -CircleEvalDiff::CircleEvalDiff(std::unique_ptr &&ctx) - : _ctx(std::move(ctx)), _runner(nullptr) +std::vector> interpret(const luci::Module *module, + const InputDataLoader::Data &data) +{ + auto interpreter = std::make_unique(module); + + auto input_nodes = ::inputs_of(module); + auto output_nodes = ::outputs_of(module); + + for (uint32_t input_idx = 0; input_idx < data.size(); input_idx++) + { + auto input_node = loco::must_cast(input_nodes[input_idx]); + assert(input_node->index() == input_idx); + + auto input_data = data.at(input_idx); + interpreter->writeInputTensor(input_node, input_data.buffer(), input_data.byte_size()); + } + + interpreter->interpret(); + + std::vector> outputs; + for (uint32_t output_idx = 0; output_idx < output_nodes.size(); output_idx++) + { + auto output_node = loco::must_cast(output_nodes[output_idx]); + assert(output_node->index() == output_idx); + + auto tensor = createEmptyTensor(output_node); + interpreter->readOutputTensor(output_node, tensor->buffer(), tensor->byte_size()); + outputs.emplace_back(tensor); + } + + return outputs; +} + +CircleEvalDiff::CircleEvalDiff(std::unique_ptr &&ctx) : _ctx(std::move(ctx)) { + // DO NOTHING } CircleEvalDiff::~CircleEvalDiff() = default; void CircleEvalDiff::init() { + _first_module = import(_ctx->first_model_path); + _second_module = import(_ctx->second_model_path); + + // Check modules have the same output signature (dtype/shape) + // Exception will be thrown if they have different signature + checkOutputs(_first_module.get(), _second_module.get()); + // Set metric std::unique_ptr metric; - switch (_ctx->metric) + for (auto metric : _ctx->metric) { - case Metric::MAE: - metric = std::make_unique(); - break; - default: - throw std::runtime_error("Unsupported metric."); + switch (metric) + { + case Metric::MAE: + { + _metrics.emplace_back(std::make_unique()); + break; + } + case Metric::MAPE: + { + _metrics.emplace_back(std::make_unique()); + break; + } + case Metric::MPEIR: + { + _metrics.emplace_back(std::make_unique()); + break; + } + case Metric::MTOP1: + { + _metrics.emplace_back(std::make_unique(1)); + break; + } + case Metric::MTOP5: + { + _metrics.emplace_back(std::make_unique(5)); + break; + } + case Metric::MSE: + { + _metrics.emplace_back(std::make_unique()); + break; + } + default: + throw std::runtime_error("Unsupported metric."); + } + _metrics.back()->init(_first_module.get(), _second_module.get()); } +} - auto first_module = import(_ctx->first_model_path); - auto second_module = import(_ctx->second_model_path); +void CircleEvalDiff::evalDiff(void) const +{ + auto first_input_loader = circle_eval_diff::makeDataLoader( + _ctx->first_input_data_path, _ctx->input_format, ::inputs_of(_first_module.get())); + auto second_input_loader = circle_eval_diff::makeDataLoader( + _ctx->second_input_data_path, _ctx->input_format, ::inputs_of(_second_module.get())); - // Set runner - switch (_ctx->input_format) + for (uint32_t data_idx = 0; data_idx < first_input_loader->size(); data_idx++) { - case InputFormat::H5: - _runner = std::make_unique(std::move(first_module), std::move(second_module), - std::move(metric)); - break; - default: - throw std::runtime_error("Unsupported input format."); + std::cout << "Evaluating " << data_idx << "'th data" << std::endl; + + auto first_data = first_input_loader->get(data_idx); + auto second_data = second_input_loader->get(data_idx); + + auto first_output = interpret(_first_module.get(), first_data); + auto second_output = interpret(_second_module.get(), second_data); + + for (auto &metric : _metrics) + { + metric->accumulate(first_output, second_output); + } + + if (_ctx.get()->output_prefix.empty()) + continue; + + for (uint32_t i = 0; i < first_output.size(); i++) + { + auto out = first_output[i]; + writeDataToFile(_ctx.get()->output_prefix + "." + std::to_string(data_idx) + ".first.output" + + std::to_string(i), + (char *)(out->buffer()), out->byte_size()); + } + for (uint32_t i = 0; i < second_output.size(); i++) + { + auto out = second_output[i]; + writeDataToFile(_ctx.get()->output_prefix + "." + std::to_string(data_idx) + + ".second.output" + std::to_string(i), + (char *)(out->buffer()), out->byte_size()); + } } -} -void CircleEvalDiff::evalDiff(const std::string &first_input_data_path, - const std::string &second_input_data_path) const -{ - _runner->evalDiff(first_input_data_path, second_input_data_path); + for (auto &metric : _metrics) + { + std::cout << metric.get() << std::endl; + } } } // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/InputDataLoader.cpp b/compiler/circle-eval-diff/src/InputDataLoader.cpp new file mode 100644 index 0000000..99276f3 --- /dev/null +++ b/compiler/circle-eval-diff/src/InputDataLoader.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "InputDataLoader.h" + +#include +#include +#include + +#include +#include +#include +#include + +using DataType = loco::DataType; +using Shape = std::vector; + +namespace circle_eval_diff +{ + +// Check the type and the shape of CircleInput +void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape) +{ + // Type check + if (dtype != input_node->dtype()) + throw std::runtime_error("Wrong input type."); + + if (shape.size() != input_node->rank()) + throw std::runtime_error("Input rank mismatch."); + + for (uint32_t i = 0; i < shape.size(); i++) + { + if (not(shape.at(i) == input_node->dim(i))) + throw std::runtime_error("Input shape mismatch."); + } +} + +std::vector getEachByteSizeOf(const std::vector &nodes) +{ + std::vector vec; + + for (const auto node : nodes) + { + const auto input_node = loco::must_cast(node); + size_t element_size = 1; + + for (uint32_t index = 0; index < input_node->rank(); index++) + { + element_size *= input_node->dim(index).value(); + } + + vec.push_back(element_size); + } + + return vec; +} + +size_t getTotalByteSizeOf(const std::vector &nodes) +{ + size_t total_byte_size = 0; + + for (const auto node : nodes) + { + const auto input_node = loco::must_cast(node); + size_t byte_size = loco::size(input_node->dtype()); + + for (uint32_t index = 0; index < input_node->rank(); index++) + { + byte_size *= input_node->dim(index).value(); + } + + total_byte_size += byte_size; + } + + return total_byte_size; +} + +} // namespace circle_eval_diff + +namespace circle_eval_diff +{ + +HDF5Loader::HDF5Loader(const std::string &file_path, const std::vector &input_nodes) + : _input_nodes{input_nodes} +{ + try + { + using HDF5Importer = dio::hdf5::HDF5Importer; + + _hdf5 = std::make_unique(file_path); + _hdf5->importGroup("value"); + } + catch (const H5::Exception &e) + { + H5::Exception::printErrorStack(); + throw std::runtime_error("HDF5 error occurred."); + } +} + +uint32_t HDF5Loader::size(void) const { return _hdf5->numData(); } + +InputDataLoader::Data HDF5Loader::get(uint32_t data_idx) const +{ + Data data; + data.resize(_input_nodes.size()); + + for (uint32_t input_idx = 0; input_idx < _input_nodes.size(); input_idx++) + { + auto input_node = loco::must_cast(_input_nodes.at(input_idx)); + assert(input_node->index() == input_idx); + + data.at(input_idx) = *createEmptyTensor(input_node).get(); + + auto input_buffer = data.at(input_idx).buffer(); + try + { + if (_hdf5->isRawData()) + { + _hdf5->readTensor(data_idx, input_idx, input_buffer); + } + else + { + DataType dtype; + Shape shape; + _hdf5->readTensor(data_idx, input_idx, &dtype, &shape, input_buffer); + + // Check the type and the shape of the input data is valid + verifyTypeShape(input_node, dtype, shape); + } + } + catch (const H5::Exception &e) + { + H5::Exception::printErrorStack(); + throw std::runtime_error("HDF5 error occurred."); + } + } + + return data; +} + +DirectoryLoader::DirectoryLoader(const std::string &dir_path, + const std::vector &input_nodes) + : _input_nodes{input_nodes} +{ + DIR *dir = opendir(dir_path.c_str()); + if (not dir) + { + throw std::runtime_error("Cannot open directory \"" + dir_path + "\"."); + } + + struct dirent *entry = nullptr; + const auto input_total_bytes = getTotalByteSizeOf(input_nodes); + while (entry = readdir(dir)) + { + // Skip if the entry is not a regular file + if (entry->d_type != DT_REG) + continue; + + _data_paths.push_back(dir_path + "/" + entry->d_name); + } + + closedir(dir); +} + +uint32_t DirectoryLoader::size(void) const { return _data_paths.size(); } + +InputDataLoader::Data DirectoryLoader::get(uint32_t data_idx) const +{ + // Read raw data + const auto input_total_bytes = getTotalByteSizeOf(_input_nodes); + std::vector input_data(input_total_bytes); + const auto raw_data_path = _data_paths.at(data_idx); + std::ifstream fs(raw_data_path, std::ifstream::binary); + + if (fs.fail()) + { + throw std::runtime_error("Cannot open file \"" + raw_data_path + "\"."); + } + if (fs.read(input_data.data(), input_total_bytes).fail()) + { + throw std::runtime_error("Failed to read raw data from file \"" + raw_data_path + "\"."); + } + + // Make Tensor from raw data + auto input_data_cur = input_data.data(); + + Data data; + data.resize(_input_nodes.size()); + std::vector input_bytes = getEachByteSizeOf(_input_nodes); + for (uint32_t index = 0; index < _input_nodes.size(); index++) + { + const auto input_node = loco::must_cast(_input_nodes.at(index)); + auto &tensor = data.at(index); + tensor = *createEmptyTensor(input_node).get(); + auto buffer = tensor.buffer(); + std::memcpy(buffer, input_data_cur, input_bytes.at(index)); + input_data_cur += input_bytes.at(index); + } + + return data; +} + +std::unique_ptr makeDataLoader(const std::string &file_path, + const InputFormat &format, + const std::vector &input_nodes) +{ + switch (format) + { + case InputFormat::H5: + { + return std::make_unique(file_path, input_nodes); + } + case InputFormat::DIR: + { + return std::make_unique(file_path, input_nodes); + } + default: + throw std::runtime_error{"Unsupported input format."}; + } +} + +} // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/InputDataLoader.h b/compiler/circle-eval-diff/src/InputDataLoader.h new file mode 100644 index 0000000..14921b2 --- /dev/null +++ b/compiler/circle-eval-diff/src/InputDataLoader.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __CIRCLE_EVAL_DIFF_INPUT_DATA_LOADER_H__ +#define __CIRCLE_EVAL_DIFF_INPUT_DATA_LOADER_H__ + +#include +#include +#include + +#include "Tensor.h" + +#include +#include + +namespace circle_eval_diff +{ + +void verifyTypeShape(const luci::CircleInput *input_node, const loco::DataType &dtype, + const std::vector &shape); + +} // namespace circle_eval_diff + +namespace circle_eval_diff +{ + +enum class InputFormat +{ + Undefined, // For debugging + H5, + DIR, // directory + // TODO Implement Random, Directory +}; + +class InputDataLoader +{ +public: + using Data = std::vector; + +public: + virtual ~InputDataLoader() = default; + +public: + virtual uint32_t size(void) const = 0; + +public: + virtual Data get(uint32_t data_idx) const = 0; +}; + +class HDF5Loader final : public InputDataLoader +{ +public: + HDF5Loader(const std::string &file_path, const std::vector &input_nodes); + +public: + uint32_t size(void) const final; + Data get(uint32_t data_idx) const final; + +private: + const std::vector _input_nodes; + std::unique_ptr _hdf5; +}; + +// This class loads the directory that has raw data binary files. +class DirectoryLoader final : public InputDataLoader +{ +public: + DirectoryLoader(const std::string &dir_path, const std::vector &input_nodes); + +public: + uint32_t size(void) const final; + Data get(uint32_t data_idx) const final; + +private: + const std::vector _input_nodes; + std::vector _data_paths; +}; + +std::unique_ptr makeDataLoader(const std::string &file_path, + const InputFormat &format, + const std::vector &input_nodes); + +} // namespace circle_eval_diff + +#endif // __CIRCLE_EVAL_DIFF_INPUT_DATA_LOADER_H__ diff --git a/compiler/circle-eval-diff/src/InputDataLoader.test.cpp b/compiler/circle-eval-diff/src/InputDataLoader.test.cpp new file mode 100644 index 0000000..cbe7879 --- /dev/null +++ b/compiler/circle-eval-diff/src/InputDataLoader.test.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 + +#include + +#include "InputDataLoader.h" + +using namespace circle_eval_diff; + +TEST(CircleEvalInputDataLoaderTest, verifyTypeShapeTest) +{ + luci::CircleInput input; + input.dtype(loco::DataType::FLOAT32); + input.rank(4); + input.dim(0).set(1); + input.dim(1).set(3); + input.dim(2).set(3); + input.dim(3).set(2); + + loco::DataType right_data_type{loco::DataType::FLOAT32}; + std::vector right_shape; + right_shape.emplace_back(1); + right_shape.emplace_back(3); + right_shape.emplace_back(3); + right_shape.emplace_back(2); + + EXPECT_NO_THROW(verifyTypeShape(&input, right_data_type, right_shape)); +} + +TEST(CircleEvalInputDataLoaderTest, verifyTypeShapeTest_NEG) +{ + luci::CircleInput input; + input.dtype(loco::DataType::FLOAT32); + input.rank(4); + input.dim(0).set(1); + input.dim(1).set(4); + input.dim(2).set(4); + input.dim(3).set(2); + + loco::DataType right_data_type{loco::DataType::FLOAT32}; + loco::DataType wrong_data_type{loco::DataType::FLOAT16}; + std::vector wrong_shape; + wrong_shape.emplace_back(1); + wrong_shape.emplace_back(3); + wrong_shape.emplace_back(3); + wrong_shape.emplace_back(2); + + EXPECT_ANY_THROW(verifyTypeShape(&input, right_data_type, wrong_shape)); + EXPECT_ANY_THROW(verifyTypeShape(&input, wrong_data_type, wrong_shape)); +} diff --git a/compiler/circle-eval-diff/src/MetricPrinter.cpp b/compiler/circle-eval-diff/src/MetricPrinter.cpp index d65eb9b..ec84084 100644 --- a/compiler/circle-eval-diff/src/MetricPrinter.cpp +++ b/compiler/circle-eval-diff/src/MetricPrinter.cpp @@ -18,6 +18,7 @@ #include +#include #include #include @@ -30,6 +31,16 @@ using Tensor = circle_eval_diff::Tensor; namespace { +uint32_t num_elems(const luci::CircleNode *node) +{ + uint32_t res = 1; + + for (uint32_t i = 0; i < node->rank(); i++) + res *= node->dim(i).value(); + + return res; +} + template bool same_shape(const T a, const T b) { if (a->rank() != b->rank()) @@ -44,6 +55,8 @@ template bool same_shape(const T a, const T b) return true; } +template bool same_dtype(const T a, const T b) { return a->dtype() == b->dtype(); } + template std::shared_ptr to_fp32(const std::shared_ptr &tensor) { assert(tensor->dtype() == DT); // FIX_CALLER_UNLESS @@ -97,7 +110,6 @@ void MAEPrinter::init(const luci::Module *first, const luci::Module *second) { const auto first_node = loco::must_cast(first_output[i]); const auto second_node = loco::must_cast(second_output[i]); - assert(same_shape(first_node, second_node)); // FIX_CALLER_UNLESS // Create tensors to store intermediate results _intermediate.emplace_back(); @@ -180,6 +192,471 @@ void MAEPrinter::dump(std::ostream &os) const } } +// TODO Remove duplicate codes with MAEPrinter +void MAPEPrinter::init(const luci::Module *first, const luci::Module *second) +{ + THROW_UNLESS(first != nullptr, "Invalid module."); + THROW_UNLESS(second != nullptr, "Invalid module."); + + const auto first_output = loco::output_nodes(first->graph()); + const auto second_output = loco::output_nodes(second->graph()); + + assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < first_output.size(); i++) + { + const auto first_node = loco::must_cast(first_output[i]); + const auto second_node = loco::must_cast(second_output[i]); + + // Create tensors to store intermediate results + _intermediate.emplace_back(); + _intermediate.at(i).dtype(loco::DataType::FLOAT32); + // NOTE Use both first_node and second_node to avoid release build break + _intermediate.at(i).rank(first_node->rank()); + uint32_t num_elems = 1; + for (uint32_t j = 0; j < second_node->rank(); j++) + { + _intermediate.at(i).dim(j) = second_node->dim(j); + num_elems *= second_node->dim(j).value(); + } + _intermediate.at(i).size(num_elems); + + // Check the buffer is initilized with zero + for (uint32_t j = 0; j < num_elems; j++) + assert(_intermediate.at(i).at(j) == 0.0); + + // Save output names for logging + _output_names.emplace_back(first_node->name()); + } +} + +// Accumulate |(a - b) / a| +void MAPEPrinter::accum_mean_absolute_error(uint32_t output_idx, const std::shared_ptr &a, + const std::shared_ptr &b) +{ + assert(a->dtype() == loco::DataType::FLOAT32 and + b->dtype() == loco::DataType::FLOAT32); // FIX_CALLER_UNLESS + assert(same_shape(a.get(), b.get())); // FIX_CALLER_UNLESS + assert(output_idx < _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < a->size(); i++) + { + const auto a_val = a->at(i); + const auto b_val = b->at(i); + _intermediate.at(output_idx).at(i) += + std::abs((a_val - b_val) / a_val); + } +} + +// Assumption +// first: the result of fp32 model +// second: the result of fake-quantized model +void MAPEPrinter::accumulate(const std::vector> &first, + const std::vector> &second) +{ + assert(first.size() == second.size()); // FIX_CALLER_UNLESS + assert(first.size() == _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto first_output = first[output_idx]; + const auto second_output = second[output_idx]; + + // Cast data to fp32 and then compute absolute error + const auto fp32_first_output = fp32(first_output); + const auto fp32_second_output = fp32(second_output); + + accum_mean_absolute_error(output_idx, fp32_first_output, fp32_second_output); + } + + _num_data++; +} + +void MAPEPrinter::dump(std::ostream &os) const +{ + os << "Mean Absolute Percentage Error (MAPE)" << std::endl; + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto name = _output_names.at(output_idx); + const auto &inter = _intermediate.at(output_idx); + assert(inter.dtype() == loco::DataType::FLOAT32); // FIX_ME_UNLESS + const auto elem_count = inter.size(); + + // Compute MAPE + float mape = 0.0; + for (uint32_t elem_idx = 0; elem_idx < elem_count; elem_idx++) + mape += inter.at(elem_idx); + + mape = mape / elem_count; + mape = mape / _num_data; + mape *= 100.0; + + os << "MAPE for " << name << " is " << mape << "%" << std::endl; + } +} + +// TODO Remove duplicate codes with MAEPrinter +void MPEIRPrinter::init(const luci::Module *first, const luci::Module *second) +{ + THROW_UNLESS(first != nullptr, "Invalid module."); + THROW_UNLESS(second != nullptr, "Invalid module."); + + const auto first_output = loco::output_nodes(first->graph()); + const auto second_output = loco::output_nodes(second->graph()); + + assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < first_output.size(); i++) + { + const auto first_node = loco::must_cast(first_output[i]); + const auto second_node = loco::must_cast(second_output[i]); + + // Create places to store intermediate results + _intermediate.emplace_back(0.0); + + // Save output names for logging + _output_names.emplace_back(first_node->name()); + } +} + +// Accumulate PEIR (Peak Error to Interval Ratio) +// PEIR = max(|a - b|) / (max(a) - min(a)) +// PEIR >= 0 (lower is better) +void MPEIRPrinter::accum_peir(uint32_t output_idx, const std::shared_ptr &a, + const std::shared_ptr &b) +{ + assert(a->dtype() == loco::DataType::FLOAT32 and + b->dtype() == loco::DataType::FLOAT32); // FIX_CALLER_UNLESS + assert(same_shape(a.get(), b.get())); // FIX_CALLER_UNLESS + assert(output_idx < _intermediate.size()); // FIX_CALLER_UNLESS + + float min = std::numeric_limits::max(); + float max = std::numeric_limits::lowest(); + + for (uint32_t i = 0; i < a->size(); i++) + { + const auto a_val = a->at(i); + min = std::min(a_val, min); + max = std::max(a_val, max); + } + + float interval = max - min; + + // Corner case: All values are the same. We set interval = 1 in this case + if (interval == 0) + interval = 1.0; + + float peak_error = std::numeric_limits::lowest(); + + for (uint32_t i = 0; i < a->size(); i++) + { + const auto a_val = a->at(i); + const auto b_val = b->at(i); + const auto error = std::abs(a_val - b_val); + peak_error = std::max(error, peak_error); + } + + _intermediate.at(output_idx) += peak_error / interval; +} + +// Assumption (when testing the accuracy of quantized model) +// first: the result of fp32 model +// second: the result of fake-quantized model +void MPEIRPrinter::accumulate(const std::vector> &first, + const std::vector> &second) +{ + assert(first.size() == second.size()); // FIX_CALLER_UNLESS + assert(first.size() == _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto first_output = first[output_idx]; + const auto second_output = second[output_idx]; + + // Cast data to fp32 for ease of computation + const auto fp32_first_output = fp32(first_output); + const auto fp32_second_output = fp32(second_output); + + accum_peir(output_idx, fp32_first_output, fp32_second_output); + } + + _num_data++; +} + +void MPEIRPrinter::dump(std::ostream &os) const +{ + os << "Mean Peak Error to Interval Ratio (MPEIR)" << std::endl; + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto name = _output_names.at(output_idx); + const auto sum_of_peir = _intermediate.at(output_idx); + + // Compute MPEIR + float mpeir = sum_of_peir / _num_data; + + os << "MPEIR for " << name << " is " << mpeir << std::endl; + } +} + +// TODO Remove duplicate codes with MAEPrinter +void TopKMatchPrinter::init(const luci::Module *first, const luci::Module *second) +{ + THROW_UNLESS(first != nullptr, "Invalid module."); + THROW_UNLESS(second != nullptr, "Invalid module."); + + const auto first_output = loco::output_nodes(first->graph()); + const auto second_output = loco::output_nodes(second->graph()); + + assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < first_output.size(); i++) + { + const auto first_node = loco::must_cast(first_output[i]); + const auto second_node = loco::must_cast(second_output[i]); + + // Create places to store intermediate results + _intermediate.emplace_back(0.0); + + // Save output names for logging + _output_names.emplace_back(first_node->name()); + + // If num_elems of an output is less than k, + // the output index is added to the skip list + if (num_elems(first_node) < _k) + { + std::cout << "Top-" << _k << "metric for " << first_node->name() + << " is ignored, because it has elements less than " << _k << std::endl; + _skip_output.emplace_back(i); + } + } +} + +void TopKMatchPrinter::accum_topk_accuracy(uint32_t output_idx, const std::shared_ptr &a, + const std::shared_ptr &b) +{ + assert(a->dtype() == loco::DataType::FLOAT32 and + b->dtype() == loco::DataType::FLOAT32); // FIX_CALLER_UNLESS + assert(same_shape(a.get(), b.get())); // FIX_CALLER_UNLESS + assert(output_idx < _intermediate.size()); // FIX_CALLER_UNLESS + + // Find Top-k largest elements + // This implementation is a variant of "Method 2 (Use temporary array)" in + // https://www.geeksforgeeks.org/k-largestor-smallest-elements-in-an-array/ + // We sort top-k elements by value and index to ensure that the element with an earlier + // index comes first if multiple elements have the same value. + auto find_topk = [this](const std::shared_ptr &tensor) { + assert(_k <= tensor->size()); // FIX_CALLER_UNLESS + + // first: value, second: index + std::vector> topk; + topk.resize(_k); + + // Initialize + for (uint32_t i = 0; i < _k; i++) + { + topk[i] = std::make_pair(tensor->at(i), i); + } + + // Input pair: (value, index) + // Return true if a has smaller value than b. If a and b have the same value, + // return true if a has larger index. + auto compare = [](const std::pair &a, const std::pair &b) { + if (a.first == b.first) + return a.second > b.second; + + return a.first < b.first; + }; + + for (uint32_t i = _k; i < tensor->size(); i++) + { + auto val = std::make_pair(tensor->at(i), i); + + auto min = std::min_element(topk.begin(), topk.end(), compare); + if (compare(*min, val)) + { + // val is larger than min. Replace min with val. + auto min_index = std::distance(topk.begin(), min); + topk[min_index] = val; + } + } + + return topk; + }; + + auto first_topk = find_topk(a); + auto second_topk = find_topk(b); + + uint32_t matched = 0; + for (uint32_t i = 0; i < _k; i++) + { + for (uint32_t j = 0; j < _k; j++) + { + if (first_topk[i].second == second_topk[j].second) + { + matched++; + break; + } + } + } + + float matched_ratio = static_cast(matched) / _k; + + _intermediate.at(output_idx) += matched_ratio; +} + +bool TopKMatchPrinter::in_skip_list(uint32_t output_index) const +{ + for (auto skip : _skip_output) + { + if (output_index == skip) + return true; + } + + return false; +} + +void TopKMatchPrinter::accumulate(const std::vector> &first, + const std::vector> &second) +{ + assert(first.size() == second.size()); // FIX_CALLER_UNLESS + assert(first.size() == _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + if (in_skip_list(output_idx)) + continue; + + const auto first_output = first[output_idx]; + const auto second_output = second[output_idx]; + + // Cast data to fp32 for ease of computation + const auto fp32_first_output = fp32(first_output); + const auto fp32_second_output = fp32(second_output); + + accum_topk_accuracy(output_idx, fp32_first_output, fp32_second_output); + } + + _num_data++; +} + +void TopKMatchPrinter::dump(std::ostream &os) const +{ + os << "Ratio of Matched Indices between Top-" << _k << " results of the models" << std::endl; + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + if (in_skip_list(output_idx)) + continue; + + const auto name = _output_names.at(output_idx); + const auto sum_of_topk_accuracy = _intermediate.at(output_idx); + + // Compute TopKMatch + float mean_topk = sum_of_topk_accuracy / _num_data; + + os << "Mean Top-" << _k << " match ratio for " << name << " is " << mean_topk << std::endl; + } +} + +void MSEPrinter::init(const luci::Module *first, const luci::Module *second) +{ + THROW_UNLESS(first != nullptr, "Invalid module."); + THROW_UNLESS(second != nullptr, "Invalid module."); + + const auto first_output = loco::output_nodes(first->graph()); + const auto second_output = loco::output_nodes(second->graph()); + + assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < first_output.size(); i++) + { + const auto first_node = loco::must_cast(first_output[i]); + const auto second_node = loco::must_cast(second_output[i]); + + // Create tensors to store intermediate results + _intermediate.emplace_back(); + _intermediate.at(i).dtype(loco::DataType::FLOAT32); + // NOTE Use both first_node and second_node to avoid release build break + _intermediate.at(i).rank(first_node->rank()); + uint32_t num_elems = 1; + for (uint32_t j = 0; j < second_node->rank(); j++) + { + _intermediate.at(i).dim(j) = second_node->dim(j); + num_elems *= second_node->dim(j).value(); + } + _intermediate.at(i).size(num_elems); + + // Check the buffer is initilized with zero + for (uint32_t j = 0; j < num_elems; j++) + assert(_intermediate.at(i).at(j) == 0.0); + + // Save output names for logging + _output_names.emplace_back(first_node->name()); + } +} + +void MSEPrinter::accum_squared_error(uint32_t output_idx, const std::shared_ptr &a, + const std::shared_ptr &b) +{ + assert(a->dtype() == loco::DataType::FLOAT32 and + b->dtype() == loco::DataType::FLOAT32); // FIX_CALLER_UNLESS + assert(same_shape(a.get(), b.get())); // FIX_CALLER_UNLESS + assert(output_idx < _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t i = 0; i < a->size(); i++) + { + _intermediate.at(output_idx).at(i) += + (a->at(i) - b->at(i)) * + (a->at(i) - b->at(i)); + } +} + +void MSEPrinter::accumulate(const std::vector> &first, + const std::vector> &second) +{ + assert(first.size() == second.size()); // FIX_CALLER_UNLESS + assert(first.size() == _intermediate.size()); // FIX_CALLER_UNLESS + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto first_output = first[output_idx]; + const auto second_output = second[output_idx]; + + // Cast data to fp32 and then compute absolute error + const auto fp32_first_output = fp32(first_output); + const auto fp32_second_output = fp32(second_output); + + accum_squared_error(output_idx, fp32_first_output, fp32_second_output); + } + + _num_data++; +} + +void MSEPrinter::dump(std::ostream &os) const +{ + os << "Mean Squared Error (MSE)" << std::endl; + + for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++) + { + const auto name = _output_names.at(output_idx); + const auto &inter = _intermediate.at(output_idx); + assert(inter.dtype() == loco::DataType::FLOAT32); // FIX_ME_UNLESS + const auto elem_count = inter.size(); + + // Compute MSE + float mse = 0.0; + for (uint32_t elem_idx = 0; elem_idx < elem_count; elem_idx++) + mse += inter.at(elem_idx); + + mse = mse / elem_count; + mse = mse / _num_data; + + os << "MSE for " << name << " is " << mse << std::endl; + } +} + } // namespace circle_eval_diff #undef THROW_UNLESS diff --git a/compiler/circle-eval-diff/src/MetricPrinter.h b/compiler/circle-eval-diff/src/MetricPrinter.h index b51581c..c8f2751 100644 --- a/compiler/circle-eval-diff/src/MetricPrinter.h +++ b/compiler/circle-eval-diff/src/MetricPrinter.h @@ -85,6 +85,133 @@ private: uint32_t _num_data = 0; }; +// Mean Squared Error +class MSEPrinter final : public MetricPrinter +{ +public: + void init(const luci::Module *first, const luci::Module *second); + + void accumulate(const std::vector> &first, + const std::vector> &second); + + void dump(std::ostream &os) const; + +private: + void accum_squared_error(uint32_t index, const std::shared_ptr &a, + const std::shared_ptr &b); + +private: + // Store accumulated sum of absolute error for each output + std::vector _intermediate; + std::vector _output_names; + uint32_t _num_data = 0; +}; + +// Mean Absolute Percentage Error +class MAPEPrinter final : public MetricPrinter +{ +public: + void init(const luci::Module *first, const luci::Module *second); + + void accumulate(const std::vector> &first, + const std::vector> &second); + + void dump(std::ostream &os) const; + +private: + void accum_mean_absolute_error(uint32_t index, const std::shared_ptr &a, + const std::shared_ptr &b); + +private: + // Store accumulated sum of absolute error for each output + std::vector _intermediate; + std::vector _output_names; + uint32_t _num_data = 0; +}; + +// Mean Peak Error to Interval Ratio (PEIR) +// PEIR = max(|a - b|) / (max(a) - min(a)) +// PEIR >= 0 (lower is better) +// +// When testing the accuracy of quantized model, +// the first model should be the original fp32 model, and +// the second model should be the fake-quantized fp32 model +class MPEIRPrinter final : public MetricPrinter +{ +public: + void init(const luci::Module *first, const luci::Module *second); + + void accumulate(const std::vector> &first, + const std::vector> &second); + + void dump(std::ostream &os) const; + +private: + void accum_peir(uint32_t index, const std::shared_ptr &a, + const std::shared_ptr &b); + +private: + // Store accumulated sum of PEIR for each output + std::vector _intermediate; + std::vector _output_names; + uint32_t _num_data = 0; +}; + +// Ratio of matched indices between top-k results of two models (a, b). +// +// top-k match = intersection(top_k_idx(a), top_k_idx(b)) / k +// mean top-k match = sum(top-k match) / num_data +// +// For example, +// num_data = 2 +// first model output = [1, 2, 3], [2, 3, 1] +// second model output = [2, 4, 6], [3, 2, 1] +// +// if k = 1, +// first model top-1 index = ([2], [1]) +// second model top-1 index = ([2], [0]) +// mean top-1 accuracy = (1 + 0) / 2 = 0.5 +// +// if k = 2, +// first model output = [1, 2, 3], [2, 3, 1] +// second model output = [2, 4, 6], [3, 2, 1] +// first model top-2 index = ([2, 1], [1, 0]) +// second model top-2 index = ([2, 1], [0, 1]) +// mean top-2 accuracy = (2 + 2) / 4 = 1 +// +// NOTE Order of elements is ignored when comparing two top-k sets. +// NOTE If two elements have the same value and only one can be included in top-k, +// the one with an earlier index will be included. +class TopKMatchPrinter : public MetricPrinter +{ +public: + TopKMatchPrinter(uint32_t k) : _k(k) {} + +public: + void init(const luci::Module *first, const luci::Module *second); + + void accumulate(const std::vector> &first, + const std::vector> &second); + + void dump(std::ostream &os) const; + +private: + void accum_topk_accuracy(uint32_t index, const std::shared_ptr &a, + const std::shared_ptr &b); + + // Return true if the output is in the skip list (_skip_output) + bool in_skip_list(uint32_t output_index) const; + +private: + const uint32_t _k = 0; + // Store accumulated accuracy + std::vector _intermediate; + std::vector _output_names; + uint32_t _num_data = 0; + // Save index of output whose num_elements is less than k + std::vector _skip_output; +}; + } // namespace circle_eval_diff #endif // __CIRCLE_EVAL_DIFF_METRIC_PRINTER_H__ diff --git a/compiler/circle-eval-diff/src/MetricPrinter.test.cpp b/compiler/circle-eval-diff/src/MetricPrinter.test.cpp index 51ca897..0e71b80 100644 --- a/compiler/circle-eval-diff/src/MetricPrinter.test.cpp +++ b/compiler/circle-eval-diff/src/MetricPrinter.test.cpp @@ -180,6 +180,23 @@ std::shared_ptr output_tensor_with_value(const luci::Module *module, flo return tensor; } +std::shared_ptr output_tensor_with_value(const luci::Module *module, + std::vector &value) +{ + auto outputs = loco::output_nodes(module->graph()); + assert(outputs.size() == 1); + auto output = *outputs.begin(); + auto output_cnode = loco::must_cast(output); + auto tensor = create_empty_tensor(output_cnode); + auto tensor_size = tensor->size(); + assert(tensor_size == value.size()); + for (uint32_t i = 0; i < tensor_size; i++) + { + tensor->at(i) = value[i]; + } + return tensor; +} + } // namespace namespace circle_eval_diff @@ -233,4 +250,299 @@ TEST(CircleEvalMetricPrinterTest, MAE_init_with_null_NEG) EXPECT_ANY_THROW(mae.init(nullptr, nullptr)); } +TEST(CircleEvalMetricPrinterTest, MAPE_simple) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + MAPEPrinter mape; + + mape.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + auto output = output_tensor_with_value(&first, 2.0); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + auto output = output_tensor_with_value(&second, 1.0); + second_result.emplace_back(output); + } + + mape.accumulate(first_result, second_result); + + std::stringstream ss; + mape.dump(ss); + std::string result = ss.str(); + + EXPECT_NE(std::string::npos, result.find("MAPE for output_0 is 50%")); +} + +TEST(CircleEvalMetricPrinterTest, MAPE_init_with_null_NEG) +{ + MAPEPrinter mape; + + EXPECT_ANY_THROW(mape.init(nullptr, nullptr)); +} + +TEST(CircleEvalMetricPrinterTest, MPEIR_simple) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + MPEIRPrinter mpeir; + + mpeir.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + std::vector val; + val.resize(16); + for (uint32_t i = 0; i < 16; i++) + val[i] = i; + + auto output = output_tensor_with_value(&first, val); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + auto output = output_tensor_with_value(&second, 0.0); + second_result.emplace_back(output); + } + + mpeir.accumulate(first_result, second_result); + + std::stringstream ss; + mpeir.dump(ss); + std::string result = ss.str(); + + EXPECT_NE(std::string::npos, result.find("MPEIR for output_0 is 1")); +} + +TEST(CircleEvalMetricPrinterTest, MPEIR_init_with_null_NEG) +{ + MPEIRPrinter mpeir; + + EXPECT_ANY_THROW(mpeir.init(nullptr, nullptr)); +} + +TEST(CircleEvalMetricPrinterTest, TopK_simple) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + TopKMatchPrinter top5(5); + + top5.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + std::vector val; + val.resize(16); + for (uint32_t i = 0; i < 16; i++) + val[i] = i; + + auto output = output_tensor_with_value(&first, val); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + std::vector val; + val.resize(16); + for (uint32_t i = 0; i < 16; i++) + val[i] = i * 2; + auto output = output_tensor_with_value(&second, val); + second_result.emplace_back(output); + } + + top5.accumulate(first_result, second_result); + + std::stringstream ss; + top5.dump(ss); + std::string result = ss.str(); + + EXPECT_NE(std::string::npos, result.find("Mean Top-5 match ratio for output_0 is 1")); +} + +TEST(CircleEvalMetricPrinterTest, TopK_tie) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + TopKMatchPrinter top5(5); + + top5.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + std::vector val; + val.resize(16); + for (uint32_t i = 0; i < 16; i++) + val[i] = i; + + auto output = output_tensor_with_value(&first, val); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + std::vector val{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 16}; + + auto output = output_tensor_with_value(&second, val); + second_result.emplace_back(output); + } + + top5.accumulate(first_result, second_result); + + std::stringstream ss; + top5.dump(ss); + std::string result = ss.str(); + + EXPECT_NE(std::string::npos, result.find("Mean Top-5 match ratio for output_0 is 0.8")); +} + +TEST(CircleEvalMetricPrinterTest, TopK_num_elem_less_than_k_NEG) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + TopKMatchPrinter top100(100); + + top100.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + auto output = output_tensor_with_value(&first, 0); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + auto output = output_tensor_with_value(&second, 0); + second_result.emplace_back(output); + } + + top100.accumulate(first_result, second_result); + + std::stringstream ss; + top100.dump(ss); + std::string result = ss.str(); + + EXPECT_EQ(std::string::npos, result.find("Mean Top-100 match ratio")); +} + +TEST(CircleEvalMetricPrinterTest, TopK_init_with_null_NEG) +{ + TopKMatchPrinter topk(5); + + EXPECT_ANY_THROW(topk.init(nullptr, nullptr)); +} + +TEST(CircleEvalMetricPrinterTest, MSE_simple) +{ + luci::Module first; + AddOneGraph first_g; + first_g.init(); + + first.add(std::move(first_g.graph())); + + luci::Module second; + AddTwoGraph second_g; + second_g.init(); + + second.add(std::move(second_g.graph())); + + MSEPrinter mse; + + mse.init(&first, &second); + + // This test does not actually evaluate the modules, but create + // fake results. + std::vector> first_result; + { + auto output = output_tensor_with_value(&first, 1.0); + first_result.emplace_back(output); + } + + std::vector> second_result; + { + auto output = output_tensor_with_value(&second, 2.0); + second_result.emplace_back(output); + } + + mse.accumulate(first_result, second_result); + + std::stringstream ss; + mse.dump(ss); + std::string result = ss.str(); + + EXPECT_NE(std::string::npos, result.find("MSE for output_0 is 1")); +} + +TEST(CircleEvalMetricPrinterTest, MSE_init_with_null_NEG) +{ + MSEPrinter mse; + + EXPECT_ANY_THROW(mse.init(nullptr, nullptr)); +} + } // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/ModuleEvalDiff.cpp b/compiler/circle-eval-diff/src/ModuleEvalDiff.cpp deleted file mode 100644 index 85f9858..0000000 --- a/compiler/circle-eval-diff/src/ModuleEvalDiff.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 "ModuleEvalDiff.h" -#include "Tensor.h" - -#include -#include - -#include -#include -#include -#include - -using Tensor = circle_eval_diff::Tensor; -using DataType = loco::DataType; -using Shape = std::vector; -using HDF5Importer = dio::hdf5::HDF5Importer; - -namespace -{ - -// Check the type and the shape of CircleInput -void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape) -{ - // Type check - if (dtype != input_node->dtype()) - throw std::runtime_error("Wrong input type."); - - if (shape.size() != input_node->rank()) - throw std::runtime_error("Input rank mismatch."); - - for (uint32_t i = 0; i < shape.size(); i++) - { - if (not(shape.at(i) == input_node->dim(i))) - throw std::runtime_error("Input shape mismatch."); - } -} - -// Return number of elements of the node. -uint32_t numElements(const luci::CircleNode *node) -{ - uint32_t num_elem = 1; - for (uint32_t i = 0; i < node->rank(); ++i) - num_elem *= node->dim(i).value(); - return num_elem; -} - -// Return Tensor which has the same dtype and shape with node. -// Buffer does not have any data yet. -std::shared_ptr createEmptyTensor(const luci::CircleNode *node) -{ - auto tensor = std::make_shared(); - { - tensor->dtype(node->dtype()); - tensor->rank(node->rank()); - for (uint32_t i = 0; i < node->rank(); i++) - tensor->dim(i) = node->dim(i); - - switch (node->dtype()) - { - case loco::DataType::FLOAT32: - tensor->size(numElements(node)); - break; - case loco::DataType::U8: - tensor->size(numElements(node)); - break; - case loco::DataType::S16: - tensor->size(numElements(node)); - break; - case loco::DataType::S32: - tensor->size(numElements(node)); - break; - case loco::DataType::S64: - tensor->size(numElements(node)); - break; - default: - throw std::runtime_error("Unsupported input tensor dtype for " + node->name()); - } - } - - return tensor; -} - -} // namespace - -namespace circle_eval_diff -{ - -void H5InputEvalDiff::evalDiff(const std::string &first_input_data_path, - const std::string &second_input_data_path) const -{ - const auto interp = std::make_unique(_first_module.get()); - - _metric->init(_first_module.get(), _second_module.get()); - - try - { - HDF5Importer first_h5(first_input_data_path); - first_h5.importGroup("value"); - - HDF5Importer second_h5(second_input_data_path); - second_h5.importGroup("value"); - - const auto first_num_data = first_h5.numData(); - const auto second_num_data = second_h5.numData(); - - if (first_num_data != second_num_data) - throw std::runtime_error( - "Number of data in the first data file and the second data file mismatches."); - - if (first_num_data == 0) - throw std::runtime_error("Input data file does not contain any record."); - - const auto first_input_nodes = loco::input_nodes(_first_module->graph()); - const auto first_num_inputs = first_input_nodes.size(); - const auto first_output_nodes = loco::output_nodes(_first_module->graph()); - const auto first_num_outputs = first_output_nodes.size(); - - const auto second_input_nodes = loco::input_nodes(_second_module->graph()); - const auto second_num_inputs = second_input_nodes.size(); - const auto second_output_nodes = loco::output_nodes(_second_module->graph()); - const auto second_num_outputs = second_output_nodes.size(); - - for (int32_t data_idx = 0; data_idx < first_num_data; data_idx++) - { - std::cout << "Evaluating " << data_idx << "'th data" << std::endl; - - if (first_num_inputs != first_h5.numInputs(data_idx) || - second_num_inputs != second_h5.numInputs(data_idx)) - throw std::runtime_error("Wrong number of inputs in " + std::to_string(data_idx) + - "th data."); - - // Do inference and return output - auto eval = [&](HDF5Importer &h5, uint32_t num_inputs, - const std::vector &input_nodes, uint32_t num_outputs, - const std::vector &output_nodes) { - // Write input data - for (uint32_t input_idx = 0; input_idx < num_inputs; input_idx++) - { - const auto *input_node = - loco::must_cast(input_nodes[input_idx]); - assert(input_node->index() == input_idx); - - auto tensor = createEmptyTensor(input_node); - if (h5.isRawData()) - { - h5.readTensor(data_idx, input_idx, tensor->buffer()); - } - else - { - DataType dtype; - Shape shape; - h5.readTensor(data_idx, input_idx, &dtype, &shape, tensor->buffer()); - - // Check the type and the shape of the input data is valid - verifyTypeShape(input_node, dtype, shape); - } - - interp->writeInputTensor(input_node, tensor->buffer(), tensor->byte_size()); - } - - // Interpret - interp->interpret(); - - // Read output data - std::vector> outputs; - for (uint32_t output_idx = 0; output_idx < num_outputs; output_idx++) - { - const auto *output_node = - loco::must_cast(output_nodes[output_idx]); - assert(output_node->index() == output_idx); - - auto tensor = createEmptyTensor(output_node); - interp->readOutputTensor(output_node, tensor->buffer(), tensor->byte_size()); - outputs.emplace_back(tensor); - } - - return outputs; - }; - - auto first_output = - eval(first_h5, first_num_inputs, first_input_nodes, first_num_outputs, first_output_nodes); - auto second_output = eval(second_h5, second_num_inputs, second_input_nodes, - second_num_outputs, second_output_nodes); - - // Accumulate diffs - _metric->accumulate(first_output, second_output); - } - - std::cout << "Evaluation finished. Number of data: " << first_num_data << std::endl; - } - catch (const H5::Exception &e) - { - H5::Exception::printErrorStack(); - throw std::runtime_error("HDF5 error occurred."); - } - - // Print metric - std::cout << _metric.get() << std::endl; -} - -} // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/ModuleEvalDiff.h b/compiler/circle-eval-diff/src/ModuleEvalDiff.h deleted file mode 100644 index c7642f6..0000000 --- a/compiler/circle-eval-diff/src/ModuleEvalDiff.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __CIRCLE_EVAL_DIFF_MODULE_EVAL_DIFF_H__ -#define __CIRCLE_EVAL_DIFF_MODULE_EVAL_DIFF_H__ - -#include "MetricPrinter.h" - -#include - -#include - -namespace circle_eval_diff -{ - -class ModuleEvalDiff -{ -public: - ModuleEvalDiff(std::unique_ptr &&first, std::unique_ptr &&second, - std::unique_ptr &&metric) - : _first_module(std::move(first)), _second_module(std::move(second)), _metric(std::move(metric)) - { - } - - virtual ~ModuleEvalDiff() = default; - - // Implement this in the child class - virtual void evalDiff(const std::string &first_input_data_path, - const std::string &second_input_data_path) const = 0; - -protected: - std::unique_ptr _first_module; - std::unique_ptr _second_module; - std::unique_ptr _metric; -}; - -class H5InputEvalDiff final : public ModuleEvalDiff -{ -public: - H5InputEvalDiff(std::unique_ptr &&first, std::unique_ptr &&second, - std::unique_ptr &&metric) - : ModuleEvalDiff(std::move(first), std::move(second), std::move(metric)) - { - } - - void evalDiff(const std::string &first_input_data_path, - const std::string &second_input_data_path) const; -}; - -// TODO Implement ModuleEvalDiff for random input and directory input - -} // namespace circle_eval_diff - -#endif // __CIRCLE_EVAL_DIFF_MODULE_EVAL_DIFF_H__ diff --git a/compiler/circle-eval-diff/src/Tensor.cpp b/compiler/circle-eval-diff/src/Tensor.cpp index 6710e8c..c3efc44 100644 --- a/compiler/circle-eval-diff/src/Tensor.cpp +++ b/compiler/circle-eval-diff/src/Tensor.cpp @@ -16,8 +16,24 @@ #include "Tensor.h" +#include + #include +namespace +{ + +// Return number of elements of the node. +uint32_t numElements(const luci::CircleNode *node) +{ + uint32_t num_elem = 1; + for (uint32_t i = 0; i < node->rank(); ++i) + num_elem *= node->dim(i).value(); + return num_elem; +} + +} // namespace + namespace circle_eval_diff { @@ -69,4 +85,40 @@ INSTANTIATE(loco::DataType::FLOAT32); #undef INSTANTIATE +// Return Tensor which has the same dtype and shape with node. +// Buffer does not have any data yet. +std::shared_ptr createEmptyTensor(const luci::CircleNode *node) +{ + auto tensor = std::make_shared(); + { + tensor->dtype(node->dtype()); + tensor->rank(node->rank()); + for (uint32_t i = 0; i < node->rank(); i++) + tensor->dim(i) = node->dim(i); + + switch (node->dtype()) + { + case loco::DataType::FLOAT32: + tensor->size(numElements(node)); + break; + case loco::DataType::U8: + tensor->size(numElements(node)); + break; + case loco::DataType::S16: + tensor->size(numElements(node)); + break; + case loco::DataType::S32: + tensor->size(numElements(node)); + break; + case loco::DataType::S64: + tensor->size(numElements(node)); + break; + default: + throw std::runtime_error("Unsupported input tensor dtype for " + node->name()); + } + } + + return tensor; +} + } // namespace circle_eval_diff diff --git a/compiler/circle-eval-diff/src/Tensor.h b/compiler/circle-eval-diff/src/Tensor.h index 65ab606..d4f65d9 100644 --- a/compiler/circle-eval-diff/src/Tensor.h +++ b/compiler/circle-eval-diff/src/Tensor.h @@ -18,6 +18,7 @@ #define __CIRCLE_EVAL_DIFF_TENSOR_H__ #include +#include #include @@ -76,6 +77,8 @@ private: std::vector _data; }; +std::shared_ptr createEmptyTensor(const luci::CircleNode *node); + } // namespace circle_eval_diff #endif // __CIRCLE_EVAL_DIFF_TENSOR_H__ diff --git a/compiler/circle-eval-diff/src/Tensor.test.cpp b/compiler/circle-eval-diff/src/Tensor.test.cpp index 3bdeaec..3958657 100644 --- a/compiler/circle-eval-diff/src/Tensor.test.cpp +++ b/compiler/circle-eval-diff/src/Tensor.test.cpp @@ -18,6 +18,8 @@ #include +#include + using Tensor = circle_eval_diff::Tensor; namespace @@ -99,3 +101,29 @@ TEST(CircleEvalDiffTensorTest, out_of_buffer_range_NEG) SUCCEED(); } + +TEST(CircleEvalDiffTensorTest, createEmptyTensorTest) +{ + luci::CircleInput input; + input.dtype(loco::DataType::FLOAT32); + input.rank(4); + input.dim(0).set(1); + input.dim(1).set(3); + input.dim(2).set(3); + input.dim(3).set(2); + + loco::DataType right_data_type{loco::DataType::FLOAT32}; + std::vector right_shape; + right_shape.emplace_back(1); + right_shape.emplace_back(3); + right_shape.emplace_back(3); + right_shape.emplace_back(2); + + auto tensor = circle_eval_diff::createEmptyTensor(&input); + EXPECT_EQ(loco::DataType::FLOAT32, tensor->dtype()); + EXPECT_EQ(4, tensor->rank()); + EXPECT_EQ(1, tensor->dim(0)); + EXPECT_EQ(3, tensor->dim(1)); + EXPECT_EQ(3, tensor->dim(2)); + EXPECT_EQ(2, tensor->dim(3)); +} diff --git a/compiler/circle-execution-plan/CMakeLists.txt b/compiler/circle-execution-plan/CMakeLists.txt index 2f657c1..da74e02 100644 --- a/compiler/circle-execution-plan/CMakeLists.txt +++ b/compiler/circle-execution-plan/CMakeLists.txt @@ -1,3 +1,9 @@ +nnas_find_package(Jsoncpp) +if(NOT Jsoncpp_FOUND) + message(STATUS "Build circle-execution-plan: FAILED (missing jsoncpp)") + return() +endif(NOT Jsoncpp_FOUND) + set(SOURCES pal/IScratchpadHelper.h pal/ScratchpadHelperLinux.h @@ -10,6 +16,9 @@ set(SOURCES ) add_executable(circle_execution_plan "${SOURCES}") +target_include_directories(circle_execution_plan PRIVATE ${Jsoncpp_INCLUDE_DIRS}) + +target_link_libraries(circle_execution_plan ${Jsoncpp_STATIC_LIB}) target_link_libraries(circle_execution_plan foder) target_link_libraries(circle_execution_plan safemain) target_link_libraries(circle_execution_plan luci_env) diff --git a/compiler/circle-execution-plan/src/CircleExecutionPlan.cpp b/compiler/circle-execution-plan/src/CircleExecutionPlan.cpp index 1788124..d5ddf0c 100644 --- a/compiler/circle-execution-plan/src/CircleExecutionPlan.cpp +++ b/compiler/circle-execution-plan/src/CircleExecutionPlan.cpp @@ -33,20 +33,22 @@ int entry(int argc, char **argv) { arser::Arser arser("circle_execution_plan provides model with execution plan meta information"); - arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); - arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); - arser.add_argument("--platform") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .default_value("linux") - .help("Platform name: linux mcu cmsisnn"); + arser.add_argument("input").help("Input circle model"); + arser.add_argument("output").help("Output circle model"); + arser.add_argument("--platform").default_value("linux").help("Platform name: linux mcu cmsisnn"); arser.add_argument("--use_dsp") .nargs(1) .type(arser::DataType::BOOL) .required(false) .default_value(false) .help("Plan with or without dsp (now can be used only with cmsisnn)"); + arser.add_argument("--save_allocations") + .nargs(1) + .required(false) + .default_value("") + .help("Path for output JSON file to save memory allocation info. " + "Note: path end of file should have 'tracealloc.json' (example path: " + "'../exec_plan_info.tracealloc.json')"); try { @@ -63,6 +65,7 @@ int entry(int argc, char **argv) const std::string output_path = arser.get("output"); const std::string platform_name = arser.get("--platform"); const bool use_dsp = arser.get("--use_dsp"); + const std::string json_path = arser.get("--save_allocations"); if (platform_name != "cmsisnn" && use_dsp) { @@ -89,6 +92,13 @@ int entry(int argc, char **argv) return EXIT_FAILURE; } + bool is_save_allocations = false; + + if (!json_path.empty()) + { + is_save_allocations = true; + } + foder::FileLoader file_loader{input_path}; std::vector model_data; @@ -124,6 +134,9 @@ int entry(int argc, char **argv) circle_planner::ExecutionPlanner execution_planner(module->graph(), {platform_type, use_dsp}); execution_planner.make_execution_plan(); + if (is_save_allocations) + execution_planner.create_json_allocation_file(json_path); + // Export to output Circle file luci::CircleExporter exporter; luci::CircleFileExpContract contract(module.get(), output_path); diff --git a/compiler/circle-execution-plan/src/ExecutionPlanner.cpp b/compiler/circle-execution-plan/src/ExecutionPlanner.cpp index ec2ec13..a1e6f7e 100644 --- a/compiler/circle-execution-plan/src/ExecutionPlanner.cpp +++ b/compiler/circle-execution-plan/src/ExecutionPlanner.cpp @@ -18,6 +18,9 @@ #include #include +#include +#include + namespace circle_planner { namespace @@ -58,6 +61,29 @@ bool isTensorProducingNode(const luci::CircleNode *node) } } +// Create allocation node part for current circle node for json allocation info file +void create_allocation_node(Json::Value &allocations_node, + AllocationNodeInformation &alloca_node_inform, uint32_t alive_till_max, + luci::CircleNode *circle_node) +{ + Json::Value allocation_node; + if (alloca_node_inform.size == 0) + return; + + allocation_node["offset"] = alloca_node_inform.offset; + allocation_node["size"] = alloca_node_inform.size; + allocation_node["alive_from"] = alloca_node_inform.first_node; + + if (alloca_node_inform.last_node == node_not_assigned) + allocation_node["alive_till"] = alive_till_max + 1; + else + allocation_node["alive_till"] = alloca_node_inform.last_node; + + allocation_node["origin"] = circle_node->name(); + + allocations_node.append(allocation_node); +} + } // namespace void ExecutionPlanner::make_execution_plan() @@ -74,6 +100,50 @@ void ExecutionPlanner::make_execution_plan() settings->set(luci::UserSettings::Key::ExecutionPlanGen, true); } +void ExecutionPlanner::create_json_allocation_file(const std::string &json_path) +{ + Json::Value main_tree; + Json::Value segments_node; + Json::Value allocations_node; + + uint32_t alive_till_max = 0; + + // Find max dealloc value to assign to nodes with node_not_assigned value + for (const auto elem : _dealloc_node) + { + if (alive_till_max < elem and elem != node_not_assigned) + alive_till_max = elem; + } + + for (auto &alloc_node_inform : _alloc_node_inform_vector) + { + const auto node_num = alloc_node_inform.node_num; + const auto circle_node = loco::must_cast(_ordered_nodes[node_num]); + + create_allocation_node(allocations_node, alloc_node_inform, alive_till_max, circle_node); + } + + // Create segment part + Json::Value segment_node; + segment_node["name"] = "Segment1"; + segment_node["allocations"] = allocations_node; + segments_node.append(segment_node); + + main_tree["schema_version"] = 1; + main_tree["segments"] = segments_node; + + Json::StreamWriterBuilder builder; + const std::unique_ptr writer(builder.newStreamWriter()); + + // Write to json file + std::ofstream out; + out.open(json_path); + if (out.is_open()) + { + writer->write(main_tree, &out); + } +} + void ExecutionPlanner::get_default_execution_order_plan() { // Get execution order in _ordered_nodes diff --git a/compiler/circle-execution-plan/src/ExecutionPlanner.h b/compiler/circle-execution-plan/src/ExecutionPlanner.h index e0833c4..af3fba3 100644 --- a/compiler/circle-execution-plan/src/ExecutionPlanner.h +++ b/compiler/circle-execution-plan/src/ExecutionPlanner.h @@ -104,6 +104,8 @@ public: _is_null_scratchpads = is_null_scratchpads; }; + void create_json_allocation_file(const std::string &json_path); + private: // Method gets default execution order plan and saves it in _ordered_nodes vector. // There can be different variants of execution order and this method provides main one. diff --git a/compiler/circle-inspect/driver/Driver.cpp b/compiler/circle-inspect/driver/Driver.cpp index 10e185d..318a582 100644 --- a/compiler/circle-inspect/driver/Driver.cpp +++ b/compiler/circle-inspect/driver/Driver.cpp @@ -36,7 +36,7 @@ int entry(int argc, char **argv) .help("Dump Conv2D series weight operators in circle file"); arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in circle file"); arser.add_argument("--tensor_dtype").nargs(0).help("Dump dtype of tensors"); - arser.add_argument("circle").type(arser::DataType::STR).help("Circle file to inspect"); + arser.add_argument("circle").help("Circle file to inspect"); try { diff --git a/compiler/circle-inspect/requires.cmake b/compiler/circle-inspect/requires.cmake index 362d67c..183dfe2 100644 --- a/compiler/circle-inspect/requires.cmake +++ b/compiler/circle-inspect/requires.cmake @@ -1,3 +1,4 @@ require("arser") +require("foder") require("mio-circle04") require("safemain") diff --git a/compiler/circle-inspect/src/Dump.cpp b/compiler/circle-inspect/src/Dump.cpp index bba5e56..aa8fed2 100644 --- a/compiler/circle-inspect/src/Dump.cpp +++ b/compiler/circle-inspect/src/Dump.cpp @@ -15,7 +15,9 @@ */ #include "Dump.h" -#include "Reader.h" + +#include +#include #include @@ -24,7 +26,7 @@ namespace circleinspect void DumpOperators::run(std::ostream &os, const circle::Model *model) { - circleinspect::Reader reader(model); + mio::circle::Reader reader(model); const uint32_t subgraph_size = reader.num_subgraph(); @@ -50,7 +52,7 @@ void DumpOperators::run(std::ostream &os, const circle::Model *model) namespace { -const circle::Operator *operator_match_output(circleinspect::Reader &reader, const int32_t tensor) +const circle::Operator *operator_match_output(mio::circle::Reader &reader, const int32_t tensor) { auto ops = reader.operators(); @@ -58,7 +60,7 @@ const circle::Operator *operator_match_output(circleinspect::Reader &reader, con { const auto op = ops->Get(i); - const std::vector &outputs = circleinspect::as_index_vector(op->outputs()); + const std::vector &outputs = mio::circle::as_index_vector(op->outputs()); for (auto output : outputs) { @@ -69,7 +71,7 @@ const circle::Operator *operator_match_output(circleinspect::Reader &reader, con return nullptr; } -size_t tensor_buffer_size(circleinspect::Reader &reader, const int32_t tensor_id) +size_t tensor_buffer_size(mio::circle::Reader &reader, const int32_t tensor_id) { auto tensors = reader.tensors(); @@ -93,7 +95,7 @@ namespace circleinspect void DumpConv2DWeight::run(std::ostream &os, const circle::Model *model) { - circleinspect::Reader reader(model); + mio::circle::Reader reader(model); const uint32_t subgraph_size = reader.num_subgraph(); @@ -110,7 +112,7 @@ void DumpConv2DWeight::run(std::ostream &os, const circle::Model *model) if (bc == circle::BuiltinOperator_CONV_2D || bc == circle::BuiltinOperator_DEPTHWISE_CONV_2D) { - const std::vector &inputs = circleinspect::as_index_vector(op->inputs()); + const std::vector &inputs = mio::circle::as_index_vector(op->inputs()); if (inputs.size() < 2) { throw std::runtime_error("Operator has invalid input"); @@ -147,7 +149,7 @@ void DumpOperatorVersion::run(std::ostream &os, const circle::Model *model) { std::map op_version_map; - circleinspect::Reader reader(model); + mio::circle::Reader reader(model); // This assert is subject to be changed later assert(reader.num_subgraph() == 1); @@ -181,7 +183,7 @@ namespace circleinspect void DumpTensorDType::run(std::ostream &os, const circle::Model *model) { - circleinspect::Reader reader(model); + mio::circle::Reader reader(model); const uint32_t subgraph_size = reader.num_subgraph(); diff --git a/compiler/circle-inspect/src/Reader.cpp b/compiler/circle-inspect/src/Reader.cpp deleted file mode 100644 index 0e28652..0000000 --- a/compiler/circle-inspect/src/Reader.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 "Reader.h" - -#include - -#include -#include - -namespace circleinspect -{ - -Reader::Reader(const circle::Model *model) -{ - _subgraphs = model->subgraphs(); - _buffers = model->buffers(); - - auto opcodes = model->operator_codes(); - for (const ::circle::OperatorCode *opcode : *opcodes) - { - _op_codes.push_back(opcode); - } -} - -size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) -{ - if (buff_data != nullptr) - { - *buff_data = nullptr; - } - - if (buf_idx == 0) - return 0; - - if (auto *buffer = (*_buffers)[buf_idx]) - { - if (auto *array = buffer->data()) - { - if (size_t size = array->size()) - { - if (buff_data != nullptr) - { - *buff_data = reinterpret_cast(array->data()); - } - return size; - } - } - } - - return 0; -} - -circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - return mio::circle::builtin_code_neutral(opcode); -} - -std::string Reader::opcode_name(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - if (!mio::circle::is_valid(opcode)) - { - std::ostringstream oss; - oss << "(invalid: " << index << ")"; - return oss.str(); - } - - return mio::circle::opcode_name(opcode); -} - -std::string Reader::tensor_name(const circle::Tensor *tensor) const -{ - return mio::circle::tensor_name(tensor); -} - -std::string Reader::tensor_dtype(const circle::Tensor *tensor) const -{ - return mio::circle::tensor_type(tensor); -} - -bool Reader::select_subgraph(uint32_t sgindex) -{ - _tensors = nullptr; - _operators = nullptr; - - _inputs.clear(); - _outputs.clear(); - - if (_subgraphs->Length() <= sgindex) - { - assert(false); - return false; - } - - const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; - - _tensors = subgraph->tensors(); - _operators = subgraph->operators(); - - _inputs = as_index_vector(subgraph->inputs()); - _outputs = as_index_vector(subgraph->outputs()); - - return true; -} - -} // namespace circleinspect diff --git a/compiler/circle-inspect/src/Reader.h b/compiler/circle-inspect/src/Reader.h deleted file mode 100644 index c38ec39..0000000 --- a/compiler/circle-inspect/src/Reader.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __READER_H__ -#define __READER_H__ - -#include - -#include -#include -#include - -namespace circleinspect -{ - -template std::vector as_index_vector(const flatbuffers::Vector *flat_array) -{ - std::vector ret(flat_array->Length()); - for (uint32_t i = 0; i < flat_array->Length(); i++) - { - ret[i] = flat_array->Get(i); - } - return ret; -} - -/** - * @brief Loads Circle file and provides helpers to access attributes - */ -class Reader -{ -private: - using CircleSubGraphs_t = flatbuffers::Vector>; - using CircleBuffers_t = flatbuffers::Vector>; - using CircleTensors_t = flatbuffers::Vector>; - using CircleOperators_t = flatbuffers::Vector>; - -public: - Reader(const circle::Model *model); - - Reader() = delete; - -public: - const std::vector &opcodes() { return _op_codes; } - const CircleBuffers_t *buffers() { return _buffers; } - const CircleTensors_t *tensors() { return _tensors; } - const CircleOperators_t *operators() { return _operators; } - const std::vector &inputs() const { return _inputs; } - const std::vector &outputs() const { return _outputs; } - - uint32_t num_subgraph() const { return _subgraphs->Length(); } - - size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); - circle::BuiltinOperator builtin_code(const circle::Operator *op) const; - std::string opcode_name(const circle::Operator *op) const; - std::string tensor_name(const circle::Tensor *tensor) const; - std::string tensor_dtype(const circle::Tensor *tensor) const; - -public: - bool select_subgraph(uint32_t subgraph); - -private: - const CircleSubGraphs_t *_subgraphs{nullptr}; - const CircleBuffers_t *_buffers{nullptr}; - const CircleTensors_t *_tensors{nullptr}; - const CircleOperators_t *_operators{nullptr}; - - std::vector _op_codes; - std::vector _inputs; - std::vector _outputs; -}; - -} // namespace circleinspect - -#endif // __READER_H__ diff --git a/compiler/circle-interpreter/CMakeLists.txt b/compiler/circle-interpreter/CMakeLists.txt new file mode 100644 index 0000000..d18db3e --- /dev/null +++ b/compiler/circle-interpreter/CMakeLists.txt @@ -0,0 +1,13 @@ +set(INTERPRETER + src/CircleInterpreter.cpp + ) + +add_executable(circle-interpreter ${INTERPRETER}) +target_link_libraries(circle-interpreter PRIVATE arser) +target_link_libraries(circle-interpreter PRIVATE loco) +target_link_libraries(circle-interpreter PRIVATE luci_import) +target_link_libraries(circle-interpreter PRIVATE luci_interpreter) +target_link_libraries(circle-interpreter PRIVATE safemain) +target_link_libraries(circle-interpreter PRIVATE vconone) + +install(TARGETS circle-interpreter DESTINATION bin) diff --git a/compiler/circle-interpreter/requires.cmake b/compiler/circle-interpreter/requires.cmake new file mode 100644 index 0000000..a565df6 --- /dev/null +++ b/compiler/circle-interpreter/requires.cmake @@ -0,0 +1,6 @@ +require("arser") +require("loco") +require("luci") +require("luci-interpreter") +require("safemain") +require("vconone") diff --git a/compiler/circle-interpreter/src/CircleInterpreter.cpp b/compiler/circle-interpreter/src/CircleInterpreter.cpp new file mode 100644 index 0000000..1d24127 --- /dev/null +++ b/compiler/circle-interpreter/src/CircleInterpreter.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ + +void readDataFromFile(const std::string &filename, char *data, size_t data_size) +{ + std::ifstream fs(filename, std::ifstream::binary); + if (fs.fail()) + throw std::runtime_error("Cannot open file \"" + filename + "\".\n"); + if (fs.read(data, data_size).fail()) + throw std::runtime_error("Failed to read data from file \"" + filename + "\".\n"); +} + +void writeDataToFile(const std::string &filename, const char *data, size_t data_size) +{ + std::ofstream fs(filename, std::ofstream::binary); + if (fs.fail()) + throw std::runtime_error("Cannot open file \"" + filename + "\".\n"); + if (fs.write(data, data_size).fail()) + { + throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n"); + } +} + +template size_t getTensorSize(const NodeT *node) +{ + uint32_t tensor_size = loco::size(node->dtype()); + for (uint32_t i = 0; i < node->rank(); ++i) + tensor_size *= node->dim(i).value(); + return tensor_size; +} + +void print_version(void) +{ + std::cout << "circle-interpreter version " << vconone::get_string() << std::endl; + std::cout << vconone::get_copyright() << std::endl; +} + +} // namespace + +/* + * @brief CircleInterpreter main + * + * Driver to invoke luci-interpreter + * + */ +int entry(int argc, char **argv) +{ + arser::Arser arser("Interpreter driver for circle models"); + + arser::Helper::add_version(arser, print_version); + + arser.add_argument("model_path").help("Circle model filepath"); + arser.add_argument("input_prefix") + .help("Input data filepath for circle model. " + "n-th input data is read from ${input_prefix}n, " + "for example, Add.circle.input0, Add.circle.input1"); + arser.add_argument("output_prefix") + .help("Output data filepath for circle model. " + "Output data is written in ${output_file}n, " + "for example, Add.circle.output0"); + + try + { + arser.parse(argc, argv); + } + catch (const std::runtime_error &err) + { + std::cout << err.what() << std::endl; + std::cout << arser; + return EXIT_FAILURE; + } + + const auto filename = arser.get("model_path"); + const auto input_prefix = arser.get("input_prefix"); + const auto output_prefix = arser.get("output_prefix"); + + // Load model from the file + luci::ImporterEx importer; + std::unique_ptr module = importer.importVerifyModule(filename); + if (module == nullptr) + { + std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl; + return EXIT_FAILURE; + } + + // Create interpreter. + luci_interpreter::Interpreter interpreter(module.get()); + + // Set input. + // Data for n'th input is read from ${input_prefix}n + // (ex: Add.circle.input0, Add.circle.input1 ..) + const auto input_nodes = loco::input_nodes(module->graph()); + for (int32_t i = 0; i < input_nodes.size(); i++) + { + const auto *input_node = loco::must_cast(input_nodes[i]); + std::vector input_data(getTensorSize(input_node)); + readDataFromFile(std::string(input_prefix) + std::to_string(i), input_data.data(), + input_data.size()); + interpreter.writeInputTensor(input_node, input_data.data(), input_data.size()); + } + + // Do inference. + interpreter.interpret(); + + // Get output. + const auto output_nodes = loco::output_nodes(module->graph()); + for (int i = 0; i < module->graph()->outputs()->size(); i++) + { + const auto *output_node = loco::must_cast(output_nodes[i]); + std::vector output_data(getTensorSize(output_node)); + interpreter.readOutputTensor(output_node, output_data.data(), output_data.size()); + + // Output data is written in ${output_file}n + // (ex: Add.circle.output0) + writeDataToFile(std::string(output_prefix) + std::to_string(i), output_data.data(), + output_data.size()); + } + return EXIT_SUCCESS; +} diff --git a/compiler/circle-operator-test/CMakeLists.txt b/compiler/circle-operator-test/CMakeLists.txt new file mode 100644 index 0000000..2ebd533 --- /dev/null +++ b/compiler/circle-operator-test/CMakeLists.txt @@ -0,0 +1,18 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) +get_target_property(CIRCLE_OPERATOR_PATH circle-operator BINARY_DIR) +set(CIRCLE_OPERATOR_PATH "${CIRCLE_OPERATOR_PATH}/circle-operator") + +nnas_find_package(GTest REQUIRED) + +file(GLOB_RECURSE TESTS "src/*.test.cpp") + +GTest_AddTest(circle-operator-test ${TESTS}) + +set_tests_properties(circle-operator-test + PROPERTIES + ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH};CIRCLE_OPERATOR_PATH=${CIRCLE_OPERATOR_PATH}" + ) diff --git a/compiler/circle-operator-test/README.md b/compiler/circle-operator-test/README.md new file mode 100644 index 0000000..d07c64d --- /dev/null +++ b/compiler/circle-operator-test/README.md @@ -0,0 +1,7 @@ +# circle-operator-test + +_circle-operator-test_ provides test of circle-operator tool is working as expected. + +Current tests includes +- input arguments test is working as expected +- output of this tool is as expected diff --git a/compiler/circle-operator-test/requires.cmake b/compiler/circle-operator-test/requires.cmake new file mode 100644 index 0000000..8ad3b8a --- /dev/null +++ b/compiler/circle-operator-test/requires.cmake @@ -0,0 +1,2 @@ +require("circle-operator") +require("common-artifacts") diff --git a/compiler/circle-operator-test/src/circle-operator.test.cpp b/compiler/circle-operator-test/src/circle-operator.test.cpp new file mode 100644 index 0000000..29c6f37 --- /dev/null +++ b/compiler/circle-operator-test/src/circle-operator.test.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 + +#include +#include +#include + +class cirlce_operator_test : public ::testing::Test +{ +protected: + bool initialize(void); + bool run(const std::string &command); + +protected: + bool load(const std::string &file); + +protected: + std::string _artifacts_path; + std::string _circle_operator_path; + std::string _result; +}; + +bool cirlce_operator_test::initialize(void) +{ + char *path = std::getenv("ARTIFACTS_PATH"); + if (path == nullptr) + { + std::cerr << "ARTIFACTS_PATH not found" << std::endl; + return false; + } + _artifacts_path = path; + + path = std::getenv("CIRCLE_OPERATOR_PATH"); + if (path == nullptr) + { + std::cerr << "ARTIFACTS_BIN_PATH not found" << std::endl; + return false; + } + _circle_operator_path = path; + + return true; +} + +bool cirlce_operator_test::run(const std::string &command) +{ + std::vector buffer(260); + std::string result = ""; + std::string cmd_err = command + " 2>&1"; + FILE *pipe = popen(cmd_err.c_str(), "r"); + if (!pipe) + { + return false; + } + try + { + while (fgets(&buffer[0], buffer.size(), pipe) != NULL) + { + result += &buffer[0]; + } + } + catch (...) + { + pclose(pipe); + return false; + } + pclose(pipe); + _result = result; + + std::cout << _result << std::endl; + + return true; +} + +bool cirlce_operator_test::load(const std::string &file) +{ + std::ifstream tmp(file.c_str()); + if (tmp.fail()) + return false; + + std::stringstream buffer; + buffer << tmp.rdbuf(); + _result = buffer.str(); + return true; +} + +TEST_F(cirlce_operator_test, valid_names) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/Add_000.circle"; + std::string command = _circle_operator_path + " --name " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ofm"); + ASSERT_NE(std::string::npos, pos); +} + +TEST_F(cirlce_operator_test, valid_codes) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/Add_000.circle"; + std::string command = _circle_operator_path + " --code " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ADD"); + ASSERT_NE(std::string::npos, pos); +} + +TEST_F(cirlce_operator_test, invalid_option_NEG) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/Add_000.circle"; + std::string command = _circle_operator_path + " --opname " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("Invalid argument"); + ASSERT_NE(std::string::npos, pos); +} + +TEST_F(cirlce_operator_test, check_code_name) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/Add_000.circle"; + std::string command = _circle_operator_path + " --code --name " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ofm"); + ASSERT_NE(std::string::npos, pos); + const auto pos2 = _result.find("ADD"); + ASSERT_NE(std::string::npos, pos2); +} + +TEST_F(cirlce_operator_test, nonexist_file_NEG) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/non_exist_file.foo"; + std::string command = _circle_operator_path + " --name " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ERROR"); + ASSERT_NE(std::string::npos, pos); +} + +TEST_F(cirlce_operator_test, invalid_file_NEG) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string model = _artifacts_path + "/Add_000.recipe"; + std::string command = _circle_operator_path + " --name " + model; + if (!run(command)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ERROR"); + ASSERT_NE(std::string::npos, pos); +} + +TEST_F(cirlce_operator_test, output_file) +{ + if (!initialize()) + { + FAIL(); + return; + } + + std::string fileName("/tmp/a.txt"); + std::remove(fileName.c_str()); + std::string model = _artifacts_path + "/Add_000.circle"; + std::string command = _circle_operator_path + " --code --output_path " + fileName + " " + model; + if (!run(command)) + { + FAIL(); + return; + } + if (!load(fileName)) + { + FAIL(); + return; + } + + const auto pos = _result.find("ADD"); + ASSERT_NE(std::string::npos, pos); +} diff --git a/compiler/circle-operator/CMakeLists.txt b/compiler/circle-operator/CMakeLists.txt new file mode 100644 index 0000000..6817a86 --- /dev/null +++ b/compiler/circle-operator/CMakeLists.txt @@ -0,0 +1,17 @@ +if(NOT TARGET mio_circle04) + return() +endif(NOT TARGET mio_circle04) + +set(DRIVER "driver/Driver.cpp") + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(circle-operator ${DRIVER} ${SOURCES}) +target_include_directories(circle-operator PRIVATE src) +target_link_libraries(circle-operator arser) +target_link_libraries(circle-operator foder) +target_link_libraries(circle-operator mio_circle04) +target_link_libraries(circle-operator mio_circle04_helper) +target_link_libraries(circle-operator safemain) + +install(TARGETS circle-operator DESTINATION bin) diff --git a/compiler/circle-operator/README.md b/compiler/circle-operator/README.md new file mode 100644 index 0000000..86a923f --- /dev/null +++ b/compiler/circle-operator/README.md @@ -0,0 +1,70 @@ +# circle-operator + +_circle-operator_ allows users to retrieve operators information from a Circle model file + +NOTE: this tool is primary for ONE-vscode where PartEditor needs names and codes +of the operators. + +## Information with operators + +Operators with `--name` +- show operator names one line at a time in execution order + +Example +``` +$ circle-operator --name model.circle +``` + +Result +``` +conv1_pad/Pad +conv1_conv/BiasAdd +pool1_pad/Pad +``` + +Operators codes with `--code` +- show operator codes one line at a time in execution order + +Example +``` +$ circle-operator --code model.circle +``` + +Result +``` +PAD +CONV_2D +PAD +``` + +Operators with both `--code` and `--name` +- show operator both codes and name separated with `,` one line at a time in execution order + +Example +``` +$ circle-operator --code --name model.circle +``` + +Result +``` +PAD,conv1_pad/Pad +CONV_2D,conv1_conv/BiasAdd +PAD,pool1_pad/Pad +``` + +## Save to file + +Use `--output_path` to save results to a file. + +Example +``` +$ circle-operator --name --output_path /tmp/result model.circle +``` + +Result +``` +$ cat /tmp/result +conv1_pad/Pad +conv1_conv/BiasAdd +pool1_pad/Pad +``` diff --git a/compiler/circle-operator/driver/Driver.cpp b/compiler/circle-operator/driver/Driver.cpp new file mode 100644 index 0000000..f5fd807 --- /dev/null +++ b/compiler/circle-operator/driver/Driver.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Dump.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +void handle_segfault(int signal, siginfo_t *si, void *arg) +{ + std::cerr << "ERROR: Failed to load file" << std::endl; + exit(255); +} + +int entry(int argc, char **argv) +{ + // TODO add option to dump for all sub-graphs + arser::Arser arser{ + "circle-operator allows users to retrieve operator information from a Circle model file"}; + arser.add_argument("--name").nargs(0).help("Dump operators name in circle file"); + arser.add_argument("--code").nargs(0).help("Dump operators code in circle file"); + arser.add_argument("--output_path").help("Save output to file (default output is console)"); + arser.add_argument("circle").help("Circle file to dump"); + + try + { + arser.parse(argc, argv); + } + catch (const std::runtime_error &err) + { + std::cerr << err.what() << std::endl; + std::cerr << arser; + return 255; + } + + cirops::DumpOption option; + option.names = arser["--name"]; + option.codes = arser["--code"]; + + std::ofstream oFstream; + std::ostream *oStream = &std::cout; + if (arser["--output_path"]) + { + auto output_path = arser.get("--output_path"); + oFstream.open(output_path, std::ofstream::out | std::ofstream::trunc); + if (oFstream.fail()) + { + std::cerr << "ERROR: Failed to create output to file " << output_path << std::endl; + return 255; + } + oStream = &oFstream; + } + + // hook segment fault + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = handle_segfault; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &sa, NULL); + + std::string modelFile = arser.get("circle"); + // Load Circle model from a circle file + try + { + foder::FileLoader fileLoader{modelFile}; + std::vector modelData = fileLoader.load(); + const circle::Model *circleModel = circle::GetModel(modelData.data()); + if (circleModel == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << modelFile << "'" << std::endl; + return 255; + } + cirops::DumpOperators dump; + dump.run(*oStream, circleModel, option); + } + catch (const std::runtime_error &err) + { + std::cerr << "ERROR: " << err.what() << std::endl; + return 255; + } + + if (oFstream.is_open()) + { + oFstream.close(); + } + + return 0; +} diff --git a/compiler/circle-operator/requires.cmake b/compiler/circle-operator/requires.cmake new file mode 100644 index 0000000..183dfe2 --- /dev/null +++ b/compiler/circle-operator/requires.cmake @@ -0,0 +1,4 @@ +require("arser") +require("foder") +require("mio-circle04") +require("safemain") diff --git a/compiler/circle-operator/src/Dump.cpp b/compiler/circle-operator/src/Dump.cpp new file mode 100644 index 0000000..36bfe86 --- /dev/null +++ b/compiler/circle-operator/src/Dump.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Dump.h" + +#include +#include + +#include + +namespace +{ + +void dump_ops(std::ostream &os, mio::circle::Reader &reader, const cirops::DumpOption &option) +{ + auto ops = reader.operators(); + for (uint32_t i = 0; i < ops->Length(); ++i) + { + const auto op = ops->Get(i); + const auto op_name = reader.opcode_name(op); + + if (option.all_graphs) + { + // NOTE all_graphs is false for now + // TODO check using '$' as split key + os << i << "$"; + } + + if (option.codes) + { + const auto op_name = reader.opcode_name(op); + os << op_name; + } + if (option.names) + { + // TODO multiple outputs? + const auto tensors = reader.tensors(); + const auto output_tensors = reader.outputs(op); + const auto output = output_tensors.at(0); + const auto tensor = tensors->Get(output); + const std::string name = mio::circle::tensor_name(tensor); + if (option.codes) + { + os << ","; + } + os << name; + } + os << std::endl; + } +} + +} // namespace + +namespace cirops +{ + +void DumpOperators::run(std::ostream &os, const circle::Model *model, const DumpOption &option) +{ + mio::circle::Reader reader(model); + + const uint32_t subgraph_size = reader.num_subgraph(); + for (uint32_t g = 0; g < subgraph_size; g++) + { + reader.select_subgraph(g); + dump_ops(os, reader, option); + + if (!option.all_graphs) + break; + } +} + +} // namespace cirops diff --git a/compiler/circle-operator/src/Dump.h b/compiler/circle-operator/src/Dump.h new file mode 100644 index 0000000..aa1d1be --- /dev/null +++ b/compiler/circle-operator/src/Dump.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __DUMP_H__ +#define __DUMP_H__ + +#include + +#include + +namespace cirops +{ + +struct DumpOption +{ + bool names = false; + bool codes = false; + bool all_graphs = false; +}; + +class DumpOperators +{ +public: + DumpOperators() = default; + +public: + void run(std::ostream &os, const circle::Model *model, const DumpOption &option); +}; + +} // namespace cirops + +#endif // __DUMP_H__ diff --git a/compiler/circle-opselector/driver/Driver.cpp b/compiler/circle-opselector/driver/Driver.cpp index a1ace4f..4b39a6d 100644 --- a/compiler/circle-opselector/driver/Driver.cpp +++ b/compiler/circle-opselector/driver/Driver.cpp @@ -159,26 +159,16 @@ int entry(int argc, char **argv) arser::Arser arser("circle-opselector provides selecting operations in circle model"); - arser.add_argument("--version") - .nargs(0) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); + arser::Helper::add_version(arser, print_version); // TODO Add new options! - arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); - arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); + arser.add_argument("input").help("Input circle model"); + arser.add_argument("output").help("Output circle model"); // select option - arser.add_argument("--by_id") - .nargs(1) - .type(arser::DataType::STR) - .help("Input operation id to select nodes."); - arser.add_argument("--by_name") - .nargs(1) - .type(arser::DataType::STR) - .help("Input operation name to select nodes."); + arser.add_argument("--by_id").help("Input operation id to select nodes."); + arser.add_argument("--by_name").help("Input operation name to select nodes."); try { diff --git a/compiler/circle-part-value-test/CMakeLists.txt b/compiler/circle-part-value-test/CMakeLists.txt index 0657607..ffe1b89 100644 --- a/compiler/circle-part-value-test/CMakeLists.txt +++ b/compiler/circle-part-value-test/CMakeLists.txt @@ -82,7 +82,8 @@ foreach(IDX RANGE ${RECIPE_LENGTH_M1}) # Run partitioner add_custom_command(OUTPUT ${PARTITIONER_CONN_JSON} - COMMAND circle-partitioner "${PART_FILE}" "${PARTITION_NAME}.circle" "${PARTITIONER_OUTPUT_PATH}" + COMMAND circle-partitioner "--part_file" "${PART_FILE}" "--input_file" + "${PARTITION_NAME}.circle" "--work_path" "${PARTITIONER_OUTPUT_PATH}" DEPENDS circle-partitioner ${PART_DST_PATH} ${CIRCLE_DST_PATH} COMMENT "Parition ${RECIPE_NAME}.circle with ${PART_FILE}" ) diff --git a/compiler/circle-partitioner-test/CMakeLists.txt b/compiler/circle-partitioner-test/CMakeLists.txt index e29a66b..7b26b3b 100644 --- a/compiler/circle-partitioner-test/CMakeLists.txt +++ b/compiler/circle-partitioner-test/CMakeLists.txt @@ -57,7 +57,8 @@ foreach(IDX RANGE ${RECIPE_LENGTH_M1}) # Run partitioner set(PART_CONN_JSON "${PART_OUT_PATH}/${PART_NAME}.conn.json") add_custom_command(OUTPUT ${PART_CONN_JSON} - COMMAND circle-partitioner "${PART_FILE}" "${PART_NAME}.circle" "${PART_OUT_PATH}" + COMMAND circle-partitioner "--part_file" "${PART_FILE}" "--input_file" + "${PART_NAME}.circle" "--work_path" "${PART_OUT_PATH}" DEPENDS circle-partitioner ${CIRCLE_DST_PATH} ${PART_DST_PATH} COMMENT "Parition ${RECIPE_NAME}.circle with ${PART_FILE}" ) diff --git a/compiler/circle-partitioner/CMakeLists.txt b/compiler/circle-partitioner/CMakeLists.txt index 9b8f5af..abc5d93 100644 --- a/compiler/circle-partitioner/CMakeLists.txt +++ b/compiler/circle-partitioner/CMakeLists.txt @@ -1,7 +1,6 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(circle-partitioner "${SOURCES}") -target_link_libraries(circle-partitioner foder) target_link_libraries(circle-partitioner crew) target_link_libraries(circle-partitioner safemain) target_link_libraries(circle-partitioner luci_lang) @@ -17,22 +16,3 @@ target_link_libraries(circle-partitioner vconone) target_link_libraries(circle-partitioner nncc_common) install(TARGETS circle-partitioner DESTINATION bin) - -# TODO remove circle_partitioner -add_executable(circle_partitioner "${SOURCES}") -target_link_libraries(circle_partitioner foder) -target_link_libraries(circle_partitioner crew) -target_link_libraries(circle_partitioner safemain) -target_link_libraries(circle_partitioner luci_lang) -target_link_libraries(circle_partitioner luci_log) -target_link_libraries(circle_partitioner luci_import) -target_link_libraries(circle_partitioner luci_service) -target_link_libraries(circle_partitioner luci_pass) -target_link_libraries(circle_partitioner luci_export) -target_link_libraries(circle_partitioner luci_partition) -target_link_libraries(circle_partitioner arser) -target_link_libraries(circle_partitioner pepper_csv2vec) -target_link_libraries(circle_partitioner vconone) -target_link_libraries(circle_partitioner nncc_common) - -install(TARGETS circle_partitioner DESTINATION bin) diff --git a/compiler/circle-partitioner/README.md b/compiler/circle-partitioner/README.md index 2e0a986..760cf28 100644 --- a/compiler/circle-partitioner/README.md +++ b/compiler/circle-partitioner/README.md @@ -4,10 +4,10 @@ _circle-partitioner_ provides model partitioning of circle model to two or more ## How circle-partitioner work -_circle-partitioner_ requires 3 positional arguments -- first: `partition` file -- second: `input` circle model file -- third: `work` folder +_circle-partitioner_ requires 3 arguments for inputs files +- `--part_file`: `partition` file, use extension `.part` +- `--input_file`: `input` circle model file +- `--work_path`: `work` path where input files reside. this is optional and CWD if omitted And options to override `partition` file as a helper to try out without editing `partition` file. - `--backends`: override `backends` of `[partition]` section @@ -20,7 +20,7 @@ are read from `work` folder. Outputs are (1) one or more partitioned circle models and (2) connection file that gives how the partitioned models should be connected to act like the source `input` model. -Why does input files be placed in `work` folder too? +Why does input files be placed in `work` path too? - this is still work in progress condition - use cases are still ambigious - original `input` model file can be used by the backend, so `.conn` file links it as `source` @@ -94,7 +94,8 @@ Net_InstanceNorm_003/ Command example ``` -./circle-partitioner Net_InstanceNorm_003.part Net_InstanceNorm_003.circle Net_InstanceNorm_003 +./circle-partitioner --part_file Net_InstanceNorm_003.part \ +--input_file Net_InstanceNorm_003.circle --work_path= Net_InstanceNorm_003 ``` Result of _circle-partitioner_ @@ -171,11 +172,11 @@ Consider partitioning with backends of OneRT Let's try with this command: ``` -circle_partitioner \ - --partition Net_InstanceNorm_003.part \ - --backends cpu,acl_cl \ - --default cpu \ - Net_InstanceNorm_003.circle Net_InstanceNorm_003 +circle-partitioner \ + --backends cpu,acl_cl --default cpu \ + --part_file Net_InstanceNorm_003.part \ + --input_file Net_InstanceNorm_003.circle \ + --work_path Net_InstanceNorm_003 ``` where `Net_InstanceNorm_003.part` is like this for initial design diff --git a/compiler/circle-partitioner/requires.cmake b/compiler/circle-partitioner/requires.cmake index 690d953..82d9c2b 100644 --- a/compiler/circle-partitioner/requires.cmake +++ b/compiler/circle-partitioner/requires.cmake @@ -1,4 +1,3 @@ -require("foder") require("crew") require("pepper-csv2vec") require("safemain") diff --git a/compiler/circle-partitioner/src/CirclePartitioner.cpp b/compiler/circle-partitioner/src/CirclePartitioner.cpp index 0151e92..5cecb9a 100644 --- a/compiler/circle-partitioner/src/CirclePartitioner.cpp +++ b/compiler/circle-partitioner/src/CirclePartitioner.cpp @@ -18,9 +18,7 @@ #include "PartitionExport.h" #include "HelperPath.h" -#include - -#include +#include #include #include #include @@ -41,9 +39,9 @@ namespace const char *opt_bks = "--backends"; const char *opt_def = "--default"; -const char *opt_part = "partition"; -const char *opt_input = "input"; -const char *opt_work = "work"; +const char *opt_part_file = "--part_file"; +const char *opt_input_file = "--input_file"; +const char *opt_work_path = "--work_path"; void print_version(void) { @@ -53,63 +51,25 @@ void print_version(void) void build_arser(arser::Arser &arser) { - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument(opt_bks) - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Backends in CSV to use for partitioning"); - - arser.add_argument(opt_def) - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Default backend to assign"); - - arser.add_argument(opt_part) - .nargs(1) - .type(arser::DataType::STR) + arser::Helper::add_version(arser, print_version); + + arser.add_argument(opt_bks).help("Backends in CSV to use for partitioning"); + + arser.add_argument(opt_def).help("Default backend to assign"); + + arser.add_argument(opt_part_file) + .required(true) .help("Partition file which provides backend to assign"); - arser.add_argument(opt_input) - .nargs(1) - .type(arser::DataType::STR) - .help("Input circle model filename"); - arser.add_argument(opt_work) - .nargs(1) - .type(arser::DataType::STR) + arser.add_argument(opt_input_file).required(true).help("Input circle model filename"); + arser.add_argument(opt_work_path) .help("Work folder of partition, input files exist and output files are produced"); } std::unique_ptr load_model(const std::string &input_path) { - // Load model from the file - foder::FileLoader file_loader{input_path}; - std::vector model_data = file_loader.load(); - - // Verify flatbuffers - flatbuffers::Verifier verifier{reinterpret_cast(model_data.data()), model_data.size()}; - if (!circle::VerifyModelBuffer(verifier)) - { - std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; - return nullptr; - } - - const circle::Model *circle_model = circle::GetModel(model_data.data()); - if (circle_model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; - return nullptr; - } - // Import from input Circle file - luci::Importer importer; - return importer.importModule(circle_model); + luci::ImporterEx importerex; + return importerex.importVerifyModule(input_path); } } // namespace @@ -133,9 +93,14 @@ int entry(int argc, char **argv) return EXIT_FAILURE; } - std::string partition_file = arser.get(opt_part); - std::string input_file = arser.get(opt_input); - std::string work_folder = arser.get(opt_work); + std::string partition_file = arser.get(opt_part_file); + std::string input_file = arser.get(opt_input_file); + std::string work_folder = "."; + + if (arser[opt_work_path]) + { + work_folder = arser.get(opt_work_path); + } std::string partition_path = work_folder + "/" + partition_file; std::string input_path = work_folder + "/" + input_file; diff --git a/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt b/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt index 5ec8b6e..a3a2902 100644 --- a/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt +++ b/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt @@ -18,7 +18,7 @@ unset(TEST_NAMES) get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) set(options USE_QCONFIG) -set(oneValueArgs DTYPE GRANULARITY) +set(oneValueArgs DTYPE GRANULARITY INPUT_DTYPE OUTPUT_DTYPE) set(multiValueArgs "") macro(Add RECIPE) @@ -29,6 +29,16 @@ macro(Add RECIPE) set(QCONFIG_OPT "--config" "${ARTIFACTS_BIN_PATH}/${RECIPE}.qconf.json") endif() + set(INPUT_DTYPE_OPT "") + if(ARG_INPUT_DTYPE) + set(INPUT_DTYPE_OPT "--input_type" "${ARG_INPUT_DTYPE}") + endif() + + set(OUTPUT_DTYPE_OPT "") + if(ARG_OUTPUT_DTYPE) + set(OUTPUT_DTYPE_OPT "--output_type" "${ARG_OUTPUT_DTYPE}") + endif() + set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE}.circle") set(FAKE_QUANT_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.fq.circle") set(RECORDED_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.recorded.circle") @@ -38,7 +48,10 @@ macro(Add RECIPE) add_custom_command(OUTPUT ${QUANT_CIRCLE_PATH} COMMAND $ --quantize_dequantize_weights float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${CIRCLE_PATH} ${FAKE_QUANT_CIRCLE_PATH} COMMAND $ --input_model ${FAKE_QUANT_CIRCLE_PATH} --output_model ${RECORDED_CIRCLE_PATH} - COMMAND $ --quantize_with_minmax float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${RECORDED_CIRCLE_PATH} ${QUANT_CIRCLE_PATH} + COMMAND $ + --quantize_with_minmax float32 ${ARG_DTYPE} ${ARG_GRANULARITY} + ${QCONFIG_OPT} ${RECORDED_CIRCLE_PATH} ${QUANT_CIRCLE_PATH} + ${INPUT_DTYPE_OPT} ${OUTPUT_DTYPE_OPT} DEPENDS circle-quantizer record-minmax diff --git a/compiler/circle-quantizer-dredd-recipe-test/test.lst b/compiler/circle-quantizer-dredd-recipe-test/test.lst index 1881030..58f89c7 100644 --- a/compiler/circle-quantizer-dredd-recipe-test/test.lst +++ b/compiler/circle-quantizer-dredd-recipe-test/test.lst @@ -6,10 +6,75 @@ ## TFLITE RECIPE +# MPQ Test (default: u8, target: s16) +Add(Quant_Add_001 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_AveragePool2D_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_BatchMatMul_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Concatenation_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Conv_003 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_DepthwiseConv2D_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_FullyConnected_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_LeakyRelu_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Logistic_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_MaxPool2D_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Mean_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Mul_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Neg_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Pad_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_PRelu_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_ReLU_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_ReLU6_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Reshape_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_ResizeBilinear_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_ResizeNearestNeighbor_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Slice_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Softmax_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Tanh_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Transpose_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_TransposeConv_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) + +# MPQ Test (default: s16, target: u8) +Add(Quant_Add_002 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_AveragePool2D_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_BatchMatMul_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Concatenation_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Conv_004 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_DepthwiseConv2D_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_FullyConnected_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_LeakyRelu_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Logistic_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_MaxPool2D_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Mean_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Mul_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Neg_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Pad_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_PRelu_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_ReLU_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_ReLU6_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Reshape_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_ResizeBilinear_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_ResizeNearestNeighbor_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Slice_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Softmax_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Tanh_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_Transpose_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) +Add(Quant_TransposeConv_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) + Add(Quant_Conv_Mul_Add_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) Add(Quant_Conv_Mul_Add_001 DTYPE uint8 GRANULARITY channel USE_QCONFIG) Add(Quant_Conv_Mul_Add_002 DTYPE uint8 GRANULARITY channel USE_QCONFIG) Add(Quant_Split_Add_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) Add(Quant_Split_Add_001 DTYPE uint8 GRANULARITY channel USE_QCONFIG) +Add(Quant_Conv_000 DTYPE uint8 GRANULARITY channel INPUT_DTYPE float32) +Add(Quant_Conv_001 DTYPE uint8 GRANULARITY channel OUTPUT_DTYPE float32) +Add(Quant_Conv_002 DTYPE uint8 GRANULARITY channel INPUT_DTYPE float32 OUTPUT_DTYPE float32) AddFakeQuant(Quant_Add_000) + +## CIRCLE RECIPE + +# MPQ Test (default: u8, target: s16) +Add(Quant_InstanceNorm_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG) + +# MPQ Test (default: s16, target: u8) +Add(Quant_InstanceNorm_001 DTYPE int16 GRANULARITY channel USE_QCONFIG) diff --git a/compiler/circle-quantizer/CMakeLists.txt b/compiler/circle-quantizer/CMakeLists.txt index 14e0097..16e41a3 100644 --- a/compiler/circle-quantizer/CMakeLists.txt +++ b/compiler/circle-quantizer/CMakeLists.txt @@ -10,7 +10,6 @@ add_executable(circle-quantizer "${SOURCES}") target_include_directories(circle-quantizer PRIVATE ${Jsoncpp_INCLUDE_DIRS}) target_link_libraries(circle-quantizer ${Jsoncpp_STATIC_LIB}) -target_link_libraries(circle-quantizer foder) target_link_libraries(circle-quantizer safemain) target_link_libraries(circle-quantizer oops) target_link_libraries(circle-quantizer loco) diff --git a/compiler/circle-quantizer/requires.cmake b/compiler/circle-quantizer/requires.cmake index c21e28e..4fcee18 100644 --- a/compiler/circle-quantizer/requires.cmake +++ b/compiler/circle-quantizer/requires.cmake @@ -1,4 +1,3 @@ -require("foder") require("loco") require("locop") require("safemain") diff --git a/compiler/circle-quantizer/src/CircleQuantizer.cpp b/compiler/circle-quantizer/src/CircleQuantizer.cpp index e0c85cb..f1e31ed 100644 --- a/compiler/circle-quantizer/src/CircleQuantizer.cpp +++ b/compiler/circle-quantizer/src/CircleQuantizer.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include - -#include +#include #include #include #include @@ -59,13 +57,31 @@ std::vector> read_layer_params(std::string &filename std::vector> p; for (auto layer : layers) { - auto l = std::make_shared(); + if (layer.isMember("name")) { - l->name = layer["name"].asString(); - l->dtype = layer["dtype"].asString(); - l->granularity = layer["granularity"].asString(); + auto l = std::make_shared(); + { + l->name = layer["name"].asString(); + l->dtype = layer["dtype"].asString(); + l->granularity = layer["granularity"].asString(); + } + p.emplace_back(l); + } + + // Multiple names with the same dtype & granularity + if (layer.isMember("names")) + { + for (auto name : layer["names"]) + { + auto l = std::make_shared(); + { + l->name = name.asString(); + l->dtype = layer["dtype"].asString(); + l->granularity = layer["granularity"].asString(); + } + p.emplace_back(l); + } } - p.emplace_back(l); } return p; @@ -109,23 +125,12 @@ int entry(int argc, char **argv) arser::Arser arser("circle-quantizer provides circle model quantization"); - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument("-V", "--verbose") - .nargs(0) - .required(false) - .default_value(false) - .help("output additional information to stdout or stderr"); + arser::Helper::add_version(arser, print_version); + arser::Helper::add_verbose(arser); arser.add_argument(qdqw) .nargs(3) .type(arser::DataType::STR_VEC) - .required(false) .help("Quantize-dequantize weight values required action before quantization. " "Three arguments required: input_model_dtype(float32) " "output_model_dtype(uint8) granularity(layer, channel)"); @@ -133,28 +138,24 @@ int entry(int argc, char **argv) arser.add_argument(qwmm) .nargs(3) .type(arser::DataType::STR_VEC) - .required(false) .help("Quantize with min/max values. " "Three arguments required: input_model_dtype(float32) " "output_model_dtype(uint8) granularity(layer, channel)"); arser.add_argument(tf_maxpool) .nargs(0) - .required(false) .default_value(false) .help("Force MaxPool Op to have the same input/output quantparams. NOTE: This feature can " "degrade accuracy of some models"); arser.add_argument(fake_quant) .nargs(0) - .required(false) .help("Convert a quantized model to a fake-quantized model. NOTE: This feature will " "generate an fp32 model."); arser.add_argument(rq) .nargs(2) .type(arser::DataType::STR_VEC) - .required(false) .help("Requantize a quantized model. " "Two arguments required: input_model_dtype(int8) " "output_model_dtype(uint8)"); @@ -162,7 +163,6 @@ int entry(int argc, char **argv) arser.add_argument(fq) .nargs(3) .type(arser::DataType::STR_VEC) - .required(false) .accumulated(true) .help("Write quantization parameters to the specified tensor. " "Three arguments required: tensor_name(string), " @@ -171,32 +171,21 @@ int entry(int argc, char **argv) arser.add_argument(cq) .nargs(2) .type(arser::DataType::STR_VEC) - .required(false) .accumulated(true) .help("Copy quantization parameter from a tensor to another tensor." "Two arguments required: source_tensor_name(string), " "destination_tensor_name(string)"); arser.add_argument("--input_type") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Input type of quantized model (uint8 or int16)"); + .help("Input type of quantized model (uint8, int16, or float32)"); arser.add_argument("--output_type") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Output type of quantized model (uint8 or int16)"); + .help("Output type of quantized model (uint8, int16, or float32)"); - arser.add_argument(cfg) - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Path to the quantization configuration file"); + arser.add_argument(cfg).help("Path to the quantization configuration file"); - arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); - arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); + arser.add_argument("input").help("Input circle model"); + arser.add_argument("output").help("Output circle model"); arser.add_argument(gpd).nargs(0).required(false).default_value(false).help( "This will turn on profiling data generation."); @@ -384,27 +373,10 @@ int entry(int argc, char **argv) settings->set(luci::UserSettings::Key::ProfilingDataGen, true); // Load model from the file - foder::FileLoader file_loader{input_path}; - std::vector model_data = file_loader.load(); - - // Verify flatbuffers - flatbuffers::Verifier verifier{reinterpret_cast(model_data.data()), model_data.size()}; - if (!circle::VerifyModelBuffer(verifier)) - { - std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; - return EXIT_FAILURE; - } - - const circle::Model *circle_model = circle::GetModel(model_data.data()); - if (circle_model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + luci::ImporterEx importerex; + auto module = importerex.importVerifyModule(input_path); + if (module.get() == nullptr) return EXIT_FAILURE; - } - - // Import from input Circle file - luci::Importer importer; - auto module = importer.importModule(circle_model); for (size_t idx = 0; idx < module->size(); ++idx) { diff --git a/compiler/circle-tensordump/driver/Driver.cpp b/compiler/circle-tensordump/driver/Driver.cpp index 70f3c8d..c32dc3f 100644 --- a/compiler/circle-tensordump/driver/Driver.cpp +++ b/compiler/circle-tensordump/driver/Driver.cpp @@ -31,11 +31,9 @@ int entry(int argc, char **argv) arser::Arser arser{ "circle-tensordump allows users to retrieve tensor information from a Circle model file"}; - arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Circle file path to dump"); + arser.add_argument("circle").help("Circle file path to dump"); arser.add_argument("--tensors").nargs(0).help("Dump to console"); arser.add_argument("--tensors_to_hdf5") - .nargs(1) - .type(arser::DataType::STR) .help("Dump to hdf5 file. Specify hdf5 file path to be dumped"); try diff --git a/compiler/circle-tensordump/src/Dump.cpp b/compiler/circle-tensordump/src/Dump.cpp index e477a74..49afa73 100644 --- a/compiler/circle-tensordump/src/Dump.cpp +++ b/compiler/circle-tensordump/src/Dump.cpp @@ -15,7 +15,8 @@ */ #include "Dump.h" -#include "Reader.h" + +#include #include @@ -102,7 +103,7 @@ namespace circletensordump void DumpTensors::run(std::ostream &os, const circle::Model *model, const std::string &) { - circletensordump::Reader reader(model); + mio::circle::Reader reader(model); uint32_t num_subgraph = reader.num_subgraph(); auto buffers = reader.buffers(); @@ -296,7 +297,7 @@ void DumpTensorsToHdf5::run(std::ostream &os, const circle::Model *model, const std::string &output_path) { // loads a circle model - circletensordump::Reader reader(model); + mio::circle::Reader reader(model); uint32_t num_subgraph = reader.num_subgraph(); // create a hdf5 file diff --git a/compiler/circle-tensordump/src/Reader.cpp b/compiler/circle-tensordump/src/Reader.cpp deleted file mode 100644 index 47b8760..0000000 --- a/compiler/circle-tensordump/src/Reader.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 "Reader.h" - -#include - -#include -#include - -namespace circletensordump -{ - -Reader::Reader(const circle::Model *model) -{ - _subgraphs = model->subgraphs(); - _buffers = model->buffers(); - - auto opcodes = model->operator_codes(); - for (const ::circle::OperatorCode *opcode : *opcodes) - { - _op_codes.push_back(opcode); - } -} - -size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) -{ - if (buff_data != nullptr) - { - *buff_data = nullptr; - } - - if (buf_idx == 0) - return 0; - - if (auto *buffer = (*_buffers)[buf_idx]) - { - if (auto *array = buffer->data()) - { - if (size_t size = array->size()) - { - if (buff_data != nullptr) - { - *buff_data = reinterpret_cast(array->data()); - } - return size; - } - } - } - - return 0; -} - -circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - return mio::circle::builtin_code_neutral(opcode); -} - -std::string Reader::opcode_name(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - if (!mio::circle::is_valid(opcode)) - { - std::ostringstream oss; - oss << "(invalid: " << index << ")"; - return oss.str(); - } - - return mio::circle::opcode_name(opcode); -} - -bool Reader::select_subgraph(uint32_t sgindex) -{ - _tensors = nullptr; - _operators = nullptr; - - _inputs.clear(); - _outputs.clear(); - - if (_subgraphs->Length() <= sgindex) - { - assert(false); - return false; - } - - const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; - - _tensors = subgraph->tensors(); - _operators = subgraph->operators(); - - _inputs = as_index_vector(subgraph->inputs()); - _outputs = as_index_vector(subgraph->outputs()); - - return true; -} - -} // namespace circletensordump diff --git a/compiler/circle-tensordump/src/Reader.h b/compiler/circle-tensordump/src/Reader.h deleted file mode 100644 index c868bc2..0000000 --- a/compiler/circle-tensordump/src/Reader.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __CIRCLE_TENSORDUMP_READER_H__ -#define __CIRCLE_TENSORDUMP_READER_H__ - -#include - -#include -#include -#include - -namespace circletensordump -{ - -template std::vector as_index_vector(const flatbuffers::Vector *flat_array) -{ - std::vector ret(flat_array->Length()); - for (uint32_t i = 0; i < flat_array->Length(); i++) - { - ret[i] = flat_array->Get(i); - } - return ret; -} - -/** - * @brief Loads Circle file and provides helpers to access attributes - */ -class Reader -{ -private: - using CircleSubGraphs_t = flatbuffers::Vector>; - using CircleBuffers_t = flatbuffers::Vector>; - using CircleTensors_t = flatbuffers::Vector>; - using CircleOperators_t = flatbuffers::Vector>; - -public: - Reader(const circle::Model *model); - - Reader() = delete; - -public: - const std::vector &opcodes() { return _op_codes; } - const CircleBuffers_t *buffers() { return _buffers; } - const CircleTensors_t *tensors() { return _tensors; } - const CircleOperators_t *operators() { return _operators; } - const std::vector &inputs() const { return _inputs; } - const std::vector &outputs() const { return _outputs; } - - uint32_t num_subgraph() const { return _subgraphs->Length(); } - - size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); - circle::BuiltinOperator builtin_code(const circle::Operator *op) const; - std::string opcode_name(const circle::Operator *op) const; - -public: - bool select_subgraph(uint32_t subgraph); - -private: - const CircleSubGraphs_t *_subgraphs{nullptr}; - const CircleBuffers_t *_buffers{nullptr}; - const CircleTensors_t *_tensors{nullptr}; - const CircleOperators_t *_operators{nullptr}; - - std::vector _op_codes; - std::vector _inputs; - std::vector _outputs; -}; - -} // namespace circletensordump - -#endif // __CIRCLE_TENSORDUMP_READER_H__ diff --git a/compiler/circle-verify/src/Driver.cpp b/compiler/circle-verify/src/Driver.cpp index 7a44c65..c3a4147 100644 --- a/compiler/circle-verify/src/Driver.cpp +++ b/compiler/circle-verify/src/Driver.cpp @@ -25,7 +25,7 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to verify"); + arser.add_argument("circle").help("Circle file path to verify"); try { diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst index f41aac3..a6f2786 100644 --- a/compiler/circle2circle-dredd-recipe-test/test.lst +++ b/compiler/circle2circle-dredd-recipe-test/test.lst @@ -31,6 +31,8 @@ Add(Net_TConv_Add_002 PASS fuse_add_with_tconv) Add(Net_TConv_BN_000 PASS fuse_batchnorm_with_tconv) Add(Net_TConv_BN_001 PASS fuse_batchnorm_with_tconv) Add(Net_TConv_BN_002 PASS fuse_batchnorm_with_tconv) +Add(Net_TConv_BN_003 PASS fuse_batchnorm_with_tconv) +Add(Net_TConv_BN_004 PASS fuse_batchnorm_with_tconv) Add(Net_InstanceNorm_001 PASS fuse_instnorm) Add(Net_InstanceNorm_003 PASS fuse_instnorm) Add(Net_InstanceNorm_004 PASS fuse_instnorm) @@ -46,6 +48,7 @@ Add(StridedSlice_003 PASS substitute_strided_slice_to_reshape) Add(MaxPoolWithArgmax_000 PASS resolve_customop_max_pool_with_argmax) Add(MaxPoolWithArgmax_001 PASS resolve_customop_max_pool_with_argmax) Add(MaxPoolWithArgmax_002 PASS resolve_customop_max_pool_with_argmax) +Add(FullyConnected_007 PASS replace_non_const_fc_with_batch_matmul) ## CIRCLE RECIPE diff --git a/compiler/circle2circle/CMakeLists.txt b/compiler/circle2circle/CMakeLists.txt index cd79967..dbe485b 100644 --- a/compiler/circle2circle/CMakeLists.txt +++ b/compiler/circle2circle/CMakeLists.txt @@ -4,7 +4,6 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_executable(circle2circle "${SOURCES}") target_include_directories(circle2circle PRIVATE src) -target_link_libraries(circle2circle foder) target_link_libraries(circle2circle nncc_common) target_link_libraries(circle2circle safemain) target_link_libraries(circle2circle oops) @@ -29,7 +28,6 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(circle2circle_test ${TESTS} ${SOURCES}) target_include_directories(circle2circle_test PRIVATE src) -target_link_libraries(circle2circle_test foder) target_link_libraries(circle2circle_test nncc_common) target_link_libraries(circle2circle_test oops) target_link_libraries(circle2circle_test hermes) diff --git a/compiler/circle2circle/requires.cmake b/compiler/circle2circle/requires.cmake index b6c6119..4e5ed0d 100644 --- a/compiler/circle2circle/requires.cmake +++ b/compiler/circle2circle/requires.cmake @@ -1,4 +1,3 @@ -require("foder") require("loco") require("locop") require("logo-core") diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp index ae677a3..f5cf0d7 100644 --- a/compiler/circle2circle/src/Circle2Circle.cpp +++ b/compiler/circle2circle/src/Circle2Circle.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#include - -#include +#include #include #include #include @@ -54,6 +52,11 @@ void csv_tokenize(const std::string &data, std::vector &result) result.push_back(token); } +void add_switch(arser::Arser &arser, const char *opt, const char *desc) +{ + arser.add_argument(opt).nargs(0).default_value(false).help(desc); +} + int entry(int argc, char **argv) { // Simple argument parser (based on map) @@ -64,368 +67,125 @@ int entry(int argc, char **argv) arser::Arser arser("circle2circle provides circle model optimization and transformations"); - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument("-V", "--verbose") - .nargs(0) - .required(false) - .default_value(false) - .help("output additional information to stdout or stderr"); - - arser.add_argument("--O1").nargs(0).required(false).default_value(false).help( - "Enable O1 optimize options"); - - arser.add_argument("--fold_add_v2") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold AddV2 operators with constant inputs"); - - arser.add_argument("--fold_cast") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold Cast operators with constant input"); - - arser.add_argument("--fold_dequantize") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold dequantize op"); - - arser.add_argument("--fold_dwconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold Depthwise Convolution operator with constant inputs"); - - arser.add_argument("--fold_gather") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold Gather operator"); - - arser.add_argument("--fold_sparse_to_dense") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold SparseToDense operator"); - - arser.add_argument("--forward_reshape_to_unaryop") - .nargs(0) - .required(false) - .default_value(false) - .help("This will move Reshape after UnaryOp for centain condition"); - - arser.add_argument("--fuse_activation_function") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Activation function to a preceding operator"); - - arser.add_argument("--fuse_add_with_fully_connected") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Add operator to FullyConnected operator"); - - arser.add_argument("--fuse_add_with_tconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Add operator to Transposed Convolution operator"); - - arser.add_argument("--fuse_batchnorm_with_conv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators to Convolution operator"); - - arser.add_argument("--fuse_batchnorm_with_dwconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators to Depthwise Convolution operator"); - - arser.add_argument("--fuse_batchnorm_with_tconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators to Transposed Convolution operator"); - - arser.add_argument("--fuse_bcq") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse operators and apply Binary Coded Quantization"); - - arser.add_argument("--fuse_instnorm") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse operators to InstanceNorm operator"); - - arser.add_argument("--fuse_mean_with_mean") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse two Mean operations when they follow one by one." - "This will fold them into one operation and merge reduction indices."); - - arser.add_argument("--fuse_transpose_with_mean") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Mean operation with a preceding Transpose under certain conditions."); - - arser.add_argument("--make_batchnorm_gamma_positive") - .nargs(0) - .required(false) - .default_value(false) - .help("This will make negative gamma of BatchNorm into a small positive value (1e-10). Note " - "that this pass can change the execution result of the model. So, use it only when the " - "impact is known to be acceptable."); - - arser.add_argument("--fuse_preactivation_batchnorm") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators of pre-activations to Convolution operator"); - - arser.add_argument("--remove_fakequant") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove FakeQuant operators"); - - arser.add_argument("--remove_quantdequant") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove Quantize-Dequantize sequence"); - - arser.add_argument("--remove_redundant_quantize") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove redundant Quantize operators"); - - arser.add_argument("--remove_redundant_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse or remove subsequent Reshape operators"); - - arser.add_argument("--remove_redundant_transpose") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse or remove subsequent Transpose operators"); - - arser.add_argument("--remove_unnecessary_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove unnecessary reshape operators"); - - arser.add_argument("--remove_unnecessary_slice") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove unnecessary slice operators"); - - arser.add_argument("--remove_unnecessary_strided_slice") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove unnecessary strided slice operators"); - - arser.add_argument("--remove_unnecessary_split") - .nargs(0) - .required(false) - .default_value(false) - .help("This will remove unnecessary split operators"); - - arser.add_argument("--replace_cw_mul_add_with_depthwise_conv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will replace channel-wise mul/add with DepthwiseConv2D operator"); - - arser.add_argument("--replace_sub_with_add") - .nargs(0) - .required(false) - .default_value(false) - .help("This will replace sub with add operator"); - - arser.add_argument("--resolve_customop_add") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(Add) to Add operator"); - - arser.add_argument("--resolve_customop_batchmatmul") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(BatchMatmul) to BatchMatmul operator"); - - arser.add_argument("--resolve_customop_matmul") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(Matmul) to Matmul operator"); - - arser.add_argument("--resolve_customop_max_pool_with_argmax") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(MaxPoolWithArgmax) to equivalent set of operators"); - - arser.add_argument("--shuffle_weight_to_16x1float32") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. Note that " - "it only converts weights whose row is a multiple of 16"); - - arser.add_argument("--substitute_pack_to_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert single input Pack to Reshape"); - - arser.add_argument("--substitute_padv2_to_pad") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert certain condition PadV2 to Pad"); - - arser.add_argument("--substitute_splitv_to_split") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert certain condition SplitV to Split operator"); - - arser.add_argument("--substitute_squeeze_to_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert certain condition Squeeze to Reshape"); - - arser.add_argument("--substitute_strided_slice_to_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert certain condition Strided_Slice to Reshape"); - - arser.add_argument("--substitute_transpose_to_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert single input Transpose to Reshape"); - - arser.add_argument("--expand_broadcast_const") - .nargs(0) - .required(false) - .default_value(false) - .help("This will expand broadcastable constant inputs"); - - arser.add_argument("--convert_nchw_to_nhwc") - .nargs(0) - .required(false) - .default_value(false) - .help("Experimental: This will convert NCHW operators to NHWC under the assumption that " - "input model is NCHW."); - - arser.add_argument("--nchw_to_nhwc_input_shape") - .nargs(0) - .required(false) - .default_value(false) - .help("Convert the input shape of the model (argument for --convert_nchw_to_nhwc)."); - - arser.add_argument("--nchw_to_nhwc_output_shape") - .nargs(0) - .required(false) - .default_value(false) - .help("Convert the output shape of the model (argument for --convert_nchw_to_nhwc)."); - - arser.add_argument("--transform_min_max_to_relu6") - .nargs(0) - .required(false) - .default_value(false) - .help("Transform Minimum(6)-Maximum(0) pattern to Relu6 operator"); - - arser.add_argument("--transform_min_relu_to_relu6") - .nargs(0) - .required(false) - .default_value(false) - .help("Transform Minimum(6)-Relu pattern to Relu6 operator"); - - arser.add_argument("--mute_warnings") - .nargs(0) - .required(false) - .default_value(false) - .help("This will turn off warning messages"); - - arser.add_argument("--disable_validation") - .nargs(0) - .required(false) - .default_value(false) - .help("This will turn off operator validations. May help input model investigation."); - - arser.add_argument("--generate_profile_data") - .nargs(0) - .required(false) - .default_value(false) - .help("This will turn on profiling data generation."); + arser::Helper::add_version(arser, print_version); + arser::Helper::add_verbose(arser); + + add_switch(arser, "--fold_add_v2", "This will fold AddV2 operators with constant inputs"); + add_switch(arser, "--fold_cast", "This will fold Cast operators with constant input"); + add_switch(arser, "--fold_densify", + "This will fold Densify operators with sparse constant input"); + add_switch(arser, "--fold_dequantize", "This will fold dequantize op"); + add_switch(arser, "--fold_dwconv", + "This will fold Depthwise Convolution operator with constant inputs"); + add_switch(arser, "--fold_gather", "This will fold Gather operator"); + add_switch(arser, "--fold_sparse_to_dense", "This will fold SparseToDense operator"); + add_switch(arser, "--forward_reshape_to_unaryop", + "This will move Reshape after UnaryOp for centain condition"); + add_switch(arser, "--fuse_activation_function", + "This will fuse Activation function to a preceding operator"); + add_switch(arser, "--fuse_add_with_fully_connected", + "This will fuse Add operator to FullyConnected operator"); + add_switch(arser, "--fuse_add_with_tconv", + "This will fuse Add operator to Transposed Convolution operator"); + add_switch(arser, "--fuse_batchnorm_with_conv", + "This will fuse BatchNorm operators to Convolution operator"); + add_switch(arser, "--fuse_batchnorm_with_dwconv", + "This will fuse BatchNorm operators to Depthwise Convolution operator"); + add_switch(arser, "--fuse_batchnorm_with_tconv", + "This will fuse BatchNorm operators to Transposed Convolution operator"); + add_switch(arser, "--fuse_bcq", "This will fuse operators and apply Binary Coded Quantization"); + add_switch(arser, "--fuse_instnorm", "This will fuse operators to InstanceNorm operator"); + add_switch(arser, "--fuse_mean_with_mean", + "This will fuse two Mean operations when they follow one by one. This will fold them " + "into one operation and merge reduction indices."); + add_switch(arser, "--fuse_transpose_with_mean", + "This will fuse Mean operation with a preceding Transpose under certain conditions."); + add_switch(arser, "--make_batchnorm_gamma_positive", + "This will make negative gamma of BatchNorm into a small positive value (1e-10). " + "Note that this pass can change the execution result of the model. So, use it only " + "when the impact is known to be acceptable."); + add_switch(arser, "--fuse_preactivation_batchnorm", + "This will fuse BatchNorm operators of pre-activations to Convolution operator"); + add_switch(arser, "--remove_fakequant", "This will remove FakeQuant operators"); + add_switch(arser, "--remove_quantdequant", "This will remove Quantize-Dequantize sequence"); + add_switch(arser, "--remove_redundant_quantize", "This will remove redundant Quantize operators"); + add_switch(arser, "--remove_redundant_reshape", + "This will fuse or remove subsequent Reshape operators"); + add_switch(arser, "--remove_redundant_transpose", + "This will fuse or remove subsequent Transpose operators"); + add_switch(arser, "--remove_unnecessary_reshape", + "This will remove unnecessary reshape operators"); + add_switch(arser, "--remove_unnecessary_slice", "This will remove unnecessary slice operators"); + add_switch(arser, "--remove_unnecessary_strided_slice", + "This will remove unnecessary strided slice operators"); + add_switch(arser, "--remove_unnecessary_split", "This will remove unnecessary split operators"); + add_switch(arser, "--replace_cw_mul_add_with_depthwise_conv", + "This will replace channel-wise mul/add with DepthwiseConv2D operator"); + add_switch(arser, "--replace_sub_with_add", "This will replace sub with add operator"); + add_switch(arser, "--resolve_customop_add", "This will convert Custom(Add) to Add operator"); + add_switch(arser, "--resolve_customop_batchmatmul", + "This will convert Custom(BatchMatmul) to BatchMatmul operator"); + add_switch(arser, "--resolve_customop_matmul", + "This will convert Custom(Matmul) to Matmul operator"); + add_switch(arser, "--resolve_customop_max_pool_with_argmax", + "This will convert Custom(MaxPoolWithArgmax) to equivalent set of operators"); + add_switch(arser, "--resolve_customop_splitv", + "This will convert Custom(SplitV) to SplitV operator"); + add_switch(arser, "--shuffle_weight_to_16x1float32", + "This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. Note that " + "it only converts weights whose row is a multiple of 16"); + add_switch(arser, "--replace_non_const_fc_with_batch_matmul", + "Replace FullyConnected with BatchMatMul when its weight is non-constant"); + add_switch(arser, "--substitute_pack_to_reshape", + "This will convert single input Pack to Reshape"); + add_switch(arser, "--substitute_padv2_to_pad", + "This will convert certain condition PadV2 to Pad"); + add_switch(arser, "--substitute_splitv_to_split", + "This will convert certain condition SplitV to Split operator"); + add_switch(arser, "--substitute_squeeze_to_reshape", + "This will convert certain condition Squeeze to Reshape"); + add_switch(arser, "--substitute_strided_slice_to_reshape", + "This will convert certain condition Strided_Slice to Reshape"); + add_switch(arser, "--substitute_transpose_to_reshape", + "This will convert single input Transpose to Reshape"); + add_switch(arser, "--expand_broadcast_const", "This will expand broadcastable constant inputs"); + add_switch(arser, "--convert_nchw_to_nhwc", + "Experimental: This will convert NCHW operators to NHWC under the assumption that " + "input model is NCHW."); + add_switch(arser, "--nchw_to_nhwc_input_shape", + "Convert the input shape of the model (argument for --convert_nchw_to_nhwc)."); + add_switch(arser, "--nchw_to_nhwc_output_shape", + "Convert the output shape of the model (argument for --convert_nchw_to_nhwc)."); + add_switch(arser, "--transform_min_max_to_relu6", + "Transform Minimum(6)-Maximum(0) pattern to Relu6 operator"); + add_switch(arser, "--transform_min_relu_to_relu6", + "Transform Minimum(6)-Relu pattern to Relu6 operator"); + add_switch(arser, "--mute_warnings", "This will turn off warning messages"); + add_switch(arser, "--disable_validation", + "This will turn off operator validations. May help input model investigation."); + add_switch(arser, "--generate_profile_data", "This will turn on profiling data generation."); arser.add_argument("--change_outputs") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .help("Experimental: Change first subgraph output nodes to CSV names"); - arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); - arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); + arser.add_argument("input").help("Input circle model"); + arser.add_argument("output").help("Output circle model"); // sparsification argument - arser.add_argument("--sparsify_tensor") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Tensor name that you want to sparsify"); + arser.add_argument("--sparsify_tensor").help("Tensor name that you want to sparsify"); arser.add_argument("--sparsify_traversal_order") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .default_value("0,1,2,3") .help("Traversal order of dimensions. Default value: 0,1,2,3"); arser.add_argument("--sparsify_format") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .default_value("d,s") .help("Format of each dimension. 'd' stands for dense, 's' stands for sparse(CSR). Default " "value: d,s"); - arser.add_argument("--sparsify_block_size") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Size of each block dimension"); + arser.add_argument("--sparsify_block_size").help("Size of each block dimension"); arser.add_argument("--sparsify_block_map") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .default_value("0,1") .help("Map from block dimension to the original tensor dimension. Default value: 0,1"); @@ -446,20 +206,12 @@ int entry(int argc, char **argv) // If REPLACE is zero, it does not overwrite an existing value. setenv("LUCI_LOG", "100", 0); } - if (arser.get("--O1")) - { - options->enable(Algorithms::FuseBCQ); - options->enable(Algorithms::FuseInstanceNorm); - options->enable(Algorithms::ResolveCustomOpAdd); - options->enable(Algorithms::ResolveCustomOpBatchMatMul); - options->enable(Algorithms::ResolveCustomOpMatMul); - options->enable(Algorithms::RemoveRedundantTranspose); - options->enable(Algorithms::SubstitutePackToReshape); - } if (arser.get("--fold_add_v2")) options->enable(Algorithms::FoldAddV2); if (arser.get("--fold_cast")) options->enable(Algorithms::FoldCast); + if (arser.get("--fold_densify")) + options->enable(Algorithms::FoldDensify); if (arser.get("--fold_dequantize")) options->enable(Algorithms::FoldDequantize); if (arser.get("--fold_dwconv")) @@ -524,8 +276,12 @@ int entry(int argc, char **argv) options->enable(Algorithms::ResolveCustomOpMatMul); if (arser.get("--resolve_customop_max_pool_with_argmax")) options->enable(Algorithms::ResolveCustomOpMaxPoolWithArgmax); + if (arser.get("--resolve_customop_splitv")) + options->enable(Algorithms::ResolveCustomOpSplitV); if (arser.get("--shuffle_weight_to_16x1float32")) options->enable(Algorithms::ShuffleWeightTo16x1Float32); + if (arser.get("--replace_non_const_fc_with_batch_matmul")) + options->enable(Algorithms::ReplaceNonConstFCWithBatchMatMul); if (arser.get("--substitute_pack_to_reshape")) options->enable(Algorithms::SubstitutePackToReshape); if (arser.get("--substitute_padv2_to_pad")) @@ -595,37 +351,11 @@ int entry(int argc, char **argv) csv_tokenize(csv_nodes, new_outputs); } - // Load model from the file - foder::FileLoader file_loader{input_path}; - std::vector model_data; - - try - { - model_data = file_loader.load(); - } - catch (const std::runtime_error &err) - { - std::cerr << err.what() << std::endl; - return EXIT_FAILURE; - } - - flatbuffers::Verifier verifier{reinterpret_cast(model_data.data()), model_data.size()}; - if (!circle::VerifyModelBuffer(verifier)) - { - std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; - return EXIT_FAILURE; - } - - const circle::Model *circle_model = circle::GetModel(model_data.data()); - if (circle_model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; - return EXIT_FAILURE; - } - // Import from input Circle file - luci::Importer importer; - auto module = importer.importModule(circle_model); + luci::ImporterEx importerex; + auto module = importerex.importVerifyModule(input_path); + if (module.get() == nullptr) + return EXIT_FAILURE; if (change_outputs) { diff --git a/compiler/circlechef/tools/file/Driver.cpp b/compiler/circlechef/tools/file/Driver.cpp index 76d0f3f..9c4256b 100644 --- a/compiler/circlechef/tools/file/Driver.cpp +++ b/compiler/circlechef/tools/file/Driver.cpp @@ -28,10 +28,8 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("recipe") - .type(arser::DataType::STR) - .help("Source recipe file path to convert"); - arser.add_argument("circle").type(arser::DataType::STR).help("Target circle file path"); + arser.add_argument("recipe").help("Source recipe file path to convert"); + arser.add_argument("circle").help("Target circle file path"); try { diff --git a/compiler/circlechef/tools/reverse/Driver.cpp b/compiler/circlechef/tools/reverse/Driver.cpp index 639e0af..c8ef07c 100644 --- a/compiler/circlechef/tools/reverse/Driver.cpp +++ b/compiler/circlechef/tools/reverse/Driver.cpp @@ -25,10 +25,8 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("circle") - .type(arser::DataType::STR) - .help("Source circle file path to convert"); - arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path"); + arser.add_argument("circle").help("Source circle file path to convert"); + arser.add_argument("recipe").help("Target recipe file path"); try { diff --git a/compiler/circledump/CMakeLists.txt b/compiler/circledump/CMakeLists.txt index b65c066..7485ff8 100644 --- a/compiler/circledump/CMakeLists.txt +++ b/compiler/circledump/CMakeLists.txt @@ -10,6 +10,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(circledump ${DRIVER} ${SOURCES}) target_include_directories(circledump PRIVATE include) target_link_libraries(circledump arser) +target_link_libraries(circledump foder) target_link_libraries(circledump mio_circle04) target_link_libraries(circledump mio_circle04_helper) target_link_libraries(circledump safemain) diff --git a/compiler/circledump/driver/Driver.cpp b/compiler/circledump/driver/Driver.cpp index 657f24f..5b0871a 100644 --- a/compiler/circledump/driver/Driver.cpp +++ b/compiler/circledump/driver/Driver.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include #include @@ -23,7 +23,7 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to dump"); + arser.add_argument("circle").help("Circle file path to dump"); try { @@ -38,14 +38,10 @@ int entry(int argc, char **argv) std::string circle_path = arser.get("circle"); // Load Circle model from a circle file - std::unique_ptr model = circleread::load_circle(circle_path); - if (model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl; - return 255; - } - - const circle::Model *circlemodel = model->model(); + foder::FileLoader fileLoader{circle_path}; + std::vector modelData = fileLoader.load(); + const circle::Model *circlemodel = circle::GetModel(modelData.data()); + // const circle::Model *circlemodel = model->model(); if (circlemodel == nullptr) { std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl; diff --git a/compiler/circledump/include/circleread/Model.h b/compiler/circledump/include/circleread/Model.h deleted file mode 100644 index 234db8b..0000000 --- a/compiler/circledump/include/circleread/Model.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __CIRCLEREAD_MODEL_H__ -#define __CIRCLEREAD_MODEL_H__ - -#include - -#include - -namespace circleread -{ - -struct Model -{ - virtual ~Model() = default; - - virtual const ::circle::Model *model(void) const = 0; -}; - -/** - * @brief Load Circle model (as a raw Model) from a given path - * - * @note May return a nullptr - */ -std::unique_ptr load_circle(const std::string &path); - -} // namespace circleread - -#endif // __CIRCLEREAD_MODEL_H__ diff --git a/compiler/circledump/requires.cmake b/compiler/circledump/requires.cmake index 362d67c..183dfe2 100644 --- a/compiler/circledump/requires.cmake +++ b/compiler/circledump/requires.cmake @@ -1,3 +1,4 @@ require("arser") +require("foder") require("mio-circle04") require("safemain") diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp index 0b256dd..69427a2 100644 --- a/compiler/circledump/src/Dump.cpp +++ b/compiler/circledump/src/Dump.cpp @@ -16,8 +16,8 @@ #include #include +#include -#include "Read.h" #include "OpPrinter.h" #include "MetadataPrinter.h" @@ -122,7 +122,7 @@ std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector *fbvect) return os; } -void dump_sub_graph(std::ostream &os, circleread::Reader &reader) +void dump_sub_graph(std::ostream &os, mio::circle::Reader &reader) { auto tensors = reader.tensors(); auto operators = reader.operators(); @@ -150,14 +150,14 @@ void dump_sub_graph(std::ostream &os, circleread::Reader &reader) std::vector dims = {-1}; if (tensor->shape()) - dims = circleread::as_index_vector(tensor->shape()); + dims = mio::circle::as_index_vector(tensor->shape()); os << "T(" << reader.subgraph_index() << ":" << i << ") " << mio::circle::tensor_type(tensor) << " "; os << "(" << dims << ") "; if (tensor->shape_signature()) { - std::vector dims_sig = circleread::as_index_vector(tensor->shape_signature()); + std::vector dims_sig = mio::circle::as_index_vector(tensor->shape_signature()); os << "(" << dims_sig << ") "; } os << "B(" << tensor->buffer() << ") "; @@ -299,8 +299,8 @@ void dump_sub_graph(std::ostream &os, circleread::Reader &reader) const auto op = operators->Get(i); circle::BuiltinOperator builtincode = reader.builtin_code(op); - const std::vector &inputs = circleread::as_index_vector(op->inputs()); - const std::vector &outputs = circleread::as_index_vector(op->outputs()); + const std::vector &inputs = mio::circle::as_index_vector(op->inputs()); + const std::vector &outputs = mio::circle::as_index_vector(op->outputs()); auto op_name = reader.opcode_name(op); os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " "; @@ -356,7 +356,7 @@ void dump_sub_graph(std::ostream &os, circleread::Reader &reader) void dump_model(std::ostream &os, const circle::Model *model) { - circleread::Reader reader(model); + mio::circle::Reader reader(model); uint32_t num_subgraph = reader.num_subgraph(); diff --git a/compiler/circledump/src/Load.cpp b/compiler/circledump/src/Load.cpp deleted file mode 100644 index 67e7fa5..0000000 --- a/compiler/circledump/src/Load.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 - -#include -#include -#include -#include - -namespace -{ - -class MemoryMappedModel final : public circleread::Model -{ -public: - /** - * @require fd and data SHOULD be valid - */ - explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} - { - // DO NOTHING - } - -public: - ~MemoryMappedModel() - { - munmap(_data, _size); - close(_fd); - } - -public: - MemoryMappedModel(const MemoryMappedModel &) = delete; - MemoryMappedModel(MemoryMappedModel &&) = delete; - -public: - const ::circle::Model *model(void) const override { return ::circle::GetModel(_data); } - -private: - int _fd = -1; - void *_data = nullptr; - size_t _size = 0; -}; - -class FileDescriptor final -{ -public: - FileDescriptor(int value) : _value{value} - { - // DO NOTHING - } - -public: - // NOTE Copy is not allowed - FileDescriptor(const FileDescriptor &) = delete; - -public: - // NOTE Move is allowed - FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); } - -public: - ~FileDescriptor() - { - if (_value != -1) - { - // Close on destructor - close(_value); - } - } - -public: - int value(void) const { return _value; } - -public: - int release(void) - { - auto res = _value; - _value = -1; - return res; - } - -private: - int _value = -1; -}; - -} // namespace - -namespace circleread -{ - -std::unique_ptr load_circle(const std::string &path) -{ - FileDescriptor fd = open(path.c_str(), O_RDONLY); - - if (fd.value() == -1) - { - // Return nullptr on open failure - return nullptr; - } - - struct stat st; - if (fstat(fd.value(), &st) == -1) - { - // Return nullptr on fstat failure - return nullptr; - } - - auto size = st.st_size; - auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0); - - if (data == MAP_FAILED) - { - // Return nullptr on mmap failure - return nullptr; - } - - return std::unique_ptr{new MemoryMappedModel(fd.release(), data, size)}; -} - -} // namespace circleread diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp index 02e5c26..817371d 100644 --- a/compiler/circledump/src/OpPrinter.cpp +++ b/compiler/circledump/src/OpPrinter.cpp @@ -15,7 +15,8 @@ */ #include "OpPrinter.h" -#include "Read.h" + +#include #include @@ -233,7 +234,7 @@ public: { if (auto *reshape_params = op->builtin_options_as_ReshapeOptions()) { - auto new_shape = circleread::as_index_vector(reshape_params->new_shape()); + auto new_shape = mio::circle::as_index_vector(reshape_params->new_shape()); os << " "; os << "NewShape(" << new_shape << ")"; os << std::endl; @@ -802,6 +803,7 @@ OpPrinterRegistry::OpPrinterRegistry() // There is no Option for CEIL _op_map[circle::BuiltinOperator_CONCATENATION] = make_unique(); _op_map[circle::BuiltinOperator_CONV_2D] = make_unique(); + // There is no Option for DENSIFY _op_map[circle::BuiltinOperator_DEPTH_TO_SPACE] = make_unique(); _op_map[circle::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique(); // There is no Option for DEQUANTIZE diff --git a/compiler/circledump/src/Read.cpp b/compiler/circledump/src/Read.cpp deleted file mode 100644 index 3a7e98c..0000000 --- a/compiler/circledump/src/Read.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 "Read.h" - -#include - -#include -#include - -namespace circleread -{ - -Reader::Reader(const circle::Model *model) -{ - _version = model->version(); - _subgraphs = model->subgraphs(); - _buffers = model->buffers(); - _metadata = model->metadata(); - _signature_defs = model->signature_defs(); - - auto opcodes = model->operator_codes(); - for (const ::circle::OperatorCode *opcode : *opcodes) - { - _op_codes.push_back(opcode); - } -} - -size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) -{ - *buff_data = nullptr; - - if (buf_idx == 0) - return 0; - - if (auto *buffer = (*_buffers)[buf_idx]) - { - if (auto *array = buffer->data()) - { - if (size_t size = array->size()) - { - *buff_data = reinterpret_cast(array->data()); - return size; - } - } - } - - return 0; -} - -circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - return opcode->builtin_code(); -} - -std::string Reader::opcode_name(const circle::Operator *op) const -{ - uint32_t index = op->opcode_index(); - assert(index < _op_codes.size()); - const circle::OperatorCode *opcode = _op_codes.at(index); - - if (!mio::circle::is_valid(opcode)) - { - std::ostringstream oss; - oss << "(invalid: " << index << ")"; - return oss.str(); - } - - return mio::circle::opcode_name(opcode); -} - -bool Reader::select_subgraph(uint32_t sgindex) -{ - _subgraph_index = sgindex; - _tensors = nullptr; - _operators = nullptr; - - _inputs.clear(); - _outputs.clear(); - - if (_subgraphs->Length() <= sgindex) - { - assert(false); - return false; - } - - const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; - - auto name = subgraph->name(); - _subgraph_name = name ? name->c_str() : "(noname)"; - - _tensors = subgraph->tensors(); - _operators = subgraph->operators(); - _data_format = subgraph->data_format(); - - _inputs = as_index_vector(subgraph->inputs()); - _outputs = as_index_vector(subgraph->outputs()); - - return true; -} - -} // namespace circleread diff --git a/compiler/circledump/src/Read.h b/compiler/circledump/src/Read.h deleted file mode 100644 index 05b0e50..0000000 --- a/compiler/circledump/src/Read.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __CIRCLEREAD_READ_H__ -#define __CIRCLEREAD_READ_H__ - -#include - -#include -#include -#include - -namespace circleread -{ - -template std::vector as_index_vector(const flatbuffers::Vector *flat_array) -{ - if (flat_array == nullptr) - { - throw std::runtime_error("flat array is nullptr"); - } - - std::vector ret(flat_array->Length()); - for (uint32_t i = 0; i < flat_array->Length(); i++) - { - ret[i] = flat_array->Get(i); - } - return ret; -} - -/** - * @brief Loads Circle file and provides helpers to access attributes - */ -class Reader -{ -private: - using CircleSubGraphs_t = flatbuffers::Vector>; - using CircleBuffers_t = flatbuffers::Vector>; - using CircleTensors_t = flatbuffers::Vector>; - using CircleOperators_t = flatbuffers::Vector>; - using CircleMetadata_t = flatbuffers::Vector>; - using CircleSignatureDef_t = flatbuffers::Vector>; - -public: - Reader(const circle::Model *model); - - Reader() = delete; - -public: - uint32_t version() const { return _version; } - - const std::vector &opcodes() { return _op_codes; } - const CircleBuffers_t *buffers() { return _buffers; } - const CircleTensors_t *tensors() { return _tensors; } - const CircleOperators_t *operators() { return _operators; } - const std::vector &inputs() const { return _inputs; } - const std::vector &outputs() const { return _outputs; } - const circle::DataFormat &data_format() const { return _data_format; } - const CircleMetadata_t *metadata() const { return _metadata; } - const CircleSignatureDef_t *signature_defs() const { return _signature_defs; } - - uint32_t num_subgraph() const { return _subgraphs->Length(); } - - size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); - circle::BuiltinOperator builtin_code(const circle::Operator *op) const; - std::string opcode_name(const circle::Operator *op) const; - -public: - bool select_subgraph(uint32_t subgraph); - const std::string &subgraph_name(void) const { return _subgraph_name; } - uint32_t subgraph_index(void) const { return _subgraph_index; } - -private: - uint32_t _version; - - const CircleSubGraphs_t *_subgraphs{nullptr}; - const CircleBuffers_t *_buffers{nullptr}; - const CircleTensors_t *_tensors{nullptr}; - const CircleOperators_t *_operators{nullptr}; - const CircleMetadata_t *_metadata{nullptr}; - const CircleSignatureDef_t *_signature_defs{nullptr}; - - uint32_t _subgraph_index = 0; - std::string _subgraph_name; - std::vector _op_codes; - std::vector _inputs; - std::vector _outputs; - circle::DataFormat _data_format = circle::DataFormat::DataFormat_CHANNELS_FIRST; -}; - -} // namespace circleread - -#endif // __CIRCLEREAD_READ_H__ diff --git a/compiler/cli/CMakeLists.txt b/compiler/cli/CMakeLists.txt index 0fb99dd..4ab0ea2 100644 --- a/compiler/cli/CMakeLists.txt +++ b/compiler/cli/CMakeLists.txt @@ -10,5 +10,5 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest QUIET) -GTest_AddTEst(cli_test ${TESTS}) +GTest_AddTest(cli_test ${TESTS}) target_link_libraries(cli_test cli) diff --git a/compiler/coco/core/src/IR/Module.cpp b/compiler/coco/core/src/IR/Module.cpp index 420cf6f..0db7894 100644 --- a/compiler/coco/core/src/IR/Module.cpp +++ b/compiler/coco/core/src/IR/Module.cpp @@ -144,7 +144,7 @@ std::unique_ptr Module::create(void) m->_input = make_unique(); m->_output = make_unique(); - return std::move(m); + return m; } } // namespace coco diff --git a/compiler/coco/generic/src/IR/Data.cpp b/compiler/coco/generic/src/IR/Data.cpp index 5ab7069..361dcc2 100644 --- a/compiler/coco/generic/src/IR/Data.cpp +++ b/compiler/coco/generic/src/IR/Data.cpp @@ -209,8 +209,7 @@ std::unique_ptr Data::create(void) data->_blob = std::move(blob); data->_fp32 = std::move(fp32); - // GCC 4.9 tries to copy data (while GCC 6.X doesn't) - return std::move(data); + return data; } } // namespace coco diff --git a/compiler/common-artifacts/CMakeLists.txt b/compiler/common-artifacts/CMakeLists.txt index 404149c..34a3a4d 100644 --- a/compiler/common-artifacts/CMakeLists.txt +++ b/compiler/common-artifacts/CMakeLists.txt @@ -12,14 +12,6 @@ if(${PYTHON_VERSION_MINOR} LESS 8) return() endif() -# Create python virtual environment with tensorflow 2.6.0 -set(VIRTUALENV_OVERLAY_TF_2_6_0 "${NNCC_OVERLAY_DIR}/venv_2_6_0") - -add_custom_command( - OUTPUT ${VIRTUALENV_OVERLAY_TF_2_6_0} - COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_2_6_0} -) - # Create python virtual environment with tensorflow 2.8.0 set(VIRTUALENV_OVERLAY_TF_2_8_0 "${NNCC_OVERLAY_DIR}/venv_2_8_0") @@ -30,33 +22,36 @@ add_custom_command( # Create requirements.txt and install required pip packages set(REQUIREMENTS_FILE "requirements.txt") -set(REQUIREMENTS_OVERLAY_PATH_TF_2_6_0 "${VIRTUALENV_OVERLAY_TF_2_6_0}/${REQUIREMENTS_FILE}") set(REQUIREMENTS_OVERLAY_PATH_TF_2_8_0 "${VIRTUALENV_OVERLAY_TF_2_8_0}/${REQUIREMENTS_FILE}") -add_custom_command( - OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} - COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} - COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.6.0" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} - COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==1.12" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} - COMMAND ${VIRTUALENV_OVERLAY_TF_2_6_0}/bin/python3.8 -m pip --default-timeout=1000 install --upgrade pip setuptools - COMMAND ${VIRTUALENV_OVERLAY_TF_2_6_0}/bin/python3.8 -m pip --default-timeout=1000 install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} --upgrade - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_6_0} -) +set(PYTHON_OVERLAY python3) +if(PYTHON_EXECUTABLE MATCHES python3.8) + set(PYTHON_OVERLAY python3.8) +endif() +# NOTE when using behind proxy with self signed certificate, need to set '--trusted-host' options +set(PIP_OPTION_TRUSTED_HOST ) +if(DEFINED ENV{ONE_PIP_OPTION_TRUST_HOST}) + set(PIP_OPTION_TRUSTED_HOST --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --trusted-host pypi.org) +endif() + +# NOTE refer https://github.com/protocolbuffers/protobuf/issues/10051 +# TODO remove protobuf==3.20.1 when issue is resolved add_custom_command( OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.8.0" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==1.12" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/python3.8 -m pip --default-timeout=1000 install --upgrade pip setuptools - COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/python3.8 -m pip --default-timeout=1000 install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} --upgrade + COMMAND ${CMAKE_COMMAND} -E echo "protobuf==3.20.1" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} + COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + ${PIP_OPTION_TRUSTED_HOST} install --upgrade pip setuptools + COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + ${PIP_OPTION_TRUSTED_HOST} install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} --upgrade DEPENDS ${VIRTUALENV_OVERLAY_TF_2_8_0} ) add_custom_target(common_artifacts_python_deps ALL - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_6_0} - ${VIRTUALENV_OVERLAY_TF_2_8_0} - ${REQUIREMENTS_OVERLAY_PATH_TF_2_6_0} + DEPENDS ${VIRTUALENV_OVERLAY_TF_2_8_0} ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} ) @@ -246,7 +241,13 @@ foreach(RECIPE IN ITEMS ${RECIPES}) if(NOT DEFINED NO_OPTIMIZE_${RECIPE}) # Generate optimized .circle add_custom_command(OUTPUT ${OPT_CIRCLE_OUTPUT_PATH} - COMMAND $ --O1 ${CIRCLE_OUTPUT_PATH} ${OPT_CIRCLE_OUTPUT_PATH} + # NOTE --resolve_customop_add is just to added for old -O1, no particular meaning + # --fold_dequantize is added to fold Tensor(FLOAT16) + DEQUANTIZE (Net_Dequantize_Add) + # model. FLOAT16 in general is NOT supported but only Tensor(FLOAT16) + DEQUANTIZE + # sequence accepted as folded to Tensor(FLOAT32). + # TODO revise giving options from the list file + COMMAND $ --resolve_customop_add --fold_dequantize --fold_densify + ${CIRCLE_OUTPUT_PATH} ${OPT_CIRCLE_OUTPUT_PATH} DEPENDS $ ${CIRCLE_OUTPUT_PATH} COMMENT "Generate ${OPT_CIRCLE_FILE}" ) diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst index 92b07fd..2275a42 100644 --- a/compiler/common-artifacts/exclude.lst +++ b/compiler/common-artifacts/exclude.lst @@ -32,6 +32,7 @@ tcgenerate(BroadcastTo_000) # luci-interpreter doesn't support custom operator tcgenerate(Ceil_000) tcgenerate(Conv2D_003) # runtime doesn't support dilation tcgenerate(Cos_000) +tcgenerate(Densify_000) # luci-interpreter doesn't support tcgenerate(DepthwiseConv2D_001) # runtime doesn't support dilation tcgenerate(DepthwiseConv2D_003) # runtime doesn't support dilation tcgenerate(DepthwiseConv2D_U8_001) # luci-interpreter doesn't support channel-wise quantization yet @@ -67,6 +68,8 @@ tcgenerate(Neg_000) tcgenerate(Net_BroadcastTo_AddV2_001) # luci-interpreter doesn't support custom operator tcgenerate(Net_Conv_FakeQuant_000) # luci-interpreter doesn't support FakeQuant yet tcgenerate(Net_Dangle_001) +tcgenerate(Net_Densify_Add_000) # luci-interpreter doesn't support Densify yet +tcgenerate(Net_Densify_Dequantize_Add_000) # luci-interpreter doesn't support Densify/Dequantize yet tcgenerate(Net_Gather_SparseToDense_AddV2_000) # luci-interpreter doesn't support custom operator tcgenerate(Net_ZeroDim_001) # luci-interpreter doesn't support zero dim tcgenerate(OneHot_000) diff --git a/compiler/common-artifacts/src/TestDataGenerator.cpp b/compiler/common-artifacts/src/TestDataGenerator.cpp index 33cecbb..7481050 100644 --- a/compiler/common-artifacts/src/TestDataGenerator.cpp +++ b/compiler/common-artifacts/src/TestDataGenerator.cpp @@ -142,23 +142,15 @@ void fill_random_range(void *data, uint32_t size, loco::DataType dtype, int32_t int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test"); - arser.add_argument("--input_data") - .required(true) - .nargs(1) - .type(arser::DataType::STR) - .help("Path to generate input data h5 file"); + arser.add_argument("circle").help("Circle file you want to test"); + arser.add_argument("--input_data").required(true).help("Path to generate input data h5 file"); arser.add_argument("--expected_data") .required(true) - .nargs(1) - .type(arser::DataType::STR) .help("Path to generate expected data h5 file"); arser.add_argument("--fixed_seed") - .required(false) .nargs(0) .help("Put a fixed seed into the random number generator"); arser.add_argument("--input_range") - .required(false) .nargs(3) .type(arser::DataType::STR_VEC) .help("Set random number range [min max] for the input as 'name min max'"); diff --git a/compiler/crew/CMakeLists.txt b/compiler/crew/CMakeLists.txt index 1824d86..45cda75 100644 --- a/compiler/crew/CMakeLists.txt +++ b/compiler/crew/CMakeLists.txt @@ -12,9 +12,12 @@ if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) +configure_file("src/test_read_semicolon.ini" "test_read_semicolon.ini" COPYONLY) + nnas_find_package(GTest REQUIRED) GTest_AddTest(crew_test ${TESTS}) target_include_directories(crew_test PRIVATE src) target_link_libraries(crew_test nncc_common) target_link_libraries(crew_test crew) +target_link_libraries(crew_test foder) diff --git a/compiler/crew/src/PConfigIni.cpp b/compiler/crew/src/PConfigIni.cpp index f0e3e8e..5177843 100644 --- a/compiler/crew/src/PConfigIni.cpp +++ b/compiler/crew/src/PConfigIni.cpp @@ -26,10 +26,36 @@ #include #include #include +#include namespace crew { +namespace +{ + +std::string filter_escape(const std::string &source) +{ + std::string key = source; + + // if key is surrounded with quotation + // TODO for quotation + + // if key has '\\' + ';', remove '\\' + auto pos = key.find("\\;"); + while (pos != std::string::npos) + { + auto k1 = key.substr(0, pos); + auto k2 = key.substr(pos + 1); + key = k1 + k2; + pos = key.find("\\;"); + } + + return key; +} + +} // namespace + Sections read_ini(const char *data, size_t length) { assert(data != nullptr); @@ -84,6 +110,7 @@ Sections read_ini(const char *data, size_t length) { auto key = string_line.substr(0, pos); auto val = string_line.substr(pos + 1); + key = filter_escape(key); section.items.emplace(key, val); } } @@ -107,11 +134,53 @@ Sections read_ini(const std::string &path) return read_ini(ini_data.data(), ini_data.size()); } +namespace +{ + +void replace(std::string &source, const std::string &token, const std::string &replace) +{ + size_t pos = 0; + while ((pos = source.find(token, pos)) != std::string::npos) + { + source.replace(pos, token.length(), replace); + pos += replace.length(); // Handles the case where 'replace' is a substring of 'token' + } +} + +Sections insert_escape(const Sections &inputs) +{ + Sections sections; + + // for all section in sections; + // if key has ';' then replace with '\;' + for (auto &input : inputs) + { + Section section; + section.name = input.name; + + for (auto &item : input.items) + { + auto key = item.first; + auto value = item.second; + + replace(key, ";", "\\;"); + section.items[key] = value; + } + sections.push_back(section); + } + + return sections; +} + +} // namespace + void write_ini(std::ostream &os, const Sections §ions) { std::stringstream ss; - ss << sections; + auto processed = insert_escape(sections); + + ss << processed; std::string strss = ss.str(); diff --git a/compiler/crew/src/PConfigIni.test.cpp b/compiler/crew/src/PConfigIni.test.cpp index bdd2ccc..c062c69 100644 --- a/compiler/crew/src/PConfigIni.test.cpp +++ b/compiler/crew/src/PConfigIni.test.cpp @@ -17,12 +17,14 @@ #include "crew/PConfigIni.h" #include "crew/PConfigIniDump.h" +#include + #include #include #include -TEST(ConfigIniTest, read_ini_non_exist_file) +TEST(ConfigIniTest, read_ini_non_exist_file_NEG) { EXPECT_THROW(crew::read_ini("/hello/world/not_a_file"), std::runtime_error); } @@ -85,3 +87,60 @@ TEST(ConfigIniTest, write_ini_file_error_NEG) crew::Sections sections; EXPECT_THROW(crew::write_ini("/abc/def/cannot_access", sections), std::runtime_error); } + +TEST(ConfigIniTest, read_file_escape_semicolon) +{ + auto sections = crew::read_ini("test_read_semicolon.ini"); + ASSERT_EQ(1UL, sections.size()); + + auto its = sections.begin(); + ASSERT_NE(sections.end(), its); + EXPECT_TRUE("hello" == its->name); + ASSERT_EQ(1UL, its->items.size()); + + auto it = its->items.begin(); + ASSERT_NE(its->items.end(), it); + + EXPECT_TRUE("keya;keyb;keyc;keyd" == it->first); + EXPECT_TRUE("world" == it->second); +} + +TEST(ConfigIniTest, write_file_escape_semicolon) +{ + std::string path("test_write_semicolon.ini"); + + // save key with ';' + { + crew::Sections sections; + crew::Section hello; + hello.name = "hello"; + hello.items["keya;keyb;keyc;keyd"] = "world"; + sections.push_back(hello); + crew::write_ini(path, sections); + } + + // load the file and check if there is '\\' + std::string strbuffer; + { + foder::FileLoader file_loader{path}; + auto ini_data = file_loader.load(); + + auto buffer = std::vector(); + auto length = ini_data.size(); + buffer.reserve(length + 1); + + char *pbuffer = buffer.data(); + memcpy(pbuffer, ini_data.data(), length); + *(pbuffer + length) = 0; + + strbuffer = pbuffer; + } + int32_t count = 0; + size_t pos = 0; + while ((pos = strbuffer.find("\\;", pos)) != std::string::npos) + { + count++; + pos++; + } + EXPECT_TRUE(count == 3); +} diff --git a/compiler/crew/src/test_read_semicolon.ini b/compiler/crew/src/test_read_semicolon.ini new file mode 100644 index 0000000..d966fb7 --- /dev/null +++ b/compiler/crew/src/test_read_semicolon.ini @@ -0,0 +1,2 @@ +[hello] +keya\;keyb\;keyc\;keyd=world diff --git a/compiler/enco/core/src/CppGen/Host.cpp b/compiler/enco/core/src/CppGen/Host.cpp index 7f94562..63baf0b 100644 --- a/compiler/enco/core/src/CppGen/Host.cpp +++ b/compiler/enco/core/src/CppGen/Host.cpp @@ -299,7 +299,7 @@ std::unique_ptr HostBlockCompiler::compile(const coco::Block res->append(ins->accept(prn)); } - return std::move(res); + return res; } } // namespace enco diff --git a/compiler/enco/core/src/CppGen/Subnet.cpp b/compiler/enco/core/src/CppGen/Subnet.cpp index 599b079..3fc14ed 100644 --- a/compiler/enco/core/src/CppGen/Subnet.cpp +++ b/compiler/enco/core/src/CppGen/Subnet.cpp @@ -373,7 +373,7 @@ std::unique_ptr SubnetStructBuilder::build(const ANNBinder *binder // Finalize compilation res->ctor()->append("ANeuralNetworksCompilation_finish(", cname, ");"); - return std::move(res); + return res; } std::unique_ptr SubnetBlockCompiler::compile(const ANNBinder *binder) const @@ -415,7 +415,7 @@ std::unique_ptr SubnetBlockCompiler::compile(const ANNBinder res->append("ANeuralNetworksExecution_free(execution);"); - return std::move(res); + return res; } } // namespace enco diff --git a/compiler/enco/core/src/Transforms/Split.cpp b/compiler/enco/core/src/Transforms/Split.cpp index 714c27a..4bb21b0 100644 --- a/compiler/enco/core/src/Transforms/Split.cpp +++ b/compiler/enco/core/src/Transforms/Split.cpp @@ -656,7 +656,7 @@ public: app->ofm(ofm); app->ker(ker); - return std::move(app); + return app; } else { @@ -676,7 +676,7 @@ public: app->ofm(ofm); app->ker(ker); - return std::move(app); + return app; } } } @@ -704,7 +704,7 @@ public: app->right(right); app->out(out); - return std::move(app); + return app; } } else if (auto op = eval->op()->asMul()) @@ -731,7 +731,7 @@ public: app->right(right); app->out(out); - return std::move(app); + return app; } } else if (auto op = eval->op()->asPadF()) @@ -754,7 +754,7 @@ public: app->ifm(ifm); app->ofm(ofm); - return std::move(app); + return app; } } else if (auto maxpool = eval->op()->asMaxPool2D()) @@ -779,7 +779,7 @@ public: app->ifm(ifm); app->ofm(ofm); - return std::move(app); + return app; } } else if (auto avgpool = eval->op()->asAvgPool2D()) @@ -808,7 +808,7 @@ public: app->ifm(ifm); app->ofm(ofm); - return std::move(app); + return app; } } } @@ -831,7 +831,7 @@ public: app->ifm(ifm); app->ofm(ofm); - return std::move(app); + return app; } } else if (auto relu6 = eval->op()->asReLU6()) @@ -853,7 +853,7 @@ public: app->ifm(ifm); app->ofm(ofm); - return std::move(app); + return app; } } else if (auto op = eval->op()->asConcatF()) @@ -880,7 +880,7 @@ public: app->right(right); app->out(out); - return std::move(app); + return app; } } else if (auto op = eval->op()->asSub()) @@ -907,7 +907,7 @@ public: app->right(right); app->out(out); - return std::move(app); + return app; } } else if (auto op = eval->op()->asDiv()) @@ -934,7 +934,7 @@ public: app->right(right); app->out(out); - return std::move(app); + return app; } } @@ -967,7 +967,7 @@ std::unique_ptr make_appender(coco::Instr *ins) app->left(depth_concat->fst()->asFeature()); app->right(depth_concat->snd()->asFeature()); - return std::move(app); + return app; } // Build ANN IR from ANNConv2D instruction @@ -986,7 +986,7 @@ std::unique_ptr make_appender(coco::Instr *ins) app->ker(conv2d->ker()->asKernel()); app->bias(coco::safe_cast(conv2d->bias())); - return std::move(app); + return app; } return nullptr; diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp index aa2cad7..32ad443 100644 --- a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp +++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp @@ -25,6 +25,8 @@ #include #include +#include + namespace exo { diff --git a/compiler/kuma/src/IntervalSet.h b/compiler/kuma/src/IntervalSet.h index 3b6c5f6..1e26581 100644 --- a/compiler/kuma/src/IntervalSet.h +++ b/compiler/kuma/src/IntervalSet.h @@ -17,6 +17,7 @@ #ifndef __KUMA_DETAILS_LIVE_INTERVAL_SET_H__ #define __KUMA_DETAILS_LIVE_INTERVAL_SET_H__ +#include #include namespace kuma diff --git a/compiler/loco/include/loco/IR/DataTypeTraits.h b/compiler/loco/include/loco/IR/DataTypeTraits.h index 1f78c9f..6be46c3 100644 --- a/compiler/loco/include/loco/IR/DataTypeTraits.h +++ b/compiler/loco/include/loco/IR/DataTypeTraits.h @@ -83,6 +83,13 @@ template <> struct DataTypeImpl using Type = uint64_t; }; +template <> struct DataTypeImpl +{ + // float16 type with 16bit value, encoded with help of FP16 library + // https://github.com/Maratyszcza/FP16/ + using Type = uint16_t; +}; + template <> struct DataTypeImpl { // Use C++ float type for IEEE 32-bit floating-point numbers @@ -132,6 +139,8 @@ inline uint32_t size(DataType data_type) return sizeof(DataTypeImpl::Type); case DataType::U64: return sizeof(DataTypeImpl::Type); + case DataType::FLOAT16: + return sizeof(DataTypeImpl::Type); case DataType::FLOAT32: return sizeof(DataTypeImpl::Type); case DataType::FLOAT64: diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp index 500f086..40ddb13 100644 --- a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp +++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp @@ -122,9 +122,6 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) { using namespace loco; - auto encoder = encode_node->encoder(); - assert(encoder != nullptr); - auto decode_node = dynamic_cast(encode_node->input()); if (decode_node == nullptr) { @@ -132,6 +129,9 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) } assert(decode_node->input() != nullptr); + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + auto decoder = decode_node->decoder(); assert(decoder != nullptr); @@ -302,9 +302,6 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) { using namespace loco; - auto encoder = encode_node->encoder(); - assert(encoder != nullptr); - auto decode_node = dynamic_cast(encode_node->input()); if (decode_node == nullptr) { @@ -312,6 +309,9 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) } assert(decode_node->input() != nullptr); + auto encoder = encode_node->encoder(); + assert(encoder != nullptr); + auto decoder = decode_node->decoder(); assert(decoder != nullptr); diff --git a/compiler/luci-eval-driver/src/EvalDriver.cpp b/compiler/luci-eval-driver/src/EvalDriver.cpp index 4762cff..0ed3543 100644 --- a/compiler/luci-eval-driver/src/EvalDriver.cpp +++ b/compiler/luci-eval-driver/src/EvalDriver.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include #include #include @@ -47,18 +47,6 @@ void writeDataToFile(const std::string &filename, const char *data, size_t data_ } } -std::unique_ptr importModel(const std::string &filename) -{ - std::ifstream fs(filename, std::ifstream::binary); - if (fs.fail()) - { - throw std::runtime_error("Cannot open model file \"" + filename + "\".\n"); - } - std::vector model_data((std::istreambuf_iterator(fs)), - std::istreambuf_iterator()); - return luci::Importer().importModule(circle::GetModel(model_data.data())); -} - template size_t getTensorSize(const NodeT *node) { uint32_t tensor_size = loco::size(node->dtype()); @@ -91,7 +79,8 @@ int entry(int argc, char **argv) const char *output_file = argv[4]; // Load model from the file - std::unique_ptr module = importModel(filename); + luci::ImporterEx importer; + std::unique_ptr module = importer.importVerifyModule(filename); if (module == nullptr) { std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl; diff --git a/compiler/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst b/compiler/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst index d134a6b..f0df58d 100644 --- a/compiler/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst +++ b/compiler/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst @@ -12,6 +12,7 @@ REGISTER_KERNEL(Div) REGISTER_KERNEL(Elu) REGISTER_KERNEL(Exp) REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) REGISTER_KERNEL(Floor) REGISTER_KERNEL(FloorDiv) REGISTER_KERNEL(Equal) @@ -44,6 +45,7 @@ REGISTER_KERNEL(Reshape) REGISTER_KERNEL(ResizeBilinear) REGISTER_KERNEL(ResizeNearestNeighbor) REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) REGISTER_KERNEL(Softmax) REGISTER_KERNEL(SpaceToBatchND) REGISTER_KERNEL(SpaceToDepth) diff --git a/compiler/luci-interpreter/pal/cmsisnn/PALDequantize.h b/compiler/luci-interpreter/pal/cmsisnn/PALDequantize.h index 15ff032..efa6b16 100644 --- a/compiler/luci-interpreter/pal/cmsisnn/PALDequantize.h +++ b/compiler/luci-interpreter/pal/cmsisnn/PALDequantize.h @@ -18,7 +18,7 @@ #define LUCI_INTERPRETER_PAL_DEQUANTIZE_H #include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" -#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "PALreference_ops.h" namespace luci_interpreter_pal { diff --git a/compiler/luci-interpreter/pal/cmsisnn/PALQuantize.h b/compiler/luci-interpreter/pal/cmsisnn/PALQuantize.h index 6046789..effb85d 100644 --- a/compiler/luci-interpreter/pal/cmsisnn/PALQuantize.h +++ b/compiler/luci-interpreter/pal/cmsisnn/PALQuantize.h @@ -17,7 +17,7 @@ #ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H #define LUCI_INTERPRETER_PAL_QUANTIZE_H -#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "PALreference_ops.h" namespace luci_interpreter_pal { diff --git a/compiler/luci-interpreter/pal/cmsisnn/PALreference_ops.h b/compiler/luci-interpreter/pal/cmsisnn/PALreference_ops.h new file mode 100644 index 0000000..813b1ec --- /dev/null +++ b/compiler/luci-interpreter/pal/cmsisnn/PALreference_ops.h @@ -0,0 +1,1568 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +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. +==============================================================================*/ +#ifndef LUCI_INTERPRETER_PAL_REFERENCE_OPS_H +#define LUCI_INTERPRETER_PAL_REFERENCE_OPS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "third_party/eigen3/Eigen/Core" +#include "fixedpoint/fixedpoint.h" +#include "ruy/profiler/instrumentation.h" // from @ruy +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/add.h" +#include "tensorflow/lite/kernels/internal/reference/add_n.h" +#include "tensorflow/lite/kernels/internal/reference/arg_min_max.h" +#include "tensorflow/lite/kernels/internal/reference/batch_matmul.h" +#include "tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h" +#include "tensorflow/lite/kernels/internal/reference/binary_function.h" +#include "tensorflow/lite/kernels/internal/reference/cast.h" +#include "tensorflow/lite/kernels/internal/reference/ceil.h" +#include "tensorflow/lite/kernels/internal/reference/comparisons.h" +#include "tensorflow/lite/kernels/internal/reference/concatenation.h" +#include "tensorflow/lite/kernels/internal/reference/conv.h" +#include "tensorflow/lite/kernels/internal/reference/depth_to_space.h" +#include "tensorflow/lite/kernels/internal/reference/dequantize.h" +#include "tensorflow/lite/kernels/internal/reference/div.h" +#include "tensorflow/lite/kernels/internal/reference/elu.h" +#include "tensorflow/lite/kernels/internal/reference/exp.h" +#include "tensorflow/lite/kernels/internal/reference/fill.h" +#include "tensorflow/lite/kernels/internal/reference/floor.h" +#include "tensorflow/lite/kernels/internal/reference/floor_div.h" +#include "tensorflow/lite/kernels/internal/reference/floor_mod.h" +#include "tensorflow/lite/kernels/internal/reference/fully_connected.h" +#include "tensorflow/lite/kernels/internal/reference/gather.h" +#include "tensorflow/lite/kernels/internal/reference/hard_swish.h" +#include "tensorflow/lite/kernels/internal/reference/l2normalization.h" +#include "tensorflow/lite/kernels/internal/reference/leaky_relu.h" +#include "tensorflow/lite/kernels/internal/reference/log_softmax.h" +#include "tensorflow/lite/kernels/internal/reference/logistic.h" +#include "tensorflow/lite/kernels/internal/reference/maximum_minimum.h" +#include "tensorflow/lite/kernels/internal/reference/mul.h" +#include "tensorflow/lite/kernels/internal/reference/neg.h" +#include "tensorflow/lite/kernels/internal/reference/pad.h" +#include "tensorflow/lite/kernels/internal/reference/pooling.h" +#include "tensorflow/lite/kernels/internal/reference/prelu.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/reference/quantize.h" +#include "tensorflow/lite/kernels/internal/reference/reduce.h" +#include "tensorflow/lite/kernels/internal/reference/requantize.h" +#include "tensorflow/lite/kernels/internal/reference/resize_bilinear.h" +#include "tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h" +#include "tensorflow/lite/kernels/internal/reference/round.h" +#include "tensorflow/lite/kernels/internal/reference/softmax.h" +#include "tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h" +#include "tensorflow/lite/kernels/internal/reference/space_to_depth.h" +#include "tensorflow/lite/kernels/internal/reference/strided_slice.h" +#include "tensorflow/lite/kernels/internal/reference/string_comparisons.h" +#include "tensorflow/lite/kernels/internal/reference/sub.h" +#include "tensorflow/lite/kernels/internal/reference/tanh.h" +#include "tensorflow/lite/kernels/internal/reference/transpose.h" +#include "tensorflow/lite/kernels/internal/reference/transpose_conv.h" +#include "tensorflow/lite/kernels/internal/strided_slice_logic.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/internal/types.h" +namespace tflite +{ + +namespace reference_ops +{ + +template +inline void Relu(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T lower = 0; + const T clamped = val < lower ? lower : val; + output_data[i] = clamped; + } +} + +template +inline void Relu1(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Relu1 (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T upper = 1; + const T lower = -1; + const T clamped = val > upper ? upper : val < lower ? lower : val; + output_data[i] = clamped; + } +} + +inline void Relu6(const RuntimeShape &input_shape, const float *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + ruy::profiler::ScopeLabel label("Relu6 (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const float val = input_data[i]; + const float upper = 6; + const float lower = 0; + const float clamped = val > upper ? upper : val < lower ? lower : val; + output_data[i] = clamped; + } +} + +template +inline void ReluX(const tflite::ReluParams ¶ms, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const int32 val = static_cast(input_data[i]); + int32 clamped = params.output_offset + MultiplyByQuantizedMultiplier(val - params.input_offset, + params.output_multiplier, + params.output_shift); + clamped = std::max(params.quantized_activation_min, clamped); + clamped = std::min(params.quantized_activation_max, clamped); + output_data[i] = static_cast(clamped); + } +} + +template +inline void ReluX(const tflite::ActivationParams ¶ms, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + const T max_value = params.quantized_activation_max; + const T min_value = params.quantized_activation_min; + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T clamped = val > max_value ? max_value : val < min_value ? min_value : val; + output_data[i] = clamped; + } +} + +// TODO(jiawen): We can implement BroadcastMul on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +inline void BroadcastMulFivefold(const ArithmeticParams &unswitched_params, + const RuntimeShape &unswitched_input1_shape, + const uint8 *unswitched_input1_data, + const RuntimeShape &unswitched_input2_shape, + const uint8 *unswitched_input2_data, + const RuntimeShape &output_shape, uint8 *output_data) +{ + ArithmeticParams switched_params = unswitched_params; + switched_params.input1_offset = unswitched_params.input2_offset; + switched_params.input2_offset = unswitched_params.input1_offset; + + const bool use_unswitched = unswitched_params.broadcast_category == + tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast; + + const ArithmeticParams ¶ms = use_unswitched ? unswitched_params : switched_params; + const uint8 *input1_data = use_unswitched ? unswitched_input1_data : unswitched_input2_data; + const uint8 *input2_data = use_unswitched ? unswitched_input2_data : unswitched_input1_data; + + // Fivefold nested loops. The second input resets its position for each + // iteration of the second loop. The first input resets its position at the + // beginning of the fourth loop. The innermost loop is an elementwise Mul of + // sections of the arrays. + uint8 *output_data_ptr = output_data; + const uint8 *input1_data_ptr = input1_data; + const uint8 *input2_data_reset = input2_data; + int y0 = params.broadcast_shape[0]; + int y1 = params.broadcast_shape[1]; + int y2 = params.broadcast_shape[2]; + int y3 = params.broadcast_shape[3]; + int y4 = params.broadcast_shape[4]; + for (int i0 = 0; i0 < y0; ++i0) + { + const uint8 *input2_data_ptr; + for (int i1 = 0; i1 < y1; ++i1) + { + input2_data_ptr = input2_data_reset; + for (int i2 = 0; i2 < y2; ++i2) + { + for (int i3 = 0; i3 < y3; ++i3) + { + MulElementwise(y4, params, input1_data_ptr, input2_data_ptr, output_data_ptr); + input2_data_ptr += y4; + output_data_ptr += y4; + } + input1_data_ptr += y4; + } + } + input2_data_reset = input2_data_ptr; + } +} + +inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16 *input1_data, const RuntimeShape &input2_shape, + const int16 *input2_data, const RuntimeShape &output_shape, int16 *output_data) +{ + ruy::profiler::ScopeLabel label("Mul/Int16"); + + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + + for (int i = 0; i < flat_size; i++) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + + F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); + output_data[i] = unclamped_result.raw(); + } +} + +inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16 *input1_data, const RuntimeShape &input2_shape, + const int16 *input2_data, const RuntimeShape &output_shape, uint8 *output_data) +{ + ruy::profiler::ScopeLabel label("Mul/Int16Uint8"); + int32 output_offset = params.output_offset; + int32 output_activation_min = params.quantized_activation_min; + int32 output_activation_max = params.quantized_activation_max; + TFLITE_DCHECK_LE(output_activation_min, output_activation_max); + + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + + for (int i = 0; i < flat_size; i++) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + + F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); + int16 rescaled_result = gemmlowp::RoundingDivideByPOT(unclamped_result.raw(), 8); + int16 clamped_result = std::min(output_activation_max - output_offset, rescaled_result); + clamped_result = std::max(output_activation_min - output_offset, clamped_result); + output_data[i] = output_offset + clamped_result; + } +} + +inline void Sub16(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16_t *input1_data, const RuntimeShape &input2_shape, + const int16_t *input2_data, const RuntimeShape &output_shape, + int16_t *output_data) +{ + ruy::profiler::ScopeLabel label("Sub/Int16"); + const int input1_shift = params.input1_shift; + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + const int16 output_activation_min = params.quantized_activation_min; + const int16 output_activation_max = params.quantized_activation_max; + + TFLITE_DCHECK(input1_shift == 0 || params.input2_shift == 0); + TFLITE_DCHECK_LE(input1_shift, 0); + TFLITE_DCHECK_LE(params.input2_shift, 0); + const int16 *not_shift_input = input1_shift == 0 ? input1_data : input2_data; + const int16 *shift_input = input1_shift == 0 ? input2_data : input1_data; + const int input_right_shift = input1_shift == 0 ? -params.input2_shift : -input1_shift; + + if (input1_shift == 0) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + for (int i = 0; i < flat_size; ++i) + { + F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); + F0 scaled_input = + F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); + F0 result = SaturatingSub(input_ready_scaled, scaled_input); + const int16 raw_output = result.raw(); + const int16 clamped_output = + std::min(output_activation_max, std::max(output_activation_min, raw_output)); + output_data[i] = clamped_output; + } + } + else + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + for (int i = 0; i < flat_size; ++i) + { + F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); + F0 scaled_input = + F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); + F0 result = SaturatingSub(scaled_input, input_ready_scaled); + const int16 raw_output = result.raw(); + const int16 clamped_output = + std::min(output_activation_max, std::max(output_activation_min, raw_output)); + output_data[i] = clamped_output; + } + } +} + +template +void Pack(const PackParams ¶ms, const RuntimeShape *const *input_shapes, + const Scalar *const *input_data, const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("Pack"); + const int dimensions = output_shape.DimensionsCount(); + int axis = params.axis; + int inputs_count = params.inputs_count; + + int outer_size = 1; + for (int i = 0; i < axis; i++) + { + outer_size *= output_shape.Dims(i); + } + int copy_size = 1; + for (int i = params.axis + 1; i < dimensions; i++) + { + copy_size *= output_shape.Dims(i); + } + TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); + + for (int i = 0; i < inputs_count; ++i) + { + for (int k = 0; k < outer_size; k++) + { + const Scalar *input_ptr = input_data[i] + copy_size * k; + int loc = k * inputs_count * copy_size + i * copy_size; + memcpy(output_data + loc, input_ptr, copy_size * sizeof(Scalar)); + } + } +} + +template +void Unpack(const UnpackParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *const *output_datas) +{ + ruy::profiler::ScopeLabel label("Unpack"); + const int dimensions = input_shape.DimensionsCount(); + const int outputs_count = params.num_split; + + int outer_size = 1; + int axis = params.axis; + if (axis < 0) + { + axis += dimensions; + } + TFLITE_DCHECK_GE(axis, 0); + TFLITE_DCHECK_LT(axis, dimensions); + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + int copy_size = 1; + for (int i = axis + 1; i < dimensions; ++i) + { + copy_size *= input_shape.Dims(i); + } + TFLITE_DCHECK_EQ(output_shape.FlatSize(), copy_size * outer_size); + + for (int i = 0; i < outputs_count; ++i) + { + for (int k = 0; k < outer_size; k++) + { + Scalar *output_ptr = output_datas[i] + copy_size * k; + int loc = k * outputs_count * copy_size + i * copy_size; + memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); + } + } +} + +template +void PackWithScaling(const PackParams ¶ms, const RuntimeShape *const *input_shapes, + const uint8 *const *input_data, const RuntimeShape &output_shape, + uint8 *output_data) +{ + ruy::profiler::ScopeLabel label("PackWithScaling"); + const int dimensions = output_shape.DimensionsCount(); + int axis = params.axis; + const int32 *input_zeropoint = params.input_zeropoint; + const float *input_scale = params.input_scale; + int inputs_count = params.inputs_count; + const int32 output_zeropoint = params.output_zeropoint; + const float output_scale = params.output_scale; + + int outer_size = 1; + for (int i = 0; i < axis; i++) + { + outer_size *= output_shape.Dims(i); + } + int copy_size = 1; + for (int i = axis + 1; i < dimensions; i++) + { + copy_size *= output_shape.Dims(i); + } + TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); + + Scalar *output_ptr = output_data; + const float inverse_output_scale = 1.f / output_scale; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < inputs_count; ++i) + { + if (input_zeropoint[i] == output_zeropoint && input_scale[i] == output_scale) + { + memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); + } + else + { + assert(false); + const float scale = input_scale[i] * inverse_output_scale; + const float bias = -input_zeropoint[i] * scale; + auto input_ptr = input_data[i]; + for (int j = 0; j < copy_size; ++j) + { + const int value = + static_cast(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; + output_ptr[j] = static_cast(std::max(std::min(255, value), 0)); + } + } + output_ptr += copy_size; + } + } +} + +template +void DepthConcatenation(const ConcatenationParams ¶ms, const RuntimeShape *const *input_shapes, + const Scalar *const *input_data, const RuntimeShape &output_shape, + Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("DepthConcatenation"); + auto params_copy = params; + params_copy.axis = 3; + Concatenation(params_copy, input_shapes, input_data, output_shape, output_data); +} + +inline void LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, + const float *input_data, const RuntimeShape &unextended_prev_activ_shape, + const float *prev_activ_data, const RuntimeShape &weights_shape, + const float *weights_data, const RuntimeShape &unextended_bias_shape, + const float *bias_data, const RuntimeShape &unextended_prev_state_shape, + const float *prev_state_data, + const RuntimeShape &unextended_output_state_shape, float *output_state_data, + const RuntimeShape &unextended_output_activ_shape, float *output_activ_data, + const RuntimeShape &unextended_concat_temp_shape, float *concat_temp_data, + const RuntimeShape &unextended_activ_temp_shape, float *activ_temp_data) +{ + TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); + const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); + const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); + const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); + const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); + const RuntimeShape output_state_shape = + RuntimeShape::ExtendedShape(4, unextended_output_state_shape); + const RuntimeShape output_activ_shape = + RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); + const RuntimeShape concat_temp_shape = + RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); + const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); + TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); + + const int weights_dim_count = weights_shape.DimensionsCount(); + const int batches = MatchingDim(input_shape, 0, prev_activ_shape, 0, prev_state_shape, 0, + output_state_shape, 0, output_activ_shape, 0); + const int height = MatchingDim(input_shape, 1, prev_activ_shape, 1, prev_state_shape, 1, + output_state_shape, 1, output_activ_shape, 1); + const int width = MatchingDim(input_shape, 2, prev_activ_shape, 2, prev_state_shape, 2, + output_state_shape, 2, output_activ_shape, 2); + const int input_depth = input_shape.Dims(3); + const int prev_activ_depth = prev_activ_shape.Dims(3); + const int total_input_depth = prev_activ_depth + input_depth; + TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); + TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); + const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); + TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); + TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); + const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, + 3, output_activ_shape, 3); + TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); + + // Concatenate prev_activ and input data together + std::vector concat_input_arrays_data; + std::vector concat_input_arrays_shapes; + concat_input_arrays_data.push_back(input_data); + concat_input_arrays_data.push_back(prev_activ_data); + concat_input_arrays_shapes.push_back(&input_shape); + concat_input_arrays_shapes.push_back(&prev_activ_shape); + tflite::ConcatenationParams concat_params; + concat_params.axis = 3; + concat_params.inputs_count = concat_input_arrays_data.size(); + Concatenation(concat_params, &(concat_input_arrays_shapes[0]), &(concat_input_arrays_data[0]), + concat_temp_shape, concat_temp_data); + + // Fully connected + tflite::FullyConnectedParams fc_params; + fc_params.float_activation_min = std::numeric_limits::lowest(); + fc_params.float_activation_max = std::numeric_limits::max(); + FullyConnected(fc_params, concat_temp_shape, concat_temp_data, weights_shape, weights_data, + bias_shape, bias_data, activ_temp_shape, activ_temp_data); + + // Memory state update (the LSTM "guts") + for (int b = 0; b < batches; ++b) + { + for (int w = 0; w < width; ++w) + { + for (int h = 0; h < height; ++h) + { + for (int c = 0; c < output_depth; ++c) + { + const float input_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 0 * output_depth + c)])); + const float new_input = + std::tanh(activ_temp_data[Offset(activ_temp_shape, b, h, w, 1 * output_depth + c)]); + const float forget_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 2 * output_depth + c)])); + const float output_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 3 * output_depth + c)])); + const float new_state = + input_gate * new_input + + forget_gate * prev_state_data[Offset(prev_state_shape, b, h, w, c)]; + output_state_data[Offset(output_state_shape, b, h, w, c)] = new_state; + output_activ_data[Offset(output_activ_shape, b, h, w, c)] = + output_gate * std::tanh(new_state); + } + } + } + } +} + +// Quantized LSTM cell implementation. +// The quantization of the input, output arrays is as follows: +// - The input activations are quantized as uint8 on the interval +// [-1, 127/128]. +// The rationale for that is that is the natural interval for output +// activations (see next point) and these need to be concatenated together. +// We could accommodate different ranges by re-scaling, but we empirically +// found that setting the input activations range to be [-1, 127/128] in the +// first place, removing the need for re-scaling, greatly improves accuracy. +// - The output activations are quantized as uint8 on the interval +// [-1, 127/128]. +// The rationale for that is that the definition of a LSTM cell makes them +// intrinsically constrained in [-1, 1]; tweaking that to [-1, 127/128] +// makes for simpler, more accurate fixed-point arithmetic. +// - The output-at-previous-timestep state array is obviously quantized as +// the output activations. +// - The internal LSTM memory (not the output-at-previous-timestep, the other +// internal state array) is int16-quantized and may use any power-of-two, +// symmetric range i.e. [-2^N, 2^N * 32767/32768] for any N, which we call +// StateIntegerBits below, see the below discussion of that template +// parameter ("The StateIntegerBits template parameter"). +// - The output of the internal fully-connected node is int16-quantized +// on the interval [-8, 8 * 32767/32768], the rationale for which is +// explained just below ("Why [-8, 8] for fully-connected output?"). +// +// +// === The StateIntegerBits template parameter === +// +// The StateIntegerBits template parameter controls the fixed-point format used +// to represent the internal memory of the LSTM cell (not the +// output-at-previous-timestep, the other internal state array). It's currently +// a template parameter so that the model can control that. The most typical +// value for StateIntegerBits is 4. Other plausible values are anywhere between +// 3 and 5. We might eventually standardize on a single supported value, e.g. 4, +// and drop that template parameter. The reason why it can't be a runtime +// parameter is that this controls the fixed-point format used, i.e. we need to +// generate actually different code based on it. In particular, we generate code +// for a fixed-point tanh() implementation for that format, which internally +// uses a fixed-point exp() implementation, which internally uses a +// barrel-shifter with a number of steps that depends on StateIntegerBits. +// Another consequence of that is that a higher value of StateIntegerBits +// results in a more expensive implementation (more barrel shifter steps +// needed). +// +// +// === Why [-8, 8] for fully-connected output? === +// +// This array is only fed to Logistic and Tanh functions, for which +// the quantized implementation will want to use fixed-point arithmetic, +// requiring a power-of-two representation interval. Thus, we should right +// away quantize this array to a power-of-two interval; otherwise, +// implementation will need to rescale that, losing any benefit that a tighter +// representation interval might otherwise yield, while introducing some +// numerical error and computational overhead. +// +// Now, Logistic and Tanh +// are nearly constant (nearly equal to their horizontal asymptotes) +// outside of a small bounded interval around 0: +// +// Logistic(4) = 1 - 1.8e-2 Tanh(4) = 1 - 6.7e-4 +// Logistic(8) = 1 - 3.4e-4 Tanh(8) = 1 - 2.3e-7 +// Logistic(16) = 1 - 1.1e-7 Tanh(16) = 1 - 2.5e-14 +// +// From this, we see that clamping to [-4, 4] would be too inaccurate +// (the error of 1.8e-2 on Logistic would be felt even in 8bit precision) +// while clamping to [-16, 16] would make no difference even in float32. +// However, for a fixed-point implementation in 16-bit integers, using 5 +// integer bits to represent the [-16, 16] range would leave only 11 +// fractional bits, giving an increment of 2^-11 = 4.9e-4 between consecutive +// representable values. Notice that is higher than the +// worst-case clamping error with clamping to [-8, 8]: 3.4e-4 for Logistic. +// Using [-8, 8] thus seems like the better compromise overall, enjoying +// an increment of 2.4e-4 between representable values and a worst-case +// clamping error of 3.4e-4, both better than the increment of 4.9e-4 with +// [-16, 16]. +// +// Moreover, all other things being equal, it is nice to choose the narrower +// representation range, as that makes the implementation of fixed-point +// math functions a little cheaper (each integer bit requires an additional +// barrel-shifter atep in the implementation of exp(-x)). That is further +// reason to prefer [-8, 8] over [-16, 16]. The choice of [-16, 16] would make +// sense for 32-bit float or 32-bit fixed-point quantization, but we are +// aiming for 16-bit fixed-point quantization of these internal nodes here. +// +template +inline void +LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, + const uint8 *input_data_uint8, const RuntimeShape &unextended_prev_activ_shape, + const uint8 *prev_activ_data_uint8, const RuntimeShape &weights_shape, + const uint8 *weights_data_uint8, const RuntimeShape &unextended_bias_shape, + const int32 *bias_data_int32, const RuntimeShape &unextended_prev_state_shape, + const int16 *prev_state_data_int16, const RuntimeShape &unextended_output_state_shape, + int16 *output_state_data_int16, const RuntimeShape &unextended_output_activ_shape, + uint8 *output_activ_data_uint8, const RuntimeShape &unextended_concat_temp_shape, + uint8 *concat_temp_data_uint8, const RuntimeShape &unextended_activ_temp_shape, + int16 *activ_temp_data_int16, void *gemmlowp_context) +{ + (void)gemmlowp_context; // only used in optimized code. + int32 weights_zero_point = params.weights_zero_point; + int32 accum_multiplier = params.accum_multiplier; + int accum_shift = params.accum_shift; + TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); + const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); + const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); + const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); + const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); + const RuntimeShape output_state_shape = + RuntimeShape::ExtendedShape(4, unextended_output_state_shape); + const RuntimeShape output_activ_shape = + RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); + const RuntimeShape concat_temp_shape = + RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); + const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); + TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); + + // Gather dimensions information, and perform consistency checks. + const int weights_dim_count = weights_shape.DimensionsCount(); + const int outer_size = MatchingFlatSizeSkipDim(input_shape, 3, prev_activ_shape, prev_state_shape, + output_state_shape, output_activ_shape); + const int input_depth = input_shape.Dims(3); + const int prev_activ_depth = prev_activ_shape.Dims(3); + const int total_input_depth = prev_activ_depth + input_depth; + TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); + const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); + TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); + TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); + TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); + const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, + 3, output_activ_shape, 3); + TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); + const int fc_batches = FlatSizeSkipDim(activ_temp_shape, 3); + const int fc_output_depth = + MatchingDim(weights_shape, weights_dim_count - 2, activ_temp_shape, 3); + const int fc_accum_depth = total_input_depth; + TFLITE_DCHECK_EQ(fc_output_depth, 4 * output_depth); + + // Depth-concatenate prev_activ and input data together. + uint8 const *concat_input_arrays_data[2] = {input_data_uint8, prev_activ_data_uint8}; + const RuntimeShape *concat_input_arrays_shapes[2] = {&input_shape, &prev_activ_shape}; + tflite::ConcatenationParams concat_params; + concat_params.axis = 3; + concat_params.inputs_count = 2; + Concatenation(concat_params, concat_input_arrays_shapes, concat_input_arrays_data, + concat_temp_shape, concat_temp_data_uint8); + + // Implementation of the fully connected node inside the LSTM cell. + // The operands are 8-bit integers, the accumulators are internally 32bit + // integers, and the output is 16-bit fixed-point with 3 integer bits so + // the output range is [-2^3, 2^3] == [-8, 8]. The rationale for that + // is explained in the function comment above. + for (int b = 0; b < fc_batches; ++b) + { + for (int out_c = 0; out_c < fc_output_depth; ++out_c) + { + // Internal accumulation. + // Initialize accumulator with the bias-value. + int32 accum = bias_data_int32[out_c]; + // Accumulation loop. + for (int d = 0; d < fc_accum_depth; ++d) + { + int16 input_val = concat_temp_data_uint8[b * fc_accum_depth + d] - 128; + int16 weights_val = weights_data_uint8[out_c * fc_accum_depth + d] - weights_zero_point; + accum += input_val * weights_val; + } + // Down-scale the final int32 accumulator to the scale used by our + // (16-bit, using 3 integer bits) fixed-point format. The quantized + // multiplier and shift here have been pre-computed offline + // (e.g. by toco). + accum = MultiplyByQuantizedMultiplier(accum, accum_multiplier, accum_shift); + // Saturate, cast to int16, and store to the temporary activations array. + accum = std::max(-32768, std::min(32767, static_cast(accum))); + activ_temp_data_int16[out_c + fc_output_depth * b] = accum; + } + } + + // Rest of the LSTM cell: tanh and logistic math functions, and some adds + // and muls, all done in 16-bit fixed-point. + for (int b = 0; b < outer_size; ++b) + { + for (int c = 0; c < output_depth; ++c) + { + // Define the fixed-point data types that we will use here. All use + // int16 as the underlying integer type i.e. all are 16-bit fixed-point. + // They only differ by the number of integral vs. fractional bits, + // determining the range of values that they can represent. + // + // F0 uses 0 integer bits, range [-1, 1]. + // This is the return type of math functions such as tanh, logistic, + // whose range is in [-1, 1]. + using F0 = gemmlowp::FixedPoint; + // F3 uses 3 integer bits, range [-8, 8]. + // This is the range of the previous fully-connected node's output, + // which is our input here. + using F3 = gemmlowp::FixedPoint; + // FS uses StateIntegerBits integer bits, range [-2^StateIntegerBits, + // 2^StateIntegerBits]. It's used to represent the internal state, whose + // number of integer bits is currently dictated by the model. See comment + // on the StateIntegerBits template parameter above. + using FS = gemmlowp::FixedPoint; + // Implementation of input gate, using fixed-point logistic function. + F3 input_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 0 * output_depth + c]); + F0 input_gate_output = gemmlowp::logistic(input_gate_input); + // Implementation of input modulation gate, using fixed-point tanh + // function. + F3 input_modulation_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 1 * output_depth + c]); + F0 input_modulation_gate_output = gemmlowp::tanh(input_modulation_gate_input); + // Implementation of forget gate, using fixed-point logistic function. + F3 forget_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 2 * output_depth + c]); + F0 forget_gate_output = gemmlowp::logistic(forget_gate_input); + // Implementation of output gate, using fixed-point logistic function. + F3 output_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 3 * output_depth + c]); + F0 output_gate_output = gemmlowp::logistic(output_gate_input); + // Implementation of internal multiplication nodes, still in fixed-point. + F0 input_times_input_modulation = input_gate_output * input_modulation_gate_output; + FS prev_state = FS::FromRaw(prev_state_data_int16[b * output_depth + c]); + FS prev_state_times_forget_state = forget_gate_output * prev_state; + // Implementation of internal addition node, saturating. + FS new_state = + gemmlowp::SaturatingAdd(gemmlowp::Rescale(input_times_input_modulation), + prev_state_times_forget_state); + // Implementation of last internal Tanh node, still in fixed-point. + // Since a Tanh fixed-point implementation is specialized for a given + // number or integer bits, and each specialization can have a substantial + // code size, and we already used above a Tanh on an input with 3 integer + // bits, and per the table in the above function comment there is no + // significant accuracy to be lost by clamping to [-8, +8] for a + // 3-integer-bits representation, let us just do that. This helps people + // porting this to targets where code footprint must be minimized. + F3 new_state_f3 = gemmlowp::Rescale<3>(new_state); + F0 output_activ_int16 = output_gate_output * gemmlowp::tanh(new_state_f3); + // Store the new internal state back to memory, as 16-bit integers. + // Note: here we store the original value with StateIntegerBits, not + // the rescaled 3-integer-bits value fed to tanh. + output_state_data_int16[b * output_depth + c] = new_state.raw(); + // Down-scale the output activations to 8-bit integers, saturating, + // and store back to memory. + int16 rescaled_output_activ = gemmlowp::RoundingDivideByPOT(output_activ_int16.raw(), 8); + int16 clamped_output_activ = + std::max(-128, std::min(127, rescaled_output_activ)); + output_activ_data_uint8[b * output_depth + c] = 128 + clamped_output_activ; + } + } +} + +template +void Split(const SplitParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape *const *output_shapes, Scalar *const *output_data) +{ + ruy::profiler::ScopeLabel label("Split"); + const int split_dimensions = input_shape.DimensionsCount(); + int axis = params.axis < 0 ? params.axis + split_dimensions : params.axis; + int outputs_count = params.num_split; + TFLITE_DCHECK_LT(axis, split_dimensions); + + int64_t split_size = 0; + for (int i = 0; i < outputs_count; i++) + { + TFLITE_DCHECK_EQ(output_shapes[i]->DimensionsCount(), split_dimensions); + for (int j = 0; j < split_dimensions; j++) + { + if (j != axis) + { + MatchingDim(*output_shapes[i], j, input_shape, j); + } + } + split_size += output_shapes[i]->Dims(axis); + } + TFLITE_DCHECK_EQ(split_size, input_shape.Dims(axis)); + int64_t outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + // For all output arrays, + // FlatSize() = outer_size * Dims(axis) * base_inner_size; + int64_t base_inner_size = 1; + for (int i = axis + 1; i < split_dimensions; ++i) + { + base_inner_size *= input_shape.Dims(i); + } + + const Scalar *input_ptr = input_data; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < outputs_count; ++i) + { + const int copy_size = output_shapes[i]->Dims(axis) * base_inner_size; + memcpy(output_data[i] + k * copy_size, input_ptr, copy_size * sizeof(Scalar)); + input_ptr += copy_size; + } + } +} + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +inline void LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, + const RuntimeShape &input_shape, const float *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + const int trailing_dim = input_shape.DimensionsCount() - 1; + const int outer_size = MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); + const int depth = MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); + + for (int i = 0; i < outer_size; ++i) + { + for (int c = 0; c < depth; ++c) + { + const int begin_input_c = std::max(0, static_cast(c - op_params.range)); + const int end_input_c = std::min(depth, static_cast(c + op_params.range)); + float accum = 0.f; + for (int input_c = begin_input_c; input_c < end_input_c; ++input_c) + { + const float input_val = input_data[i * depth + input_c]; + accum += input_val * input_val; + } + const float multiplier = std::pow(op_params.bias + op_params.alpha * accum, -op_params.beta); + output_data[i * depth + c] = input_data[i * depth + c] * multiplier; + } + } +} + +inline void Dequantize(const RuntimeShape &input_shape, const Eigen::half *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; i++) + { + output_data[i] = static_cast(input_data[i]); + } +} + +inline void FakeQuant(const tflite::FakeQuantParams &op_params, const RuntimeShape &input_shape, + const float *input_data, const RuntimeShape &output_shape, float *output_data) +{ + ruy::profiler::ScopeLabel label("FakeQuant"); + float rmin = op_params.minmax.min; + float rmax = op_params.minmax.max; + int num_bits = op_params.num_bits; + // 0 should always be a representable value. Let's assume that the initial + // min,max range contains 0. + TFLITE_DCHECK_LE(rmin, 0.0f); + TFLITE_DCHECK_GE(rmax, 0.0f); + TFLITE_DCHECK_LT(rmin, rmax); + + // Code matches tensorflow's FakeQuantWithMinMaxArgsFunctor. + int quant_min = 0; + int quant_max = (1 << num_bits) - 1; + float nudged_min, nudged_max, nudged_scale; + NudgeQuantizationRange(rmin, rmax, quant_min, quant_max, &nudged_min, &nudged_max, &nudged_scale); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + FakeQuantizeArray(nudged_scale, nudged_min, nudged_max, input_data, output_data, flat_size); +} + +// Common subroutine for both `GatherNd` and `GatherNdString`. +struct GatherNdHelperResult +{ + int n_slices; + int slice_size; + int indices_nd; + std::vector dims_to_count; +}; + +// Returns common values being used on both `GatherNd` and `GatherNdString`. +inline GatherNdHelperResult GatherNdHelper(const RuntimeShape ¶ms_shape, + const RuntimeShape &indices_shape) +{ + GatherNdHelperResult ret; + ret.n_slices = 1; + ret.slice_size = 1; + const int indices_dims = indices_shape.DimensionsCount(); + ret.indices_nd = indices_shape.Dims(indices_dims - 1); + const int params_dims = params_shape.DimensionsCount(); + for (int i = 0; i < indices_dims - 1; ++i) + { + ret.n_slices *= indices_shape.Dims(i); + } + for (int i = ret.indices_nd; i < params_dims; ++i) + { + ret.slice_size *= params_shape.Dims(i); + } + + int remain_flat_size = params_shape.FlatSize(); + ret.dims_to_count = std::vector(ret.indices_nd, 0); + for (int i = 0; i < ret.indices_nd; ++i) + { + ret.dims_to_count[i] = remain_flat_size / params_shape.Dims(i); + remain_flat_size = ret.dims_to_count[i]; + } + + return ret; +} + +template +inline void GatherNd(const RuntimeShape ¶ms_shape, const ParamsT *params_data, + const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &output_shape, ParamsT *output_data) +{ + ruy::profiler::ScopeLabel label("GatherNd"); + + const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); + for (int i = 0; i < res.n_slices; ++i) + { + int from_pos = 0; + for (int j = 0; j < res.indices_nd; ++j) + { + from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; + } + std::memcpy(output_data + i * res.slice_size, params_data + from_pos, + sizeof(ParamsT) * res.slice_size); + } +} + +#ifndef TF_LITE_STATIC_MEMORY +template +inline void GatherNdString(const RuntimeShape ¶ms_shape, const TfLiteTensor *params_data, + const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &output_shape, TfLiteTensor *output_data) +{ + ruy::profiler::ScopeLabel label("GatherNdString"); + + const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); + DynamicBuffer buffer; + for (int i = 0; i < res.n_slices; ++i) + { + int from_pos = 0; + for (int j = 0; j < res.indices_nd; ++j) + { + from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; + } + for (int j = 0; j < res.slice_size; ++j) + { + buffer.AddString(GetString(params_data, from_pos + j)); + } + } + buffer.WriteToTensor(output_data, /*new_shape=*/nullptr); +} +#endif + +template +inline void ScatterNd(const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &updates_shape, const UpdatesT *updates_data, + const RuntimeShape &output_shape, UpdatesT *output_data) +{ + ruy::profiler::ScopeLabel label("ScatterNd"); + + int n_slices = 1; + int slice_size = 1; + const int outer_dims = indices_shape.DimensionsCount() - 1; + const int indices_nd = indices_shape.Dims(outer_dims); + const int updates_dims = updates_shape.DimensionsCount(); + for (int i = 0; i < outer_dims; ++i) + { + n_slices *= indices_shape.Dims(i); + } + for (int i = outer_dims; i < updates_dims; ++i) + { + slice_size *= updates_shape.Dims(i); + } + + int output_flat_size = output_shape.FlatSize(); + int remain_flat_size = output_flat_size; + std::vector dims_to_count(indices_nd, 0); + for (int i = 0; i < indices_nd; ++i) + { + dims_to_count[i] = remain_flat_size / output_shape.Dims(i); + remain_flat_size = dims_to_count[i]; + } + + memset(output_data, 0, sizeof(UpdatesT) * output_flat_size); + for (int i = 0; i < n_slices; ++i) + { + int to_pos = 0; + for (int j = 0; j < indices_nd; ++j) + { + IndicesT idx = indices_data[i * indices_nd + j]; + TFLITE_DCHECK(0 <= idx && idx < output_shape.Dims(j)); + to_pos += idx * dims_to_count[j]; + } + for (int j = 0; j < slice_size; j++) + { + output_data[to_pos + j] += updates_data[i * slice_size + j]; + } + } +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const RuntimeShape &output_shape, SequentialTensorWriter *writer) +{ + const RuntimeShape ext_shape = RuntimeShape::ExtendedShape(5, input_shape); + TFLITE_DCHECK_LE(op_params.begin_count, 5); + TFLITE_DCHECK_LE(op_params.size_count, 5); + const int begin_count = op_params.begin_count; + const int size_count = op_params.size_count; + // We front-pad the begin and size vectors. + std::array start; + std::array stop; + for (int i = 0; i < 5; ++i) + { + int padded_i = 5 - i; + start[i] = begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; + stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) + ? ext_shape.Dims(i) + : start[i] + op_params.size[size_count - padded_i]; + } + + for (int i0 = start[0]; i0 < stop[0]; ++i0) + { + for (int i1 = start[1]; i1 < stop[1]; ++i1) + { + for (int i2 = start[2]; i2 < stop[2]; ++i2) + { + for (int i3 = start[3]; i3 < stop[3]; ++i3) + { + for (int i4 = start[4]; i4 < stop[4]; ++i4) + { + writer->Write(Offset(ext_shape, i0, i1, i2, i3, i4)); + } + } + } + } + } +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + SequentialTensorWriter writer(input_data, output_data); + return Slice(op_params, input_shape, output_shape, &writer); +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const TfLiteTensor *input, const RuntimeShape &output_shape, TfLiteTensor *output) +{ + SequentialTensorWriter writer(input, output); + return Slice(op_params, input_shape, output_shape, &writer); +} + +template +void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, output_shape); + + auto min_value = input2_data[0]; + for (int i = 0; i < flat_size; i++) + { + output_data[i] = input1_data[i] > min_value ? min_value : input1_data[i]; + } +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, + const T *input2_data, const RuntimeShape &output_shape, T *output_data) +{ + // Drop shape of second input: not needed. + Minimum(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, output_shape); + + auto max_value = input2_data[0]; + for (int i = 0; i < flat_size; i++) + { + output_data[i] = input1_data[i] < max_value ? max_value : input1_data[i]; + } +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, + const T *input2_data, const RuntimeShape &output_shape, T *output_data) +{ + // Drop shape of second input: not needed. + Maximum(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, const T3 *input2_data, + const RuntimeShape &output_shape, T2 *output_data) +{ + ArgMinMax(input1_shape, input1_data, input2_data, output_shape, output_data, std::greater()); +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, + const RuntimeShape &input2_shape, const T3 *input2_data, + const RuntimeShape &output_shape, T2 *output_data) +{ + // Drop shape of second input: not needed. + ArgMax(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void Select(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + int64_t flatsize; + // Allow select operator executions on mixed scalar tensors and one element + // tensors. + if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 && + input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) + { + flatsize = 1; + } + else + { + flatsize = MatchingFlatSize(input_condition_shape, input_x_shape, input_y_shape, output_shape); + } + for (int64_t i = 0; i < flatsize; ++i) + { + output_data[i] = input_condition_data[i] ? input_x_data[i] : input_y_data[i]; + } +} + +template +void RankOneSelect(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int64_t outer_size = input_condition_shape.FlatSize(); + int64_t inner_size; + if (input_condition_shape.DimensionsCount() == 0) + { + inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape); + } + else + { + TFLITE_DCHECK_EQ(MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0), outer_size); + inner_size = MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape); + } + + int64_t offset = 0; + for (int64_t i = 0; i < outer_size; i++) + { + const T *input_data = input_condition_data[i] ? input_x_data : input_y_data; + memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T)); + offset += inner_size; + } +} + +template +void BroadcastSelect4DSlow(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4); + + const RuntimeShape extended_output_shape = RuntimeShape::ExtendedShape(4, output_shape); + + NdArrayDesc<4> desc_condition; + NdArrayDesc<4> desc_x; + NdArrayDesc<4> desc_y; + NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape, input_y_shape, + &desc_condition, &desc_x, &desc_y); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest + // stride, typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for + // the best cache behavior. + for (int b = 0; b < extended_output_shape.Dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.Dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.Dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.Dims(3); ++c) + { + const int condition_index = SubscriptToIndex(desc_condition, b, y, x, c); + const int x_index = SubscriptToIndex(desc_x, b, y, x, c); + const int y_index = SubscriptToIndex(desc_y, b, y, x, c); + output_data[Offset(extended_output_shape, b, y, x, c)] = + input_condition_data[condition_index] ? input_x_data[x_index] : input_y_data[y_index]; + } + } + } + } +} + +template +void SelectTrueCoords(const RuntimeShape &input_condition_shape, const D *input_condition_data, + T *output_data) +{ + const size_t size = input_condition_shape.FlatSize(); + if (size == 0) + { + // Dimension is zero, in which case we don't need to output. + return; + } + const size_t cond_rank = input_condition_shape.DimensionsCount(); + + std::vector dims_to_count(cond_rank, 0); + int cur_flat_size = size; + for (int i = 0; i < cond_rank; ++i) + { + dims_to_count[i] = cur_flat_size / input_condition_shape.Dims(i); + cur_flat_size = dims_to_count[i]; + } + + int output_index = 0; + for (int i = 0; i < size; ++i) + { + if (input_condition_data[i]) + { + // Insert the coordinate of the current item (row major) into output. + int flat_index = i; + for (int j = 0; j < cond_rank; ++j) + { + int coord_j = flat_index / dims_to_count[j]; + output_data[output_index * cond_rank + j] = coord_j; + flat_index %= dims_to_count[j]; + } + output_index++; + } + } +} + +// For easy implementation, the indices is always a vector of size-4 vectors. +template +inline void SparseToDense(const std::vector> &indices, const T *values, + T default_value, bool value_is_scalar, + const RuntimeShape &unextended_output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); + const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); + const int value_count = indices.size(); + + // First fill the output_data with default value. + const int num_elements = output_shape.FlatSize(); + for (int i = 0; i < num_elements; ++i) + { + output_data[i] = default_value; + } + + // Special handle for value is scalar case to avoid checking the boolean + // condition within the loop every time. + if (value_is_scalar) + { + for (int i = 0; i < value_count; ++i) + { + const std::vector &index = indices[i]; + TFLITE_DCHECK_EQ(index.size(), 4); + const T value = *values; // just use the first value. + output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; + } + return; + } + + // Go through the values and indices to fill the sparse values. + for (int i = 0; i < value_count; ++i) + { + const std::vector &index = indices[i]; + TFLITE_DCHECK_EQ(index.size(), 4); + const T value = values[i]; + output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; + } +} + +template +inline void Pow(const RuntimeShape &input1_shape, const T *input1_data, + const RuntimeShape &input2_shape, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, input2_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = std::pow(input1_data[i], input2_data[i]); + } +} + +template +inline void BroadcastPow4DSlow(const RuntimeShape &unextended_input1_shape, const T *input1_data, + const RuntimeShape &unextended_input2_shape, const T *input2_data, + const RuntimeShape &unextended_output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); + const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); + + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, + &desc2); + + for (int b = 0; b < output_shape.Dims(0); ++b) + { + for (int y = 0; y < output_shape.Dims(1); ++y) + { + for (int x = 0; x < output_shape.Dims(2); ++x) + { + for (int c = 0; c < output_shape.Dims(3); ++c) + { + auto out_idx = Offset(output_shape, b, y, x, c); + auto in1_idx = SubscriptToIndex(desc1, b, y, x, c); + auto in2_idx = SubscriptToIndex(desc2, b, y, x, c); + auto in1_val = input1_data[in1_idx]; + auto in2_val = input2_data[in2_idx]; + output_data[out_idx] = std::pow(in1_val, in2_val); + } + } + } + } +} + +template +void Reverse(int axis, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("Reverse"); + + int outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + + int copy_size = 1; + for (int i = axis + 1; i < input_shape.DimensionsCount(); ++i) + { + copy_size *= input_shape.Dims(i); + } + + const int dims_at_axis = input_shape.Dims(axis); + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_axis; ++j) + { + const int start_pos = (i * dims_at_axis + j) * copy_size; + Scalar *output_ptr = output_data + start_pos; + int loc = (i * dims_at_axis + dims_at_axis - j - 1) * copy_size; + memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); + } + } +} + +template +void ReverseSequence(const TS *seq_lengths, const int seq_dim, const int batch_dim, + const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("ReverseSequence"); + + int outer_size = 1; + int outer_dim = std::min(batch_dim, seq_dim); + int medium_dim = std::max(batch_dim, seq_dim); + for (int i = 0; i < outer_dim; ++i) + { + outer_size *= input_shape.Dims(i); + } + + int medium_size = 1; + for (int i = outer_dim + 1; i < medium_dim; ++i) + { + medium_size *= input_shape.Dims(i); + } + + int copy_size = 1; + for (int i = medium_dim + 1; i < input_shape.DimensionsCount(); ++i) + { + copy_size *= input_shape.Dims(i); + } + + const int dims_at_outer_dim = input_shape.Dims(outer_dim); + const int dims_at_medium_dim = input_shape.Dims(medium_dim); + + Scalar *output_ptr; + if (batch_dim > seq_dim) + { + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_outer_dim; ++j) + { + const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; + for (int p = 0; p < medium_size; ++p) + { + for (int q = 0; q < dims_at_medium_dim; ++q) + { + const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; + const Scalar *in_ptr = input_data + in_pos; + int sl = seq_lengths[q] - 1; + if (j > sl) + { + output_ptr = output_data + in_pos; + } + else + { + const int out_pos_base = (i * dims_at_outer_dim + sl - j) * medium_size; + const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + q) * copy_size; + output_ptr = output_data + out_pos; + } + memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); + } + } + } + } + } + else if (batch_dim < seq_dim) + { + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_outer_dim; ++j) + { + const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; + int sl = seq_lengths[j] - 1; + const int out_pos_base = (i * dims_at_outer_dim + j) * medium_size; + for (int p = 0; p < medium_size; ++p) + { + for (int q = 0; q < dims_at_medium_dim; ++q) + { + const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; + const Scalar *in_ptr = input_data + in_pos; + if (q > sl) + { + output_ptr = output_data + in_pos; + } + else + { + const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + sl - q) * copy_size; + output_ptr = output_data + out_pos; + } + memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); + } + } + } + } + } +} + +template +inline void SegmentSum(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &segment_ids_shape, const int32_t *segment_ids_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int segment_flat_size = MatchingFlatSizeSkipDim(input_shape, 0, output_shape); + + memset(output_data, 0, sizeof(T) * output_shape.FlatSize()); + + for (int i = 0; i < input_shape.Dims(0); i++) + { + int output_index = segment_ids_data[i]; + for (int j = 0; j < segment_flat_size; ++j) + { + output_data[output_index * segment_flat_size + j] += input_data[i * segment_flat_size + j]; + } + } +} + +} // namespace reference_ops +} // namespace tflite + +#endif // LUCI_INTERPRETER_PAL_REFERENCE_OPS_H diff --git a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst index 428b15e..1e6c41e 100644 --- a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst +++ b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst @@ -13,6 +13,7 @@ REGISTER_KERNEL(Div) REGISTER_KERNEL(Elu) REGISTER_KERNEL(Exp) REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) REGISTER_KERNEL(Floor) REGISTER_KERNEL(FloorDiv) REGISTER_KERNEL(Equal) @@ -48,6 +49,7 @@ REGISTER_KERNEL(PadV2) REGISTER_KERNEL(Pow) REGISTER_KERNEL(PRelu) REGISTER_KERNEL(Quantize) +REGISTER_KERNEL(ReduceMax) REGISTER_KERNEL(Relu) REGISTER_KERNEL(Relu6) REGISTER_KERNEL(Reshape) @@ -55,6 +57,7 @@ REGISTER_KERNEL(ResizeBilinear) REGISTER_KERNEL(ResizeNearestNeighbor) REGISTER_KERNEL(ReverseV2) REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) REGISTER_KERNEL(Slice) REGISTER_KERNEL(Softmax) REGISTER_KERNEL(SpaceToBatchND) diff --git a/compiler/luci-interpreter/pal/linux/PALreference_ops.h b/compiler/luci-interpreter/pal/linux/PALreference_ops.h new file mode 100644 index 0000000..825ebfe --- /dev/null +++ b/compiler/luci-interpreter/pal/linux/PALreference_ops.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_REFERENCE_OPS_H +#define LUCI_INTERPRETER_PAL_REFERENCE_OPS_H + +#include + +#endif // LUCI_INTERPRETER_PAL_REFERENCE_OPS_H diff --git a/compiler/luci-interpreter/pal/mcu/KernelsToBuild.lst b/compiler/luci-interpreter/pal/mcu/KernelsToBuild.lst index d134a6b..f0df58d 100644 --- a/compiler/luci-interpreter/pal/mcu/KernelsToBuild.lst +++ b/compiler/luci-interpreter/pal/mcu/KernelsToBuild.lst @@ -12,6 +12,7 @@ REGISTER_KERNEL(Div) REGISTER_KERNEL(Elu) REGISTER_KERNEL(Exp) REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) REGISTER_KERNEL(Floor) REGISTER_KERNEL(FloorDiv) REGISTER_KERNEL(Equal) @@ -44,6 +45,7 @@ REGISTER_KERNEL(Reshape) REGISTER_KERNEL(ResizeBilinear) REGISTER_KERNEL(ResizeNearestNeighbor) REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) REGISTER_KERNEL(Softmax) REGISTER_KERNEL(SpaceToBatchND) REGISTER_KERNEL(SpaceToDepth) diff --git a/compiler/luci-interpreter/pal/mcu/PALDequantize.h b/compiler/luci-interpreter/pal/mcu/PALDequantize.h index 15ff032..efa6b16 100644 --- a/compiler/luci-interpreter/pal/mcu/PALDequantize.h +++ b/compiler/luci-interpreter/pal/mcu/PALDequantize.h @@ -18,7 +18,7 @@ #define LUCI_INTERPRETER_PAL_DEQUANTIZE_H #include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" -#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "PALreference_ops.h" namespace luci_interpreter_pal { diff --git a/compiler/luci-interpreter/pal/mcu/PALQuantize.h b/compiler/luci-interpreter/pal/mcu/PALQuantize.h index 6046789..effb85d 100644 --- a/compiler/luci-interpreter/pal/mcu/PALQuantize.h +++ b/compiler/luci-interpreter/pal/mcu/PALQuantize.h @@ -17,7 +17,7 @@ #ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H #define LUCI_INTERPRETER_PAL_QUANTIZE_H -#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" +#include "PALreference_ops.h" namespace luci_interpreter_pal { diff --git a/compiler/luci-interpreter/pal/mcu/PALreference_ops.h b/compiler/luci-interpreter/pal/mcu/PALreference_ops.h new file mode 100644 index 0000000..62c7209 --- /dev/null +++ b/compiler/luci-interpreter/pal/mcu/PALreference_ops.h @@ -0,0 +1,1556 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_REFERENCE_OPS_H +#define LUCI_INTERPRETER_PAL_REFERENCE_OPS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "third_party/eigen3/Eigen/Core" +#include "fixedpoint/fixedpoint.h" +#include "ruy/profiler/instrumentation.h" // from @ruy +#include "tensorflow/lite/c/common.h" +#include "tensorflow/lite/kernels/internal/common.h" +#include "tensorflow/lite/kernels/internal/quantization_util.h" +#include "tensorflow/lite/kernels/internal/reference/add.h" +#include "tensorflow/lite/kernels/internal/reference/add_n.h" +#include "tensorflow/lite/kernels/internal/reference/arg_min_max.h" +#include "tensorflow/lite/kernels/internal/reference/batch_matmul.h" +#include "tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h" +#include "tensorflow/lite/kernels/internal/reference/binary_function.h" +#include "tensorflow/lite/kernels/internal/reference/cast.h" +#include "tensorflow/lite/kernels/internal/reference/ceil.h" +#include "tensorflow/lite/kernels/internal/reference/comparisons.h" +#include "tensorflow/lite/kernels/internal/reference/concatenation.h" +#include "tensorflow/lite/kernels/internal/reference/conv.h" +#include "tensorflow/lite/kernels/internal/reference/depth_to_space.h" +#include "tensorflow/lite/kernels/internal/reference/dequantize.h" +#include "tensorflow/lite/kernels/internal/reference/div.h" +#include "tensorflow/lite/kernels/internal/reference/elu.h" +#include "tensorflow/lite/kernels/internal/reference/exp.h" +#include "tensorflow/lite/kernels/internal/reference/fill.h" +#include "tensorflow/lite/kernels/internal/reference/floor.h" +#include "tensorflow/lite/kernels/internal/reference/floor_div.h" +#include "tensorflow/lite/kernels/internal/reference/floor_mod.h" +#include "tensorflow/lite/kernels/internal/reference/fully_connected.h" +#include "tensorflow/lite/kernels/internal/reference/gather.h" +#include "tensorflow/lite/kernels/internal/reference/hard_swish.h" +#include "tensorflow/lite/kernels/internal/reference/l2normalization.h" +#include "tensorflow/lite/kernels/internal/reference/leaky_relu.h" +#include "tensorflow/lite/kernels/internal/reference/log_softmax.h" +#include "tensorflow/lite/kernels/internal/reference/logistic.h" +#include "tensorflow/lite/kernels/internal/reference/maximum_minimum.h" +#include "tensorflow/lite/kernels/internal/reference/mul.h" +#include "tensorflow/lite/kernels/internal/reference/neg.h" +#include "tensorflow/lite/kernels/internal/reference/pad.h" +#include "tensorflow/lite/kernels/internal/reference/pooling.h" +#include "tensorflow/lite/kernels/internal/reference/prelu.h" +#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" +#include "tensorflow/lite/kernels/internal/reference/quantize.h" +#include "tensorflow/lite/kernels/internal/reference/reduce.h" +#include "tensorflow/lite/kernels/internal/reference/requantize.h" +#include "tensorflow/lite/kernels/internal/reference/resize_bilinear.h" +#include "tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h" +#include "tensorflow/lite/kernels/internal/reference/round.h" +#include "tensorflow/lite/kernels/internal/reference/softmax.h" +#include "tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h" +#include "tensorflow/lite/kernels/internal/reference/space_to_depth.h" +#include "tensorflow/lite/kernels/internal/reference/strided_slice.h" +#include "tensorflow/lite/kernels/internal/reference/string_comparisons.h" +#include "tensorflow/lite/kernels/internal/reference/sub.h" +#include "tensorflow/lite/kernels/internal/reference/tanh.h" +#include "tensorflow/lite/kernels/internal/reference/transpose.h" +#include "tensorflow/lite/kernels/internal/reference/transpose_conv.h" +#include "tensorflow/lite/kernels/internal/strided_slice_logic.h" +#include "tensorflow/lite/kernels/internal/tensor.h" +#include "tensorflow/lite/kernels/internal/types.h" +namespace tflite +{ + +namespace reference_ops +{ + +template +inline void Relu(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T lower = 0; + const T clamped = val < lower ? lower : val; + output_data[i] = clamped; + } +} + +template +inline void Relu1(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Relu1 (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T upper = 1; + const T lower = -1; + const T clamped = val > upper ? upper : val < lower ? lower : val; + output_data[i] = clamped; + } +} + +inline void Relu6(const RuntimeShape &input_shape, const float *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + ruy::profiler::ScopeLabel label("Relu6 (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const float val = input_data[i]; + const float upper = 6; + const float lower = 0; + const float clamped = val > upper ? upper : val < lower ? lower : val; + output_data[i] = clamped; + } +} + +template +inline void ReluX(const tflite::ReluParams ¶ms, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + const int32 val = static_cast(input_data[i]); + int32 clamped = params.output_offset + MultiplyByQuantizedMultiplier(val - params.input_offset, + params.output_multiplier, + params.output_shift); + clamped = std::max(params.quantized_activation_min, clamped); + clamped = std::min(params.quantized_activation_max, clamped); + output_data[i] = static_cast(clamped); + } +} + +template +inline void ReluX(const tflite::ActivationParams ¶ms, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + const T max_value = params.quantized_activation_max; + const T min_value = params.quantized_activation_min; + for (int i = 0; i < flat_size; ++i) + { + const T val = input_data[i]; + const T clamped = val > max_value ? max_value : val < min_value ? min_value : val; + output_data[i] = clamped; + } +} + +// TODO(jiawen): We can implement BroadcastMul on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +inline void BroadcastMulFivefold(const ArithmeticParams &unswitched_params, + const RuntimeShape &unswitched_input1_shape, + const uint8 *unswitched_input1_data, + const RuntimeShape &unswitched_input2_shape, + const uint8 *unswitched_input2_data, + const RuntimeShape &output_shape, uint8 *output_data) +{ + ArithmeticParams switched_params = unswitched_params; + switched_params.input1_offset = unswitched_params.input2_offset; + switched_params.input2_offset = unswitched_params.input1_offset; + + const bool use_unswitched = unswitched_params.broadcast_category == + tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast; + + const ArithmeticParams ¶ms = use_unswitched ? unswitched_params : switched_params; + const uint8 *input1_data = use_unswitched ? unswitched_input1_data : unswitched_input2_data; + const uint8 *input2_data = use_unswitched ? unswitched_input2_data : unswitched_input1_data; + + // Fivefold nested loops. The second input resets its position for each + // iteration of the second loop. The first input resets its position at the + // beginning of the fourth loop. The innermost loop is an elementwise Mul of + // sections of the arrays. + uint8 *output_data_ptr = output_data; + const uint8 *input1_data_ptr = input1_data; + const uint8 *input2_data_reset = input2_data; + int y0 = params.broadcast_shape[0]; + int y1 = params.broadcast_shape[1]; + int y2 = params.broadcast_shape[2]; + int y3 = params.broadcast_shape[3]; + int y4 = params.broadcast_shape[4]; + for (int i0 = 0; i0 < y0; ++i0) + { + const uint8 *input2_data_ptr; + for (int i1 = 0; i1 < y1; ++i1) + { + input2_data_ptr = input2_data_reset; + for (int i2 = 0; i2 < y2; ++i2) + { + for (int i3 = 0; i3 < y3; ++i3) + { + MulElementwise(y4, params, input1_data_ptr, input2_data_ptr, output_data_ptr); + input2_data_ptr += y4; + output_data_ptr += y4; + } + input1_data_ptr += y4; + } + } + input2_data_reset = input2_data_ptr; + } +} + +inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16 *input1_data, const RuntimeShape &input2_shape, + const int16 *input2_data, const RuntimeShape &output_shape, int16 *output_data) +{ + ruy::profiler::ScopeLabel label("Mul/Int16"); + + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + + for (int i = 0; i < flat_size; i++) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + + F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); + output_data[i] = unclamped_result.raw(); + } +} + +inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16 *input1_data, const RuntimeShape &input2_shape, + const int16 *input2_data, const RuntimeShape &output_shape, uint8 *output_data) +{ + ruy::profiler::ScopeLabel label("Mul/Int16Uint8"); + int32 output_offset = params.output_offset; + int32 output_activation_min = params.quantized_activation_min; + int32 output_activation_max = params.quantized_activation_max; + TFLITE_DCHECK_LE(output_activation_min, output_activation_max); + + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + + for (int i = 0; i < flat_size; i++) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + + F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); + int16 rescaled_result = gemmlowp::RoundingDivideByPOT(unclamped_result.raw(), 8); + int16 clamped_result = std::min(output_activation_max - output_offset, rescaled_result); + clamped_result = std::max(output_activation_min - output_offset, clamped_result); + output_data[i] = output_offset + clamped_result; + } +} + +inline void Sub16(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, + const int16_t *input1_data, const RuntimeShape &input2_shape, + const int16_t *input2_data, const RuntimeShape &output_shape, + int16_t *output_data) +{ + ruy::profiler::ScopeLabel label("Sub/Int16"); + const int input1_shift = params.input1_shift; + const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); + const int16 output_activation_min = params.quantized_activation_min; + const int16 output_activation_max = params.quantized_activation_max; + + TFLITE_DCHECK(input1_shift == 0 || params.input2_shift == 0); + TFLITE_DCHECK_LE(input1_shift, 0); + TFLITE_DCHECK_LE(params.input2_shift, 0); + const int16 *not_shift_input = input1_shift == 0 ? input1_data : input2_data; + const int16 *shift_input = input1_shift == 0 ? input2_data : input1_data; + const int input_right_shift = input1_shift == 0 ? -params.input2_shift : -input1_shift; + + if (input1_shift == 0) + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + for (int i = 0; i < flat_size; ++i) + { + F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); + F0 scaled_input = + F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); + F0 result = SaturatingSub(input_ready_scaled, scaled_input); + const int16 raw_output = result.raw(); + const int16 clamped_output = + std::min(output_activation_max, std::max(output_activation_min, raw_output)); + output_data[i] = clamped_output; + } + } + else + { + // F0 uses 0 integer bits, range [-1, 1]. + using F0 = gemmlowp::FixedPoint; + for (int i = 0; i < flat_size; ++i) + { + F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); + F0 scaled_input = + F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); + F0 result = SaturatingSub(scaled_input, input_ready_scaled); + const int16 raw_output = result.raw(); + const int16 clamped_output = + std::min(output_activation_max, std::max(output_activation_min, raw_output)); + output_data[i] = clamped_output; + } + } +} + +template +void Pack(const PackParams ¶ms, const RuntimeShape *const *input_shapes, + const Scalar *const *input_data, const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("Pack"); + const int dimensions = output_shape.DimensionsCount(); + int axis = params.axis; + int inputs_count = params.inputs_count; + + int outer_size = 1; + for (int i = 0; i < axis; i++) + { + outer_size *= output_shape.Dims(i); + } + int copy_size = 1; + for (int i = params.axis + 1; i < dimensions; i++) + { + copy_size *= output_shape.Dims(i); + } + TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); + + for (int i = 0; i < inputs_count; ++i) + { + for (int k = 0; k < outer_size; k++) + { + const Scalar *input_ptr = input_data[i] + copy_size * k; + int loc = k * inputs_count * copy_size + i * copy_size; + memcpy(output_data + loc, input_ptr, copy_size * sizeof(Scalar)); + } + } +} + +template +void Unpack(const UnpackParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *const *output_datas) +{ + ruy::profiler::ScopeLabel label("Unpack"); + const int dimensions = input_shape.DimensionsCount(); + const int outputs_count = params.num_split; + + int outer_size = 1; + int axis = params.axis; + if (axis < 0) + { + axis += dimensions; + } + TFLITE_DCHECK_GE(axis, 0); + TFLITE_DCHECK_LT(axis, dimensions); + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + int copy_size = 1; + for (int i = axis + 1; i < dimensions; ++i) + { + copy_size *= input_shape.Dims(i); + } + TFLITE_DCHECK_EQ(output_shape.FlatSize(), copy_size * outer_size); + + for (int i = 0; i < outputs_count; ++i) + { + for (int k = 0; k < outer_size; k++) + { + Scalar *output_ptr = output_datas[i] + copy_size * k; + int loc = k * outputs_count * copy_size + i * copy_size; + memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); + } + } +} + +template +void PackWithScaling(const PackParams ¶ms, const RuntimeShape *const *input_shapes, + const uint8 *const *input_data, const RuntimeShape &output_shape, + uint8 *output_data) +{ + ruy::profiler::ScopeLabel label("PackWithScaling"); + const int dimensions = output_shape.DimensionsCount(); + int axis = params.axis; + const int32 *input_zeropoint = params.input_zeropoint; + const float *input_scale = params.input_scale; + int inputs_count = params.inputs_count; + const int32 output_zeropoint = params.output_zeropoint; + const float output_scale = params.output_scale; + + int outer_size = 1; + for (int i = 0; i < axis; i++) + { + outer_size *= output_shape.Dims(i); + } + int copy_size = 1; + for (int i = axis + 1; i < dimensions; i++) + { + copy_size *= output_shape.Dims(i); + } + TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); + + Scalar *output_ptr = output_data; + const float inverse_output_scale = 1.f / output_scale; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < inputs_count; ++i) + { + if (input_zeropoint[i] == output_zeropoint && input_scale[i] == output_scale) + { + memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); + } + else + { + assert(false); + const float scale = input_scale[i] * inverse_output_scale; + const float bias = -input_zeropoint[i] * scale; + auto input_ptr = input_data[i]; + for (int j = 0; j < copy_size; ++j) + { + const int value = + static_cast(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; + output_ptr[j] = static_cast(std::max(std::min(255, value), 0)); + } + } + output_ptr += copy_size; + } + } +} + +template +void DepthConcatenation(const ConcatenationParams ¶ms, const RuntimeShape *const *input_shapes, + const Scalar *const *input_data, const RuntimeShape &output_shape, + Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("DepthConcatenation"); + auto params_copy = params; + params_copy.axis = 3; + Concatenation(params_copy, input_shapes, input_data, output_shape, output_data); +} + +inline void LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, + const float *input_data, const RuntimeShape &unextended_prev_activ_shape, + const float *prev_activ_data, const RuntimeShape &weights_shape, + const float *weights_data, const RuntimeShape &unextended_bias_shape, + const float *bias_data, const RuntimeShape &unextended_prev_state_shape, + const float *prev_state_data, + const RuntimeShape &unextended_output_state_shape, float *output_state_data, + const RuntimeShape &unextended_output_activ_shape, float *output_activ_data, + const RuntimeShape &unextended_concat_temp_shape, float *concat_temp_data, + const RuntimeShape &unextended_activ_temp_shape, float *activ_temp_data) +{ + TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); + const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); + const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); + const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); + const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); + const RuntimeShape output_state_shape = + RuntimeShape::ExtendedShape(4, unextended_output_state_shape); + const RuntimeShape output_activ_shape = + RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); + const RuntimeShape concat_temp_shape = + RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); + const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); + TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); + + const int weights_dim_count = weights_shape.DimensionsCount(); + const int batches = MatchingDim(input_shape, 0, prev_activ_shape, 0, prev_state_shape, 0, + output_state_shape, 0, output_activ_shape, 0); + const int height = MatchingDim(input_shape, 1, prev_activ_shape, 1, prev_state_shape, 1, + output_state_shape, 1, output_activ_shape, 1); + const int width = MatchingDim(input_shape, 2, prev_activ_shape, 2, prev_state_shape, 2, + output_state_shape, 2, output_activ_shape, 2); + const int input_depth = input_shape.Dims(3); + const int prev_activ_depth = prev_activ_shape.Dims(3); + const int total_input_depth = prev_activ_depth + input_depth; + TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); + TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); + const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); + TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); + TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); + const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, + 3, output_activ_shape, 3); + TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); + + // Concatenate prev_activ and input data together + std::vector concat_input_arrays_data; + std::vector concat_input_arrays_shapes; + concat_input_arrays_data.push_back(input_data); + concat_input_arrays_data.push_back(prev_activ_data); + concat_input_arrays_shapes.push_back(&input_shape); + concat_input_arrays_shapes.push_back(&prev_activ_shape); + tflite::ConcatenationParams concat_params; + concat_params.axis = 3; + concat_params.inputs_count = concat_input_arrays_data.size(); + Concatenation(concat_params, &(concat_input_arrays_shapes[0]), &(concat_input_arrays_data[0]), + concat_temp_shape, concat_temp_data); + + // Fully connected + tflite::FullyConnectedParams fc_params; + fc_params.float_activation_min = std::numeric_limits::lowest(); + fc_params.float_activation_max = std::numeric_limits::max(); + FullyConnected(fc_params, concat_temp_shape, concat_temp_data, weights_shape, weights_data, + bias_shape, bias_data, activ_temp_shape, activ_temp_data); + + // Memory state update (the LSTM "guts") + for (int b = 0; b < batches; ++b) + { + for (int w = 0; w < width; ++w) + { + for (int h = 0; h < height; ++h) + { + for (int c = 0; c < output_depth; ++c) + { + const float input_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 0 * output_depth + c)])); + const float new_input = + std::tanh(activ_temp_data[Offset(activ_temp_shape, b, h, w, 1 * output_depth + c)]); + const float forget_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 2 * output_depth + c)])); + const float output_gate = + 1.f / + (1.f + + std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 3 * output_depth + c)])); + const float new_state = + input_gate * new_input + + forget_gate * prev_state_data[Offset(prev_state_shape, b, h, w, c)]; + output_state_data[Offset(output_state_shape, b, h, w, c)] = new_state; + output_activ_data[Offset(output_activ_shape, b, h, w, c)] = + output_gate * std::tanh(new_state); + } + } + } + } +} + +// Quantized LSTM cell implementation. +// The quantization of the input, output arrays is as follows: +// - The input activations are quantized as uint8 on the interval +// [-1, 127/128]. +// The rationale for that is that is the natural interval for output +// activations (see next point) and these need to be concatenated together. +// We could accommodate different ranges by re-scaling, but we empirically +// found that setting the input activations range to be [-1, 127/128] in the +// first place, removing the need for re-scaling, greatly improves accuracy. +// - The output activations are quantized as uint8 on the interval +// [-1, 127/128]. +// The rationale for that is that the definition of a LSTM cell makes them +// intrinsically constrained in [-1, 1]; tweaking that to [-1, 127/128] +// makes for simpler, more accurate fixed-point arithmetic. +// - The output-at-previous-timestep state array is obviously quantized as +// the output activations. +// - The internal LSTM memory (not the output-at-previous-timestep, the other +// internal state array) is int16-quantized and may use any power-of-two, +// symmetric range i.e. [-2^N, 2^N * 32767/32768] for any N, which we call +// StateIntegerBits below, see the below discussion of that template +// parameter ("The StateIntegerBits template parameter"). +// - The output of the internal fully-connected node is int16-quantized +// on the interval [-8, 8 * 32767/32768], the rationale for which is +// explained just below ("Why [-8, 8] for fully-connected output?"). +// +// +// === The StateIntegerBits template parameter === +// +// The StateIntegerBits template parameter controls the fixed-point format used +// to represent the internal memory of the LSTM cell (not the +// output-at-previous-timestep, the other internal state array). It's currently +// a template parameter so that the model can control that. The most typical +// value for StateIntegerBits is 4. Other plausible values are anywhere between +// 3 and 5. We might eventually standardize on a single supported value, e.g. 4, +// and drop that template parameter. The reason why it can't be a runtime +// parameter is that this controls the fixed-point format used, i.e. we need to +// generate actually different code based on it. In particular, we generate code +// for a fixed-point tanh() implementation for that format, which internally +// uses a fixed-point exp() implementation, which internally uses a +// barrel-shifter with a number of steps that depends on StateIntegerBits. +// Another consequence of that is that a higher value of StateIntegerBits +// results in a more expensive implementation (more barrel shifter steps +// needed). +// +// +// === Why [-8, 8] for fully-connected output? === +// +// This array is only fed to Logistic and Tanh functions, for which +// the quantized implementation will want to use fixed-point arithmetic, +// requiring a power-of-two representation interval. Thus, we should right +// away quantize this array to a power-of-two interval; otherwise, +// implementation will need to rescale that, losing any benefit that a tighter +// representation interval might otherwise yield, while introducing some +// numerical error and computational overhead. +// +// Now, Logistic and Tanh +// are nearly constant (nearly equal to their horizontal asymptotes) +// outside of a small bounded interval around 0: +// +// Logistic(4) = 1 - 1.8e-2 Tanh(4) = 1 - 6.7e-4 +// Logistic(8) = 1 - 3.4e-4 Tanh(8) = 1 - 2.3e-7 +// Logistic(16) = 1 - 1.1e-7 Tanh(16) = 1 - 2.5e-14 +// +// From this, we see that clamping to [-4, 4] would be too inaccurate +// (the error of 1.8e-2 on Logistic would be felt even in 8bit precision) +// while clamping to [-16, 16] would make no difference even in float32. +// However, for a fixed-point implementation in 16-bit integers, using 5 +// integer bits to represent the [-16, 16] range would leave only 11 +// fractional bits, giving an increment of 2^-11 = 4.9e-4 between consecutive +// representable values. Notice that is higher than the +// worst-case clamping error with clamping to [-8, 8]: 3.4e-4 for Logistic. +// Using [-8, 8] thus seems like the better compromise overall, enjoying +// an increment of 2.4e-4 between representable values and a worst-case +// clamping error of 3.4e-4, both better than the increment of 4.9e-4 with +// [-16, 16]. +// +// Moreover, all other things being equal, it is nice to choose the narrower +// representation range, as that makes the implementation of fixed-point +// math functions a little cheaper (each integer bit requires an additional +// barrel-shifter atep in the implementation of exp(-x)). That is further +// reason to prefer [-8, 8] over [-16, 16]. The choice of [-16, 16] would make +// sense for 32-bit float or 32-bit fixed-point quantization, but we are +// aiming for 16-bit fixed-point quantization of these internal nodes here. +// +template +inline void +LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, + const uint8 *input_data_uint8, const RuntimeShape &unextended_prev_activ_shape, + const uint8 *prev_activ_data_uint8, const RuntimeShape &weights_shape, + const uint8 *weights_data_uint8, const RuntimeShape &unextended_bias_shape, + const int32 *bias_data_int32, const RuntimeShape &unextended_prev_state_shape, + const int16 *prev_state_data_int16, const RuntimeShape &unextended_output_state_shape, + int16 *output_state_data_int16, const RuntimeShape &unextended_output_activ_shape, + uint8 *output_activ_data_uint8, const RuntimeShape &unextended_concat_temp_shape, + uint8 *concat_temp_data_uint8, const RuntimeShape &unextended_activ_temp_shape, + int16 *activ_temp_data_int16, void *gemmlowp_context) +{ + (void)gemmlowp_context; // only used in optimized code. + int32 weights_zero_point = params.weights_zero_point; + int32 accum_multiplier = params.accum_multiplier; + int accum_shift = params.accum_shift; + TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); + const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); + const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); + const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); + const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); + const RuntimeShape output_state_shape = + RuntimeShape::ExtendedShape(4, unextended_output_state_shape); + const RuntimeShape output_activ_shape = + RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); + const RuntimeShape concat_temp_shape = + RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); + const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); + TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); + + // Gather dimensions information, and perform consistency checks. + const int weights_dim_count = weights_shape.DimensionsCount(); + const int outer_size = MatchingFlatSizeSkipDim(input_shape, 3, prev_activ_shape, prev_state_shape, + output_state_shape, output_activ_shape); + const int input_depth = input_shape.Dims(3); + const int prev_activ_depth = prev_activ_shape.Dims(3); + const int total_input_depth = prev_activ_depth + input_depth; + TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); + const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); + TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); + TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); + TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); + const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, + 3, output_activ_shape, 3); + TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); + const int fc_batches = FlatSizeSkipDim(activ_temp_shape, 3); + const int fc_output_depth = + MatchingDim(weights_shape, weights_dim_count - 2, activ_temp_shape, 3); + const int fc_accum_depth = total_input_depth; + TFLITE_DCHECK_EQ(fc_output_depth, 4 * output_depth); + + // Depth-concatenate prev_activ and input data together. + uint8 const *concat_input_arrays_data[2] = {input_data_uint8, prev_activ_data_uint8}; + const RuntimeShape *concat_input_arrays_shapes[2] = {&input_shape, &prev_activ_shape}; + tflite::ConcatenationParams concat_params; + concat_params.axis = 3; + concat_params.inputs_count = 2; + Concatenation(concat_params, concat_input_arrays_shapes, concat_input_arrays_data, + concat_temp_shape, concat_temp_data_uint8); + + // Implementation of the fully connected node inside the LSTM cell. + // The operands are 8-bit integers, the accumulators are internally 32bit + // integers, and the output is 16-bit fixed-point with 3 integer bits so + // the output range is [-2^3, 2^3] == [-8, 8]. The rationale for that + // is explained in the function comment above. + for (int b = 0; b < fc_batches; ++b) + { + for (int out_c = 0; out_c < fc_output_depth; ++out_c) + { + // Internal accumulation. + // Initialize accumulator with the bias-value. + int32 accum = bias_data_int32[out_c]; + // Accumulation loop. + for (int d = 0; d < fc_accum_depth; ++d) + { + int16 input_val = concat_temp_data_uint8[b * fc_accum_depth + d] - 128; + int16 weights_val = weights_data_uint8[out_c * fc_accum_depth + d] - weights_zero_point; + accum += input_val * weights_val; + } + // Down-scale the final int32 accumulator to the scale used by our + // (16-bit, using 3 integer bits) fixed-point format. The quantized + // multiplier and shift here have been pre-computed offline + // (e.g. by toco). + accum = MultiplyByQuantizedMultiplier(accum, accum_multiplier, accum_shift); + // Saturate, cast to int16, and store to the temporary activations array. + accum = std::max(-32768, std::min(32767, static_cast(accum))); + activ_temp_data_int16[out_c + fc_output_depth * b] = accum; + } + } + + // Rest of the LSTM cell: tanh and logistic math functions, and some adds + // and muls, all done in 16-bit fixed-point. + for (int b = 0; b < outer_size; ++b) + { + for (int c = 0; c < output_depth; ++c) + { + // Define the fixed-point data types that we will use here. All use + // int16 as the underlying integer type i.e. all are 16-bit fixed-point. + // They only differ by the number of integral vs. fractional bits, + // determining the range of values that they can represent. + // + // F0 uses 0 integer bits, range [-1, 1]. + // This is the return type of math functions such as tanh, logistic, + // whose range is in [-1, 1]. + using F0 = gemmlowp::FixedPoint; + // F3 uses 3 integer bits, range [-8, 8]. + // This is the range of the previous fully-connected node's output, + // which is our input here. + using F3 = gemmlowp::FixedPoint; + // FS uses StateIntegerBits integer bits, range [-2^StateIntegerBits, + // 2^StateIntegerBits]. It's used to represent the internal state, whose + // number of integer bits is currently dictated by the model. See comment + // on the StateIntegerBits template parameter above. + using FS = gemmlowp::FixedPoint; + // Implementation of input gate, using fixed-point logistic function. + F3 input_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 0 * output_depth + c]); + F0 input_gate_output = gemmlowp::logistic(input_gate_input); + // Implementation of input modulation gate, using fixed-point tanh + // function. + F3 input_modulation_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 1 * output_depth + c]); + F0 input_modulation_gate_output = gemmlowp::tanh(input_modulation_gate_input); + // Implementation of forget gate, using fixed-point logistic function. + F3 forget_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 2 * output_depth + c]); + F0 forget_gate_output = gemmlowp::logistic(forget_gate_input); + // Implementation of output gate, using fixed-point logistic function. + F3 output_gate_input = + F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 3 * output_depth + c]); + F0 output_gate_output = gemmlowp::logistic(output_gate_input); + // Implementation of internal multiplication nodes, still in fixed-point. + F0 input_times_input_modulation = input_gate_output * input_modulation_gate_output; + FS prev_state = FS::FromRaw(prev_state_data_int16[b * output_depth + c]); + FS prev_state_times_forget_state = forget_gate_output * prev_state; + // Implementation of internal addition node, saturating. + FS new_state = + gemmlowp::SaturatingAdd(gemmlowp::Rescale(input_times_input_modulation), + prev_state_times_forget_state); + // Implementation of last internal Tanh node, still in fixed-point. + // Since a Tanh fixed-point implementation is specialized for a given + // number or integer bits, and each specialization can have a substantial + // code size, and we already used above a Tanh on an input with 3 integer + // bits, and per the table in the above function comment there is no + // significant accuracy to be lost by clamping to [-8, +8] for a + // 3-integer-bits representation, let us just do that. This helps people + // porting this to targets where code footprint must be minimized. + F3 new_state_f3 = gemmlowp::Rescale<3>(new_state); + F0 output_activ_int16 = output_gate_output * gemmlowp::tanh(new_state_f3); + // Store the new internal state back to memory, as 16-bit integers. + // Note: here we store the original value with StateIntegerBits, not + // the rescaled 3-integer-bits value fed to tanh. + output_state_data_int16[b * output_depth + c] = new_state.raw(); + // Down-scale the output activations to 8-bit integers, saturating, + // and store back to memory. + int16 rescaled_output_activ = gemmlowp::RoundingDivideByPOT(output_activ_int16.raw(), 8); + int16 clamped_output_activ = + std::max(-128, std::min(127, rescaled_output_activ)); + output_activ_data_uint8[b * output_depth + c] = 128 + clamped_output_activ; + } + } +} + +template +void Split(const SplitParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape *const *output_shapes, Scalar *const *output_data) +{ + ruy::profiler::ScopeLabel label("Split"); + const int split_dimensions = input_shape.DimensionsCount(); + int axis = params.axis < 0 ? params.axis + split_dimensions : params.axis; + int outputs_count = params.num_split; + TFLITE_DCHECK_LT(axis, split_dimensions); + + int64_t split_size = 0; + for (int i = 0; i < outputs_count; i++) + { + TFLITE_DCHECK_EQ(output_shapes[i]->DimensionsCount(), split_dimensions); + for (int j = 0; j < split_dimensions; j++) + { + if (j != axis) + { + MatchingDim(*output_shapes[i], j, input_shape, j); + } + } + split_size += output_shapes[i]->Dims(axis); + } + TFLITE_DCHECK_EQ(split_size, input_shape.Dims(axis)); + int64_t outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + // For all output arrays, + // FlatSize() = outer_size * Dims(axis) * base_inner_size; + int64_t base_inner_size = 1; + for (int i = axis + 1; i < split_dimensions; ++i) + { + base_inner_size *= input_shape.Dims(i); + } + + const Scalar *input_ptr = input_data; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < outputs_count; ++i) + { + const int copy_size = output_shapes[i]->Dims(axis) * base_inner_size; + memcpy(output_data[i] + k * copy_size, input_ptr, copy_size * sizeof(Scalar)); + input_ptr += copy_size; + } + } +} + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +inline void LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, + const RuntimeShape &input_shape, const float *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + const int trailing_dim = input_shape.DimensionsCount() - 1; + const int outer_size = MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); + const int depth = MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); + + for (int i = 0; i < outer_size; ++i) + { + for (int c = 0; c < depth; ++c) + { + const int begin_input_c = std::max(0, static_cast(c - op_params.range)); + const int end_input_c = std::min(depth, static_cast(c + op_params.range)); + float accum = 0.f; + for (int input_c = begin_input_c; input_c < end_input_c; ++input_c) + { + const float input_val = input_data[i * depth + input_c]; + accum += input_val * input_val; + } + const float multiplier = std::pow(op_params.bias + op_params.alpha * accum, -op_params.beta); + output_data[i * depth + c] = input_data[i * depth + c] * multiplier; + } + } +} + +inline void Dequantize(const RuntimeShape &input_shape, const Eigen::half *input_data, + const RuntimeShape &output_shape, float *output_data) +{ + const int flat_size = MatchingFlatSize(input_shape, output_shape); + for (int i = 0; i < flat_size; i++) + { + output_data[i] = static_cast(input_data[i]); + } +} + +inline void FakeQuant(const tflite::FakeQuantParams &op_params, const RuntimeShape &input_shape, + const float *input_data, const RuntimeShape &output_shape, float *output_data) +{ + ruy::profiler::ScopeLabel label("FakeQuant"); + float rmin = op_params.minmax.min; + float rmax = op_params.minmax.max; + int num_bits = op_params.num_bits; + // 0 should always be a representable value. Let's assume that the initial + // min,max range contains 0. + TFLITE_DCHECK_LE(rmin, 0.0f); + TFLITE_DCHECK_GE(rmax, 0.0f); + TFLITE_DCHECK_LT(rmin, rmax); + + // Code matches tensorflow's FakeQuantWithMinMaxArgsFunctor. + int quant_min = 0; + int quant_max = (1 << num_bits) - 1; + float nudged_min, nudged_max, nudged_scale; + NudgeQuantizationRange(rmin, rmax, quant_min, quant_max, &nudged_min, &nudged_max, &nudged_scale); + const int flat_size = MatchingFlatSize(input_shape, output_shape); + FakeQuantizeArray(nudged_scale, nudged_min, nudged_max, input_data, output_data, flat_size); +} + +// Common subroutine for both `GatherNd` and `GatherNdString`. +struct GatherNdHelperResult +{ + int n_slices; + int slice_size; + int indices_nd; + std::vector dims_to_count; +}; + +// Returns common values being used on both `GatherNd` and `GatherNdString`. +inline GatherNdHelperResult GatherNdHelper(const RuntimeShape ¶ms_shape, + const RuntimeShape &indices_shape) +{ + GatherNdHelperResult ret; + ret.n_slices = 1; + ret.slice_size = 1; + const int indices_dims = indices_shape.DimensionsCount(); + ret.indices_nd = indices_shape.Dims(indices_dims - 1); + const int params_dims = params_shape.DimensionsCount(); + for (int i = 0; i < indices_dims - 1; ++i) + { + ret.n_slices *= indices_shape.Dims(i); + } + for (int i = ret.indices_nd; i < params_dims; ++i) + { + ret.slice_size *= params_shape.Dims(i); + } + + int remain_flat_size = params_shape.FlatSize(); + ret.dims_to_count = std::vector(ret.indices_nd, 0); + for (int i = 0; i < ret.indices_nd; ++i) + { + ret.dims_to_count[i] = remain_flat_size / params_shape.Dims(i); + remain_flat_size = ret.dims_to_count[i]; + } + + return ret; +} + +template +inline void GatherNd(const RuntimeShape ¶ms_shape, const ParamsT *params_data, + const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &output_shape, ParamsT *output_data) +{ + ruy::profiler::ScopeLabel label("GatherNd"); + + const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); + for (int i = 0; i < res.n_slices; ++i) + { + int from_pos = 0; + for (int j = 0; j < res.indices_nd; ++j) + { + from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; + } + std::memcpy(output_data + i * res.slice_size, params_data + from_pos, + sizeof(ParamsT) * res.slice_size); + } +} + +#ifndef TF_LITE_STATIC_MEMORY +template +inline void GatherNdString(const RuntimeShape ¶ms_shape, const TfLiteTensor *params_data, + const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &output_shape, TfLiteTensor *output_data) +{ + ruy::profiler::ScopeLabel label("GatherNdString"); + + const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); + DynamicBuffer buffer; + for (int i = 0; i < res.n_slices; ++i) + { + int from_pos = 0; + for (int j = 0; j < res.indices_nd; ++j) + { + from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; + } + for (int j = 0; j < res.slice_size; ++j) + { + buffer.AddString(GetString(params_data, from_pos + j)); + } + } + buffer.WriteToTensor(output_data, /*new_shape=*/nullptr); +} +#endif + +template +inline void ScatterNd(const RuntimeShape &indices_shape, const IndicesT *indices_data, + const RuntimeShape &updates_shape, const UpdatesT *updates_data, + const RuntimeShape &output_shape, UpdatesT *output_data) +{ + ruy::profiler::ScopeLabel label("ScatterNd"); + + int n_slices = 1; + int slice_size = 1; + const int outer_dims = indices_shape.DimensionsCount() - 1; + const int indices_nd = indices_shape.Dims(outer_dims); + const int updates_dims = updates_shape.DimensionsCount(); + for (int i = 0; i < outer_dims; ++i) + { + n_slices *= indices_shape.Dims(i); + } + for (int i = outer_dims; i < updates_dims; ++i) + { + slice_size *= updates_shape.Dims(i); + } + + int output_flat_size = output_shape.FlatSize(); + int remain_flat_size = output_flat_size; + std::vector dims_to_count(indices_nd, 0); + for (int i = 0; i < indices_nd; ++i) + { + dims_to_count[i] = remain_flat_size / output_shape.Dims(i); + remain_flat_size = dims_to_count[i]; + } + + memset(output_data, 0, sizeof(UpdatesT) * output_flat_size); + for (int i = 0; i < n_slices; ++i) + { + int to_pos = 0; + for (int j = 0; j < indices_nd; ++j) + { + IndicesT idx = indices_data[i * indices_nd + j]; + TFLITE_DCHECK(0 <= idx && idx < output_shape.Dims(j)); + to_pos += idx * dims_to_count[j]; + } + for (int j = 0; j < slice_size; j++) + { + output_data[to_pos + j] += updates_data[i * slice_size + j]; + } + } +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const RuntimeShape &output_shape, SequentialTensorWriter *writer) +{ + const RuntimeShape ext_shape = RuntimeShape::ExtendedShape(5, input_shape); + TFLITE_DCHECK_LE(op_params.begin_count, 5); + TFLITE_DCHECK_LE(op_params.size_count, 5); + const int begin_count = op_params.begin_count; + const int size_count = op_params.size_count; + // We front-pad the begin and size vectors. + std::array start; + std::array stop; + for (int i = 0; i < 5; ++i) + { + int padded_i = 5 - i; + start[i] = begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; + stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) + ? ext_shape.Dims(i) + : start[i] + op_params.size[size_count - padded_i]; + } + + for (int i0 = start[0]; i0 < stop[0]; ++i0) + { + for (int i1 = start[1]; i1 < stop[1]; ++i1) + { + for (int i2 = start[2]; i2 < stop[2]; ++i2) + { + for (int i3 = start[3]; i3 < stop[3]; ++i3) + { + for (int i4 = start[4]; i4 < stop[4]; ++i4) + { + writer->Write(Offset(ext_shape, i0, i1, i2, i3, i4)); + } + } + } + } + } +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const T *input_data, const RuntimeShape &output_shape, T *output_data) +{ + SequentialTensorWriter writer(input_data, output_data); + return Slice(op_params, input_shape, output_shape, &writer); +} + +template +inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, + const TfLiteTensor *input, const RuntimeShape &output_shape, TfLiteTensor *output) +{ + SequentialTensorWriter writer(input, output); + return Slice(op_params, input_shape, output_shape, &writer); +} + +template +void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, output_shape); + + auto min_value = input2_data[0]; + for (int i = 0; i < flat_size; i++) + { + output_data[i] = input1_data[i] > min_value ? min_value : input1_data[i]; + } +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, + const T *input2_data, const RuntimeShape &output_shape, T *output_data) +{ + // Drop shape of second input: not needed. + Minimum(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, output_shape); + + auto max_value = input2_data[0]; + for (int i = 0; i < flat_size; i++) + { + output_data[i] = input1_data[i] < max_value ? max_value : input1_data[i]; + } +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, + const T *input2_data, const RuntimeShape &output_shape, T *output_data) +{ + // Drop shape of second input: not needed. + Maximum(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, const T3 *input2_data, + const RuntimeShape &output_shape, T2 *output_data) +{ + ArgMinMax(input1_shape, input1_data, input2_data, output_shape, output_data, std::greater()); +} + +// Convenience version that allows, for example, generated-code calls to be +// the same as other binary ops. +template +inline void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, + const RuntimeShape &input2_shape, const T3 *input2_data, + const RuntimeShape &output_shape, T2 *output_data) +{ + // Drop shape of second input: not needed. + ArgMax(input1_shape, input1_data, input2_data, output_shape, output_data); +} + +template +void Select(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + int64_t flatsize; + // Allow select operator executions on mixed scalar tensors and one element + // tensors. + if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 && + input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) + { + flatsize = 1; + } + else + { + flatsize = MatchingFlatSize(input_condition_shape, input_x_shape, input_y_shape, output_shape); + } + for (int64_t i = 0; i < flatsize; ++i) + { + output_data[i] = input_condition_data[i] ? input_x_data[i] : input_y_data[i]; + } +} + +template +void RankOneSelect(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int64_t outer_size = input_condition_shape.FlatSize(); + int64_t inner_size; + if (input_condition_shape.DimensionsCount() == 0) + { + inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape); + } + else + { + TFLITE_DCHECK_EQ(MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0), outer_size); + inner_size = MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape); + } + + int64_t offset = 0; + for (int64_t i = 0; i < outer_size; i++) + { + const T *input_data = input_condition_data[i] ? input_x_data : input_y_data; + memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T)); + offset += inner_size; + } +} + +template +void BroadcastSelect4DSlow(const RuntimeShape &input_condition_shape, const D *input_condition_data, + const RuntimeShape &input_x_shape, const T *input_x_data, + const RuntimeShape &input_y_shape, const T *input_y_data, + const RuntimeShape &output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4); + + const RuntimeShape extended_output_shape = RuntimeShape::ExtendedShape(4, output_shape); + + NdArrayDesc<4> desc_condition; + NdArrayDesc<4> desc_x; + NdArrayDesc<4> desc_y; + NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape, input_y_shape, + &desc_condition, &desc_x, &desc_y); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest + // stride, typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for + // the best cache behavior. + for (int b = 0; b < extended_output_shape.Dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.Dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.Dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.Dims(3); ++c) + { + const int condition_index = SubscriptToIndex(desc_condition, b, y, x, c); + const int x_index = SubscriptToIndex(desc_x, b, y, x, c); + const int y_index = SubscriptToIndex(desc_y, b, y, x, c); + output_data[Offset(extended_output_shape, b, y, x, c)] = + input_condition_data[condition_index] ? input_x_data[x_index] : input_y_data[y_index]; + } + } + } + } +} + +template +void SelectTrueCoords(const RuntimeShape &input_condition_shape, const D *input_condition_data, + T *output_data) +{ + const size_t size = input_condition_shape.FlatSize(); + if (size == 0) + { + // Dimension is zero, in which case we don't need to output. + return; + } + const size_t cond_rank = input_condition_shape.DimensionsCount(); + + std::vector dims_to_count(cond_rank, 0); + int cur_flat_size = size; + for (int i = 0; i < cond_rank; ++i) + { + dims_to_count[i] = cur_flat_size / input_condition_shape.Dims(i); + cur_flat_size = dims_to_count[i]; + } + + int output_index = 0; + for (int i = 0; i < size; ++i) + { + if (input_condition_data[i]) + { + // Insert the coordinate of the current item (row major) into output. + int flat_index = i; + for (int j = 0; j < cond_rank; ++j) + { + int coord_j = flat_index / dims_to_count[j]; + output_data[output_index * cond_rank + j] = coord_j; + flat_index %= dims_to_count[j]; + } + output_index++; + } + } +} + +// For easy implementation, the indices is always a vector of size-4 vectors. +template +inline void SparseToDense(const std::vector> &indices, const T *values, + T default_value, bool value_is_scalar, + const RuntimeShape &unextended_output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); + const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); + const int value_count = indices.size(); + + // First fill the output_data with default value. + const int num_elements = output_shape.FlatSize(); + for (int i = 0; i < num_elements; ++i) + { + output_data[i] = default_value; + } + + // Special handle for value is scalar case to avoid checking the boolean + // condition within the loop every time. + if (value_is_scalar) + { + for (int i = 0; i < value_count; ++i) + { + const std::vector &index = indices[i]; + TFLITE_DCHECK_EQ(index.size(), 4); + const T value = *values; // just use the first value. + output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; + } + return; + } + + // Go through the values and indices to fill the sparse values. + for (int i = 0; i < value_count; ++i) + { + const std::vector &index = indices[i]; + TFLITE_DCHECK_EQ(index.size(), 4); + const T value = values[i]; + output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; + } +} + +template +inline void Pow(const RuntimeShape &input1_shape, const T *input1_data, + const RuntimeShape &input2_shape, const T *input2_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = MatchingFlatSize(input1_shape, input2_shape, output_shape); + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = std::pow(input1_data[i], input2_data[i]); + } +} + +template +inline void BroadcastPow4DSlow(const RuntimeShape &unextended_input1_shape, const T *input1_data, + const RuntimeShape &unextended_input2_shape, const T *input2_data, + const RuntimeShape &unextended_output_shape, T *output_data) +{ + TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4); + TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); + const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); + + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, + &desc2); + + for (int b = 0; b < output_shape.Dims(0); ++b) + { + for (int y = 0; y < output_shape.Dims(1); ++y) + { + for (int x = 0; x < output_shape.Dims(2); ++x) + { + for (int c = 0; c < output_shape.Dims(3); ++c) + { + auto out_idx = Offset(output_shape, b, y, x, c); + auto in1_idx = SubscriptToIndex(desc1, b, y, x, c); + auto in2_idx = SubscriptToIndex(desc2, b, y, x, c); + auto in1_val = input1_data[in1_idx]; + auto in2_val = input2_data[in2_idx]; + output_data[out_idx] = std::pow(in1_val, in2_val); + } + } + } + } +} + +template +void Reverse(int axis, const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("Reverse"); + + int outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= input_shape.Dims(i); + } + + int copy_size = 1; + for (int i = axis + 1; i < input_shape.DimensionsCount(); ++i) + { + copy_size *= input_shape.Dims(i); + } + + const int dims_at_axis = input_shape.Dims(axis); + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_axis; ++j) + { + const int start_pos = (i * dims_at_axis + j) * copy_size; + Scalar *output_ptr = output_data + start_pos; + int loc = (i * dims_at_axis + dims_at_axis - j - 1) * copy_size; + memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); + } + } +} + +template +void ReverseSequence(const TS *seq_lengths, const int seq_dim, const int batch_dim, + const RuntimeShape &input_shape, const Scalar *input_data, + const RuntimeShape &output_shape, Scalar *output_data) +{ + ruy::profiler::ScopeLabel label("ReverseSequence"); + + int outer_size = 1; + int outer_dim = std::min(batch_dim, seq_dim); + int medium_dim = std::max(batch_dim, seq_dim); + for (int i = 0; i < outer_dim; ++i) + { + outer_size *= input_shape.Dims(i); + } + + int medium_size = 1; + for (int i = outer_dim + 1; i < medium_dim; ++i) + { + medium_size *= input_shape.Dims(i); + } + + int copy_size = 1; + for (int i = medium_dim + 1; i < input_shape.DimensionsCount(); ++i) + { + copy_size *= input_shape.Dims(i); + } + + const int dims_at_outer_dim = input_shape.Dims(outer_dim); + const int dims_at_medium_dim = input_shape.Dims(medium_dim); + + Scalar *output_ptr; + if (batch_dim > seq_dim) + { + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_outer_dim; ++j) + { + const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; + for (int p = 0; p < medium_size; ++p) + { + for (int q = 0; q < dims_at_medium_dim; ++q) + { + const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; + const Scalar *in_ptr = input_data + in_pos; + int sl = seq_lengths[q] - 1; + if (j > sl) + { + output_ptr = output_data + in_pos; + } + else + { + const int out_pos_base = (i * dims_at_outer_dim + sl - j) * medium_size; + const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + q) * copy_size; + output_ptr = output_data + out_pos; + } + memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); + } + } + } + } + } + else if (batch_dim < seq_dim) + { + for (int i = 0; i < outer_size; ++i) + { + for (int j = 0; j < dims_at_outer_dim; ++j) + { + const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; + int sl = seq_lengths[j] - 1; + const int out_pos_base = (i * dims_at_outer_dim + j) * medium_size; + for (int p = 0; p < medium_size; ++p) + { + for (int q = 0; q < dims_at_medium_dim; ++q) + { + const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; + const Scalar *in_ptr = input_data + in_pos; + if (q > sl) + { + output_ptr = output_data + in_pos; + } + else + { + const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + sl - q) * copy_size; + output_ptr = output_data + out_pos; + } + memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); + } + } + } + } + } +} + +template +inline void SegmentSum(const RuntimeShape &input_shape, const T *input_data, + const RuntimeShape &segment_ids_shape, const int32_t *segment_ids_data, + const RuntimeShape &output_shape, T *output_data) +{ + const int segment_flat_size = MatchingFlatSizeSkipDim(input_shape, 0, output_shape); + + memset(output_data, 0, sizeof(T) * output_shape.FlatSize()); + + for (int i = 0; i < input_shape.Dims(0); i++) + { + int output_index = segment_ids_data[i]; + for (int j = 0; j < segment_flat_size; ++j) + { + output_data[output_index * segment_flat_size + j] += input_data[i * segment_flat_size + j]; + } + } +} + +} // namespace reference_ops +} // namespace tflite + +#endif // LUCI_INTERPRETER_PAL_REFERENCE_OPS_H diff --git a/compiler/luci-interpreter/src/core/KernelParams.h b/compiler/luci-interpreter/src/core/KernelParams.h index 958fd4b..6c0220c 100644 --- a/compiler/luci-interpreter/src/core/KernelParams.h +++ b/compiler/luci-interpreter/src/core/KernelParams.h @@ -170,6 +170,11 @@ struct ResizeNearestNeighborParams bool half_pixel_centers; }; +struct ShapeParams +{ + loco::DataType out_type; +}; + struct SubParams { Activation activation; diff --git a/compiler/luci-interpreter/src/kernels/Fill.cpp b/compiler/luci-interpreter/src/kernels/Fill.cpp new file mode 100644 index 0000000..e09d633 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Fill.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Fill.h" +#include "kernels/Utils.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Fill::Fill(const Tensor *dims, const Tensor *value, Tensor *output) + : Kernel({dims, value}, {output}) +{ +} + +template void Fill::configureShape() +{ + const auto dims_data = getTensorData(dims()); + Shape output_shape(dims()->shape().dim(0)); + + for (int i = 0; i < output_shape.num_dims(); ++i) + { + T data = dims_data[i]; + if (data < 0) + throw std::runtime_error("Fill dimensions must be >= 0"); + + output_shape.dim(i) = data; + } + + output()->resize(output_shape); +} + +void Fill::configure() +{ + const auto dims_shape = dims()->shape(); + const auto value_shape = value()->shape(); + + // Make sure the 1st input tensor is 1-D + LUCI_INTERPRETER_CHECK(dims_shape.num_dims() == 1); + + // Make sure the 1st input tensor is int32 or int64 + LUCI_INTERPRETER_CHECK(dims()->element_type() == DataType::S32 or + dims()->element_type() == DataType::S64); + + // Make sure the 2nd input tensor is a scalar + LUCI_INTERPRETER_CHECK(value_shape.num_dims() == 0) + + // Check zero point and scale for S16 and S8 + if (value()->element_type() == loco::DataType::S16 or + value()->element_type() == loco::DataType::S8) + { + LUCI_INTERPRETER_CHECK(value()->scale() == output()->scale()); + LUCI_INTERPRETER_CHECK(value()->zero_point() == output()->zero_point()); + + if (value()->element_type() == loco::DataType::S16) + LUCI_INTERPRETER_CHECK(value()->zero_point() == 0); + } + // Resize output + switch (dims()->element_type()) + { + case DataType::S32: + configureShape(); + break; + case DataType::S64: + configureShape(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Fill::execute() const +{ + switch (output()->element_type()) + { + case DataType::S8: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S16: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S32: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S64: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::FLOAT32: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Fill.h b/compiler/luci-interpreter/src/kernels/Fill.h new file mode 100644 index 0000000..184f0cb --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Fill.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_FILL_H +#define LUCI_INTERPRETER_KERNELS_FILL_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Fill : public Kernel +{ +public: + Fill(const Tensor *dims, const Tensor *value, Tensor *output); + + const Tensor *dims() const { return _inputs[0]; } + const Tensor *value() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void configureShape(); +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_FILL_H diff --git a/compiler/luci-interpreter/src/kernels/Fill.test.cpp b/compiler/luci-interpreter/src/kernels/Fill.test.cpp new file mode 100644 index 0000000..cf56df5 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Fill.test.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Fill.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class FillTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +template void runFillIntKernel(IMemoryManager *memory_manager) +{ + Shape dims_shape{2}; + + std::vector dims_data = {2, 3}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); + Tensor value = makeInputTensor
(/*scalar*/ {}, value_data, memory_manager); + + Tensor output_tensor = makeOutputTensor(DT); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + + std::vector ref_output_shape{2, 3}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +template void runFillQuantIntKernel(IMemoryManager *memory_manager) +{ + Shape dims_shape{2}; + + std::vector dims_data = {2, 3}; + std::vector value_data = {5}; + + int32_t zero_point = 0; + + if (DT == loco::DataType::S8) + zero_point = 1; + + Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); + Tensor value = makeInputTensor
(/*scalar*/ {}, /*scale*/ 0.25, /*zero_point*/ zero_point, + value_data, memory_manager); + + Tensor output_tensor = makeOutputTensor(DT, /*scale*/ 0.25, /*zero_point*/ zero_point); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + + std::vector ref_output_shape{2, 3}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FillTest, FillInt) +{ + // Run for int32_t input + runFillIntKernel(_memory_manager.get()); + // Run for int64_t input + runFillIntKernel(_memory_manager.get()); + // Run for int8_t input + runFillQuantIntKernel(_memory_manager.get()); + // Run for int16_t input + runFillQuantIntKernel(_memory_manager.get()); + + SUCCEED(); +} + +TEST_F(FillTest, FillFloat) +{ + Shape dims_shape{3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = + makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5, 5, 5}; + + std::vector ref_output_shape{2, 2, 2}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FillTest, Invalid_Input_Shape_NEG) +{ + Shape dims_shape{1, 3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = + makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(FillTest, Invalid_Value_Shape_NEG) +{ + Shape dims_shape{3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = makeInputTensor({1}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/MirrorPad.cpp b/compiler/luci-interpreter/src/kernels/MirrorPad.cpp index 2fbeefc..bae1eac 100644 --- a/compiler/luci-interpreter/src/kernels/MirrorPad.cpp +++ b/compiler/luci-interpreter/src/kernels/MirrorPad.cpp @@ -19,6 +19,8 @@ #include "kernels/Utils.h" +#include + namespace luci_interpreter { namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/Pack.cpp b/compiler/luci-interpreter/src/kernels/Pack.cpp index 6fee938..42aab33 100644 --- a/compiler/luci-interpreter/src/kernels/Pack.cpp +++ b/compiler/luci-interpreter/src/kernels/Pack.cpp @@ -76,9 +76,8 @@ void Pack::configure() } } - if (t0->element_type() == DataType::S32 || t0->element_type() == DataType::U8 || - t0->element_type() == DataType::S8 || t0->element_type() == DataType::S16 || - t0->element_type() == DataType::S64) + if (t0->element_type() == DataType::U8 || t0->element_type() == DataType::S8 || + t0->element_type() == DataType::S16) { LUCI_INTERPRETER_CHECK(output()->zero_point() == t0->zero_point()); LUCI_INTERPRETER_CHECK(output()->scale() == t0->scale()); diff --git a/compiler/luci-interpreter/src/kernels/Pack.test.cpp b/compiler/luci-interpreter/src/kernels/Pack.test.cpp index 2404e43..d16320b 100644 --- a/compiler/luci-interpreter/src/kernels/Pack.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Pack.test.cpp @@ -38,18 +38,26 @@ void Check(std::vector> input_shapes, std::vector tmp_inputs; for (int i = 0; i < input_datas.size(); i++) { - if (std::is_same::value) + if (std::is_same::value || std::is_same::value || + std::is_same::value) { tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {}, "")); memory_manager->allocate_memory(tmp_inputs[i]); tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); } - else + else if (std::is_same::value || std::is_same::value) { tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f / 255}, {128}}, "")); memory_manager->allocate_memory(tmp_inputs[i]); tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); } + else + { + assert((std::is_same::value) && "unexpected dtype is tested"); + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f}, {0}}, "")); + memory_manager->allocate_memory(tmp_inputs[i]); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } } for (int i = 0; i < input_datas.size(); i++) { @@ -57,10 +65,14 @@ void Check(std::vector> input_shapes, } Tensor output_tensor = makeOutputTensor(element_type); - if (!std::is_same::value) + if (std::is_same::value || std::is_same::value) { output_tensor = makeOutputTensor(element_type, 1.0f / 255, 128); } + else if (std::is_same::value) + { + output_tensor = makeOutputTensor(element_type, 1.0f, 0); + } PackParams params{}; params.axis = axis; @@ -79,7 +91,7 @@ template class PackTest : public ::testing::Test { }; -using DataTypes = ::testing::Types; +using DataTypes = ::testing::Types; TYPED_TEST_SUITE(PackTest, DataTypes); TYPED_TEST(PackTest, ThreeInputs) diff --git a/compiler/luci-interpreter/src/kernels/Pad.cpp b/compiler/luci-interpreter/src/kernels/Pad.cpp index fe17288..c07f6e3 100644 --- a/compiler/luci-interpreter/src/kernels/Pad.cpp +++ b/compiler/luci-interpreter/src/kernels/Pad.cpp @@ -20,6 +20,8 @@ #include +#include + namespace luci_interpreter { namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/PadV2.cpp b/compiler/luci-interpreter/src/kernels/PadV2.cpp index e904692..197cdaa 100644 --- a/compiler/luci-interpreter/src/kernels/PadV2.cpp +++ b/compiler/luci-interpreter/src/kernels/PadV2.cpp @@ -20,6 +20,8 @@ #include +#include + namespace luci_interpreter { namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/ReduceMax.cpp b/compiler/luci-interpreter/src/kernels/ReduceMax.cpp new file mode 100644 index 0000000..d58cd15 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/ReduceMax.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ReduceMax.h" + +#include "kernels/Utils.h" + +#include + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +// Returns the number of axes that will be reduced. Removes duplicates. +static int getAxisReductionCount(const int32_t *axes_data, int num_axes, int input_num_dims) +{ + int reduction_count = num_axes; + for (int i = 0; i < num_axes; ++i) + { + int current = axes_data[i] >= 0 ? axes_data[i] : axes_data[i] + input_num_dims; + assert(current >= 0 && current < input_num_dims); + for (int j = 0; j < i; j++) + { + int previous = axes_data[j] >= 0 ? axes_data[j] : axes_data[j] + input_num_dims; + // This checks for duplicate axis + if (current == previous) + { + --reduction_count; + break; + } + } + } + return reduction_count; +} + +static Shape getOutputShape(const Shape &input_shape, const int32_t *axes_data, int num_axes, + bool keep_dims) +{ + int input_num_dims = input_shape.num_dims(); + if (input_num_dims == 0) + { + return Shape(0); + } + + if (keep_dims) + { + Shape output_shape(input_num_dims); + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + is_axis = true; + break; + } + } + if (is_axis) + { + output_shape.dim(idx) = 1; + } + else + { + output_shape.dim(idx) = input_shape.dim(idx); + } + } + return output_shape; + } + else + { + int num_reduce_axes = getAxisReductionCount(axes_data, num_axes, input_num_dims); + Shape output_shape(input_num_dims - num_reduce_axes); + int num_skip_axes = 0; + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + ++num_skip_axes; + is_axis = true; + break; + } + } + if (!is_axis) + { + output_shape.dim(idx - num_skip_axes) = input_shape.dim(idx); + } + } + return output_shape; + } +} + +ReduceMax::ReduceMax(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, const ReducerParams ¶ms) + : KernelWithParams({input, axes}, {output, temp_index, resolved_axes}, params) +{ +} + +void ReduceMax::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32); + + const Shape &input_shape = input()->shape(); + int input_num_dims = input_shape.num_dims(); + + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + LUCI_INTERPRETER_CHECK(num_axes <= 4); + + // We compute shapes of outputs in configure, assuming that outputs have + // static shape + // TODO Support dynamic shape + Shape output_shape = getOutputShape(input_shape, axes_data, num_axes, _params.keep_dims); + output()->resize(output_shape); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + + temp_index->resize(Shape(input_num_dims)); + resolved_axes->resize(Shape(num_axes)); +} + +void ReduceMax::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + // TODO Support quantized kernels + default: + throw std::runtime_error("Unsupported type."); + } +} + +void ReduceMax::evalFloat() const +{ + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + + int num_resolved_axis = 0; + LUCI_INTERPRETER_CHECK( + tflite::reference_ops::ResolveAxis(input()->shape().num_dims(), axes_data, num_axes, + getTensorData(resolved_axes), &num_resolved_axis)); + + float init_value = std::numeric_limits::lowest(); + tflite::reference_ops::ReduceGeneric( + getTensorData(input()), getTensorShape(input()).DimsData(), input()->shape().num_dims(), + getTensorData(output()), getTensorShape(output()).DimsData(), + output()->shape().num_dims(), axes_data, num_axes, _params.keep_dims, + getTensorData(temp_index), getTensorData(resolved_axes), init_value, + [](const float current, const float in) -> float { return (in > current) ? in : current; }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/ReduceMax.h b/compiler/luci-interpreter/src/kernels/ReduceMax.h new file mode 100644 index 0000000..25a6627 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/ReduceMax.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_REDUCE_MAX_H +#define LUCI_INTERPRETER_KERNELS_REDUCE_MAX_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class ReduceMax : public KernelWithParams +{ +public: + ReduceMax(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, const ReducerParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *axes() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_REDUCE_MAX_H diff --git a/compiler/luci-interpreter/src/kernels/ReduceMax.test.cpp b/compiler/luci-interpreter/src/kernels/ReduceMax.test.cpp new file mode 100644 index 0000000..ab68882 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/ReduceMax.test.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ReduceMax.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ReduceMaxTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(ReduceMaxTest, FloatNotKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{1, 0, -3, -3}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({4}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = false; + + ReduceMax kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{23, 24}; + std::initializer_list ref_output_shape{2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(ReduceMaxTest, FloatKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{0, 2}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = true; + + ReduceMax kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{20, 22, 24}; + std::initializer_list ref_output_shape{1, 3, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Shape.cpp b/compiler/luci-interpreter/src/kernels/Shape.cpp new file mode 100644 index 0000000..0429fe1 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Shape.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Shape.h" +#include "kernels/Utils.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ShapeKernel::ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void ShapeKernel::configure() +{ + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::S32 or + output()->element_type() == DataType::S64); + const auto input_shape = input()->shape(); + + Shape output_shape(1); + output_shape.dim(0) = input_shape.num_dims(); + + output()->resize(output_shape); +} + +void ShapeKernel::execute() const +{ + switch (params().out_type) + { + case DataType::S32: + evalInt(); + break; + case DataType::S64: + evalInt(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void ShapeKernel::evalInt() const +{ + const auto input_shape = input()->shape(); + + auto output_data = getTensorData(output()); + + for (int i = 0; i < input_shape.num_dims(); ++i) + { + output_data[i] = input_shape.dim(i); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Shape.h b/compiler/luci-interpreter/src/kernels/Shape.h new file mode 100644 index 0000000..cfaadec --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Shape.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SHAPE_H +#define LUCI_INTERPRETER_KERNELS_SHAPE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ShapeKernel : public KernelWithParams +{ +public: + ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void evalInt() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SHAPE_H diff --git a/compiler/luci-interpreter/src/kernels/Shape.test.cpp b/compiler/luci-interpreter/src/kernels/Shape.test.cpp new file mode 100644 index 0000000..4763e01 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Shape.test.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Shape.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ShapeTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +template void runShapeKernel(loco::DataType dataType, IMemoryManager *memory_manager) +{ + Shape input_shape{1, 3, 1, 3, 5}; + + Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); + Tensor output_tensor = makeOutputTensor(dataType); + + ShapeParams params{}; + params.out_type = dataType; + + ShapeKernel kernel(&input_tensor, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{1, 3, 1, 3, 5}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + + std::vector ref_output_shape{5}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(ShapeTest, OutTypeInt) +{ + + // Run for int32_t output + runShapeKernel(loco::DataType::S32, _memory_manager.get()); + // Run for int64_t output + runShapeKernel(loco::DataType::S64, _memory_manager.get()); + + SUCCEED(); +} + +TEST_F(ShapeTest, Invalid_Output_Type_NEG) +{ + Shape input_shape{1, 3}; + + Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + ShapeParams params{}; + params.out_type = loco::DataType::FLOAT32; + + ShapeKernel kernel(&input_tensor, &output_tensor, params); + + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/SplitV.cpp b/compiler/luci-interpreter/src/kernels/SplitV.cpp index 2819882..aa68208 100644 --- a/compiler/luci-interpreter/src/kernels/SplitV.cpp +++ b/compiler/luci-interpreter/src/kernels/SplitV.cpp @@ -43,14 +43,36 @@ void SplitV::configure() auto sizes_data = getTensorData(size_splits()); assert(size_splits()->shape().num_dims() == 1); + + int32_t sum = 0; + const auto num_dims_size_spits = size_splits()->shape().dim(0); + int32_t count_neg_dim = 0; + + for (int32_t i = 0; i < num_dims_size_spits - 1; ++i) + { + if (sizes_data[i] != -1) + { + sum += sizes_data[i]; + } + else + { + count_neg_dim++; + } + } + assert(count_neg_dim < 2); assert(size_splits()->shape().num_elements() == num_split); - assert(std::accumulate(sizes_data, sizes_data + num_split, 0) == - input()->shape().dim(_axis_value)); auto output_shape = input()->shape(); for (int32_t i = 0; i < num_split; ++i) { - output_shape.dim(_axis_value) = sizes_data[i]; + if (sizes_data[i] == -1) + { + output_shape.dim(_axis_value) = input()->shape().dim(_axis_value) - sum; + } + else + { + output_shape.dim(_axis_value) = sizes_data[i]; + } _outputs[i]->resize(output_shape); } } diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.cpp b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp index c6452cd..a8730d8 100644 --- a/compiler/luci-interpreter/src/kernels/StridedSlice.cpp +++ b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp @@ -136,6 +136,11 @@ void StridedSlice::execute() const getTensorData(input()), getTensorShape(output()), getTensorData(output())); break; + case DataType::S32: + tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; default: throw std::runtime_error("Unsupported type."); } diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp index dba3905..4020709 100644 --- a/compiler/luci-interpreter/src/loader/GraphLoader.cpp +++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp @@ -187,7 +187,7 @@ void GraphLoader::loadTensors() const auto *node = loco::must_cast(_graph->nodes()->at(i)); if (node->opcode() == luci::CircleOpcode::CUSTOM && !isSupportedCustomNode(node)) - throw std::runtime_error("Unknown Custom Node, yet."); + throw std::runtime_error("Unsupported Custom operator. " + node->name()); if (!isTensorProducingNode(node)) continue; diff --git a/compiler/luci-interpreter/src/loader/nodes/Add.cpp b/compiler/luci-interpreter/src/loader/nodes/Add.cpp index decccaa..501e847 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Add.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Add.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleAdd(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/ArgMax.cpp b/compiler/luci-interpreter/src/loader/nodes/ArgMax.cpp index 0ee3677..f3ca557 100644 --- a/compiler/luci-interpreter/src/loader/nodes/ArgMax.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/ArgMax.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleArgMax(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); const Tensor *axis = helper.getInputTensor(node->dimension()); diff --git a/compiler/luci-interpreter/src/loader/nodes/AveragePool2D.cpp b/compiler/luci-interpreter/src/loader/nodes/AveragePool2D.cpp index efb0112..a813570 100644 --- a/compiler/luci-interpreter/src/loader/nodes/AveragePool2D.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/AveragePool2D.cpp @@ -25,9 +25,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleAveragePool2D(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->value()); diff --git a/compiler/luci-interpreter/src/loader/nodes/BatchMatMul.cpp b/compiler/luci-interpreter/src/loader/nodes/BatchMatMul.cpp index aae3dba..9da2f6d 100644 --- a/compiler/luci-interpreter/src/loader/nodes/BatchMatMul.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/BatchMatMul.cpp @@ -25,9 +25,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleBatchMatMul(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *lhs = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp b/compiler/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp index 33d0e2d..ac6ebb3 100644 --- a/compiler/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleBatchToSpaceND(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Cast.cpp b/compiler/luci-interpreter/src/loader/nodes/Cast.cpp index 21ea5ce..a16354c 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Cast.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Cast.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleCast(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); diff --git a/compiler/luci-interpreter/src/loader/nodes/Concatenation.cpp b/compiler/luci-interpreter/src/loader/nodes/Concatenation.cpp index 7823a99..ba2564e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Concatenation.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Concatenation.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleConcatenation(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); std::vector inputs(node->numValues()); for (uint32_t i = 0; i < node->numValues(); ++i) { diff --git a/compiler/luci-interpreter/src/loader/nodes/Conv2D.cpp b/compiler/luci-interpreter/src/loader/nodes/Conv2D.cpp index b48d97d..218165e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Conv2D.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Conv2D.cpp @@ -25,9 +25,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleConv2D(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/DepthToSpace.cpp b/compiler/luci-interpreter/src/loader/nodes/DepthToSpace.cpp index 0310fb2..1749463 100644 --- a/compiler/luci-interpreter/src/loader/nodes/DepthToSpace.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/DepthToSpace.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleDepthToSpace(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp b/compiler/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp index db26ecf..8af1e3b 100644 --- a/compiler/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp @@ -25,9 +25,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleDepthwiseConv2D(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Dequantize.cpp b/compiler/luci-interpreter/src/loader/nodes/Dequantize.cpp index 4aae564..787322e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Dequantize.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Dequantize.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleDequantize(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); const Tensor *input = helper.getInputTensor(node->input()); Tensor *output = helper.getOutputTensor(node); diff --git a/compiler/luci-interpreter/src/loader/nodes/Div.cpp b/compiler/luci-interpreter/src/loader/nodes/Div.cpp index 56c2e98..0611dfd 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Div.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Div.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleDiv(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); const Tensor *input2 = helper.getInputTensor(node->y()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Elu.cpp b/compiler/luci-interpreter/src/loader/nodes/Elu.cpp index 98ee78b..a79985e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Elu.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Elu.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleElu(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->features()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Equal.cpp b/compiler/luci-interpreter/src/loader/nodes/Equal.cpp index 649d9bf..5969288 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Equal.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Equal.cpp @@ -25,9 +25,7 @@ std::unique_ptr build_kernel_CircleEqual(const luci::CircleNode *circle_ KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Exp.cpp b/compiler/luci-interpreter/src/loader/nodes/Exp.cpp index 411d142..30d11cb 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Exp.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Exp.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleExp(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Fill.cpp b/compiler/luci-interpreter/src/loader/nodes/Fill.cpp new file mode 100644 index 0000000..3aefdf1 --- /dev/null +++ b/compiler/luci-interpreter/src/loader/nodes/Fill.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Fill.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleFill(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const auto dims = helper.getInputTensor(node->dims()); + const auto value = helper.getInputTensor(node->value()); + auto output = helper.getOutputTensor(node); + + return std::make_unique(dims, value, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/loader/nodes/Floor.cpp b/compiler/luci-interpreter/src/loader/nodes/Floor.cpp index 6d8435f..e0a2231 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Floor.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Floor.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleFloor(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/FloorDiv.cpp b/compiler/luci-interpreter/src/loader/nodes/FloorDiv.cpp index cae2e18..a45d89e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/FloorDiv.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/FloorDiv.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleFloorDiv(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/FullyConnected.cpp b/compiler/luci-interpreter/src/loader/nodes/FullyConnected.cpp index 0b8ac44..b7b742b 100644 --- a/compiler/luci-interpreter/src/loader/nodes/FullyConnected.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/FullyConnected.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleFullyConnected(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Gather.cpp b/compiler/luci-interpreter/src/loader/nodes/Gather.cpp index 9df9775..2ee2906 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Gather.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Gather.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleGather(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *params = helper.getInputTensor(node->params()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Greater.cpp b/compiler/luci-interpreter/src/loader/nodes/Greater.cpp index 3db11b8..80aa63c 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Greater.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Greater.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleGreater(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/GreaterEqual.cpp b/compiler/luci-interpreter/src/loader/nodes/GreaterEqual.cpp index dbe051d..272f284 100644 --- a/compiler/luci-interpreter/src/loader/nodes/GreaterEqual.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/GreaterEqual.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleGreaterEqual(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/If.cpp b/compiler/luci-interpreter/src/loader/nodes/If.cpp index 5983f4d..3ac7d49 100644 --- a/compiler/luci-interpreter/src/loader/nodes/If.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/If.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleIf(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); auto output_nodes = collectOutputNodes(node); assert(node->arity() == 1 + node->input_count()); assert(output_nodes.size() == static_cast(node->output_count())); diff --git a/compiler/luci-interpreter/src/loader/nodes/InstanceNorm.cpp b/compiler/luci-interpreter/src/loader/nodes/InstanceNorm.cpp index 0a8fb85..06031e5 100644 --- a/compiler/luci-interpreter/src/loader/nodes/InstanceNorm.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/InstanceNorm.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleInstanceNorm(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/L2Normalize.cpp b/compiler/luci-interpreter/src/loader/nodes/L2Normalize.cpp index 05f9202..6e22e6d 100644 --- a/compiler/luci-interpreter/src/loader/nodes/L2Normalize.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/L2Normalize.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleL2Normalize(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/L2Pool2D.cpp b/compiler/luci-interpreter/src/loader/nodes/L2Pool2D.cpp index 0e70afa..95b5589 100644 --- a/compiler/luci-interpreter/src/loader/nodes/L2Pool2D.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/L2Pool2D.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleL2Pool2D(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->value()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LeakyRelu.cpp b/compiler/luci-interpreter/src/loader/nodes/LeakyRelu.cpp index 7b229ad..bbf5067 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LeakyRelu.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LeakyRelu.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLeakyRelu(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->features()); Tensor *output = helper.getOutputTensor(node); diff --git a/compiler/luci-interpreter/src/loader/nodes/Less.cpp b/compiler/luci-interpreter/src/loader/nodes/Less.cpp index 81156f2..ae914ec 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Less.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Less.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLess(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LessEqual.cpp b/compiler/luci-interpreter/src/loader/nodes/LessEqual.cpp index 82141e5..f1b424b 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LessEqual.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LessEqual.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLessEqual(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp b/compiler/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp index a12dce0..962ca2d 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp @@ -25,9 +25,7 @@ std::unique_ptr build_kernel_CircleLocalResponseNormalization(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->input()); Tensor *output = helper.getOutputTensor(node); diff --git a/compiler/luci-interpreter/src/loader/nodes/LogSoftmax.cpp b/compiler/luci-interpreter/src/loader/nodes/LogSoftmax.cpp index 6cf547a..4322041 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LogSoftmax.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LogSoftmax.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLogSoftmax(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->logits()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LogicalAnd.cpp b/compiler/luci-interpreter/src/loader/nodes/LogicalAnd.cpp index 2c9549f..bf3cb67 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LogicalAnd.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LogicalAnd.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLogicalAnd(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LogicalNot.cpp b/compiler/luci-interpreter/src/loader/nodes/LogicalNot.cpp index 3d327d6..fefcd9a 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LogicalNot.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LogicalNot.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLogicalNot(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/LogicalOr.cpp b/compiler/luci-interpreter/src/loader/nodes/LogicalOr.cpp index 50566bb..a416cb4 100644 --- a/compiler/luci-interpreter/src/loader/nodes/LogicalOr.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/LogicalOr.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLogicalOr(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Logistic.cpp b/compiler/luci-interpreter/src/loader/nodes/Logistic.cpp index e4160ed..4a69dee 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Logistic.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Logistic.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleLogistic(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/MaxPool2D.cpp b/compiler/luci-interpreter/src/loader/nodes/MaxPool2D.cpp index 914f228..f66a206 100644 --- a/compiler/luci-interpreter/src/loader/nodes/MaxPool2D.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/MaxPool2D.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMaxPool2D(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->value()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Maximum.cpp b/compiler/luci-interpreter/src/loader/nodes/Maximum.cpp index dc50d67..d0bff77 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Maximum.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Maximum.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMaximum(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Mean.cpp b/compiler/luci-interpreter/src/loader/nodes/Mean.cpp index 97d9120..0dec63e 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Mean.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Mean.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMean(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Minimum.cpp b/compiler/luci-interpreter/src/loader/nodes/Minimum.cpp index ff65952..1a49c10 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Minimum.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Minimum.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMinimum(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/MirrorPad.cpp b/compiler/luci-interpreter/src/loader/nodes/MirrorPad.cpp index ebf2945..b221b45 100644 --- a/compiler/luci-interpreter/src/loader/nodes/MirrorPad.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/MirrorPad.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMirrorPad(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Mul.cpp b/compiler/luci-interpreter/src/loader/nodes/Mul.cpp index 4f9da96..f998485 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Mul.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Mul.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleMul(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Neg.cpp b/compiler/luci-interpreter/src/loader/nodes/Neg.cpp index 23c0053..9a9ecf9 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Neg.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Neg.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleNeg(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/NotEqual.cpp b/compiler/luci-interpreter/src/loader/nodes/NotEqual.cpp index 8e5711f..3916a58 100644 --- a/compiler/luci-interpreter/src/loader/nodes/NotEqual.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/NotEqual.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleNotEqual(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *x = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/PRelu.cpp b/compiler/luci-interpreter/src/loader/nodes/PRelu.cpp index e31601b..f3d700c 100644 --- a/compiler/luci-interpreter/src/loader/nodes/PRelu.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/PRelu.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CirclePRelu(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Pack.cpp b/compiler/luci-interpreter/src/loader/nodes/Pack.cpp index 6994720..efc5850 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Pack.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Pack.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CirclePack(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == node->values_count()); std::vector inputs(node->values_count()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Pad.cpp b/compiler/luci-interpreter/src/loader/nodes/Pad.cpp index 7705492..67ce997 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Pad.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Pad.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CirclePad(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/PadV2.cpp b/compiler/luci-interpreter/src/loader/nodes/PadV2.cpp index 12deb15..e378a97 100644 --- a/compiler/luci-interpreter/src/loader/nodes/PadV2.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/PadV2.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CirclePadV2(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Pow.cpp b/compiler/luci-interpreter/src/loader/nodes/Pow.cpp index b430bc9..d32fc3d 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Pow.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Pow.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CirclePow(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Quantize.cpp b/compiler/luci-interpreter/src/loader/nodes/Quantize.cpp index fd98363..cb36fb6 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Quantize.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Quantize.cpp @@ -24,9 +24,8 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleQuantize(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->input()); Tensor *output = helper.getOutputTensor(node); diff --git a/compiler/luci-interpreter/src/loader/nodes/ReduceMax.cpp b/compiler/luci-interpreter/src/loader/nodes/ReduceMax.cpp new file mode 100644 index 0000000..1a8522d --- /dev/null +++ b/compiler/luci-interpreter/src/loader/nodes/ReduceMax.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ReduceMax.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleReduceMax(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *axes = helper.getInputTensor(node->reduction_indices()); + Tensor *output = helper.getOutputTensor(node); + + auto temp_index_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + temp_index_unique->set_observable(false); + temp_index_unique->set_data_buffer(nullptr); + Tensor *temp_index = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(temp_index_unique)); + + auto resolved_axes_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + resolved_axes_unique->set_observable(false); + resolved_axes_unique->set_data_buffer(nullptr); + Tensor *resolved_axes = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(resolved_axes_unique)); + + ReducerParams params{}; + params.keep_dims = node->keep_dims(); + + return std::make_unique(input, axes, output, temp_index, resolved_axes, + params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/loader/nodes/Relu.cpp b/compiler/luci-interpreter/src/loader/nodes/Relu.cpp index d53a66a..1d64c1c 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Relu.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Relu.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleRelu(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->features()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Relu6.cpp b/compiler/luci-interpreter/src/loader/nodes/Relu6.cpp index f1b5d21..e50cd25 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Relu6.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Relu6.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleRelu6(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->features()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Reshape.cpp b/compiler/luci-interpreter/src/loader/nodes/Reshape.cpp index 89e3ece..76ddd88 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Reshape.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Reshape.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleReshape(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->tensor()); diff --git a/compiler/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp b/compiler/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp index dca5658..dc2b88a 100644 --- a/compiler/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleResizeBilinear(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp b/compiler/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp index d1ea19c..c7058ae 100644 --- a/compiler/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp @@ -25,9 +25,7 @@ std::unique_ptr build_kernel_CircleResizeNearestNeighbor(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/ReverseV2.cpp b/compiler/luci-interpreter/src/loader/nodes/ReverseV2.cpp index ea00f54..c1a7f53 100644 --- a/compiler/luci-interpreter/src/loader/nodes/ReverseV2.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/ReverseV2.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleReverseV2(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->tensor()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Rsqrt.cpp b/compiler/luci-interpreter/src/loader/nodes/Rsqrt.cpp index ff87f43..0714a5d 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Rsqrt.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Rsqrt.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleRsqrt(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/SVDF.cpp b/compiler/luci-interpreter/src/loader/nodes/SVDF.cpp index 89528d5..d172ef4 100644 --- a/compiler/luci-interpreter/src/loader/nodes/SVDF.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/SVDF.cpp @@ -24,9 +24,8 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSVDF(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 5); const Tensor *input = helper.getInputTensor(node->input()); const Tensor *feature = helper.getInputTensor(node->weight_feature()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Shape.cpp b/compiler/luci-interpreter/src/loader/nodes/Shape.cpp new file mode 100644 index 0000000..d1edbc7 --- /dev/null +++ b/compiler/luci-interpreter/src/loader/nodes/Shape.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Shape.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleShape(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const auto input = helper.getInputTensor(node->input()); + auto output = helper.getOutputTensor(node); + + ShapeParams shape_params{}; + shape_params.out_type = node->out_type(); + + return std::make_unique(input, output, shape_params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/loader/nodes/Slice.cpp b/compiler/luci-interpreter/src/loader/nodes/Slice.cpp index 741cd08..60ac641 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Slice.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Slice.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSlice(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Softmax.cpp b/compiler/luci-interpreter/src/loader/nodes/Softmax.cpp index b15e4b6..f41f63f 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Softmax.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Softmax.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSoftmax(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->logits()); diff --git a/compiler/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp b/compiler/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp index 91c237a..b6e6cf5 100644 --- a/compiler/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSpaceToBatchND(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 3); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp b/compiler/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp index 3cbbd97..63fdb95 100644 --- a/compiler/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSpaceToDepth(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Split.cpp b/compiler/luci-interpreter/src/loader/nodes/Split.cpp index 32553ad..3f6d4a7 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Split.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Split.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSplit(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); auto output_nodes = collectOutputNodes(node); assert(node->arity() == 2); assert(output_nodes.size() == static_cast(node->num_split())); diff --git a/compiler/luci-interpreter/src/loader/nodes/SplitV.cpp b/compiler/luci-interpreter/src/loader/nodes/SplitV.cpp index d788164..0788822 100644 --- a/compiler/luci-interpreter/src/loader/nodes/SplitV.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/SplitV.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSplitV(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); auto output_nodes = collectOutputNodes(node); assert(node->arity() == 3); assert(output_nodes.size() == static_cast(node->num_split())); diff --git a/compiler/luci-interpreter/src/loader/nodes/Sqrt.cpp b/compiler/luci-interpreter/src/loader/nodes/Sqrt.cpp index 56dd986..b9843fe 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Sqrt.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Sqrt.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSqrt(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Square.cpp b/compiler/luci-interpreter/src/loader/nodes/Square.cpp index 43aadb9..0ad7c17 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Square.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Square.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSquare(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/SquaredDifference.cpp b/compiler/luci-interpreter/src/loader/nodes/SquaredDifference.cpp index 6a2717a..e4c6fd8 100644 --- a/compiler/luci-interpreter/src/loader/nodes/SquaredDifference.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/SquaredDifference.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSquaredDifference(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Squeeze.cpp b/compiler/luci-interpreter/src/loader/nodes/Squeeze.cpp index 583ff93..6885f80 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Squeeze.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Squeeze.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSqueeze(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/StridedSlice.cpp b/compiler/luci-interpreter/src/loader/nodes/StridedSlice.cpp index fe5fa77..359b4e3 100644 --- a/compiler/luci-interpreter/src/loader/nodes/StridedSlice.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/StridedSlice.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleStridedSlice(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 4); const Tensor *input = helper.getInputTensor(node->input()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Sub.cpp b/compiler/luci-interpreter/src/loader/nodes/Sub.cpp index bad4fbb..a6252cb 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Sub.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Sub.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleSub(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input1 = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Tanh.cpp b/compiler/luci-interpreter/src/loader/nodes/Tanh.cpp index f425529..a58ef60 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Tanh.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Tanh.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleTanh(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 1); const Tensor *input = helper.getInputTensor(node->x()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Transpose.cpp b/compiler/luci-interpreter/src/loader/nodes/Transpose.cpp index 4e095fb..ea17d83 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Transpose.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Transpose.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleTranspose(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 2); const Tensor *input = helper.getInputTensor(node->a()); diff --git a/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp b/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp index 1b954c3..d773e30 100644 --- a/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleTransposeConv(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); assert(node->arity() == 4); const Tensor *input_sizes = helper.getInputTensor(node->inputSizes()); diff --git a/compiler/luci-interpreter/src/loader/nodes/Unpack.cpp b/compiler/luci-interpreter/src/loader/nodes/Unpack.cpp index 978c738..a1c0d32 100644 --- a/compiler/luci-interpreter/src/loader/nodes/Unpack.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Unpack.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleUnpack(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); auto output_nodes = collectOutputNodes(node); assert(node->arity() == 1); assert(output_nodes.size() == static_cast(node->num())); diff --git a/compiler/luci-interpreter/src/loader/nodes/While.cpp b/compiler/luci-interpreter/src/loader/nodes/While.cpp index 284dc0c..8fde6ec 100644 --- a/compiler/luci-interpreter/src/loader/nodes/While.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/While.cpp @@ -24,9 +24,7 @@ namespace luci_interpreter std::unique_ptr build_kernel_CircleWhile(const luci::CircleNode *circle_node, KernelBuilderHelper &helper) { - const auto *node = dynamic_cast(circle_node); - if (node == nullptr) - throw std::runtime_error("wrong builder for operation"); + const auto *node = loco::must_cast(circle_node); auto output_nodes = collectOutputNodes(node); assert(node->arity() == node->input_count()); diff --git a/compiler/luci-micro/CMakeLists.txt b/compiler/luci-micro/CMakeLists.txt index c8a2e12..642cf14 100644 --- a/compiler/luci-micro/CMakeLists.txt +++ b/compiler/luci-micro/CMakeLists.txt @@ -15,7 +15,7 @@ set(CMAKE_ARM_OPTIONS -DLUCI_STATIC=ON -DBUILD_CMSIS_NN_FUNCTIONS=ON -DTARGET_CPU=cortex-m7 - "-DCMAKE_TOOLCHAIN_FILE=${NNAS_PROJECT_SOURCE_DIR}/infra/nncc/cmake/buildtool/config/arm-non-eabi-gcc.cmake" + "-DCMAKE_TOOLCHAIN_FILE=${NNAS_PROJECT_SOURCE_DIR}/infra/nncc/cmake/buildtool/config/arm-none-eabi-gcc.cmake" "-DLUCI_INTERPRETER_PAL_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../luci-interpreter/pal/mcu" "-DNNAS_PROJECT_SOURCE_DIR=${NNAS_PROJECT_SOURCE_DIR}" "-DNNAS_EXTERNALS_DIR=${NNAS_EXTERNALS_DIR}" diff --git a/compiler/luci-micro/luci-interpreter/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/CMakeLists.txt new file mode 100644 index 0000000..1f7acee --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LUCI_INTERPRETER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(LUCI_INTERPRETER_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") +if (NOT LUCI_INTERPRETER_PAL_DIR) + set(LUCI_INTERPRETER_PAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pal/linux") +endif() + +set(KERNEL_REGISTER_FILE ${LUCI_INTERPRETER_PAL_DIR}/KernelsToBuild.lst) + +if (NOT DEFINED CUSTOM_LUCI_INTERPRETER_SUFFIX) + set(LUCI_INTERPRETER_SUFFIX "") +else() + set(LUCI_INTERPRETER_SUFFIX ${CUSTOM_LUCI_INTERPRETER_SUFFIX}) +endif() + +add_subdirectory(src) diff --git a/compiler/luci-micro/luci-interpreter/README.md b/compiler/luci-micro/luci-interpreter/README.md new file mode 100644 index 0000000..77ec5c8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/README.md @@ -0,0 +1,158 @@ +# luci-interpreter + +`luci-interpreter` is an inference engine for neural networks represented in luci IR. +See `compiler/luci/lang` directory for details about IR. +You can find useful infrastructure, like importer/exporter, optimizations in `compiler/luci`. + +`luci-interpreter` provides: +- Basic inference functionality, input setters and output getters +- Interface for inspecting hidden interpreter state, like activation values during inference +- Customization mechanisms to fit the interpreter to specific platforms, like MCUs + +Public interface headers are placed in `luci-interpreter/include/luci_interpreter` directory + +## Basic usage + +Minimal usage includes: +- Setting input data +- Running inference +- Fetching inference results + +Interpreter object is reusable and can run multiple inferences. +Elements in tensors (input/output/internal) are stored contiguously and have C-like layout: +This means for tensor t=[[0, 1],[2, 3]], t[0,1] == 1. + +Input and output tensors have the same indexes as in original luci model. + +**Usage example:** +``` c++ +// Note getTensorSize is a function that computes tensor size, +// it is not part of interpreter and should be implemented by user + +luci_interpreter::Interpreter interpreter(luci_module); + +// Set inputs +// assuming model has only one input and one output +const auto input_nodes = loco::input_nodes(module->graph()); + +const auto *input_node = dynamic_cast(input_nodes[0]); +std::vector input_data(getTensorSize(input_node)); +// Initialize input data here + +interpreter.writeInputTensor(input_node, input_data.data(), input_data.size()); + +// Start inference +interpreter.interpret(); + +// Fetch inference results +const auto output_nodes = loco::output_nodes(module->graph()); +const auto *output_node = dynamic_cast(output_nodes[0]); +std::vector output_data(getTensorSize(output_node)); +interpreter.readOutputTensor(output_node, output_data.data(), output_data.size()); +``` + +## Inspecting intermediate state + +Interpreter provides interfaces to investigate internal state of interpreter during inference. + +This is done by "observer" mechanism: +- `Interpreter` class has `attachObserver` method, which takes pointer to `ExecutionObserver` object +- `ExecutionObserver` defines several callback methods user can override to inject custom code + +ExecutionObserver provides three callbacks: +- `postTensorWrite` checks contents of output tensor after operation execution +- `preOperatorExecute` notifies that interpreter is going to execute operation +- `postOperatorExecute` notifies that interpreter has finished execution of an operation + +See `luci-interpreter/include/luci_interpreter/Interpreter.h` for this interface details. + +**Usage example:** +``` c++ +class CustomExecutionObserver: public luci_interpreter::ExecutionObserver +{ +public: + void postTensorWrite(const luci::CircleNode *node, const Tensor *tensor) override + { + if (tensor->element_type() != loco::DataType::FLOAT32) + return; + for (int i = 0; i < tensor->shape().num_elements(); ++i) + std::cout << tensor->data[i] << ", "; + } + + // User observer can override only needed methods, + // others will inherit empty implementation from base observer. + + // void preOperatorExecute(const luci::CircleNode *node); + // void postOperatorExecute(const luci::CircleNode *node); +}; + +luci_interpreter::Interpreter interpreter(module); +CustomExecutionObserver observer; +interpreter.attachObserver(&observer); + +// initialize input_data +interpreter.writeInputTensor(input_node, input_data.data(), input_data.size()); + +interpreter.interpret(); +``` + +## Customizing inference + +### Memory manager + +Interpreter provides a handle for altering default memory management mechanisms. + +This is done by `MemoryManger` interface, see `luci-interpreter/include/luci_interpreter/MemoryManager.h` for implementation details. + +This header contains `IMemoryManager` abstract class which is responsible for allocation and dealocation of tensors' memory. + +User can construct an interpreter with one of predefined memory managers or their own custom memory manager. +Note that one memory manager could be shared between multiple interpreter instances, because an interpreter does not own the manager object. + +List of predefined memory managers: +- `SimpleMemoryManager` This is a simple wrapper around new/delete, default one. +- `TestMemoryManager` Memorizes all allocated memory and releases it in Manager destructor, used in kernel unit tests. +- `BuddyMemoryManager` Implements Buddy algorithm, uses external buffer for tensor data allocations, does not need new/delete. +- `StaticMemoryManger` Uses precomputed memory allocation plan. Requires preparation with MemoryPlanner, but could reduce memory consumption in restricted environments (like MCUs). + +**SimpleMemoryManager usage example:** + +No need to select anything, to use this memory manager. +``` c++ +luci_interpreter::Interpreter interpreter(module); +``` + +**TestMemoryManager usage example:** + +``` c++ +luci_interpreter::TestMemoryManager mm; +luci_interpreter::Interpreter interpreter(module, &mm); +``` + +**BuddyMemoryManager usage example:** + +`BuddyMemoryManager` implements a classic allocation algorithm: https://en.wikipedia.org/wiki/Buddy_memory_allocation. + +This allocator uses an external buffer as a memory pool. That allows to use static memory arrays for allocations. + +Limitations +- Current implementation uses only lower power-of-two bytes of given buffer. + + For example for 1000 bytes buffer, only lower 512 bytes will be used. +- Current implementation can handle maximum 4 gigabyte memory pool + +``` c++ + constexpr int buffer_size = 2048; + static uint8_t buffer[buffer_size]; + luci_interpreter::BuddyMemoryManager memory_manager(buffer, buffer_size); + luci_interpreter::Interpreter interpreter(module.get(), &memory_manager); +``` + +**StaticMemoryManager usage example:** +``` c++ +TBD when it is merged +``` + +## Further reading + +If you want to participate in development, please read `DEVELOPER.md` for SW architecture details. diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/BuddyMemoryManager.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/BuddyMemoryManager.h new file mode 100644 index 0000000..205baa6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/BuddyMemoryManager.h @@ -0,0 +1,144 @@ +/* Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/MemoryManager.h" + +#ifndef LUCI_INTERPRETER_BUDDY_MEMORY_MANAGER_H +#define LUCI_INTERPRETER_BUDDY_MEMORY_MANAGER_H + +namespace luci_interpreter +{ + +class BuddyMemoryManager : public IMemoryManager +{ +public: + BuddyMemoryManager(uint8_t *memory_start, int32_t memSize); + + void allocate_memory(luci_interpreter::Tensor &tensor) final; + void release_memory(luci_interpreter::Tensor &tensor) final; + +private: + struct Block + { + Block *next_free; + bool is_free; + uint32_t size; + // debug field + Block *self; + }; + + Block *_start_block; + int32_t _num_blocks; + uint32_t _size; + Block *_free_blocks[32]{}; + + static int32_t lowerLog2(uint32_t val) + { + int32_t i = 0; + while (val >>= 1) + i++; + + return i; + } + + void addToBlocks(Block *block, int32_t l) + { + if (!block) + return; + + block->next_free = _free_blocks[l]; + _free_blocks[l] = block; + } + + void removeFromBlocks(const Block *block, int32_t l) + { + if (!block) + return; + + Block *tmp = _free_blocks[l]; + + if (block == tmp) + { + _free_blocks[l] = block->next_free; + return; + } + + while (tmp) + { + if (tmp->next_free == block) + { + tmp->next_free = block->next_free; + return; + } + + tmp = tmp->next_free; + } + } + + void divideBlock(Block *block, int32_t l) + { + int32_t size = ((block->size + sizeof(Block)) / 2) - sizeof(Block); + + removeFromBlocks(block, l); + + // there is no need to add to the free_blocks list here + block->is_free = true; + block->size = size; + block->self = block; + + Block *buddy; + buddy = (Block *)((uint8_t *)block + sizeof(Block) + size); + buddy->is_free = true; + buddy->size = size; + buddy->self = buddy; + + addToBlocks(buddy, l - 1); + } + + Block *mergeBlock(Block *block) + { + Block *buddy; + + const int32_t l = lowerLog2(block->size + sizeof(Block)); + + const int64_t address = ((uint8_t *)block - (uint8_t *)_start_block); + buddy = (Block *)((address ^ (1 << l)) + (uint8_t *)_start_block); + + if (!buddy->is_free || buddy->size != block->size) + return nullptr; + + if (block > buddy) + { + Block *x = block; + block = buddy; + buddy = x; + } + + removeFromBlocks(block, l); + removeFromBlocks(buddy, l); + + block->size = block->size * 2 + sizeof(Block); + block->is_free = true; + block->self = block; + + addToBlocks(block, l + 1); + + return block; + } +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_BUDDY_MEMORY_MANAGER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/GraphBuilderRegistry.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/GraphBuilderRegistry.h new file mode 100644 index 0000000..375b1ae --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/GraphBuilderRegistry.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_INTERPRETER_GRAPH_BUILDER_REGISTRY__ +#define __LUCI_INTERPRETER_GRAPH_BUILDER_REGISTRY__ + +#include + +namespace luci_interpreter +{ + +/** + * @brief Creates and returns GraphBuilderSource, which allows to not copy constant buffers from + * model's file. + * + * @warning Use this source only in case when model's buffer alive longer than Interpreter. + */ +std::unique_ptr source_without_constant_copying(); + +} // namespace luci_interpreter + +#endif // __LUCI_INTERPRETER_GRAPH_BUILDER_REGISTRY__ diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/Interpreter.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/Interpreter.h new file mode 100644 index 0000000..8e2f457 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/Interpreter.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_INTERPRETER_H +#define LUCI_INTERPRETER_INTERPRETER_H + +#include "luci_interpreter/core/Tensor.h" + +#include +#include + +#include "luci_interpreter/MemoryManager.h" +#include + +#include +#include +#include + +namespace luci_interpreter +{ + +class ExecutionObserver +{ +public: + virtual ~ExecutionObserver(); + + // Called when the value of a tensor has been updated during execution. + virtual void postTensorWrite(const luci::CircleNode *node, const Tensor *tensor); + + // Called before / after executing an operator. + // Note that these methods are not called for auxiliary operators (CircleInput, CircleOutput, + // CircleConst and Circle*Out). + virtual void preOperatorExecute(const luci::CircleNode *node); + virtual void postOperatorExecute(const luci::CircleNode *node); +}; + +class Interpreter +{ +public: + explicit Interpreter(const luci::Module *module); + + explicit Interpreter(const luci::Module *module, IMemoryManager *memory_manager); + + ~Interpreter(); + + void writeInputTensor(const luci::CircleInput *input_node, const void *data, size_t data_size); + + void readOutputTensor(const luci::CircleOutput *output_node, void *data, size_t data_size); + + void interpret(); + + void attachObserver(ExecutionObserver *observer); + + const Tensor *getTensor(const loco::Node *node) { return _node_to_tensor[node]; } + +private: + // _default_memory_manager should be before _runtime_module due to + // the order of deletion in the destructor + std::unique_ptr _default_memory_manager = nullptr; + std::unique_ptr _runtime_module; + + // Observer functionality support. + std::unique_ptr _runtime_to_ir; + std::unordered_map _node_to_tensor; + std::unique_ptr _event_notifier; + std::vector _observers; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_INTERPRETER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/MemoryManager.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/MemoryManager.h new file mode 100644 index 0000000..f32c520 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/MemoryManager.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_MEMORY_MANAGER_H +#define LUCI_INTERPRETER_MEMORY_MANAGER_H + +#include "luci_interpreter/core/DataType.h" +#include "luci_interpreter/core/Tensor.h" + +namespace luci_interpreter +{ + +class IMemoryManager +{ +public: + virtual void allocate_memory(luci_interpreter::Tensor &tensor) = 0; + virtual void release_memory(luci_interpreter::Tensor &tensor) = 0; + + virtual ~IMemoryManager() = default; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_MEMORY_MANAGER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/SimpleMemoryManager.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/SimpleMemoryManager.h new file mode 100644 index 0000000..658a1c6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/SimpleMemoryManager.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_SIMPLE_MEMORY_MANAGER_H +#define LUCI_INTERPRETER_SIMPLE_MEMORY_MANAGER_H + +#include "luci_interpreter/MemoryManager.h" + +namespace luci_interpreter +{ + +class SimpleMemoryManager : public IMemoryManager +{ +public: + void allocate_memory(luci_interpreter::Tensor &tensor) final; + void release_memory(luci_interpreter::Tensor &tensor) final; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_SIMPLE_MEMORY_MANAGER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/StaticMemoryManager.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/StaticMemoryManager.h new file mode 100644 index 0000000..ded7bde --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/StaticMemoryManager.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_STATIC_MEMORY_MANAGER_H +#define LUCI_INTERPRETER_STATIC_MEMORY_MANAGER_H + +#include "luci_interpreter/MemoryManager.h" + +namespace luci_interpreter +{ + +// Used for allocations in static buffer, using offsets defined in luci model. +class StaticMemoryManager : public IMemoryManager +{ +public: + StaticMemoryManager() = delete; + + explicit StaticMemoryManager(uint8_t *buffer_ptr) : _buffer_ptr(buffer_ptr) + { /* Do nothing */ + } + + void allocate_memory(luci_interpreter::Tensor &tensor) final; + void release_memory(luci_interpreter::Tensor &tensor) final; + +private: + // Stores a pointer to the beginning of the allocated memory buffer. + uint8_t *_buffer_ptr; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_STATIC_MEMORY_MANAGER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/TestMemoryManager.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/TestMemoryManager.h new file mode 100644 index 0000000..397bbed --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/TestMemoryManager.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_TEST_MEMORY_MANAGER_H +#define LUCI_INTERPRETER_TEST_MEMORY_MANAGER_H + +#include "luci_interpreter/MemoryManager.h" + +namespace luci_interpreter +{ +// Memory Manager for using in kernels tests. This eliminates the need to manually delete the +// allocated memory in tests. This mem_manager remembers all its allocations and in destructor +// delete all allocations. +class TestMemoryManager : public IMemoryManager +{ +public: + void allocate_memory(luci_interpreter::Tensor &tensor) final; + void release_memory(luci_interpreter::Tensor &tensor) final; + + ~TestMemoryManager() override + { + for (auto allocation : allocations) + { + delete[] allocation; + } + } + +private: + std::vector allocations; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MEMORY_MANAGER_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/DataType.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/DataType.h new file mode 100644 index 0000000..27bf719 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/DataType.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_DATATYPE_H +#define LUCI_INTERPRETER_CORE_DATATYPE_H + +#include +#include + +#include + +namespace luci_interpreter +{ + +using DataType = loco::DataType; + +template using DataTypeImpl = loco::DataTypeImpl
; + +inline size_t getDataTypeSize(DataType data_type) { return loco::size(data_type); } + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_DATATYPE_H diff --git a/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h new file mode 100644 index 0000000..bb9ff6d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_TENSOR_H +#define LUCI_INTERPRETER_CORE_TENSOR_H + +#include "luci_interpreter/core/DataType.h" + +#include +#include +#include +#include +#include +#include + +namespace luci_interpreter +{ + +class Shape +{ +public: + explicit Shape(int rank) : _dims(rank, 0) {} + + Shape(std::initializer_list dims) : _dims(dims.begin(), dims.end()) {} + + int num_dims() const { return _dims.size(); } + + int32_t dim(int i) const + { + assert(i >= 0 && i < static_cast(_dims.size())); + return _dims[i]; + } + + int32_t &dim(int i) + { + assert(i >= 0 && i < static_cast(_dims.size())); + return _dims[i]; + } + + int32_t num_elements() const + { + int32_t result = 1; + for (const int32_t dim : _dims) + { + result *= dim; + } + return result; + } + + bool operator==(const Shape &other) const { return _dims == other._dims; } + + bool operator!=(const Shape &other) const { return !operator==(other); } + +private: + std::vector _dims; +}; + +// Tensor affine quantization parameters. +// +// The relationship between real and quantized values: +// real_value = (quantized_value - zero_point) * scale +// +// In per-tensor case, 'scale' and 'zero_point' are one element each. +// In per-channel case, 'scale' and 'zero_point' are N elements each, where N is the size +// of the quantized dimension. +// +// Note that due to historical and performance reasons, per-tensor quantization uses unsigned +// integer types, while per-channel uses signed types assuming 'zero_point' == 0. +struct AffineQuantization +{ + std::vector scale; + std::vector zero_point; + int32_t quantized_dimension; +}; + +class Tensor +{ +public: + Tensor(DataType element_type, Shape shape, AffineQuantization quantization, std::string name); + + DataType element_type() const { return _element_type; } + + const Shape &shape() const { return _shape; } + + float scale() const + { + assert(_quantization.scale.size() == 1); + return _quantization.scale[0]; + } + + int32_t zero_point() const + { + assert(_quantization.zero_point.size() == 1); + return _quantization.zero_point[0]; + } + + const std::vector &scales() const { return _quantization.scale; } + + const std::vector &zero_points() const { return _quantization.zero_point; } + + int32_t quantized_dimension() const { return _quantization.quantized_dimension; } + + template const T *data() const + { + static_assert(std::is_same::value or + std::is_same::value); + return reinterpret_cast(_data); + } + + template T *data() + { + static_assert(std::is_same::value or + std::is_same::value); + return reinterpret_cast(_data); + } + + const std::string &name() const { return _name; } + + void readData(void *data_ptr, size_t data_size) const; + + void writeData(const void *data_ptr, size_t data_size); + + void resize(const Shape &new_shape); + + void set_data_buffer(uint8_t *buffer) + { + if (buffer == nullptr) + { + _data_allocated = false; + } + else + { + _data_allocated = true; + } + _data = buffer; + } + + bool is_observable() const { return _is_observable; } + + void set_observable(bool value) { _is_observable = value; } + + bool is_allocatable() const { return _is_allocatable; } + + void set_allocatable(bool value) { _is_allocatable = value; } + + bool is_data_allocated() const { return _data_allocated; } + + int32_t get_offset() const { return _offset; } + + void set_offset(int32_t offset) { _offset = offset; } + +private: + DataType _element_type; + Shape _shape; + AffineQuantization _quantization; + uint8_t *_data; + std::string _name; + bool _data_allocated; + // Write of tensor is reported to registered Observers only if this tensor is observable + // This is needed for tensors used in kernel implementation, but not present in original model. + bool _is_observable = true; + // Memory manager is called for tensor only if it is "allocatable". + // Kernel configuration could disable allocation of some tensors if they are not needed for + // particular operation. + bool _is_allocatable = true; + // Used by static memory manager. + // Stores the offset from the beginning of the allocated memory buffer. + int32_t _offset = -1; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_TENSOR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst new file mode 100644 index 0000000..f0df58d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst @@ -0,0 +1,62 @@ +REGISTER_KERNEL(Add) +REGISTER_KERNEL(ArgMax) +REGISTER_KERNEL(AveragePool2D) +REGISTER_KERNEL(BatchToSpaceND) +REGISTER_KERNEL(Cast) +REGISTER_KERNEL(Concatenation) +REGISTER_KERNEL(Conv2D) +REGISTER_KERNEL(DepthToSpace) +REGISTER_KERNEL(DepthwiseConv2D) +REGISTER_KERNEL(Dequantize) +REGISTER_KERNEL(Div) +REGISTER_KERNEL(Elu) +REGISTER_KERNEL(Exp) +REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) +REGISTER_KERNEL(Floor) +REGISTER_KERNEL(FloorDiv) +REGISTER_KERNEL(Equal) +REGISTER_KERNEL(FullyConnected) +REGISTER_KERNEL(Greater) +REGISTER_KERNEL(GreaterEqual) +REGISTER_KERNEL(If) +REGISTER_KERNEL(InstanceNorm) +REGISTER_KERNEL(L2Normalize) +REGISTER_KERNEL(L2Pool2D) +REGISTER_KERNEL(LeakyRelu) +REGISTER_KERNEL(Less) +REGISTER_KERNEL(LessEqual) +REGISTER_KERNEL(LogicalAnd) +REGISTER_KERNEL(LogicalNot) +REGISTER_KERNEL(LogicalOr) +REGISTER_KERNEL(Logistic) +REGISTER_KERNEL(Maximum) +REGISTER_KERNEL(MaxPool2D) +REGISTER_KERNEL(Minimum) +REGISTER_KERNEL(MirrorPad) +REGISTER_KERNEL(Mul) +REGISTER_KERNEL(Neg) +REGISTER_KERNEL(NotEqual) +REGISTER_KERNEL(Pad) +REGISTER_KERNEL(PadV2) +REGISTER_KERNEL(PRelu) +REGISTER_KERNEL(Quantize) +REGISTER_KERNEL(Reshape) +REGISTER_KERNEL(ResizeBilinear) +REGISTER_KERNEL(ResizeNearestNeighbor) +REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) +REGISTER_KERNEL(Softmax) +REGISTER_KERNEL(SpaceToBatchND) +REGISTER_KERNEL(SpaceToDepth) +REGISTER_KERNEL(StridedSlice) +REGISTER_KERNEL(Sqrt) +REGISTER_KERNEL(Square) +REGISTER_KERNEL(SquaredDifference) +REGISTER_KERNEL(Squeeze) +REGISTER_KERNEL(Sub) +REGISTER_KERNEL(SVDF) +REGISTER_KERNEL(Tanh) +REGISTER_KERNEL(Transpose) +REGISTER_KERNEL(TransposeConv) +REGISTER_KERNEL(While) diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h new file mode 100644 index 0000000..21e6329 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H +#define LUCI_INTERPRETER_PAL_ARGMAX_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, + const T2 *axis, const tflite::RuntimeShape &output_shape, + T3 *output_data, const std::greater cmp) +{ + tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h new file mode 100644 index 0000000..a274afb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H +#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H + +#include +#include +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data, + const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) +{ + { + // MARK: At this moment this operation is not supported + assert(false && "AveragePool NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, + int8_t *scratchpad_data) +{ + assert(input_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + assert(scratchpad_data != nullptr); + + const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); + assert(batches == 1); + + const int depth = tflite::MatchingDim(input_shape, 3, output_shape, 3); + + cmsis_nn_dims input_dims; + input_dims.n = 1; + input_dims.h = input_shape.Dims(1); + input_dims.w = input_shape.Dims(2); + input_dims.c = depth; + + cmsis_nn_dims output_dims; + output_dims.n = 1; + output_dims.h = output_shape.Dims(1); + output_dims.w = output_shape.Dims(2); + output_dims.c = depth; + + cmsis_nn_pool_params pool_params; + pool_params.stride.h = params.stride_height; + pool_params.stride.w = params.stride_width; + pool_params.padding.h = params.padding_values.height; + pool_params.padding.w = params.padding_values.width; + pool_params.activation.min = params.quantized_activation_min; + pool_params.activation.max = params.quantized_activation_max; + + cmsis_nn_dims filter_dims; + filter_dims.n = 1; + filter_dims.h = params.filter_height; + filter_dims.w = params.filter_width; + filter_dims.c = 1; + + cmsis_nn_context ctx; + ctx.buf = scratchpad_data; + ctx.size = scratchpad_shape.Dims(0); + auto res = arm_avgpool_s8(&ctx, &pool_params, &input_dims, input_data, &filter_dims, &output_dims, + output_data); + assert(res == ARM_MATH_SUCCESS); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &output_shape) + +{ + if (input_data_type == luci_interpreter::DataType::S8) + { + assert(input_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + + const int32_t output_width = output_shape.Dims(2); + const int32_t depth = tflite::MatchingDim(input_shape, 3, output_shape, 3); + + const int32_t buf_size = arm_avgpool_s8_get_buffer_size(output_width, depth); + auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); + + luci_interpreter::Shape scratchpad_shape{buf_size * data_type_size}; + scratchpad->resize(scratchpad_shape); + } + else + { + scratchpad->set_allocatable(false); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h new file mode 100644 index 0000000..4dd77ff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H +#define LUCI_INTERPRETER_PAL_ARGMAX_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *crops_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::BatchToSpaceND( + unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, crops_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h new file mode 100644 index 0000000..cfb84ea --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_CONV2D_H +#define LUCI_INTERPRETER_PAL_CONV2D_H + +#include +#include +#include +#include + +namespace luci_interpreter_pal +{ +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &filter_shape, + const float *filter_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, const tflite::RuntimeShape &output_shape, + float *output_data, const tflite::RuntimeShape &scratchpad_shape, + float *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, + tflite::RuntimeShape(), nullptr); +} + +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const uint8 *input_data, const tflite::RuntimeShape &filter_shape, + const uint8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + uint8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + uint8 *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, scratchpad_shape, + scratchpad_data, nullptr); +} + +static inline void ConvPerChannel(const tflite::ConvParams ¶ms, const int32_t *mult, + const int32_t *shifts, const tflite::RuntimeShape &input_shape, + const int8 *input_data, const tflite::RuntimeShape &filter_shape, + const int8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + int8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + int8 *scratchpad_data) +{ + if (scratchpad_data) + { + cmsis_nn_conv_params conv_params; + conv_params.dilation.h = params.dilation_height_factor; + conv_params.dilation.w = params.dilation_width_factor; + + assert(conv_params.dilation.h == 1); + assert(conv_params.dilation.w == 1); + + conv_params.input_offset = params.input_offset; + conv_params.output_offset = params.output_offset; + conv_params.stride.h = params.stride_height; + conv_params.stride.w = params.stride_width; + conv_params.padding.h = params.padding_values.height; + conv_params.padding.w = params.padding_values.width; + conv_params.activation.min = params.quantized_activation_min; + conv_params.activation.max = params.quantized_activation_max; + + cmsis_nn_per_channel_quant_params quant_params; + quant_params.multiplier = const_cast(mult); + quant_params.shift = const_cast(shifts); + + assert(conv_params.activation.min <= conv_params.activation.max); + assert(input_shape.DimensionsCount() == 4); + assert(filter_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + const int batch_size = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); + const int output_depth = tflite::MatchingDim(filter_shape, 0, output_shape, 3); + if (bias_data) + { + assert(bias_shape.FlatSize() == output_depth); + } + + cmsis_nn_dims input_dims; + input_dims.n = batch_size; + input_dims.h = input_shape.Dims(1); + input_dims.w = input_shape.Dims(2); + input_dims.c = input_depth; + + cmsis_nn_dims filter_dims; + filter_dims.n = output_depth; + filter_dims.h = filter_shape.Dims(1); + filter_dims.w = filter_shape.Dims(2); + filter_dims.c = input_depth; + + cmsis_nn_dims bias_dims; + bias_dims.n = 1; + bias_dims.h = 1; + bias_dims.w = 1; + bias_dims.c = output_depth; + + cmsis_nn_dims output_dims; + output_dims.n = batch_size; + output_dims.h = output_shape.Dims(1); + output_dims.w = output_shape.Dims(2); + output_dims.c = output_depth; + + cmsis_nn_context ctx; + ctx.buf = scratchpad_data; + ctx.size = scratchpad_shape.Dims(0); + + auto res = arm_convolve_wrapper_s8(&ctx, &conv_params, &quant_params, &input_dims, input_data, + &filter_dims, filter_data, &bias_dims, bias_data, + &output_dims, output_data); + assert(res == ARM_MATH_SUCCESS); + } + else + { + tflite::reference_integer_ops::ConvPerChannel(params, mult, shifts, input_shape, input_data, + filter_shape, filter_data, bias_shape, bias_data, + output_shape, output_data); + } +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::ConvParams ¶ms, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) +{ + cmsis_nn_conv_params conv_params; + conv_params.dilation.h = params.dilation_height_factor; + conv_params.dilation.w = params.dilation_width_factor; + + if (input_data_type == loco::DataType::S8 && conv_params.dilation.h == 1 && + conv_params.dilation.w == 1) + { + const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int32_t input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); + const int32_t output_depth = tflite::MatchingDim(filter_shape, 0, output_shape, 3); + const int32_t filter_height = filter_shape.Dims(1); + const int32_t filter_width = filter_shape.Dims(2); + const int32_t output_height = output_shape.Dims(1); + const int32_t output_width = output_shape.Dims(2); + + conv_params.input_offset = params.input_offset; + conv_params.output_offset = params.output_offset; + conv_params.stride.h = params.stride_height; + conv_params.stride.w = params.stride_width; + conv_params.padding.h = params.padding_values.height; + conv_params.padding.w = params.padding_values.width; + + cmsis_nn_dims input_dims; + input_dims.n = batches; + input_dims.h = input_shape.Dims(1); + input_dims.w = input_shape.Dims(2); + input_dims.c = input_depth; + + cmsis_nn_dims filter_dims; + filter_dims.n = output_depth; + filter_dims.h = filter_height; + filter_dims.w = filter_width; + filter_dims.c = input_depth; + + cmsis_nn_dims output_dims; + output_dims.n = batches; + output_dims.h = output_height; + output_dims.w = output_width; + output_dims.c = output_depth; + + const int32_t buf_size = arm_convolve_wrapper_s8_get_buffer_size(&conv_params, &input_dims, + &filter_dims, &output_dims); + + luci_interpreter::Shape scratchpad_shape{buf_size}; + scratchpad->resize(scratchpad_shape); + } + else + { + scratchpad->set_allocatable(false); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_CONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h new file mode 100644 index 0000000..8463e57 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H +#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::DepthToSpace(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h new file mode 100644 index 0000000..120dcd8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H +#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H + +#include +#include +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void +DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &filter_shape, + const T *filter_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, const tflite::RuntimeShape &output_shape, + T *output_data, const tflite::RuntimeShape &scratchpad_shape, + T *scratchpad_data) +{ + { + // MARK: At this moment this operation is not supported + assert(false && "DepthwiseConvPerChannel NYI"); + (void)params; + (void)output_multiplier; + (void)output_shift; + (void)input_shape; + (void)output_data; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void DepthwiseConvPerChannel( + const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) +{ + if (scratchpad_data) + { + cmsis_nn_dw_conv_params dw_conv_params; + dw_conv_params.dilation.h = params.dilation_height_factor; + dw_conv_params.dilation.w = params.dilation_width_factor; + assert(dw_conv_params.dilation.h == 1); + assert(dw_conv_params.dilation.w == 1); + + dw_conv_params.input_offset = params.input_offset; + dw_conv_params.output_offset = params.output_offset; + dw_conv_params.stride.h = params.stride_height; + dw_conv_params.stride.w = params.stride_width; + dw_conv_params.padding.h = params.padding_values.height; + dw_conv_params.padding.w = params.padding_values.width; + + dw_conv_params.activation.min = params.quantized_activation_min; + dw_conv_params.activation.max = params.quantized_activation_max; + dw_conv_params.ch_mult = params.depth_multiplier; + + cmsis_nn_per_channel_quant_params quant_params; + int32_t output_multiplier = params.output_multiplier; + int32_t output_shift = params.output_shift; + + quant_params.multiplier = &output_multiplier; + quant_params.shift = &output_shift; + + assert(dw_conv_params.activation.min <= dw_conv_params.activation.max); + const int batch_size = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int output_depth = tflite::MatchingDim(filter_shape, 3, output_shape, 3); + if (bias_data) + { + assert(bias_shape.FlatSize() == output_depth); + } + + cmsis_nn_dims input_dims; + input_dims.n = batch_size; + input_dims.h = input_shape.Dims(1); + input_dims.w = input_shape.Dims(2); + input_dims.c = input_shape.Dims(3); + + cmsis_nn_dims filter_dims; + filter_dims.n = filter_shape.Dims(0); + filter_dims.h = filter_shape.Dims(1); + filter_dims.w = filter_shape.Dims(2); + filter_dims.c = output_depth; + + cmsis_nn_dims bias_dims; + bias_dims.n = 1; + bias_dims.h = 1; + bias_dims.w = 1; + bias_dims.c = output_depth; + + cmsis_nn_dims output_dims; + output_dims.n = batch_size; + output_dims.h = output_shape.Dims(1); + output_dims.w = output_shape.Dims(2); + output_dims.c = output_depth; + + cmsis_nn_context ctx; + ctx.buf = scratchpad_data; + ctx.size = scratchpad_shape.Dims(0); + + auto res = arm_depthwise_conv_wrapper_s8(&ctx, &dw_conv_params, &quant_params, &input_dims, + input_data, &filter_dims, filter_data, &bias_dims, + bias_data, &output_dims, output_data); + assert(res == ARM_MATH_SUCCESS); + } + else + { + tflite::reference_integer_ops::DepthwiseConvPerChannel( + params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data); + } +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const tflite::DepthwiseParams ¶ms, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) +{ + cmsis_nn_dw_conv_params dw_conv_params; + dw_conv_params.dilation.h = params.dilation_height_factor; + dw_conv_params.dilation.w = params.dilation_width_factor; + + if (input_data_type == loco::DataType::S8 && dw_conv_params.dilation.h == 1 && + dw_conv_params.dilation.w == 1) + { + const int batch_size = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int output_depth = tflite::MatchingDim(filter_shape, 3, output_shape, 3); + + cmsis_nn_dims input_dims; + input_dims.n = batch_size; + input_dims.h = input_shape.Dims(1); + input_dims.w = input_shape.Dims(2); + input_dims.c = input_shape.Dims(3); + + cmsis_nn_dims filter_dims; + filter_dims.n = filter_shape.Dims(0); + filter_dims.h = filter_shape.Dims(1); + filter_dims.w = filter_shape.Dims(2); + filter_dims.c = output_depth; + + cmsis_nn_dims output_dims; + output_dims.n = batch_size; + output_dims.h = output_shape.Dims(1); + output_dims.w = output_shape.Dims(2); + output_dims.c = output_depth; + + const int32_t buf_size = arm_depthwise_conv_wrapper_s8_get_buffer_size( + &dw_conv_params, &input_dims, &filter_dims, &output_dims); + + auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); + + luci_interpreter::Shape scratchpad_shape{buf_size * data_type_size}; + scratchpad->resize(scratchpad_shape); + } + else + { + scratchpad->set_allocatable(false); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h new file mode 100644 index 0000000..15ff032 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H +#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H + +#include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter_pal +{ + +template +static inline void Dequantize(tflite::DequantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_integer_ops::Dequantize(params, input_shape, input_data, output_shape, + output_data); +} + +static inline void Dequantize(tflite::DequantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const uint8_t *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALElu.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALElu.h new file mode 100644 index 0000000..4089d0a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALElu.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ELU_H +#define LUCI_INTERPRETER_PAL_ELU_H + +#include + +namespace luci_interpreter_pal +{ + +static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::Elu(input_shape, input_data, output_shape, output_data); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h new file mode 100644 index 0000000..32e9057 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_FULLYCONNECTED_H +#define LUCI_INTERPRETER_PAL_FULLYCONNECTED_H + +#include +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &filter_shape, const T *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + { + // MARK: At this moment this operation doesn't support + assert(false && "FullyConnected NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + } +} + +template <> +inline void +FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data) +{ + assert(output_shape.DimensionsCount() == 2); + + const int batches = output_shape.Dims(0); + const int output_depth = output_shape.Dims(1); + + const int filter_dim_count = filter_shape.DimensionsCount(); + const int accum_depth = filter_shape.Dims(filter_dim_count - 1); + + cmsis_nn_fc_params fc_params; + fc_params.input_offset = params.input_offset; + fc_params.output_offset = params.output_offset; + fc_params.filter_offset = params.weights_offset; + fc_params.activation.min = params.quantized_activation_min; + fc_params.activation.max = params.quantized_activation_max; + + cmsis_nn_per_tensor_quant_params quant_params; + quant_params.multiplier = params.output_multiplier; + quant_params.shift = params.output_shift; + + cmsis_nn_dims input_dims; + input_dims.n = batches; + input_dims.h = 1; + input_dims.w = 1; + input_dims.c = accum_depth; + + cmsis_nn_dims filter_dims; + filter_dims.n = accum_depth; + filter_dims.h = 1; + filter_dims.w = 1; + filter_dims.c = output_depth; + + cmsis_nn_dims bias_dims; + bias_dims.n = 1; + bias_dims.h = 1; + bias_dims.w = 1; + bias_dims.c = output_depth; + + cmsis_nn_dims output_dims; + output_dims.n = batches; + output_dims.h = 1; + output_dims.w = 1; + output_dims.c = output_depth; + + int32_t buf_size = arm_fully_connected_s8_get_buffer_size(&filter_dims); + auto buffer = std::make_unique(buf_size); + assert(buffer != nullptr); + + cmsis_nn_context ctx; + ctx.buf = buffer.get(); + ctx.size = buf_size; + + auto res = + arm_fully_connected_s8(&ctx, &fc_params, &quant_params, &input_dims, input_data, &filter_dims, + filter_data, &bias_dims, bias_data, &output_dims, output_data); + assert(res == ARM_MATH_SUCCESS); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_FULLYCONNECTED_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h new file mode 100644 index 0000000..f84742a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H +#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::L2Normalization(op_params, input_shape, input_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h new file mode 100644 index 0000000..38a302f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2POOL2D_H +#define LUCI_INTERPRETER_PAL_L2POOL2D_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Pool(const tflite::PoolParams ¶ms, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::reference_ops::L2Pool(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2POOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h new file mode 100644 index 0000000..9ccd222 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H +#define LUCI_INTERPRETER_PAL_LEAKYRELU_H + +#include + +namespace luci_interpreter_pal +{ +static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALMul.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALMul.h new file mode 100644 index 0000000..347a97a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALMul.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_MUL_H +#define LUCI_INTERPRETER_PAL_MUL_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} + +template +static inline void +BroadcastMul4DSlow(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALNeg.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALNeg.h new file mode 100644 index 0000000..be5903a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALNeg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_NEG_H +#define LUCI_INTERPRETER_PAL_NEG_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_NEG_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h new file mode 100644 index 0000000..6046789 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H +#define LUCI_INTERPRETER_PAL_QUANTIZE_H + +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter_pal +{ +template +static inline void Quantize(tflite::QuantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); +} + +template +static inline void Requantize(const Input *input_data, int32_t size, + int32_t effective_scale_multiplier, int32_t effective_scale_shift, + int32_t input_zero_point, int32_t output_zero_point, + Output *output_data) +{ + tflite::reference_ops::Requantize(input_data, size, effective_scale_multiplier, + effective_scale_shift, input_zero_point, output_zero_point, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h new file mode 100644 index 0000000..cc9f0fd --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H +#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeBilinear(const tflite::ResizeBilinearParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h new file mode 100644 index 0000000..f4d5a6e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H +#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h new file mode 100644 index 0000000..a4a5b2a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SVDF_H +#define LUCI_INTERPRETER_PAL_SVDF_H + +#include +#include + +namespace luci_interpreter_pal +{ +static inline void +IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, + const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, int16_t *activation_state_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, + int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, + int scale_2_b, int32_t input_zp, int32_t output_zp) +{ + const int32_t rank = params.rank; + const int32_t batch_size = input_shape.Dims(0); + const int32_t num_filters = weight_feature_shape.Dims(0); + const int32_t memory_size = weight_time_shape.Dims(1); + + cmsis_nn_dims input_dims; + input_dims.n = input_shape.Dims(0); + input_dims.h = input_shape.Dims(1); + + cmsis_nn_dims weights_feature_dims; + weights_feature_dims.n = weight_feature_shape.Dims(0); + weights_feature_dims.h = weight_feature_shape.Dims(1); + + cmsis_nn_dims weights_time_dims; + weights_time_dims.n = weight_time_shape.Dims(0); + weights_time_dims.h = weight_time_shape.Dims(1); + + cmsis_nn_dims bias_dims; + bias_dims.n = bias_shape.Dims(0); + + cmsis_nn_dims state_dims; + state_dims.n = batch_size; + state_dims.h = memory_size * num_filters; + + cmsis_nn_dims output_dims; + output_dims.n = output_shape.Dims(0); + output_dims.h = output_shape.Dims(1); + + cmsis_nn_svdf_params svdf_params; + svdf_params.rank = params.rank; + svdf_params.input_offset = input_zp; + svdf_params.output_offset = output_zp; + + svdf_params.input_activation.min = INT16_MIN; + svdf_params.input_activation.max = INT16_MAX; + + svdf_params.output_activation.min = INT8_MIN; + svdf_params.output_activation.max = INT8_MAX; + + cmsis_nn_per_tensor_quant_params in_quant_params; + in_quant_params.multiplier = scale_1_a; + in_quant_params.shift = scale_1_b; + + cmsis_nn_per_tensor_quant_params out_quant_params; + out_quant_params.multiplier = scale_2_a; + out_quant_params.shift = scale_2_b; + + cmsis_nn_context scratch_ctx; + scratch_ctx.buf = scratchpad_data; + + cmsis_nn_context scratch_output_ctx; + scratch_output_ctx.buf = output_temp_data; + + arm_svdf_s8(&scratch_ctx, &scratch_output_ctx, &svdf_params, &in_quant_params, &out_quant_params, + &input_dims, input_data, &state_dims, activation_state_data, &weights_feature_dims, + weight_feature_data, &weights_time_dims, weight_time_data, &bias_dims, bias_data, + &output_dims, output_data); +} +static inline void +FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &weight_feature_shape, + const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const float *weight_time_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, float *scratchpad_data, float *activation_state_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + const int32_t rank = params.rank; + const int32_t batch_size = input_shape.Dims(0); + const int32_t input_size = input_shape.Dims(1); + const int32_t num_filters = weight_feature_shape.Dims(0); + const int32_t num_units = num_filters / rank; + const int32_t memory_size = weight_time_shape.Dims(1); + + // Left shift the activation_state. + { + float *new_state_start = activation_state_data; + const float *old_state_start = activation_state_data + 1; + const float *old_state_end = activation_state_data + batch_size * num_filters * memory_size; + while (old_state_start != old_state_end) + { + *new_state_start++ = *old_state_start++; + } + } + + // Note: no need to clear the latest activation, matmul is not accumulative. + + // Compute conv1d(inputs, weights_feature). + // The activation_state's rightmost column is used to save current cycle + // activation. This is achieved by starting at state_ptr[memory_size - 1] and + // having the stride equal to memory_size. + + // Perform batched matrix vector multiply operation: + { + const float *matrix = weight_feature_data; + const float *vector = input_data; + float *result = &activation_state_data[memory_size - 1]; + float *result_in_batch = result; + for (int i = 0; i < batch_size; ++i) + { + const float *matrix_ptr = matrix; + for (int j = 0; j < num_filters; ++j) + { + float dot_prod = 0.0f; + const float *vector_in_batch = vector + i * input_size; + for (int k = 0; k < input_size; ++k) + { + dot_prod += *matrix_ptr++ * *vector_in_batch++; + } + *result_in_batch = dot_prod; + result_in_batch += memory_size; + } + } + } + + tflite::reference_ops::ApplyTimeWeightsBiasAndActivation( + batch_size, memory_size, num_filters, num_units, rank, weight_time_data, bias_data, + params.activation, activation_state_data, scratchpad_data, output_data); +} + +static inline void SetupScratchpadTensor( + const luci_interpreter::DataType &input_data_type, + const luci_interpreter::DataType &weight_feature_data_type, + luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, + luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, + luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, + const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, + const int32_t batch_size, const int32_t num_filters, const int32_t num_units) +{ + if (input_data_type == loco::DataType::FLOAT32 && + (weight_feature_data_type == loco::DataType::S8 || + weight_feature_data_type == loco::DataType::U8)) + { + (void)input_shape; + (void)weight_time_shape; + (void)scratchpad_3; + (void)scratchpad_4; + (void)scratchpad_5; + (void)scratchpad_6; + + throw std::runtime_error("Hybrid type is not supported for cmsisnn"); + } + + // Resize scratchpad_1 tensor + scratchpad_1->resize({batch_size, num_filters}); + + if (input_data_type == loco::DataType::S8) + { + // Resize scratchpad_2 for full_integer op + scratchpad_2->resize({batch_size, num_units}); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h new file mode 100644 index 0000000..6bbda48 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H +#define LUCI_INTERPRETER_PAL_SOFTMAX_H + +#include +#include + +namespace luci_interpreter_pal +{ +static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, + float beta) +{ + // Do nothing for mcu + (void)data; + (void)input_scale; + (void)beta; +} + +static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) +{ + int32 input_beta_multiplier; + int input_beta_left_shift; + static const int kScaledDiffIntegerBits = 5; + tflite::PreprocessSoftmaxScaling(beta, input_scale, kScaledDiffIntegerBits, + &input_beta_multiplier, &input_beta_left_shift); + + params->input_multiplier = input_beta_multiplier; + params->input_left_shift = input_beta_left_shift; + params->diff_min = + -tflite::CalculateInputRadius(kScaledDiffIntegerBits, params->input_left_shift); +} + +template +static inline void Softmax(const tflite::SoftmaxParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + // MARK: At this moment this operation doesn't support on mcu + assert(false && "Softmax NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)output_shape; + (void)output_data; +} + +template <> +inline void Softmax(const tflite::SoftmaxParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data) +{ + const int trailing_dim = input_shape.DimensionsCount() - 1; + const int outer_size = tflite::MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); + const int depth = tflite::MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); + const int32_t mult = params.input_multiplier; + const int32_t shift = params.input_left_shift; + const int32_t diff_min = params.diff_min; + + arm_softmax_s8(input_data, outer_size, depth, mult, shift, diff_min, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h new file mode 100644 index 0000000..fdddaa9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H +#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, + const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *paddings_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::SpaceToBatchND( + params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, paddings_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h new file mode 100644 index 0000000..816b7f6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H +#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSub.h b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSub.h new file mode 100644 index 0000000..ea57578 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/PALSub.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SUB_H +#define LUCI_INTERPRETER_PAL_SUB_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Sub(const tflite::ArithmeticParams ¶ms, + const tflite::RuntimeShape &input1_shape, const T *input1_data, + const tflite::RuntimeShape &input2_shape, const T *input2_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SUB_H diff --git a/compiler/luci-micro/luci-interpreter/pal/cmsisnn/pal.cmake b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/pal.cmake new file mode 100644 index 0000000..a68b363 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/cmsisnn/pal.cmake @@ -0,0 +1,65 @@ +macro(initialize_pal) + nnas_find_package(TensorFlowSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowEigenSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowRuySource EXACT 2.6.0 QUIET) + nnas_find_package(CMSISSource EXACT 5.8.0 QUIET) + + if (NOT TensorFlowSource_FOUND) + message(STATUS "Skipping luci-interpreter: TensorFlow not found") + return() + endif () + + if (NOT TensorFlowGEMMLowpSource_FOUND) + message(STATUS "Skipping luci-interpreter: gemmlowp not found") + return() + endif () + + if (NOT TensorFlowEigenSource_FOUND) + message(STATUS "Skipping luci-interpreter: Eigen not found") + return() + endif () + + if (NOT TensorFlowRuySource_FOUND) + message(STATUS "Skipping luci-interpreter: Ruy not found") + return() + endif () + + if (NOT CMSISSource_FOUND) + message(STATUS "Skipping luci-interpreter: CMSISSource not found") + return() + endif () + + set(PAL_INITIALIZED TRUE) +endmacro() + +macro(add_pal_to_target TGT) + target_include_directories(${TGT} PRIVATE "${PAL}") + target_include_directories(${TGT} PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}") + target_include_directories(${TGT} PRIVATE ${LUCI_INTERPRETER_PAL_DIR}) + + file(GLOB_RECURSE PAL_SOURCES "${CMSISSource_DIR}/CMSIS/NN/Source/*.c") + list(APPEND PAL_SOURCES ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc) + add_library(luci_interpreter_cmsisnn_pal STATIC ${PAL_SOURCES}) + set_property(TARGET luci_interpreter_cmsisnn_pal PROPERTY POSITION_INDEPENDENT_CODE ON) + target_include_directories(luci_interpreter_cmsisnn_pal PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}" + ) + + add_subdirectory(${CMSISSource_DIR}/CMSIS/NN ${CMAKE_CURRENT_BINARY_DIR}/CMSISNN) + target_include_directories(luci_interpreter_cmsisnn_pal PUBLIC + "${CMSISSource_DIR}/CMSIS/NN/Include" + "${CMSISSource_DIR}/CMSIS/DSP/Include" + "${CMSISSource_DIR}/CMSIS/Core/Include") + + target_link_libraries(${TGT} PRIVATE luci_interpreter_cmsisnn_pal) +endmacro() diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/KernelsToBuild.lst b/compiler/luci-micro/luci-interpreter/pal/linux/KernelsToBuild.lst new file mode 100644 index 0000000..8e20559 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/KernelsToBuild.lst @@ -0,0 +1,77 @@ +REGISTER_KERNEL(Add) +REGISTER_KERNEL(ArgMax) +REGISTER_KERNEL(AveragePool2D) +REGISTER_KERNEL(BatchMatMul) +REGISTER_KERNEL(BatchToSpaceND) +REGISTER_KERNEL(Cast) +REGISTER_KERNEL(Concatenation) +REGISTER_KERNEL(Conv2D) +REGISTER_KERNEL(DepthToSpace) +REGISTER_KERNEL(DepthwiseConv2D) +REGISTER_KERNEL(Dequantize) +REGISTER_KERNEL(Div) +REGISTER_KERNEL(Elu) +REGISTER_KERNEL(Exp) +REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) +REGISTER_KERNEL(Floor) +REGISTER_KERNEL(FloorDiv) +REGISTER_KERNEL(Equal) +REGISTER_KERNEL(FullyConnected) +REGISTER_KERNEL(Gather) +REGISTER_KERNEL(Greater) +REGISTER_KERNEL(GreaterEqual) +REGISTER_KERNEL(If) +REGISTER_KERNEL(InstanceNorm) +REGISTER_KERNEL(L2Normalize) +REGISTER_KERNEL(L2Pool2D) +REGISTER_KERNEL(LeakyRelu) +REGISTER_KERNEL(Less) +REGISTER_KERNEL(LessEqual) +REGISTER_KERNEL(LocalResponseNormalization) +REGISTER_KERNEL(LogicalAnd) +REGISTER_KERNEL(LogicalNot) +REGISTER_KERNEL(LogicalOr) +REGISTER_KERNEL(Logistic) +REGISTER_KERNEL(LogSoftmax) +REGISTER_KERNEL(Maximum) +REGISTER_KERNEL(MaxPool2D) +REGISTER_KERNEL(Mean) +REGISTER_KERNEL(Minimum) +REGISTER_KERNEL(MirrorPad) +REGISTER_KERNEL(Mul) +REGISTER_KERNEL(Neg) +REGISTER_KERNEL(NotEqual) +REGISTER_KERNEL(OneHot) +REGISTER_KERNEL(Pack) +REGISTER_KERNEL(Pad) +REGISTER_KERNEL(PadV2) +REGISTER_KERNEL(Pow) +REGISTER_KERNEL(PRelu) +REGISTER_KERNEL(Quantize) +REGISTER_KERNEL(Relu) +REGISTER_KERNEL(Relu6) +REGISTER_KERNEL(Reshape) +REGISTER_KERNEL(ResizeBilinear) +REGISTER_KERNEL(ResizeNearestNeighbor) +REGISTER_KERNEL(ReverseV2) +REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) +REGISTER_KERNEL(Slice) +REGISTER_KERNEL(Softmax) +REGISTER_KERNEL(SpaceToBatchND) +REGISTER_KERNEL(SpaceToDepth) +REGISTER_KERNEL(Split) +REGISTER_KERNEL(SplitV) +REGISTER_KERNEL(StridedSlice) +REGISTER_KERNEL(Sqrt) +REGISTER_KERNEL(Square) +REGISTER_KERNEL(SquaredDifference) +REGISTER_KERNEL(Squeeze) +REGISTER_KERNEL(Sub) +REGISTER_KERNEL(SVDF) +REGISTER_KERNEL(Tanh) +REGISTER_KERNEL(Transpose) +REGISTER_KERNEL(TransposeConv) +REGISTER_KERNEL(Unpack) +REGISTER_KERNEL(While) diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALArgMax.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALArgMax.h new file mode 100644 index 0000000..21e6329 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALArgMax.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H +#define LUCI_INTERPRETER_PAL_ARGMAX_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, + const T2 *axis, const tflite::RuntimeShape &output_shape, + T3 *output_data, const std::greater cmp) +{ + tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALAveragePool2d.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALAveragePool2d.h new file mode 100644 index 0000000..cce3060 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALAveragePool2d.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H +#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H + +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data, + const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) +{ + { + // MARK: At this moment this operation doesn't support + assert(false && "AveragePool NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, + int8_t *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + + tflite::reference_integer_ops::AveragePool(params, input_shape, input_data, output_shape, + output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &output_shape) + +{ + (void)input_data_type; + (void)input_shape; + (void)output_shape; + + scratchpad->set_allocatable(false); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchMatMul.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchMatMul.h new file mode 100644 index 0000000..3894f2d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchMatMul.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_BATCHMATMUL_H +#define LUCI_INTERPRETER_PAL_BATCHMATMUL_H + +#include + +namespace luci_interpreter_pal +{ +inline void BatchMatMul(const tflite::RuntimeShape &lhs_shape, const float *lhs_data, + const tflite::RuntimeShape &rhs_shape, const float *rhs_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::BatchMatMul(lhs_shape, lhs_data, rhs_shape, rhs_data, output_shape, + output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *lhs_scratchpad, + luci_interpreter::Tensor *rhs_scratchpad, + const tflite::RuntimeShape &lhs_shape, + const tflite::RuntimeShape &rhs_shape) +{ + // Scratchpad for transposed LHS + { + auto lhs_rank = lhs_shape.DimensionsCount(); + luci_interpreter::Shape scratchpad_size(lhs_rank); + for (int i = 0; i < lhs_rank - 2; ++i) + { + scratchpad_size.dim(i) = lhs_shape.Dims(i); + } + scratchpad_size.dim(lhs_rank - 2) = lhs_shape.Dims(lhs_rank - 1); + scratchpad_size.dim(lhs_rank - 1) = lhs_shape.Dims(lhs_rank - 2); + + lhs_scratchpad->resize(scratchpad_size); + } + // Scratchpad for transposed RHS + { + auto rhs_rank = rhs_shape.DimensionsCount(); + luci_interpreter::Shape scratchpad_size(rhs_rank); + for (int i = 0; i < rhs_rank - 2; ++i) + { + scratchpad_size.dim(i) = rhs_shape.Dims(i); + } + scratchpad_size.dim(rhs_rank - 2) = rhs_shape.Dims(rhs_rank - 1); + scratchpad_size.dim(rhs_rank - 1) = rhs_shape.Dims(rhs_rank - 2); + + rhs_scratchpad->resize(scratchpad_size); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_BATCHMATMUL_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h new file mode 100644 index 0000000..3fe2022 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H +#define LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *crops_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::BatchToSpaceND( + unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, crops_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALConv2d.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALConv2d.h new file mode 100644 index 0000000..985a15f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALConv2d.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_CONV2D_H +#define LUCI_INTERPRETER_PAL_CONV2D_H + +#include +#include + +namespace luci_interpreter_pal +{ +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &filter_shape, + const float *filter_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, const tflite::RuntimeShape &output_shape, + float *output_data, const tflite::RuntimeShape &scratchpad_shape, + float *scratchpad_data) +{ + (void)scratchpad_shape; + if (scratchpad_data) + { + const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int32_t input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); + const int32_t output_height = output_shape.Dims(1); + const int32_t output_width = output_shape.Dims(2); + const int32_t filter_height = filter_shape.Dims(1); + const int32_t filter_width = filter_shape.Dims(2); + tflite::RuntimeShape im2col_shape{batches, output_height, output_width, + input_depth * filter_height * filter_width}; + + tflite::optimized_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, im2col_shape, + scratchpad_data); + } + else + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, + tflite::RuntimeShape(), nullptr); +} + +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const uint8 *input_data, const tflite::RuntimeShape &filter_shape, + const uint8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + uint8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + uint8 *scratchpad_data) +{ + // TODO This should only be done once (although it takes only a few microseconds). + // Also, the user should be able to adjust the number of threads. + auto gemmlowp_context = std::make_unique(); + gemmlowp_context->set_max_num_threads(static_cast(std::thread::hardware_concurrency())); + + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, scratchpad_shape, + scratchpad_data, gemmlowp_context.get()); +} + +static inline void ConvPerChannel(const tflite::ConvParams ¶ms, const int32_t *mult, + const int32_t *shifts, const tflite::RuntimeShape &input_shape, + const int8 *input_data, const tflite::RuntimeShape &filter_shape, + const int8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + int8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + int8 *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + // TODO enable optimized version + tflite::reference_integer_ops::ConvPerChannel(params, mult, shifts, input_shape, input_data, + filter_shape, filter_data, bias_shape, bias_data, + output_shape, output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::ConvParams ¶ms, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) +{ + const int32_t filter_height = filter_shape.Dims(1); + const int32_t filter_width = filter_shape.Dims(2); + + // Allocate tensor for scratchpad, if needed. + // The checks here should be aligned with the actual implementation. + const bool need_dilated_scratchpad = + params.dilation_height_factor != 1 || params.dilation_width_factor != 1; + const bool need_non_dilated_scratchpad = params.stride_height != 1 || params.stride_width != 1 || + filter_height != 1 || filter_width != 1; + auto _need_scratchpad = input_data_type != luci_interpreter::DataType::S16 && + (need_dilated_scratchpad || need_non_dilated_scratchpad); + + if (_need_scratchpad) + { + const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int32_t input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); + const int32_t output_height = output_shape.Dims(1); + const int32_t output_width = output_shape.Dims(2); + + auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); + int32_t scratchpad_size = batches * output_width * output_height * input_depth * filter_height * + filter_width * data_type_size; + luci_interpreter::Shape scratchpad_shape{scratchpad_size}; + scratchpad->resize(scratchpad_shape); + } + else + { + scratchpad->set_allocatable(false); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_CONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthToSpace.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthToSpace.h new file mode 100644 index 0000000..f9ebfcf --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthToSpace.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H +#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::DepthToSpace(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h new file mode 100644 index 0000000..c9d1a29 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H +#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H + +#include +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void +DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &filter_shape, + const T *filter_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, const tflite::RuntimeShape &output_shape, + T *output_data, const tflite::RuntimeShape &scratchpad_shape, + T *scratchpad_data) +{ + { + // MARK: At this moment this operation is not supported + assert(false && "DepthwiseConvPerChannel NYI"); + (void)params; + (void)output_multiplier; + (void)output_shift; + (void)input_shape; + (void)output_data; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void DepthwiseConvPerChannel( + const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_integer_ops::DepthwiseConvPerChannel( + params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const tflite::DepthwiseParams ¶ms, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) + +{ + (void)params; + (void)input_data_type; + (void)input_shape; + (void)filter_shape; + (void)output_shape; + + scratchpad->set_allocatable(false); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALDequantize.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALDequantize.h new file mode 100644 index 0000000..3af6d07 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALDequantize.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H +#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Dequantize(tflite::DequantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALElu.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALElu.h new file mode 100644 index 0000000..cb365ff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALElu.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ELU_H +#define LUCI_INTERPRETER_PAL_ELU_H + +#include + +namespace luci_interpreter_pal +{ +static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::Elu(input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALFullyConnected.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALFullyConnected.h new file mode 100644 index 0000000..62970db --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALFullyConnected.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_FULLYCONNECTED_H +#define LUCI_INTERPRETER_PAL_FULLYCONNECTED_H + +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &filter_shape, const T *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + { + // MARK: At this moment this operation doesn't support + assert(false && "FullyConnected NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + } +} + +template <> +inline void +FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data) +{ + tflite::reference_integer_ops::FullyConnected(params, input_shape, input_data, filter_shape, + filter_data, bias_shape, bias_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_FULLYCONNECTED_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALGather.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALGather.h new file mode 100644 index 0000000..49ac35f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALGather.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_GATHER_H +#define LUCI_INTERPRETER_PAL_GATHER_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Gather(const tflite::GatherParams &op_params, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &coords_shape, const CoordsT *coords_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::Gather(op_params, input_shape, input_data, coords_shape, coords_data, + output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_GATHER_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Normalize.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Normalize.h new file mode 100644 index 0000000..6c663e2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Normalize.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H +#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::L2Normalization(op_params, input_shape, input_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Pool2D.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Pool2D.h new file mode 100644 index 0000000..aac57f2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALL2Pool2D.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2POOL2D_H +#define LUCI_INTERPRETER_PAL_L2POOL2D_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Pool(const tflite::PoolParams ¶ms, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::optimized_ops::L2Pool(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2POOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALLeakyRelu.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALLeakyRelu.h new file mode 100644 index 0000000..e8209ba --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALLeakyRelu.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H +#define LUCI_INTERPRETER_PAL_LEAKYRELU_H + +#include + +namespace luci_interpreter_pal +{ +static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h new file mode 100644 index 0000000..54f7f09 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H +#define LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H + +#include + +namespace luci_interpreter_pal +{ +static inline void +LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::LocalResponseNormalization(op_params, input_shape, input_data, + output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALLogSoftmax.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALLogSoftmax.h new file mode 100644 index 0000000..a32e3ee --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALLogSoftmax.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_LOGSOFTMAX_H +#define LUCI_INTERPRETER_PAL_LOGSOFTMAX_H + +#include + +namespace luci_interpreter_pal +{ +static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, + float beta) +{ + tflite::optimized_ops::PopulateSoftmaxLookupTable(data, input_scale, beta); +} + +static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) +{ + // Do nothing for linux + (void)params; + (void)input_scale; + (void)beta; +} + +static inline void LogSoftmax(const tflite::SoftmaxParams ¶ms, float input_scale, + const tflite::RuntimeShape &input_shape, const uint8 *input_data, + const tflite::RuntimeShape &output_shape, uint8 *output_data) +{ + tflite::optimized_ops::LogSoftmax(params, input_scale, input_shape, input_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LOGSOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALMul.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALMul.h new file mode 100644 index 0000000..a8a9d4a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALMul.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_MUL_H +#define LUCI_INTERPRETER_PAL_MUL_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::optimized_ops::Mul(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); +} + +template <> +inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const int64_t *input1_data, const tflite::RuntimeShape &input2_shape, + const int64_t *input2_data, const tflite::RuntimeShape &output_shape, + int64_t *output_data) +{ + tflite::optimized_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} + +template +static inline void +BroadcastMul4DSlow(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALNeg.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALNeg.h new file mode 100644 index 0000000..797ffee --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALNeg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_NEG_H +#define LUCI_INTERPRETER_PAL_NEG_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_NEG_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALQuantize.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALQuantize.h new file mode 100644 index 0000000..bf1d795 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALQuantize.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H +#define LUCI_INTERPRETER_PAL_QUANTIZE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Quantize(tflite::QuantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); +} + +template +static inline void Requantize(const Input *input_data, int32_t size, + int32_t effective_scale_multiplier, int32_t effective_scale_shift, + int32_t input_zero_point, int32_t output_zero_point, + Output *output_data) +{ + tflite::optimized_ops::Requantize(input_data, size, effective_scale_multiplier, + effective_scale_shift, input_zero_point, output_zero_point, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu.h new file mode 100644 index 0000000..b4c715d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RELU_H +#define LUCI_INTERPRETER_PAL_RELU_H + +#include + +namespace luci_interpreter_pal +{ +static inline void Relu(const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::Relu(input_shape, input_data, output_shape, output_data); +} + +template +static inline void ReluX(const tflite::ReluParams ¶ms, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::optimized_ops::ReluX(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu6.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu6.h new file mode 100644 index 0000000..bf2f91a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALRelu6.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RELU6_H +#define LUCI_INTERPRETER_PAL_RELU6_H + +#include + +namespace luci_interpreter_pal +{ +static inline void Relu6(const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::optimized_ops::Relu6(input_shape, input_data, output_shape, output_data); +} + +template +static inline void ReluX(const tflite::ReluParams ¶ms, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::optimized_ops::ReluX(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RELU6_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeBilinear.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeBilinear.h new file mode 100644 index 0000000..7380081 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeBilinear.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H +#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeBilinear(const tflite::ResizeBilinearParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h new file mode 100644 index 0000000..74d1926 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H +#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSVDF.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSVDF.h new file mode 100644 index 0000000..0ffba14 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSVDF.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SVDF_H +#define LUCI_INTERPRETER_PAL_SVDF_H + +#include + +namespace luci_interpreter_pal +{ +static inline void +IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, + const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, int16_t *activation_state_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, + int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, + int scale_2_b, int32_t input_zp, int32_t output_zp) +{ + tflite::reference_ops::EvalIntegerSVDF(¶ms, input_shape, input_data, weight_feature_shape, + weight_feature_data, weight_time_shape, weight_time_data, + bias_shape, bias_data, activation_state_data, output_shape, + output_data, scratchpad_data, output_temp_data, scale_1_a, + scale_1_b, scale_2_a, scale_2_b, input_zp, output_zp); +} +static inline void +FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &weight_feature_shape, + const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const float *weight_time_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, float *scratchpad_data, float *activation_state_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::EvalFloatSVDF(¶ms, input_shape, input_data, weight_feature_shape, + weight_feature_data, weight_time_shape, weight_time_data, + bias_shape, bias_data, scratchpad_data, + activation_state_data, output_shape, output_data); +} + +static inline void SetupScratchpadTensor( + const luci_interpreter::DataType &input_data_type, + const luci_interpreter::DataType &weight_feature_data_type, + luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, + luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, + luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, + const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, + const int32_t batch_size, const int32_t num_filters, const int32_t num_units) +{ + + if (input_data_type == loco::DataType::FLOAT32 && + (weight_feature_data_type == loco::DataType::S8 || + weight_feature_data_type == loco::DataType::U8)) + { + (void)input_shape; + (void)weight_time_shape; + (void)scratchpad_3; + (void)scratchpad_4; + (void)scratchpad_5; + (void)scratchpad_6; + + throw std::runtime_error("Hybrid type is not currently supported for linux platform"); + } + + // Resize scratchpad_1 tensor + scratchpad_1->resize({batch_size, num_filters}); + + if (input_data_type == loco::DataType::S8) + { + // Resize scratchpad_2 for full_integer op + scratchpad_2->resize({batch_size, num_units}); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSlice.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSlice.h new file mode 100644 index 0000000..640a716 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSlice.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SLICE_H +#define LUCI_INTERPRETER_PAL_SLICE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Slice(const tflite::SliceParams &op_params, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::Slice(op_params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SLICE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSoftmax.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSoftmax.h new file mode 100644 index 0000000..b197e79 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSoftmax.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H +#define LUCI_INTERPRETER_PAL_SOFTMAX_H + +#include + +namespace luci_interpreter_pal +{ +static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, + float beta) +{ + tflite::optimized_ops::PopulateSoftmaxLookupTable(data, input_scale, beta); +} + +static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) +{ + // Do nothing for linux + (void)params; + (void)input_scale; + (void)beta; +} + +template +static inline void Softmax(const tflite::SoftmaxParams ¶ms, + const tflite::RuntimeShape &input_shape, const In *input_data, + const tflite::RuntimeShape &output_shape, Out *output_data) +{ + tflite::optimized_ops::Softmax(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h new file mode 100644 index 0000000..5e8de9b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H +#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, + const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *paddings_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::SpaceToBatchND( + params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, paddings_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h new file mode 100644 index 0000000..52d2a5b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H +#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::optimized_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSplit.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSplit.h new file mode 100644 index 0000000..4d8da72 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSplit.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPLIT_H +#define LUCI_INTERPRETER_PAL_SPLIT_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Split(const tflite::SplitParams ¶ms, const tflite::RuntimeShape &input_shape, + const Scalar *input_data, const tflite::RuntimeShape *const *output_shapes, + Scalar *const *output_data) +{ + tflite::optimized_ops::Split(params, input_shape, input_data, output_shapes, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPLIT_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/PALSub.h b/compiler/luci-micro/luci-interpreter/pal/linux/PALSub.h new file mode 100644 index 0000000..04080d6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/PALSub.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SUB_H +#define LUCI_INTERPRETER_PAL_SUB_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Sub(const tflite::ArithmeticParams ¶ms, + const tflite::RuntimeShape &input1_shape, const T *input1_data, + const tflite::RuntimeShape &input2_shape, const T *input2_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::optimized_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SUB_H diff --git a/compiler/luci-micro/luci-interpreter/pal/linux/pal.cmake b/compiler/luci-micro/luci-interpreter/pal/linux/pal.cmake new file mode 100644 index 0000000..185700c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/linux/pal.cmake @@ -0,0 +1,82 @@ +macro(initialize_pal) + nnas_find_package(TensorFlowSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowEigenSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowRuySource EXACT 2.6.0 QUIET) + + if (NOT TensorFlowSource_FOUND) + message(STATUS "Skipping luci-interpreter: TensorFlow not found") + return() + endif () + + if (NOT TensorFlowGEMMLowpSource_FOUND) + message(STATUS "Skipping luci-interpreter: gemmlowp not found") + return() + endif () + + if (NOT TensorFlowEigenSource_FOUND) + message(STATUS "Skipping luci-interpreter: Eigen not found") + return() + endif () + + if (NOT TensorFlowRuySource_FOUND) + message(STATUS "Skipping luci-interpreter: Ruy not found") + return() + endif () + + find_package(Threads REQUIRED) + + set(PAL_INITIALIZED TRUE) +endmacro() + +macro(add_pal_to_target TGT) + target_include_directories(${TGT} PRIVATE "${PAL}") + target_include_directories(${TGT} SYSTEM PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}") + target_include_directories(${TGT} PRIVATE ${LUCI_INTERPRETER_PAL_DIR}) + + # TODO put it back, I changed my mind. + # instead add sources with visitors in this library + set(PAL_SOURCES ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc) + + if(BUILD_ARM32_NEON) + # NOTE may need to revise this list for version upgrade + set(PAL_SOURCES ${PAL_SOURCES} + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/cpu_check.cc + ${TensorFlowRuySource_DIR}/ruy/allocator.cc + ${TensorFlowRuySource_DIR}/ruy/block_map.cc + ${TensorFlowRuySource_DIR}/ruy/blocking_counter.cc + ${TensorFlowRuySource_DIR}/ruy/context_get_ctx.cc + ${TensorFlowRuySource_DIR}/ruy/cpuinfo.cc + ${TensorFlowRuySource_DIR}/ruy/ctx.cc + ${TensorFlowRuySource_DIR}/ruy/denormal.cc + ${TensorFlowRuySource_DIR}/ruy/frontend.cc + ${TensorFlowRuySource_DIR}/ruy/pack_arm.cc + ${TensorFlowRuySource_DIR}/ruy/prepacked_cache.cc + ${TensorFlowRuySource_DIR}/ruy/prepare_packed_matrices.cc + ${TensorFlowRuySource_DIR}/ruy/system_aligned_alloc.cc + ${TensorFlowRuySource_DIR}/ruy/thread_pool.cc + ${TensorFlowRuySource_DIR}/ruy/trmul.cc + ${TensorFlowRuySource_DIR}/ruy/tune.cc + ${TensorFlowRuySource_DIR}/ruy/wait.cc + ${TensorFlowRuySource_DIR}/ruy/kernel_arm32.cc + ) + endif(BUILD_ARM32_NEON) + + add_library(luci_interpreter_linux_pal STATIC ${PAL_SOURCES}) + set_target_properties(luci_interpreter_linux_pal PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(luci_interpreter_linux_pal SYSTEM PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}" + ) + + target_link_libraries(${TGT} PRIVATE Threads::Threads luci_interpreter_linux_pal) +endmacro() diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst b/compiler/luci-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst new file mode 100644 index 0000000..f0df58d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst @@ -0,0 +1,62 @@ +REGISTER_KERNEL(Add) +REGISTER_KERNEL(ArgMax) +REGISTER_KERNEL(AveragePool2D) +REGISTER_KERNEL(BatchToSpaceND) +REGISTER_KERNEL(Cast) +REGISTER_KERNEL(Concatenation) +REGISTER_KERNEL(Conv2D) +REGISTER_KERNEL(DepthToSpace) +REGISTER_KERNEL(DepthwiseConv2D) +REGISTER_KERNEL(Dequantize) +REGISTER_KERNEL(Div) +REGISTER_KERNEL(Elu) +REGISTER_KERNEL(Exp) +REGISTER_KERNEL(ExpandDims) +REGISTER_KERNEL(Fill) +REGISTER_KERNEL(Floor) +REGISTER_KERNEL(FloorDiv) +REGISTER_KERNEL(Equal) +REGISTER_KERNEL(FullyConnected) +REGISTER_KERNEL(Greater) +REGISTER_KERNEL(GreaterEqual) +REGISTER_KERNEL(If) +REGISTER_KERNEL(InstanceNorm) +REGISTER_KERNEL(L2Normalize) +REGISTER_KERNEL(L2Pool2D) +REGISTER_KERNEL(LeakyRelu) +REGISTER_KERNEL(Less) +REGISTER_KERNEL(LessEqual) +REGISTER_KERNEL(LogicalAnd) +REGISTER_KERNEL(LogicalNot) +REGISTER_KERNEL(LogicalOr) +REGISTER_KERNEL(Logistic) +REGISTER_KERNEL(Maximum) +REGISTER_KERNEL(MaxPool2D) +REGISTER_KERNEL(Minimum) +REGISTER_KERNEL(MirrorPad) +REGISTER_KERNEL(Mul) +REGISTER_KERNEL(Neg) +REGISTER_KERNEL(NotEqual) +REGISTER_KERNEL(Pad) +REGISTER_KERNEL(PadV2) +REGISTER_KERNEL(PRelu) +REGISTER_KERNEL(Quantize) +REGISTER_KERNEL(Reshape) +REGISTER_KERNEL(ResizeBilinear) +REGISTER_KERNEL(ResizeNearestNeighbor) +REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Shape) +REGISTER_KERNEL(Softmax) +REGISTER_KERNEL(SpaceToBatchND) +REGISTER_KERNEL(SpaceToDepth) +REGISTER_KERNEL(StridedSlice) +REGISTER_KERNEL(Sqrt) +REGISTER_KERNEL(Square) +REGISTER_KERNEL(SquaredDifference) +REGISTER_KERNEL(Squeeze) +REGISTER_KERNEL(Sub) +REGISTER_KERNEL(SVDF) +REGISTER_KERNEL(Tanh) +REGISTER_KERNEL(Transpose) +REGISTER_KERNEL(TransposeConv) +REGISTER_KERNEL(While) diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALArgMax.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALArgMax.h new file mode 100644 index 0000000..21e6329 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALArgMax.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H +#define LUCI_INTERPRETER_PAL_ARGMAX_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, + const T2 *axis, const tflite::RuntimeShape &output_shape, + T3 *output_data, const std::greater cmp) +{ + tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h new file mode 100644 index 0000000..cce3060 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H +#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H + +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data, + const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) +{ + { + // MARK: At this moment this operation doesn't support + assert(false && "AveragePool NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void AveragePool(const tflite::PoolParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, + int8_t *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + + tflite::reference_integer_ops::AveragePool(params, input_shape, input_data, output_shape, + output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &output_shape) + +{ + (void)input_data_type; + (void)input_shape; + (void)output_shape; + + scratchpad->set_allocatable(false); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h new file mode 100644 index 0000000..4dd77ff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H +#define LUCI_INTERPRETER_PAL_ARGMAX_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *crops_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::BatchToSpaceND( + unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, crops_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALConv2d.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALConv2d.h new file mode 100644 index 0000000..1397687 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALConv2d.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_CONV2D_H +#define LUCI_INTERPRETER_PAL_CONV2D_H + +#include +#include + +namespace luci_interpreter_pal +{ +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &filter_shape, + const float *filter_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, const tflite::RuntimeShape &output_shape, + float *output_data, const tflite::RuntimeShape &scratchpad_shape, + float *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, + tflite::RuntimeShape(), nullptr); +} + +static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, + const uint8 *input_data, const tflite::RuntimeShape &filter_shape, + const uint8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + uint8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + uint8 *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data, scratchpad_shape, + scratchpad_data, nullptr); +} + +static inline void ConvPerChannel(const tflite::ConvParams ¶ms, const int32_t *mult, + const int32_t *shifts, const tflite::RuntimeShape &input_shape, + const int8 *input_data, const tflite::RuntimeShape &filter_shape, + const int8 *filter_data, const tflite::RuntimeShape &bias_shape, + const int32 *bias_data, const tflite::RuntimeShape &output_shape, + int8 *output_data, const tflite::RuntimeShape &scratchpad_shape, + int8 *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_integer_ops::ConvPerChannel(params, mult, shifts, input_shape, input_data, + filter_shape, filter_data, bias_shape, bias_data, + output_shape, output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const luci_interpreter::DataType &input_data_type, + const tflite::ConvParams ¶ms, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) +{ + (void)input_data_type; + (void)params; + (void)input_shape; + (void)filter_shape; + (void)output_shape; + scratchpad->set_allocatable(false); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_CONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h new file mode 100644 index 0000000..8463e57 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H +#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::DepthToSpace(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h new file mode 100644 index 0000000..c9d1a29 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H +#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H + +#include +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void +DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &filter_shape, + const T *filter_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, const tflite::RuntimeShape &output_shape, + T *output_data, const tflite::RuntimeShape &scratchpad_shape, + T *scratchpad_data) +{ + { + // MARK: At this moment this operation is not supported + assert(false && "DepthwiseConvPerChannel NYI"); + (void)params; + (void)output_multiplier; + (void)output_shift; + (void)input_shape; + (void)output_data; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + (void)scratchpad_shape; + (void)scratchpad_data; + } +} + +template <> +inline void DepthwiseConvPerChannel( + const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, + const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, + const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) +{ + (void)scratchpad_shape; + (void)scratchpad_data; + tflite::reference_integer_ops::DepthwiseConvPerChannel( + params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, + bias_shape, bias_data, output_shape, output_data); +} + +static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, + const tflite::DepthwiseParams ¶ms, + const luci_interpreter::DataType &input_data_type, + const tflite::RuntimeShape &input_shape, + const tflite::RuntimeShape &filter_shape, + const tflite::RuntimeShape &output_shape) + +{ + (void)params; + (void)input_data_type; + (void)input_shape; + (void)filter_shape; + (void)output_shape; + + scratchpad->set_allocatable(false); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALDequantize.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDequantize.h new file mode 100644 index 0000000..15ff032 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALDequantize.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H +#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H + +#include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter_pal +{ + +template +static inline void Dequantize(tflite::DequantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_integer_ops::Dequantize(params, input_shape, input_data, output_shape, + output_data); +} + +static inline void Dequantize(tflite::DequantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const uint8_t *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALElu.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALElu.h new file mode 100644 index 0000000..4089d0a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALElu.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_ELU_H +#define LUCI_INTERPRETER_PAL_ELU_H + +#include + +namespace luci_interpreter_pal +{ + +static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::Elu(input_shape, input_data, output_shape, output_data); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALFullyConnected.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALFullyConnected.h new file mode 100644 index 0000000..048624d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALFullyConnected.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_FULLYCONNECTED_H +#define LUCI_INTERPRETER_PAL_FULLYCONNECTED_H + +#include +#include + +namespace luci_interpreter_pal +{ +template +static inline void FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &filter_shape, const T *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + { + // MARK: At this moment this operation is not supported + assert(false && "FullyConnected NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_shape; + (void)bias_data; + (void)output_shape; + (void)output_data; + } +} + +template <> +inline void +FullyConnected(const tflite::FullyConnectedParams ¶ms, + const tflite::RuntimeShape &input_shape, const int8_t *input_data, + const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, + const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data) +{ + tflite::reference_integer_ops::FullyConnected(params, input_shape, input_data, filter_shape, + filter_data, bias_shape, bias_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_FULLYCONNECTED_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Normalize.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Normalize.h new file mode 100644 index 0000000..f84742a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Normalize.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H +#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::L2Normalization(op_params, input_shape, input_data, output_shape, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h new file mode 100644 index 0000000..38a302f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_L2POOL2D_H +#define LUCI_INTERPRETER_PAL_L2POOL2D_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void L2Pool(const tflite::PoolParams ¶ms, const tflite::RuntimeShape &input_shape, + const T *input_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::reference_ops::L2Pool(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2POOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h new file mode 100644 index 0000000..9ccd222 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H +#define LUCI_INTERPRETER_PAL_LEAKYRELU_H + +#include + +namespace luci_interpreter_pal +{ +static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + tflite::reference_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALMul.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALMul.h new file mode 100644 index 0000000..347a97a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALMul.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_MUL_H +#define LUCI_INTERPRETER_PAL_MUL_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, + T *output_data) +{ + tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} + +template +static inline void +BroadcastMul4DSlow(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const T *input1_data, const tflite::RuntimeShape &input2_shape, + const T *input2_data, const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALNeg.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALNeg.h new file mode 100644 index 0000000..be5903a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALNeg.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_NEG_H +#define LUCI_INTERPRETER_PAL_NEG_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_NEG_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALQuantize.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALQuantize.h new file mode 100644 index 0000000..6046789 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALQuantize.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H +#define LUCI_INTERPRETER_PAL_QUANTIZE_H + +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter_pal +{ +template +static inline void Quantize(tflite::QuantizationParams ¶ms, + const tflite::RuntimeShape &input_shape, const float *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); +} + +template +static inline void Requantize(const Input *input_data, int32_t size, + int32_t effective_scale_multiplier, int32_t effective_scale_shift, + int32_t input_zero_point, int32_t output_zero_point, + Output *output_data) +{ + tflite::reference_ops::Requantize(input_data, size, effective_scale_multiplier, + effective_scale_shift, input_zero_point, output_zero_point, + output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h new file mode 100644 index 0000000..cc9f0fd --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H +#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeBilinear(const tflite::ResizeBilinearParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h new file mode 100644 index 0000000..f4d5a6e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H +#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, const T *input_data, + const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, + output_size_shape, output_size_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALSVDF.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSVDF.h new file mode 100644 index 0000000..3bba668 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSVDF.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SVDF_H +#define LUCI_INTERPRETER_PAL_SVDF_H + +#include + +namespace luci_interpreter_pal +{ +static inline void +IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, + const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, + const int32_t *bias_data, int16_t *activation_state_data, + const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, + int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, + int scale_2_b, int32_t input_zp, int32_t output_zp) +{ + const int n_rank = params.rank; + const int n_batch = input_shape.Dims(0); + const int n_input = input_shape.Dims(1); + const int n_filter = weight_feature_shape.Dims(0); + const int n_unit = n_filter / n_rank; + const int n_memory = weight_time_shape.Dims(1); + + // Left shift the activation_state. + { + int16_t *new_state_start = activation_state_data; + const int16_t *old_state_start = activation_state_data + 1; + const int16_t *old_state_end = activation_state_data + n_batch * n_filter * n_memory; + while (old_state_start != old_state_end) + { + *new_state_start++ = *old_state_start++; + } + } + + // Note: no need to clear the latest activation, matmul is not accumulative. + + // Feature matmul. + { + const int32_t output_max = std::numeric_limits::max(); + const int32_t output_min = std::numeric_limits::min(); + int16_t *result_in_batch = activation_state_data + (n_memory - 1); + for (int b = 0; b < n_batch; b++) + { + const int8_t *matrix_ptr = weight_feature_data; + for (int r = 0; r < n_filter; r++) + { + int32_t dot_prod = 0; + const int8_t *vector_in_batch = input_data + b * n_input; + for (int c = 0; c < n_input; c++) + { + dot_prod += *matrix_ptr++ * (*vector_in_batch++ - input_zp); + } + dot_prod = tflite::MultiplyByQuantizedMultiplier(dot_prod, scale_1_a, scale_1_b); + dot_prod = std::min(std::max(output_min, dot_prod), output_max); + // This assumes state is symmetrically quantized. Otherwise last bit of + // state should be initialized to its zero point and accumulate the + // dot_prod. + // Equivalent as the following: + // result_in_batch = zero point, which happens to be zero. + // result_in_batch += dot_prod_56. + *result_in_batch = dot_prod; + result_in_batch += n_memory; + } + } + } + + // Time. + { + for (int b = 0; b < n_batch; ++b) + { + int32_t *scratch_ptr_batch = scratchpad_data + b * n_filter; + + // Perform batched vector dot product: + const int16_t *vector1_ptr = weight_time_data; + const int16_t *vector2_ptr = activation_state_data + b * n_memory * n_filter; + + for (int i = 0; i < n_filter; i++) + { + *scratch_ptr_batch = 0; + for (int j = 0; j < n_memory; j++) + { + *scratch_ptr_batch += *vector1_ptr++ * *vector2_ptr++; + } + scratch_ptr_batch++; + } + } + } + + // Reduce, add bias, rescale, activation. + { + // Add bias. + if (bias_data) + { + // Vector batch assign: + for (int i = 0; i < n_batch; ++i) + { + int32_t *output_ptr = output_temp_data + i * n_unit; + const int32_t *bias_ptr = bias_data; + for (int j = 0; j < n_unit; ++j) + { + *output_ptr++ = *bias_ptr++; + } + } + } + else + { + int32_t *output_ptr = output_temp_data; + for (int i = 0; i < n_batch * n_unit; ++i) + { + *output_ptr++ = 0; + } + } + + // Reduce. + for (int b = 0; b < n_batch; ++b) + { + int32_t *output_temp_ptr = output_temp_data + b * n_unit; + int32_t *scratch_ptr_batch = scratchpad_data + b * n_filter; + + // Reduction sum vector + for (int i = 0; i < n_unit; ++i) + { + for (int j = 0; j < n_rank; ++j) + { + output_temp_ptr[i] += *scratch_ptr_batch++; + } + } + } + + // Rescale. + const int32_t output_max = std::numeric_limits::max(); + const int32_t output_min = std::numeric_limits::min(); + for (int i = 0; i < n_batch * n_unit; ++i) + { + int32_t x1 = output_temp_data[i]; + int32_t x2 = tflite::MultiplyByQuantizedMultiplier(x1, scale_2_a, scale_2_b); + int32_t x3 = x2 + output_zp; + int32_t x4 = std::min(std::max(output_min, x3), output_max); + output_data[i] = static_cast(x4); + } + } +} +static inline void +FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &weight_feature_shape, + const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, + const float *weight_time_data, const tflite::RuntimeShape &bias_shape, + const float *bias_data, float *scratchpad_data, float *activation_state_data, + const tflite::RuntimeShape &output_shape, float *output_data) +{ + const int32_t rank = params.rank; + const int32_t batch_size = input_shape.Dims(0); + const int32_t input_size = input_shape.Dims(1); + const int32_t num_filters = weight_feature_shape.Dims(0); + const int32_t num_units = num_filters / rank; + const int32_t memory_size = weight_time_shape.Dims(1); + + // Left shift the activation_state. + { + float *new_state_start = activation_state_data; + const float *old_state_start = activation_state_data + 1; + const float *old_state_end = activation_state_data + batch_size * num_filters * memory_size; + while (old_state_start != old_state_end) + { + *new_state_start++ = *old_state_start++; + } + } + + // Note: no need to clear the latest activation, matmul is not accumulative. + + // Compute conv1d(inputs, weights_feature). + // The activation_state's rightmost column is used to save current cycle + // activation. This is achieved by starting at state_ptr[memory_size - 1] and + // having the stride equal to memory_size. + + // Perform batched matrix vector multiply operation: + { + const float *matrix = weight_feature_data; + const float *vector = input_data; + float *result = &activation_state_data[memory_size - 1]; + float *result_in_batch = result; + for (int i = 0; i < batch_size; ++i) + { + const float *matrix_ptr = matrix; + for (int j = 0; j < num_filters; ++j) + { + float dot_prod = 0.0f; + const float *vector_in_batch = vector + i * input_size; + for (int k = 0; k < input_size; ++k) + { + dot_prod += *matrix_ptr++ * *vector_in_batch++; + } + *result_in_batch = dot_prod; + result_in_batch += memory_size; + } + } + } + + tflite::reference_ops::ApplyTimeWeightsBiasAndActivation( + batch_size, memory_size, num_filters, num_units, rank, weight_time_data, bias_data, + params.activation, activation_state_data, scratchpad_data, output_data); +} + +static inline void SetupScratchpadTensor( + const luci_interpreter::DataType &input_data_type, + const luci_interpreter::DataType &weight_feature_data_type, + luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, + luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, + luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, + const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, + const int32_t batch_size, const int32_t num_filters, const int32_t num_units) +{ + + if (input_data_type == loco::DataType::FLOAT32 && + (weight_feature_data_type == loco::DataType::S8 || + weight_feature_data_type == loco::DataType::U8)) + { + (void)input_shape; + (void)weight_time_shape; + (void)scratchpad_3; + (void)scratchpad_4; + (void)scratchpad_5; + (void)scratchpad_6; + + throw std::runtime_error("Hybrid type is not currently supported for mcu platform"); + } + + // Resize scratchpad_1 tensor + scratchpad_1->resize({batch_size, num_filters}); + + if (input_data_type == loco::DataType::S8) + { + // Resize scratchpad_2 for full_integer op + scratchpad_2->resize({batch_size, num_units}); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALSoftmax.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSoftmax.h new file mode 100644 index 0000000..9838b54 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSoftmax.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H +#define LUCI_INTERPRETER_PAL_SOFTMAX_H + +#include + +namespace luci_interpreter_pal +{ +static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, + float beta) +{ + // Do nothing for mcu + (void)data; + (void)input_scale; + (void)beta; +} + +static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) +{ + int32 input_beta_multiplier; + int input_beta_left_shift; + static const int kScaledDiffIntegerBits = 5; + tflite::PreprocessSoftmaxScaling(beta, input_scale, kScaledDiffIntegerBits, + &input_beta_multiplier, &input_beta_left_shift); + + params->input_multiplier = input_beta_multiplier; + params->input_left_shift = input_beta_left_shift; + params->diff_min = + -tflite::CalculateInputRadius(kScaledDiffIntegerBits, params->input_left_shift); +} + +template +static inline void Softmax(const tflite::SoftmaxParams ¶ms, + const tflite::RuntimeShape &input_shape, const T *input_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + // MARK: At this moment this operation doesn't support on mcu + assert(false && "Softmax NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)output_shape; + (void)output_data; +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h new file mode 100644 index 0000000..fdddaa9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H +#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void +SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, + const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, + const tflite::RuntimeShape &unextended_input3_shape, const int32 *paddings_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::SpaceToBatchND( + params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, + unextended_input3_shape, paddings_data, unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h new file mode 100644 index 0000000..816b7f6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H +#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, + const tflite::RuntimeShape &unextended_input_shape, + const T *input_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data) +{ + tflite::reference_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, + unextended_output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/PALSub.h b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSub.h new file mode 100644 index 0000000..ea57578 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/PALSub.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_PAL_SUB_H +#define LUCI_INTERPRETER_PAL_SUB_H + +#include + +namespace luci_interpreter_pal +{ +template +static inline void Sub(const tflite::ArithmeticParams ¶ms, + const tflite::RuntimeShape &input1_shape, const T *input1_data, + const tflite::RuntimeShape &input2_shape, const T *input2_data, + const tflite::RuntimeShape &output_shape, T *output_data) +{ + tflite::reference_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SUB_H diff --git a/compiler/luci-micro/luci-interpreter/pal/mcu/pal.cmake b/compiler/luci-micro/luci-interpreter/pal/mcu/pal.cmake new file mode 100644 index 0000000..907d51d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/pal/mcu/pal.cmake @@ -0,0 +1,56 @@ +macro(initialize_pal) + nnas_find_package(TensorFlowSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowEigenSource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowRuySource EXACT 2.6.0 QUIET) + + if (NOT TensorFlowSource_FOUND) + message(STATUS "Skipping luci-interpreter: TensorFlow not found") + return() + endif () + + if (NOT TensorFlowGEMMLowpSource_FOUND) + message(STATUS "Skipping luci-interpreter: gemmlowp not found") + return() + endif () + + if (NOT TensorFlowEigenSource_FOUND) + message(STATUS "Skipping luci-interpreter: Eigen not found") + return() + endif () + + if (NOT TensorFlowRuySource_FOUND) + message(STATUS "Skipping luci-interpreter: Ruy not found") + return() + endif () + #find_package(Threads REQUIRED) + + set(PAL_INITIALIZED TRUE) +endmacro() + +macro(add_pal_to_target TGT) + target_include_directories(${TGT} PRIVATE "${PAL}") + target_include_directories(${TGT} PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}") + target_include_directories(${TGT} PRIVATE ${LUCI_INTERPRETER_PAL_DIR}) + + # TODO put it back, I changed my mind. + # instead add sources with visitors in this library + set(PAL_SOURCES ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc) + add_library(luci_interpreter_mcu_pal STATIC ${PAL_SOURCES}) + set_target_properties(luci_interpreter_mcu_pal PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(luci_interpreter_mcu_pal PRIVATE + "${TensorFlowRuySource_DIR}" + "${TensorFlowGEMMLowpSource_DIR}" + "${TensorFlowEigenSource_DIR}" + "${TensorFlowSource_DIR}" + ) + + target_link_libraries(${TGT} PRIVATE luci_interpreter_mcu_pal) + #target_link_libraries(${TGT} PRIVATE Threads::Threads luci_interpreter_mcu_pal) +endmacro() diff --git a/compiler/luci-micro/luci-interpreter/requires.cmake b/compiler/luci-micro/luci-interpreter/requires.cmake new file mode 100644 index 0000000..f411f38 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/requires.cmake @@ -0,0 +1 @@ +require(luci) diff --git a/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.cpp b/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.cpp new file mode 100644 index 0000000..6ad1f32 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/BuddyMemoryManager.h" + +namespace luci_interpreter +{ + +BuddyMemoryManager::BuddyMemoryManager(uint8_t *memory_start, int32_t memSize) +{ + int32_t p = lowerLog2(memSize); + + // We assume that the requested size of memory does not exceed 4 GB + assert(p < 32); + memSize = 1 << p; + + _start_block = reinterpret_cast(memory_start); + _start_block->size = memSize - sizeof(Block); + _start_block->is_free = true; + _start_block->self = _start_block; + _num_blocks = 0; + _size = _start_block->size; + + for (auto &_free_block : _free_blocks) + _free_block = nullptr; + + addToBlocks(_start_block, p); +} + +void BuddyMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) +{ + const size_t element_size = getDataTypeSize(tensor.element_type()); + const int32_t num_elements = tensor.shape().num_elements(); + auto size = num_elements * element_size; + auto footprint = size + sizeof(Block); + auto l = (footprint & (footprint - 1)) == 0 + ? lowerLog2(footprint) + : lowerLog2(footprint) + 1; // check footprint is pow_of_2 + + while (l < 32 && !_free_blocks[l]) + l++; + + assert(l < 32); + + Block *tmp; + tmp = _free_blocks[l]; + removeFromBlocks(tmp, l); + + while ((tmp->size + sizeof(Block)) / 2 >= size + sizeof(Block)) + { + divideBlock(tmp, l); + l--; + } + + tmp->is_free = false; + tmp->self = tmp; + _num_blocks++; + + auto *data = (uint8_t *)(tmp + 1); + tensor.set_data_buffer(data); +} + +void BuddyMemoryManager::release_memory(luci_interpreter::Tensor &tensor) +{ + auto data = tensor.data(); + auto *tmp = (Block *)((uint8_t *)data - sizeof(Block)); + + assert(tmp->self == tmp); + + tmp->is_free = true; + addToBlocks(tmp, lowerLog2(tmp->size + sizeof(Block))); + + while (tmp) + if (tmp->size == _size) + break; + else + tmp = mergeBlock(tmp); + + _num_blocks--; + tensor.set_data_buffer(nullptr); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.test.cpp b/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.test.cpp new file mode 100644 index 0000000..29fb767 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/BuddyMemoryManager.test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/BuddyMemoryManager.h" +#include + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +TEST(BuddyMemoryManager, basic) +{ + auto mem_pool = std::make_unique(200); + auto buddy_memory_manager = std::make_unique(mem_pool.get(), 130); + Tensor first_tensor(DataType::U8, Shape({8}), AffineQuantization{}, "first_tensor"); + + buddy_memory_manager->allocate_memory(first_tensor); + + uint8_t data_1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + first_tensor.writeData(data_1, 8); + uint8_t array_1[8]; + first_tensor.readData(array_1, 8); + for (int i = 0; i < 8; i++) + { + EXPECT_EQ(data_1[i], array_1[i]); + } + + Tensor second_tensor(DataType::U8, Shape({2, 5}), AffineQuantization{}, "second_tensor"); + buddy_memory_manager->allocate_memory(second_tensor); + + uint8_t data_2[2][5] = {{11, 22, 33, 44, 55}, {12, 23, 34, 45, 56}}; + second_tensor.writeData(data_2, 10); + + uint8_t array_2[2][5]; + second_tensor.readData(array_2, 10); + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 5; j++) + { + EXPECT_EQ(data_2[i][j], array_2[i][j]); + } + } + + buddy_memory_manager->release_memory(first_tensor); + EXPECT_EQ(first_tensor.data(), nullptr); + + buddy_memory_manager->release_memory(second_tensor); + EXPECT_EQ(second_tensor.data(), nullptr); +} + +} // namespace +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/src/CMakeLists.txt new file mode 100644 index 0000000..997b75a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/CMakeLists.txt @@ -0,0 +1,61 @@ +include("${LUCI_INTERPRETER_PAL_DIR}/pal.cmake") + +initialize_pal() + +if (NOT PAL_INITIALIZED) + message("PAL Failed to initialize, skip luci-interpreter") + return() +endif() + +message(STATUS "LUCI INTERPRETER BEGIN") + +set(LUCI_INTERPRETER_BINARY "luci_interpreter${LUCI_INTERPRETER_SUFFIX}") +set(LUCI_INTERPRETER_CORE "luci_interpreter_core${LUCI_INTERPRETER_SUFFIX}") +set(LUCI_INTERPRETER_KERNELS "luci_interpreter_kernels${LUCI_INTERPRETER_SUFFIX}") +set(LUCI_INTERPRETER_LOADER "luci_interpreter_loader${LUCI_INTERPRETER_SUFFIX}") +set(LUCI_INTERPRETER_IMPORT "luci_interpreter_import${LUCI_INTERPRETER_SUFFIX}") + +add_subdirectory(core) +message(STATUS "LUCI INTERPRETER CORE") +add_subdirectory(kernels) +message(STATUS "LUCI INTERPRETER KERNELS") +add_subdirectory(loader) +message(STATUS "LUCI INTERPRETER LOADER") +add_subdirectory(import) +message(STATUS "LUCI INTERPRETER IMPORT") + +message(STATUS "LUCI INTERPTER INITALIZED") + +set(SOURCES + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/Interpreter.h" + Interpreter.cpp "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/SimpleMemoryManager.h" SimpleMemoryManager.cpp + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/TestMemoryManager.h" TestMemoryManager.cpp + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/BuddyMemoryManager.h" BuddyMemoryManager.cpp + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/StaticMemoryManager.h" StaticMemoryManager.cpp) + +if (NOT LUCI_INTERPRETER_STATIC) + add_library(${LUCI_INTERPRETER_BINARY} SHARED ${SOURCES}) +else () + add_library(${LUCI_INTERPRETER_BINARY} STATIC ${SOURCES}) +endif () + +set(TEST_SOURCES BuddyMemoryManager.test.cpp) + +target_include_directories(${LUCI_INTERPRETER_BINARY} PUBLIC "${LUCI_INTERPRETER_INCLUDE_DIR}") +target_include_directories(${LUCI_INTERPRETER_BINARY} PRIVATE "${LUCI_INTERPRETER_SOURCE_DIR}") +target_link_libraries(${LUCI_INTERPRETER_BINARY} + PUBLIC luci_lang ${LUCI_INTERPRETER_LOADER} ${LUCI_INTERPRETER_CORE} + PRIVATE nncc_common) + +install(TARGETS ${LUCI_INTERPRETER_BINARY} DESTINATION lib) +install(DIRECTORY include/ DESTINATION include + FILES_MATCHING PATTERN "*.h") + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(buddy_manager_test ${TEST_SOURCES}) +target_link_libraries(buddy_manager_test ${LUCI_INTERPRETER_BINARY}) diff --git a/compiler/luci-micro/luci-interpreter/src/Interpreter.cpp b/compiler/luci-micro/luci-interpreter/src/Interpreter.cpp new file mode 100644 index 0000000..8cf272e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/Interpreter.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/Interpreter.h" +#include "luci_interpreter/SimpleMemoryManager.h" + +#include "loader/ModuleLoader.h" + +#include + +namespace luci_interpreter +{ + +namespace +{ + +class EventNotifierImpl final : public EventNotifier +{ +public: + EventNotifierImpl(const RuntimeToIR &runtime_to_ir, + const std::vector &observers) + : _runtime_to_ir(runtime_to_ir), _observers(observers) + { + } + + void postTensorWrite(const Tensor *tensor) override + { + assert(tensor != nullptr); + for (const auto &observer : _observers) + { + observer->postTensorWrite(_runtime_to_ir.tensor_to_node.at(tensor), tensor); + } + } + + void preOperatorExecute(const Kernel *kernel) override + { + assert(kernel != nullptr); + for (const auto &observer : _observers) + { + observer->preOperatorExecute(_runtime_to_ir.kernel_to_node.at(kernel)); + } + } + + void postOperatorExecute(const Kernel *kernel) override + { + assert(kernel != nullptr); + for (const auto &observer : _observers) + { + observer->postOperatorExecute(_runtime_to_ir.kernel_to_node.at(kernel)); + } + } + +private: + const RuntimeToIR &_runtime_to_ir; + const std::vector &_observers; +}; + +} // namespace + +Interpreter::Interpreter(const luci::Module *module) +{ + _runtime_to_ir = std::make_unique(); + _event_notifier = std::make_unique(*_runtime_to_ir, _observers); + _runtime_module = std::make_unique(_event_notifier.get()); + + _default_memory_manager = std::make_unique(); + + ModuleLoader loader(module, _runtime_module.get(), *_runtime_to_ir, _node_to_tensor, + _default_memory_manager.get()); + loader.load(); +} + +Interpreter::Interpreter(const luci::Module *module, + luci_interpreter::IMemoryManager *memory_manager) +{ + assert(memory_manager && "Use Interpreter::Interpreter(module) constructor instead"); + + _runtime_to_ir = std::make_unique(); + _event_notifier = std::make_unique(*_runtime_to_ir, _observers); + _runtime_module = std::make_unique(_event_notifier.get()); + + ModuleLoader loader(module, _runtime_module.get(), *_runtime_to_ir, _node_to_tensor, + memory_manager); + loader.load(); +} + +Interpreter::~Interpreter() = default; + +void Interpreter::writeInputTensor(const luci::CircleInput *input_node, const void *data, + size_t data_size) +{ + Tensor *tensor = _runtime_module->getInputTensors()[input_node->index()]; + if (tensor == nullptr) + { + const std::string &name = input_node->name(); + throw std::runtime_error("Cannot find tensor for input node named \"" + name + "\"."); + } + if (data != nullptr) + tensor->writeData(data, data_size); +} + +void Interpreter::readOutputTensor(const luci::CircleOutput *output_node, void *data, + size_t data_size) +{ + Tensor *tensor = _runtime_module->getOutputTensors()[output_node->index()]; + if (tensor == nullptr) + { + const std::string &name = output_node->name(); + throw std::runtime_error("Cannot find tensor for output node named \"" + name + "\"."); + } + if (data != nullptr) + tensor->readData(data, data_size); +} + +void Interpreter::interpret() { _runtime_module->execute(); } + +void Interpreter::attachObserver(ExecutionObserver *observer) +{ + if (std::find(_observers.cbegin(), _observers.cend(), observer) != _observers.cend()) + throw std::runtime_error("Observer is already attached."); + _observers.push_back(observer); +} + +ExecutionObserver::~ExecutionObserver() = default; + +void ExecutionObserver::postTensorWrite(const luci::CircleNode *, const Tensor *) {} + +void ExecutionObserver::preOperatorExecute(const luci::CircleNode *) {} + +void ExecutionObserver::postOperatorExecute(const luci::CircleNode *) {} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/SimpleMemoryManager.cpp b/compiler/luci-micro/luci-interpreter/src/SimpleMemoryManager.cpp new file mode 100644 index 0000000..230e398 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/SimpleMemoryManager.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/SimpleMemoryManager.h" + +namespace luci_interpreter +{ + +void SimpleMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) +{ + if (!tensor.is_allocatable()) + { + return; + } + if (tensor.is_data_allocated()) + { + release_memory(tensor); + } + const auto element_size = getDataTypeSize(tensor.element_type()); + const auto num_elements = tensor.shape().num_elements(); + + auto *data = new uint8_t[num_elements * element_size]; + tensor.set_data_buffer(data); +} + +void SimpleMemoryManager::release_memory(luci_interpreter::Tensor &tensor) +{ + if (!tensor.is_data_allocated()) + { + tensor.set_data_buffer(nullptr); + return; + } + auto data = tensor.data(); + delete[] data; + tensor.set_data_buffer(nullptr); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/StaticMemoryManager.cpp b/compiler/luci-micro/luci-interpreter/src/StaticMemoryManager.cpp new file mode 100644 index 0000000..73a8199 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/StaticMemoryManager.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/StaticMemoryManager.h" + +namespace luci_interpreter +{ + +void StaticMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) +{ + if (!tensor.is_allocatable()) + { + return; + } + int32_t offset = tensor.get_offset(); + assert(offset >= 0); + auto tensor_ptr = _buffer_ptr + offset; + tensor.set_data_buffer(tensor_ptr); +} + +void StaticMemoryManager::release_memory(luci_interpreter::Tensor &tensor) +{ + tensor.set_data_buffer(nullptr); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/TestMemoryManager.cpp b/compiler/luci-micro/luci-interpreter/src/TestMemoryManager.cpp new file mode 100644 index 0000000..3beeee5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/TestMemoryManager.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ + +void TestMemoryManager::allocate_memory(luci_interpreter::Tensor &tensor) +{ + if (!tensor.is_allocatable()) + { + return; + } + if (tensor.is_data_allocated()) + { + release_memory(tensor); + } + const auto element_size = getDataTypeSize(tensor.element_type()); + const auto num_elements = tensor.shape().num_elements(); + + auto *data = new uint8_t[num_elements * element_size]; + allocations.push_back(data); + tensor.set_data_buffer(data); +} + +void TestMemoryManager::release_memory(luci_interpreter::Tensor &tensor) +{ + tensor.set_data_buffer(nullptr); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/core/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/src/core/CMakeLists.txt new file mode 100644 index 0000000..c2471e0 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/CMakeLists.txt @@ -0,0 +1,19 @@ +set(SOURCES + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/core/DataType.h" + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/core/Tensor.h" + EventNotifier.h + Kernel.h + KernelParams.h + RuntimeGraph.h + RuntimeGraph.cpp + RuntimeModule.h + Tensor.cpp) + +add_library(${LUCI_INTERPRETER_CORE} STATIC ${SOURCES}) +if (NOT NNCC_LIBRARY_NO_PIC) + set_target_properties(${LUCI_INTERPRETER_CORE} PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif(NOT NNCC_LIBRARY_NO_PIC) +target_include_directories(${LUCI_INTERPRETER_CORE} PUBLIC "${LUCI_INTERPRETER_INCLUDE_DIR}") +target_include_directories(${LUCI_INTERPRETER_CORE} PUBLIC "${LUCI_INTERPRETER_SOURCE_DIR}") +target_link_libraries(${LUCI_INTERPRETER_CORE} PUBLIC luci_lang) +target_link_libraries(${LUCI_INTERPRETER_CORE} PRIVATE nncc_common) diff --git a/compiler/luci-micro/luci-interpreter/src/core/EventNotifier.h b/compiler/luci-micro/luci-interpreter/src/core/EventNotifier.h new file mode 100644 index 0000000..5c4fbd3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/EventNotifier.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H +#define LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H + +namespace luci_interpreter +{ + +// Used at execution stage to tell the interpreter that the runtime state has changed in some way. +class EventNotifier +{ +public: + virtual ~EventNotifier() = default; + + virtual void postTensorWrite(const Tensor *tensor) = 0; + virtual void preOperatorExecute(const Kernel *kernel) = 0; + virtual void postOperatorExecute(const Kernel *kernel) = 0; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H diff --git a/compiler/luci-micro/luci-interpreter/src/core/Kernel.h b/compiler/luci-micro/luci-interpreter/src/core/Kernel.h new file mode 100644 index 0000000..a7c4a42 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/Kernel.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_KERNEL_H +#define LUCI_INTERPRETER_CORE_KERNEL_H + +#include "luci_interpreter/core/Tensor.h" + +#include + +namespace luci_interpreter +{ + +// Base class for all kernels. +class Kernel +{ +protected: + Kernel(std::vector inputs, std::vector outputs) + : _inputs(std::move(inputs)), _outputs(std::move(outputs)) + { + } + +public: + virtual ~Kernel() = default; + + const std::vector &getInputTensors() const { return _inputs; } + const std::vector &getOutputTensors() const { return _outputs; } + + // Configures the kernel. + // This function is currently called once for each kernel during interpreter construction, + // which makes it a convenient place for preparing (resizing) output tensors. + virtual void configure() = 0; + + // Executes the kernel. + virtual void execute() const = 0; + +protected: + // NOTE Prefer not to use these in derived classes. + const std::vector _inputs; + const std::vector _outputs; +}; + +// Base class for kernels with parameters. +template class KernelWithParams : public Kernel +{ +protected: + KernelWithParams(std::vector inputs, std::vector outputs, + const Params ¶ms) + : Kernel(std::move(inputs), std::move(outputs)), _params(params) + { + } + +public: + const Params ¶ms() const { return _params; } + +protected: + const Params _params; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_KERNEL_H diff --git a/compiler/luci-micro/luci-interpreter/src/core/KernelParams.h b/compiler/luci-micro/luci-interpreter/src/core/KernelParams.h new file mode 100644 index 0000000..6c0220c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/KernelParams.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_KERNELPARAMS_H +#define LUCI_INTERPRETER_CORE_KERNELPARAMS_H + +#include +#include +#include +#include + +#include +#include + +namespace luci_interpreter +{ + +// Inject commonly used types into `luci_interpreter` namespace for convenience. +using Activation = luci::FusedActFunc; +using Padding = luci::Padding; +using MirrorPadMode = luci::MirrorPadMode; + +struct AddParams +{ + Activation activation; +}; + +struct ArgMaxParams +{ + DataType output_type; +}; + +struct BatchMatMulParams +{ + bool adj_x; + bool adj_y; +}; + +struct ConcatenationParams +{ + int axis; + Activation activation; +}; + +struct Conv2DParams +{ + Padding padding; + int32_t stride_height; + int32_t stride_width; + int32_t dilation_height_factor; + int32_t dilation_width_factor; + Activation activation; +}; + +struct DepthToSpaceParams +{ + int block_size; +}; + +struct DepthwiseConv2DParams +{ + Padding padding; + int32_t depth_multiplier; // TODO Remove, as it can be calculated. + int32_t stride_height; + int32_t stride_width; + int32_t dilation_height_factor; + int32_t dilation_width_factor; + Activation activation; +}; + +struct DivParams +{ + Activation activation; +}; + +struct FullyConnectedParams +{ + Activation activation; + bool keep_num_dims = false; +}; + +struct GatherParams +{ + int32_t axis; + int32_t batch_dims; +}; + +struct InstanceNormParams +{ + float epsilon; + Activation activation; +}; + +struct L2NormParams +{ + Activation activation; +}; + +struct LeakyReluParams +{ + float alpha; +}; + +struct LocalResponseNormalizationParams +{ + int32_t radius; + float bias; + float alpha; + float beta; +}; + +struct MirrorPadParams +{ + MirrorPadMode mode; +}; + +struct MulParams +{ + Activation activation; +}; + +struct OneHotParams +{ + int32_t axis; +}; + +struct PackParams +{ + int32_t values_count; + int32_t axis; +}; + +struct Pool2DParams +{ + Padding padding; + int32_t filter_height; + int32_t filter_width; + int32_t stride_height; + int32_t stride_width; + Activation activation; +}; + +struct ReducerParams +{ + bool keep_dims; +}; + +struct ResizeBilinearParams +{ + bool align_corners; + bool half_pixel_centers; +}; + +struct ResizeNearestNeighborParams +{ + bool align_corners; + bool half_pixel_centers; +}; + +struct ShapeParams +{ + loco::DataType out_type; +}; + +struct SubParams +{ + Activation activation; +}; + +struct SVDFParams +{ + bool asymmetric_quantize_inputs; + int32_t svdf_rank; + Activation activation; +}; + +struct SpaceToDepthParams +{ + int block_size; +}; + +struct SoftmaxParams +{ + float beta; +}; + +struct StridedSliceParams +{ + int32_t begin_mask; + int32_t end_mask; + int32_t ellipsis_mask; + int32_t new_axis_mask; + int32_t shrink_axis_mask; +}; + +struct SqueezeParams +{ + std::vector squeeze_dims; +}; + +struct TransposeConvParams +{ + Padding padding; + int32_t stride_height; + int32_t stride_width; +}; + +struct UnpackParams +{ + int axis; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_KERNELPARAMS_H diff --git a/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.cpp b/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.cpp new file mode 100644 index 0000000..c2f8d2e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "core/RuntimeGraph.h" + +#include "core/RuntimeModule.h" + +#include +#include + +namespace luci_interpreter +{ + +class RuntimeGraph::TensorAllocPlan +{ + std::vector> _alloc_plan; + std::vector> _dealloc_plan; + bool _valid = false; + IMemoryManager *_memory_manager; + +public: + explicit TensorAllocPlan(IMemoryManager *memory_manager); + void invalidate() { _valid = false; } + bool isValid() const { return _valid; } + void build(const RuntimeGraph &graph); + void allocate(size_t kernel_index) const; + void deallocate(size_t kernel_index) const; +}; + +RuntimeGraph::TensorAllocPlan::TensorAllocPlan(IMemoryManager *memory_manager) + : _memory_manager(memory_manager) +{ +} + +void RuntimeGraph::TensorAllocPlan::build(const RuntimeGraph &graph) +{ + invalidate(); + using Lifetime = std::pair; + std::unordered_map lifetimes; + const size_t num_kernels = graph._kernels.size(); + for (size_t index = 0; index < num_kernels; ++index) + { + const auto &kernel = graph._kernels[index]; + for (const Tensor *tensor : kernel->getInputTensors()) + { + auto nc_tensor = const_cast(tensor); + if (lifetimes.count(nc_tensor) > 0) + lifetimes.at(nc_tensor).second = index; + } + for (Tensor *tensor : kernel->getOutputTensors()) + { + assert(lifetimes.count(tensor) == 0); + lifetimes[tensor] = Lifetime(index, index); + } + } + for (const Tensor *tensor : graph.getOutputTensors()) + { + auto nc_tensor = const_cast(tensor); + if (lifetimes.count(nc_tensor) > 0) + lifetimes.at(nc_tensor).second = num_kernels; + } + _alloc_plan.assign(num_kernels, std::vector()); + _dealloc_plan.assign(num_kernels + 1, std::vector()); + for (const auto &item : lifetimes) + { + _alloc_plan[item.second.first].push_back(item.first); + _dealloc_plan[item.second.second].push_back(item.first); + } + _valid = true; +} + +void RuntimeGraph::TensorAllocPlan::allocate(size_t kernel_index) const +{ + assert(_valid && kernel_index < _alloc_plan.size()); + for (Tensor *tensor : _alloc_plan[kernel_index]) + { + _memory_manager->allocate_memory(*tensor); + } +} + +void RuntimeGraph::TensorAllocPlan::deallocate(size_t kernel_index) const +{ + assert(_valid && kernel_index < _dealloc_plan.size()); + for (Tensor *tensor : _dealloc_plan[kernel_index]) + { + _memory_manager->release_memory(*tensor); + } +} + +RuntimeGraph::RuntimeGraph(RuntimeModule *owning_module, IMemoryManager *memory_manager) + : _owning_module(owning_module), _memory_manager(memory_manager), + _tensor_alloc_plan(std::make_unique(memory_manager)) +{ +} + +RuntimeGraph::~RuntimeGraph() +{ + for (auto &tensor : _tensors) + { + if (tensor->is_data_allocated()) + _memory_manager->release_memory(*tensor); + } +} + +Tensor *RuntimeGraph::addTensor(std::unique_ptr &&tensor) +{ + assert(tensor != nullptr); + _tensors.push_back(std::move(tensor)); + return _tensors.back().get(); +} + +void RuntimeGraph::setInputTensors(const std::vector &input_tensors) +{ + assert(std::all_of(input_tensors.cbegin(), input_tensors.cend(), + [](Tensor *tensor) { return tensor != nullptr; })); + _input_tensors = input_tensors; +} + +void RuntimeGraph::setOutputTensors(const std::vector &output_tensors) +{ + assert(std::all_of(output_tensors.cbegin(), output_tensors.cend(), + [](Tensor *tensor) { return tensor != nullptr; })); + _output_tensors = output_tensors; +} + +void RuntimeGraph::configureAllocations(Tensor *tensor) +{ + _memory_manager->allocate_memory(*tensor); +} + +void RuntimeGraph::addKernel(std::unique_ptr &&kernel) +{ + assert(kernel != nullptr); + _kernels.push_back(std::move(kernel)); + _tensor_alloc_plan->invalidate(); +} + +void RuntimeGraph::execute() const +{ + if (!_tensor_alloc_plan->isValid()) + _tensor_alloc_plan->build(*this); + + EventNotifier *event_notifier = _owning_module->getEventNotifier(); + + // Notify the observers that the input tensors have changed. + if (event_notifier != nullptr) + { + for (const Tensor *input_tensor : getInputTensors()) + { + if (input_tensor->is_observable()) + event_notifier->postTensorWrite(input_tensor); + } + } + + for (size_t index = 0; index < _kernels.size(); ++index) + { + const auto &kernel = _kernels[index]; + if (event_notifier != nullptr) + { + event_notifier->preOperatorExecute(kernel.get()); + } + + // TODO The `configure` method should only be called if the outputs of an operator need to be + // resized. + kernel->configure(); + + // Preallocate outputs in advance instead of relying on automatic allocation + _tensor_alloc_plan->allocate(index); + + kernel->execute(); + + if (event_notifier != nullptr) + { + event_notifier->postOperatorExecute(kernel.get()); + } + + for (const Tensor *tensor : kernel->getOutputTensors()) + { + if (event_notifier != nullptr && tensor->is_observable()) + { + event_notifier->postTensorWrite(tensor); + } + } + _tensor_alloc_plan->deallocate(index); + } +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.h b/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.h new file mode 100644 index 0000000..8184e24 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/RuntimeGraph.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H +#define LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H + +#include "luci_interpreter/core/Tensor.h" +#include "luci_interpreter/MemoryManager.h" +#include "core/Kernel.h" + +#include +#include + +namespace luci_interpreter +{ + +class RuntimeModule; + +class RuntimeGraph +{ +private: + class TensorAllocPlan; + friend class TensorAllocPlan; + +public: + explicit RuntimeGraph(RuntimeModule *owning_module, IMemoryManager *memory_manager); + ~RuntimeGraph(); + + Tensor *addTensor(std::unique_ptr &&tensor); + + void setInputTensors(const std::vector &input_tensors); + void setOutputTensors(const std::vector &output_tensors); + + void configureAllocations(Tensor *tensor); + + const std::vector &getInputTensors() const { return _input_tensors; } + const std::vector &getOutputTensors() const { return _output_tensors; } + + void addKernel(std::unique_ptr &&kernel); + + void execute() const; + +private: + IMemoryManager *_memory_manager; + RuntimeModule *_owning_module; + std::vector> _tensors; + std::vector _input_tensors; + std::vector _output_tensors; + + // Kernels in execution order. + std::vector> _kernels; + // Tensors that are not used anymore after given op + std::unique_ptr _tensor_alloc_plan; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H diff --git a/compiler/luci-micro/luci-interpreter/src/core/RuntimeModule.h b/compiler/luci-micro/luci-interpreter/src/core/RuntimeModule.h new file mode 100644 index 0000000..78873b0 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/RuntimeModule.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H +#define LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H + +#include "core/RuntimeGraph.h" +#include "core/EventNotifier.h" +#include "luci_interpreter/MemoryManager.h" + +#include +#include + +namespace luci_interpreter +{ + +class RuntimeModule +{ +public: + explicit RuntimeModule(EventNotifier *event_notifier) : _event_notifier(event_notifier) {} + + EventNotifier *getEventNotifier() const { return _event_notifier; } + + RuntimeGraph *addGraph(IMemoryManager *memory_manager) + { + _graphs.push_back(std::make_unique(this, memory_manager)); + return _graphs.back().get(); + } + + const std::vector &getInputTensors() const { return getMainGraph()->getInputTensors(); } + const std::vector &getOutputTensors() const + { + return getMainGraph()->getOutputTensors(); + } + + void execute() const { getMainGraph()->execute(); } + +private: + RuntimeGraph *getMainGraph() const { return _graphs[0].get(); } + + EventNotifier *const _event_notifier; + std::vector> _graphs; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H diff --git a/compiler/luci-micro/luci-interpreter/src/core/Tensor.cpp b/compiler/luci-micro/luci-interpreter/src/core/Tensor.cpp new file mode 100644 index 0000000..3c3c5ff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/core/Tensor.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/core/Tensor.h" + +#include +#include + +namespace luci_interpreter +{ + +Tensor::Tensor(DataType element_type, Shape shape, AffineQuantization quantization, + std::string name) + : _element_type(element_type), _shape(std::move(shape)), _quantization(std::move(quantization)), + _name(std::move(name)), _data_allocated(false) +{ +} + +void Tensor::readData(void *data_ptr, size_t data_size) const +{ + const size_t element_size = getDataTypeSize(element_type()); + const int32_t num_elements = shape().num_elements(); + if (data_size != num_elements * element_size) + { + throw std::invalid_argument("Invalid data size."); + } + assert(data_ptr != nullptr); + std::memcpy(data_ptr, data(), data_size); +} + +void Tensor::writeData(const void *data_ptr, size_t data_size) +{ + const size_t element_size = getDataTypeSize(element_type()); + const int32_t num_elements = shape().num_elements(); + if (data_size != num_elements * element_size) + { + throw std::invalid_argument("Invalid data size."); + } + assert(data_ptr != nullptr); + std::memcpy(data(), data_ptr, data_size); +} + +void Tensor::resize(const Shape &new_shape) { _shape = new_shape; } + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/import/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/src/import/CMakeLists.txt new file mode 100644 index 0000000..dd9733f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/import/CMakeLists.txt @@ -0,0 +1,15 @@ +set(SOURCES + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/GraphBuilderRegistry.h" + GraphBuilderRegistry.cpp) + +# include specific builders +file(GLOB_RECURSE NODES "Nodes/*") +list(APPEND SOURCES ${NODES}) + +add_library(${LUCI_INTERPRETER_IMPORT} STATIC ${SOURCES}) +if (NOT NNCC_LIBRARY_NO_PIC) + set_target_properties(${LUCI_INTERPRETER_IMPORT} PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif(NOT NNCC_LIBRARY_NO_PIC) + +target_include_directories(${LUCI_INTERPRETER_IMPORT} PUBLIC "${LUCI_INTERPRETER_INCLUDE_DIR}") +target_link_libraries(${LUCI_INTERPRETER_IMPORT} PUBLIC luci_import) diff --git a/compiler/luci-micro/luci-interpreter/src/import/GraphBuilderRegistry.cpp b/compiler/luci-micro/luci-interpreter/src/import/GraphBuilderRegistry.cpp new file mode 100644 index 0000000..a33bca6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/import/GraphBuilderRegistry.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci_interpreter/GraphBuilderRegistry.h" +#include "Nodes/CircleReferencingConst.h" + +namespace luci_interpreter +{ + +std::unique_ptr source_without_constant_copying() +{ + auto builder = std::make_unique(); + { + // redefine NodeBuilder of BUFFER type + builder->add(std::make_unique()); + } + + return builder; +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.cpp b/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.cpp new file mode 100644 index 0000000..14e90f2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "CircleReferencingConst.h" + +#include + +namespace +{ + +// helper struct which describes data loaded to custom_options of CircleReferencingConst node +struct ConstDataReference +{ + const uint8_t *data = nullptr; + uint32_t size = 0; +}; + +} // namespace + +namespace luci_interpreter +{ +using namespace luci; + +CircleNode *CircleReferencingConstNodeBuilder::build(TensorIndex tensor_index, + GraphBuilderContext *context) const +{ + assert(tensor_index >= 0); + + const auto graph = context->graph(); + const auto reader = context->reader(); + const auto tensors = reader->tensors(); + auto const const_tensor = tensors[tensor_index]; + assert(const_tensor != nullptr); + if (const_tensor->is_variable()) + { + // Create CircleVariable for variable + return nullptr; + } + + auto const buffer = wrap(reader->buffers()[const_tensor->buffer()]->data()); + auto const const_dims = wrap(const_tensor->shape()); // in NHWC + if (const_dims.empty() && buffer.empty()) + { + // unknown shape tensor and scalar tensor + return nullptr; + } + + // if tensor_index is used as output to some other operator, this is not a constant + auto tensoroutputs = context->tensoroutputs(); + if (tensoroutputs->find(tensor_index)) + { + // other operator output tensor + return nullptr; + } + + uint32_t num_elements = 1; + for (uint32_t r = 0; r < const_dims.size(); ++r) + { + num_elements = num_elements * const_dims[r]; + } + + if (buffer.empty() && num_elements > 0) + { + // normal empty tensor + return nullptr; + } + + // create CircleReferencingConst + auto custom_node = graph->nodes()->create(0, 1); + { + custom_node->custom_code("CircleReferencingConst"); + + copy_tensor_attributes(const_tensor, custom_node); + custom_node->shape_status(luci::ShapeStatus::VALID); + + // custom options stores size of buffer and pointer's value to buffer's data + { + std::vector custom_options(sizeof(ConstDataReference)); + { + auto &const_data_ref = *reinterpret_cast(custom_options.data()); + const_data_ref = {buffer.data(), buffer.size()}; + } + custom_node->custom_options(custom_options); + } + } + + // Output of CircleCustom node presented with CircleConstNode + auto out_node = graph->nodes()->create(); + { + out_node->index(0); + out_node->input(custom_node); + + copy_tensor_attributes(const_tensor, out_node); + out_node->shape_status(luci::ShapeStatus::VALID); + } + + return out_node; +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.h b/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.h new file mode 100644 index 0000000..ed8f951 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/import/Nodes/CircleReferencingConst.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_INTERPRETER_IMPORT_OP_CIRCLE_REFERENCING_CONST_H__ +#define __LUCI_INTERPRETER_IMPORT_OP_CIRCLE_REFERENCING_CONST_H__ + +#include + +#include + +namespace luci_interpreter +{ +using namespace luci; + +/** + * @brief Builder creates CircleCustom node with pointer to constants data from Tensor with buffer. + */ +class CircleReferencingConstNodeBuilder : public TypedNodeBuilder +{ +public: + CircleNode *build(TensorIndex tensor_index, GraphBuilderContext *ctx) const final; +}; + +} // namespace luci_interpreter + +#endif // __LUCI_INTERPRETER_IMPORT_OP_CIRCLE_REFERENCING_CONST_H__ diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Add.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Add.cpp new file mode 100644 index 0000000..d7bf308 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Add.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Add.h" + +#include "kernels/BinaryOpCommon.h" +#include "kernels/Utils.h" + +#include +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Add::Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams ¶ms) + : KernelWithParams({input1, input2}, {output}, params) +{ +} + +void Add::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); + if (input1()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(input1()->zero_points().size() == 1 && + input2()->zero_points().size() == 1); + LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 && + output()->zero_point() == 0); + } + + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Add::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Add::evalFloat() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastAdd4DSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +template void Add::evalInteger() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastAdd4DSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +void Add::evalQuantized() const +{ + const auto input1_scale = static_cast(input1()->scale()); + const auto input2_scale = static_cast(input2()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const int left_shift = 20; + const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); + const double real_input1_multiplier = input1_scale / twice_max_input_scale; + const double real_input2_multiplier = input2_scale / twice_max_input_scale; + const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); + + int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; + int input1_shift{}, input2_shift{}, output_shift{}; + quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); + quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); + quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::ArithmeticParams params{}; + params.left_shift = left_shift; + // The kernel expects inputs' zero points to be negated. + params.input1_offset = -input1()->zero_point(); // Note the '-'. + params.input1_multiplier = input1_multiplier; + params.input1_shift = input1_shift; + params.input2_offset = -input2()->zero_point(); // Note the '-'. + params.input2_multiplier = input2_multiplier; + params.input2_shift = input2_shift; + params.output_offset = output()->zero_point(); + params.output_multiplier = output_multiplier; + params.output_shift = output_shift; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastAdd4DSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +void Add::evalQuantizedS16() const +{ + const auto input1_scale = static_cast(input1()->scale()); + const auto input2_scale = static_cast(input2()->scale()); + const auto output_scale = static_cast(output()->scale()); + + constexpr int left_shift = 12; + const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); + const double real_input1_multiplier = input1_scale / twice_max_input_scale; + const double real_input2_multiplier = input2_scale / twice_max_input_scale; + const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); + + int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; + int input1_shift{}, input2_shift{}, output_shift{}; + quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); + quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); + quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + auto fn = [input1_multiplier, input1_shift, // + input2_multiplier, input2_shift, // + output_multiplier, output_shift, // + activation_min, activation_max](int16_t input1_val, int16_t input2_val) { + const int32_t shifted_input1_val = static_cast(input1_val) << left_shift; + const int32_t shifted_input2_val = static_cast(input2_val) << left_shift; + const int32_t scaled_input1_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input1_val, input1_multiplier, input1_shift); + const int32_t scaled_input2_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input2_val, input2_multiplier, input2_shift); + const int32_t raw_sum = scaled_input1_val + scaled_input2_val; + const int32_t raw_output = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( + raw_sum, output_multiplier, output_shift); + const int32_t clamped_output = std::min(activation_max, std::max(activation_min, raw_output)); + return static_cast(clamped_output); + }; + + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), fn); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Add.h b/compiler/luci-micro/luci-interpreter/src/kernels/Add.h new file mode 100644 index 0000000..91d95b6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Add.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_ADD_H +#define LUCI_INTERPRETER_KERNELS_ADD_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Add : public KernelWithParams +{ +public: + Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams ¶ms); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + void evalQuantizedS16() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_ADD_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Add.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Add.test.cpp new file mode 100644 index 0000000..b8b1c30 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Add.test.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Add.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class AddTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +// for quantized Add, the error shouldn't exceed step +float GetTolerance(float min, float max) +{ + float kQuantizedStep = (max - min) / 255.0; + return kQuantizedStep; +} + +TEST_F(AddTest, Uint8) +{ + std::initializer_list base_shape = {2, 3, 1, 2}; + std::initializer_list base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + std::initializer_list test_shapes[] = { + {1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::initializer_list test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + std::initializer_list output_shapes[] = { + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + std::vector> output_data = { + {-0.1f, 2.6f, -0.7f, 2.8f, 0.7f, 3.0f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, -0.8f, 0.4f, -0.6f, 1.8f, -0.2f, 1.4f, 3.0f, 0.8f, 3.0f, 2.2f, 3.0f, + -1.4f, 0.3f, -2.0f, 0.5f, -0.6f, 0.9f, 0.9f, -1.9f, 0.3f, -1.7f, 1.7f, -1.3f}, + {-0.1f, 2.6f, 0.5f, 1.0f, 1.8f, -0.2f, 1.4f, 3.0f, -2.0f, 0.5f, 1.7f, -1.3f}, + {-0.1f, 2.5f, 0.0f, 2.6f, -0.7f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, -0.9f, 1.1f, -0.8f, 0.4f, -1.5f, 1.7f, 3.0f, 2.2f, 3.0f, 2.1f, 3.0f, + -1.1f, 0.5f, -0.6f, 1.0f, -0.7f, 0.9f, 1.2f, -1.7f, 1.7f, -1.2f, 1.6f, -1.3f}, + {-0.1f, 2.5f, 1.2f, 0.8f, 0.4f, -1.5f, 1.7f, 3.0f, -0.6f, 1.0f, 1.6f, -1.3f}}; + float kQuantizedTolerance = GetTolerance(-3.f, 3.f); + std::pair quant_param = quantizationParams(-3.f, 3.f); + for (int i = 0; i < output_data.size(); i++) + { + Tensor input1_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + + AddParams params{}; + params.activation = Activation::NONE; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data[i], kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + } + // Re-run with exchanged inputs. + for (int i = 0; i < output_data.size(); i++) + { + Tensor input1_tensor = makeInputTensor( + test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + + AddParams params{}; + params.activation = Activation::NONE; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data[i], kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + } +} + +TEST_F(AddTest, Float) +{ + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> test_outputs = { + {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, + 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, + {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, + {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, + 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, + {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; + std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) + << "With shape number " << i; + } + // Re-run with exchanged inputs. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = + makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) + << "With shape number " << i; + } +} + +template void CheckInteger(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> test_outputs = { + {3, 3, 0, 1, 0, 8, 5, 1, 0, 0, 2, 6, 8, 0, 1, 0, 5, 1, + 5, 4, 0, 2, 2, 9, 11, 0, 4, 0, 8, 5, 11, 2, 4, 0, 8, 7}, + {3, 3, 0, 0, 5, 1, 5, 4, 4, 0, 8, 7}, + {3, 6, 0, 3, 0, 0, 5, 4, 2, 1, 0, 0, 8, 0, 5, 0, 1, 0, + 0, 2, 2, 4, 7, 9, 6, 0, 8, 0, 13, 5, 6, 0, 8, 2, 13, 7}, + {3, 6, 2, 1, 1, 0, 0, 2, 8, 0, 13, 7}}; + std::vector input1_data{-1, 2, 1, 0, 4, -5, 1, 3, 7, -1, 7, 1}; + std::vector input2_data{4, 1, -3, -1, 1, 6}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } + // Re-run with exchanged inputs. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor input2_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } +}; + +TEST_F(AddTest, SInt32) +{ + CheckInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(AddTest, SInt64) +{ + CheckInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(AddTest, SInt16) +{ + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> ref_output_shapes{ + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + + std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + std::vector> ref_outputs = { + {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, + 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, + {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, + {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, + 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, + {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; + + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, 3.0 / 32767, 0, input1_data, + _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(test_shapes[i], 1.0 / 32767, 0, + input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); + const float tolerance = output_tensor.scale(); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), + ::testing::ElementsAreArray(ref_output_shapes[i])) + << "With shape number " << i; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) + << "With shape number " << i; + } + // Re-run with exchanged inputs and different scales. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(test_shapes[i], 2.0 / 32767, 0, + input2_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(base_shape, 4.0 / 32767, 0, input1_data, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 5.0 / 32767, 0); + const float tolerance = output_tensor.scale(); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), + ::testing::ElementsAreArray(ref_output_shapes[i])) + << "With shape number " << i; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) + << "With shape number " << i; + } +} + +TEST_F(AddTest, Input_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(AddTest, Invalid_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(AddTest, Invalid_Input_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U64); + + AddParams params{}; + params.activation = Activation::RELU; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(AddTest, Invalid_Quantization_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + AddParams params{}; + params.activation = Activation::NONE; + + Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.cpp new file mode 100644 index 0000000..6561a17 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/ArgMax.h" +#include "kernels/Utils.h" +#include "PALArgMax.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ArgMax::ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams ¶ms) + : KernelWithParams({input, axis}, {output}, params) +{ +} + +void ArgMax::configure() +{ + assert(axis()->element_type() == DataType::S32 || axis()->element_type() == DataType::S64); + assert(input()->shape().num_dims() >= 1); + const Shape &input_shape = input()->shape(); + const int num_dims = input_shape.num_dims(); + Shape output_shape(num_dims - 1); + + // If axis value is negative, then update by adding input_shape's num_dims. + // If updated value also negative, then assert. + assert(axis()->shape().num_elements() == 1); + int axis_value = getTensorData(axis())[0]; + if (axis_value < 0) + axis_value = axis_value + num_dims; + assert(axis_value >= 0); + + int j = 0; + for (int i = 0; i < num_dims; i++) + { + if (i == axis_value) + continue; + output_shape.dim(j++) = input_shape.dim(i); + } + + assert(output()->element_type() == _params.output_type); + + output()->resize(output_shape); +} + +void ArgMax::execute() const +{ + +#define TF_LITE_ARG_MAX(data_type, axis_type, output_type) \ + luci_interpreter_pal::ArgMinMax(getTensorShape(input()), getTensorData(input()), \ + getTensorData(axis()), getTensorShape(output()), \ + getTensorData(output()), std::greater()) + if (axis()->element_type() == DataType::S32) + { + switch (_params.output_type) + { + case DataType::S32: + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_ARG_MAX(float, int32_t, int32_t); + break; + case DataType::U8: + TF_LITE_ARG_MAX(uint8_t, int32_t, int32_t); + break; + default: + throw std::runtime_error("Unsupported input type."); + } + break; + case DataType::S64: + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_ARG_MAX(float, int32_t, int64_t); + break; + case DataType::U8: + TF_LITE_ARG_MAX(uint8_t, int32_t, int64_t); + break; + default: + throw std::runtime_error("Unsupported input type."); + } + break; + default: + throw std::runtime_error("Unsupported output type."); + } + } + else + { + switch (_params.output_type) + { + case DataType::S32: + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_ARG_MAX(float, int64_t, int32_t); + break; + case DataType::U8: + TF_LITE_ARG_MAX(uint8_t, int64_t, int32_t); + break; + default: + throw std::runtime_error("Unsupported input type."); + } + break; + case DataType::S64: + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_ARG_MAX(float, int64_t, int64_t); + break; + case DataType::U8: + TF_LITE_ARG_MAX(uint8_t, int64_t, int64_t); + break; + default: + throw std::runtime_error("Unsupported input type."); + } + break; + default: + throw std::runtime_error("Unsupported output type."); + } + } +#undef TF_LITE_ARG_MAX +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.h b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.h new file mode 100644 index 0000000..c851b58 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_ARGMAX_H +#define LUCI_INTERPRETER_KERNELS_ARGMAX_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ArgMax : public KernelWithParams +{ +public: + ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *axis() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_ARGMAX_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.test.cpp new file mode 100644 index 0000000..474f4b3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ArgMax.test.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/ArgMax.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, + std::initializer_list dimension_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list dimension_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor dimension_tensor = + makeInputTensor(dimension_shape, dimension_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(getElementType()); + + ArgMaxParams params{}; + params.output_type = getElementType(); + ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template class ArgMaxTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(ArgMaxTest, DataTypes); + +TYPED_TEST(ArgMaxTest, Simple) +{ + Check(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{}, + /*output_shape=*/{1, 1, 1}, + /*input_data=*/ + { + 1, 9, 7, 3, // + }, + /*dimension_data=*/{3}, /*output_data=*/{1}); + Check(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{}, + /*output_shape=*/{1, 1, 1}, + /*input_data=*/ + { + 1, 9, 7, 3, // + }, + /*dimension_data=*/{3}, /*output_data=*/{1}); +} + +TYPED_TEST(ArgMaxTest, MultiDimensions) +{ + Check(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{}, + /*output_shape=*/{1, 1, 2}, + /*input_data=*/ + { + 1, 2, 7, 8, // + 1, 9, 7, 3, // + }, + /*dimension_data=*/{3}, /*output_data=*/{3, 1}); + Check(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{}, + /*output_shape=*/{1, 1, 2}, + /*input_data=*/ + { + 1, 2, 7, 8, // + 1, 9, 7, 3, // + }, + /*dimension_data=*/{3}, /*output_data=*/{3, 1}); +} + +TEST(ArgMaxTest, UnsupportedType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({1, 1, 2, 4}, + { + 1, 2, 7, 8, // + 1, 9, 7, 3, // + }, + memory_manager.get()); + Tensor dimension_tensor = makeInputTensor({}, {3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + ArgMaxParams params{}; + params.output_type = DataType::U8; + ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.cpp new file mode 100644 index 0000000..d3bade9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/AveragePool2D.h" + +#include "kernels/Utils.h" + +#include "PALAveragePool2d.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +AveragePool2D::AveragePool2D(const Tensor *input, Tensor *output, Tensor *scratchpad, + const Pool2DParams ¶ms) + : KernelWithParams({input}, {output, scratchpad}, params) +{ +} + +void AveragePool2D::configure() +{ + if (input()->element_type() != output()->element_type()) + { + throw std::runtime_error("Input Tensor and Output Tensor Type must be same"); + } + if (input()->shape().num_dims() != 4) + { + throw std::runtime_error("Input Tensor Shape must be 4-D"); + } + const Shape &input_shape = input()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t depth = input_shape.dim(3); + + const int32_t output_height = + computeOutputSize(_params.padding, input_height, _params.filter_height, _params.stride_height); + const int32_t output_width = + computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); + + _padding_height = + computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); + _padding_width = + computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); + if (input()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); + LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point()); + } + else if (input()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + } + else if (input()->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); + LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point()); + } + output()->resize({batches, output_height, output_width, depth}); + + auto scratchpad = getOutputTensors()[1]; + luci_interpreter_pal::SetupScratchpadTensor(scratchpad, input()->element_type(), + getTensorShape(input()), getTensorShape(output())); +} + +void AveragePool2D::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalSInt16(); + break; + case DataType::S8: + evalSInt8(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void AveragePool2D::evalFloat() const +{ + float activation_min{}; + float activation_max{}; + calculateActivationRange(_params.activation, &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + + tflite::reference_ops::AveragePool(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void AveragePool2D::evalQuantized() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + tflite::reference_ops::AveragePool(params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); +} + +void AveragePool2D::evalSInt8() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + auto scratchpad = getOutputTensors()[1]; + int8_t *scratchpad_data = nullptr; + if (scratchpad->is_allocatable()) + scratchpad_data = scratchpad->data(); + + luci_interpreter_pal::AveragePool( + params, getTensorShape(input()), getTensorData(input()), getTensorShape(output()), + getTensorData(output()), getTensorShape(scratchpad), scratchpad_data); +} + +void AveragePool2D::evalSInt16() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + tflite::reference_integer_ops::AveragePool( + params, getTensorShape(input()), getTensorData(input()), // + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.h b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.h new file mode 100644 index 0000000..2c8fe16 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H +#define LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class AveragePool2D : public KernelWithParams +{ +public: + AveragePool2D(const Tensor *input, Tensor *output, Tensor *scratchpad, + const Pool2DParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalSInt16() const; + void evalSInt8() const; + +private: + int32_t _padding_height{}; + int32_t _padding_width{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp new file mode 100644 index 0000000..478bfa6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/AveragePool2D.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class AveragePool2DTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(AveragePool2DTest, Float) +{ + Shape input_shape{1, 3, 5, 1}; + std::vector input_data{ + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + kernel.configure(); + _memory_manager->allocate_memory(scratchpad); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 0, 1.5, // + 4.5, 6, // + }; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1})); +} + +TEST_F(AveragePool2DTest, Uint8_0) +{ + std::vector input_data{ + 0, -6, 12, 4, // + -3, -2, 10, 7, // + }; + std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + Tensor scratchpad(DataType::U8, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + kernel.configure(); + _memory_manager->allocate_memory(scratchpad); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0.0, 6.0})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1})); +} + +TEST_F(AveragePool2DTest, Uint8_1) +{ + std::vector input_data{ + 0, 6, 12, 4, // + 3, 2, 10, 7, // + }; + + std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + Tensor scratchpad(DataType::U8, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({2.75, 6.0})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1})); +} + +TEST_F(AveragePool2DTest, SInt16) +{ + Shape input_shape{1, 3, 5, 1}; + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector input_data{ + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // + }; + std::vector ref_output_data{ + 0, 1.5, // + 4.5, 6, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + Tensor scratchpad(DataType::S16, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + kernel.configure(); + _memory_manager->allocate_memory(scratchpad); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(AveragePool2DTest, SInt8) +{ + Shape input_shape{1, 4, 5, 1}; + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector input_data{-7, -3, 0, 2, -5, 12, -15, 3, 10, 5, + 7, -6, -1, 9, -2, 0, -5, 11, -1, -7}; + std::vector ref_output_data{ + 0, 2.5, // + 1, 1.5, // + }; + + std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, quant_param.first, quant_param.second); + Tensor scratchpad(DataType::S8, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 2; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + kernel.configure(); + _memory_manager->allocate_memory(scratchpad); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(AveragePool2DTest, Invalid_Input_Shape_NEG) +{ + Shape input_shape{1, 3, 5}; + std::vector input_data{ + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(AveragePool2DTest, In_Out_Type_NEG) +{ + Shape input_shape{1, 3, 5, 1}; + std::vector input_data{ + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(AveragePool2DTest, Quant_Param_NEG) +{ + std::vector input_data{ + 0, -6, 12, 4, // + -3, -2, 10, 7, // + }; + + std::pair quant_param1 = quantizationParams(-15.9375f, 15.9375f); + std::pair quant_param2 = quantizationParams(-7.875f, 7.875f); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param1.first, quant_param1.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param2.first, quant_param2.second); + Tensor scratchpad(DataType::U8, Shape({}), {}, ""); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + params.activation = Activation::RELU6; + + AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.cpp new file mode 100644 index 0000000..24ca229 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/BatchMatMul.h" +#include "kernels/Utils.h" + +#include "PALBatchMatMul.h" + +#include + +#include + +namespace +{ + +tflite::RuntimeShape SwapRowColumnDims(const tflite::RuntimeShape &shape) +{ + tflite::RuntimeShape swapped_shape(shape); + const int32_t dims = shape.DimensionsCount(); + swapped_shape.SetDim(dims - 2, shape.Dims(dims - 1)); + swapped_shape.SetDim(dims - 1, shape.Dims(dims - 2)); + return swapped_shape; +} + +} // namespace + +namespace luci_interpreter +{ +namespace kernels +{ + +BatchMatMul::BatchMatMul(const Tensor *x, const Tensor *y, Tensor *output, Tensor *x_tmp, + Tensor *y_tmp, const BatchMatMulParams ¶ms) + : KernelWithParams({x, y}, {output, x_tmp, y_tmp}, params) +{ +} + +void BatchMatMul::configure() +{ + auto lhs = x(); + auto rhs = y(); + auto adj_x = params().adj_x; + auto adj_y = params().adj_y; + + // TODO Support non-float types + if (lhs->element_type() != DataType::FLOAT32 || rhs->element_type() != DataType::FLOAT32) + throw std::runtime_error("Unsupported type."); + + LUCI_INTERPRETER_CHECK(lhs->element_type() == rhs->element_type()); + + auto lhs_rank = lhs->shape().num_dims(); + auto rhs_rank = rhs->shape().num_dims(); + LUCI_INTERPRETER_CHECK(lhs_rank >= 2 && lhs_rank <= 4); + LUCI_INTERPRETER_CHECK(rhs_rank >= 2 && rhs_rank <= 4); + + auto lhs_scratchpad = temp_lhs(); + auto rhs_scratchpad = temp_rhs(); + luci_interpreter_pal::SetupScratchpadTensor(lhs_scratchpad, rhs_scratchpad, getTensorShape(lhs), + getTensorShape(rhs)); + + auto output_rank = std::max(lhs_rank, rhs_rank); + + auto extended_lhs_shape = tflite::RuntimeShape::ExtendedShape(output_rank, getTensorShape(lhs)); + auto extended_rhs_shape = tflite::RuntimeShape::ExtendedShape(output_rank, getTensorShape(rhs)); + + // Ensure any batch dimensions obey broacasting rules. + for (int i = 0; i < output_rank - 2; ++i) + { + const int lhs_dim = extended_lhs_shape.Dims(i); + const int rhs_dim = extended_rhs_shape.Dims(i); + if (lhs_dim != rhs_dim) + { + if (lhs_dim != 1) + { + LUCI_INTERPRETER_CHECK(rhs_dim == 1); + } + } + } + + // Ensure other dimensions work for matrix multiplication. + int accum_dim_lhs = + adj_x ? extended_lhs_shape.Dims(output_rank - 2) : extended_lhs_shape.Dims(output_rank - 1); + int accum_dim_rhs = + adj_y ? extended_rhs_shape.Dims(output_rank - 1) : extended_rhs_shape.Dims(output_rank - 2); + LUCI_INTERPRETER_CHECK(accum_dim_lhs == accum_dim_rhs); + + Shape output_shape(output_rank); + // Fill in any broadcast dimensions. + for (int i = 0; i < output_rank - 2; ++i) + { + const int lhs_dim = extended_lhs_shape.Dims(i); + const int rhs_dim = extended_rhs_shape.Dims(i); + int broadcast_dim = lhs_dim; + if ((lhs_dim != rhs_dim) && (lhs_dim == 1)) + { + broadcast_dim = rhs_dim; + } + output_shape.dim(i) = broadcast_dim; + } + // Fill in the matmul dimensions. + int lhs_rows_index = adj_x ? output_rank - 1 : output_rank - 2; + int rhs_cols_index = adj_y ? output_rank - 2 : output_rank - 1; + + output_shape.dim(output_rank - 2) = extended_lhs_shape.Dims(lhs_rows_index); + output_shape.dim(output_rank - 1) = extended_rhs_shape.Dims(rhs_cols_index); + + output()->resize(output_shape); +} + +void TransposeRowsColumns(const Tensor *tensor_in, Tensor *tensor_out) +{ + tflite::RuntimeShape transposed_shape(getTensorShape(tensor_in)); + tflite::RuntimeShape shape(getTensorShape(tensor_in)); + tflite::TransposeParams params; + int rank = shape.DimensionsCount(); + params.perm_count = rank; + for (int i = 0; i < rank - 2; ++i) + { + params.perm[i] = i; + } + // Transpose the last two dimensions. + params.perm[rank - 2] = rank - 1; + params.perm[rank - 1] = rank - 2; + transposed_shape.SetDim(rank - 1, shape.Dims(rank - 2)); + transposed_shape.SetDim(rank - 2, shape.Dims(rank - 1)); + switch (tensor_in->element_type()) + { + case DataType::FLOAT32: + tflite::reference_ops::Transpose(params, shape, getTensorData(tensor_in), + transposed_shape, getTensorData(tensor_out)); + break; + default: + throw std::runtime_error("Only suppport fp32 BatchMatMul for now."); + } +} + +void BatchMatMul::execute() const +{ + auto lhs = x(); + auto rhs = y(); + + bool adj_x = params().adj_x; + bool adj_y = params().adj_y; + + auto orig_lhs_shape = getTensorShape(lhs); + auto orig_rhs_shape = getTensorShape(rhs); + + auto rhs_tensor = adj_y ? rhs : temp_rhs(); + auto lhs_tensor = adj_x ? temp_lhs() : lhs; + if (not adj_y) + { + TransposeRowsColumns(rhs, temp_rhs()); + } + if (adj_x) + { + TransposeRowsColumns(lhs, temp_lhs()); + } + tflite::RuntimeShape rhs_shape = adj_y ? orig_rhs_shape : SwapRowColumnDims(orig_rhs_shape); + tflite::RuntimeShape lhs_shape = adj_x ? orig_lhs_shape : SwapRowColumnDims(orig_lhs_shape); + + switch (x()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::BatchMatMul(rhs_shape, getTensorData(rhs_tensor), lhs_shape, + getTensorData(lhs_tensor), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.h b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.h new file mode 100644 index 0000000..744f497 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_BATCHMATMUL_H +#define LUCI_INTERPRETER_KERNELS_BATCHMATMUL_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class BatchMatMul : public KernelWithParams +{ +public: + BatchMatMul(const Tensor *x, const Tensor *y, Tensor *output, Tensor *x_tmp, Tensor *y_tmp, + const BatchMatMulParams ¶ms); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + Tensor *temp_lhs() const { return _outputs[1]; } + Tensor *temp_rhs() const { return _outputs[2]; } +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_BATCHMATMUL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp new file mode 100644 index 0000000..edfa3a6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/BatchMatMul.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class BatchMatMulTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(BatchMatMulTest, Float) +{ + std::vector lhs_data = {1, 2, 3, 4, 5, 6}; + std::vector rhs_data = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + Tensor lhs_tensor = + makeInputTensor({1, 2, 3}, lhs_data, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 3, 4}, rhs_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + kernel.configure(); + _memory_manager->allocate_memory(lhs_scratch); + _memory_manager->allocate_memory(rhs_scratch); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({74., 80., 86., 92., 173., 188., 203., 218.})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4})); +} + +TEST_F(BatchMatMulTest, Float_SimpleRHSAdjoint) +{ + std::vector lhs_data = {1, 2, 3, 4, 5, 6}; + std::vector rhs_data = {7, 11, 15, 8, 12, 16, 9, 13, 17, 10, 14, 18}; + Tensor lhs_tensor = + makeInputTensor({1, 2, 3}, lhs_data, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 4, 3}, rhs_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = true; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + kernel.configure(); + _memory_manager->allocate_memory(lhs_scratch); + _memory_manager->allocate_memory(rhs_scratch); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({74., 80., 86., 92., 173., 188., 203., 218.})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4})); +} + +TEST_F(BatchMatMulTest, Float_SimpleLHSAdjoint) +{ + std::vector lhs_data = {1, 4, 2, 5, 3, 6}; + std::vector rhs_data = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; + Tensor lhs_tensor = + makeInputTensor({1, 3, 2}, lhs_data, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 3, 4}, rhs_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = true; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + kernel.configure(); + _memory_manager->allocate_memory(lhs_scratch); + _memory_manager->allocate_memory(rhs_scratch); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({74., 80., 86., 92., 173., 188., 203., 218.})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4})); +} + +TEST_F(BatchMatMulTest, Float_BatchSizeTwo) +{ + std::vector lhs_data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::vector rhs_data = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + Tensor lhs_tensor = + makeInputTensor({2, 2, 3}, lhs_data, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({2, 3, 4}, rhs_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + kernel.configure(); + _memory_manager->allocate_memory(lhs_scratch); + _memory_manager->allocate_memory(rhs_scratch); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({74., 80., 86., 92., 173., 188., 203., 218., 560., 584., 608., 632., + 767., 800., 833., 866.})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 2, 4})); +} + +TEST_F(BatchMatMulTest, Float_DiffBatch) +{ + std::vector lhs_data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::vector rhs_data = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}; + Tensor lhs_tensor = + makeInputTensor({2, 1, 6}, lhs_data, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 6, 4}, rhs_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + kernel.configure(); + _memory_manager->allocate_memory(lhs_scratch); + _memory_manager->allocate_memory(rhs_scratch); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({427., 448., 469., 490., 1039., 1096., 1153., 1210.})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 1, 4})); +} + +TEST_F(BatchMatMulTest, Invalid_Shape_NEG) +{ + Tensor lhs_tensor = + makeInputTensor({1, 2, 2}, {1, 2, 3, 4}, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 3, 2}, {5, 6, 7, 8, 9, 10}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(BatchMatMulTest, Invalid_Batch_NEG) +{ + Tensor lhs_tensor = + makeInputTensor({2, 1, 3}, {1, 2, 3, 4, 5, 6}, _memory_manager.get()); + Tensor rhs_tensor = makeInputTensor({3, 3, 1}, {5, 6, 7, 8, 9, 10, 11, 12, 13}, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(BatchMatMulTest, Invalid_Rank_NEG) +{ + Tensor lhs_tensor = makeInputTensor({4}, {1, 2, 3, 4}, _memory_manager.get()); + Tensor rhs_tensor = makeInputTensor({1, 4, 2}, {5, 6, 7, 8, 9, 10, 11, 12}, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(BatchMatMulTest, Invalid_Rank2_NEG) +{ + Tensor lhs_tensor = + makeInputTensor({1, 1, 1, 1, 4}, {1, 2, 3, 4}, _memory_manager.get()); + Tensor rhs_tensor = makeInputTensor({1, 4, 2}, {5, 6, 7, 8, 9, 10, 11, 12}, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(BatchMatMulTest, TypeMisMatch_NEG) +{ + Tensor lhs_tensor = + makeInputTensor({1, 2, 3}, {1, 2, 3, 4, 5, 6}, _memory_manager.get()); + Tensor rhs_tensor = + makeInputTensor({1, 3, 2}, {5, 6, 7, 8, 9, 10}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor lhs_scratch(DataType::U8, Shape({}), {}, ""); + Tensor rhs_scratch(DataType::FLOAT32, Shape({}), {}, ""); + + BatchMatMulParams params; + params.adj_x = false; + params.adj_y = false; + + BatchMatMul kernel(&lhs_tensor, &rhs_tensor, &output_tensor, &lhs_scratch, &rhs_scratch, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.cpp new file mode 100644 index 0000000..bd315ff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/BatchToSpaceND.h" +#include "kernels/Utils.h" + +#include "PALBatchToSpaceND.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +namespace +{ +const int kInputMinDimensionNum = 3; +const int kInputMaxDimensionNum = 4; +} // namespace + +BatchToSpaceND::BatchToSpaceND(const Tensor *input, const Tensor *block_shape, const Tensor *crops, + Tensor *output) + : Kernel({input, block_shape, crops}, {output}) +{ +} + +void BatchToSpaceND::configure() +{ + + const auto *block_shape_data = block_shape()->data(); + const auto *crops_data = crops()->data(); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= kInputMinDimensionNum); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= kInputMaxDimensionNum); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + int spatial_dims_num = input()->shape().num_dims() - 2; + + LUCI_INTERPRETER_CHECK(block_shape()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(block_shape()->shape().dim(0) == spatial_dims_num); + + LUCI_INTERPRETER_CHECK(crops()->shape().num_dims() == 2); + LUCI_INTERPRETER_CHECK(crops()->shape().dim(0) == spatial_dims_num); + LUCI_INTERPRETER_CHECK(crops()->shape().dim(1) == 2); + for (int i = 0; i < spatial_dims_num * 2; ++i) + { + LUCI_INTERPRETER_CHECK(crops_data[i] >= 0); + } + + Shape output_shape = Shape(input()->shape().num_dims()); + int output_batch_size = input()->shape().dim(0); + for (int i = 0; i < spatial_dims_num; ++i) + { + LUCI_INTERPRETER_CHECK(output_batch_size % block_shape_data[i] == 0); + output_batch_size = output_batch_size / block_shape_data[i]; + output_shape.dim(i + 1) = + input()->shape().dim(i + 1) * block_shape_data[i] - crops_data[i * 2] - crops_data[i * 2 + 1]; + } + + output_shape.dim(0) = output_batch_size; + output_shape.dim(input()->shape().num_dims() - 1) = + input()->shape().dim(input()->shape().num_dims() - 1); + output()->resize(output_shape); +} + +void BatchToSpaceND::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::BatchToSpaceND( + getTensorShape(input()), getTensorData(input()), getTensorShape(block_shape()), + getTensorData(block_shape()), getTensorShape(crops()), + getTensorData(crops()), getTensorShape(output()), getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::BatchToSpaceND( + getTensorShape(input()), getTensorData(input()), getTensorShape(block_shape()), + getTensorData(block_shape()), getTensorShape(crops()), + getTensorData(crops()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.h b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.h new file mode 100644 index 0000000..57703ea --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H +#define LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class BatchToSpaceND : public Kernel +{ +public: + BatchToSpaceND(const Tensor *input, const Tensor *block_shape, const Tensor *crops, + Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *block_shape() const { return _inputs[1]; } + const Tensor *crops() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp new file mode 100644 index 0000000..52647a7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/BatchToSpaceND.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, + std::initializer_list block_shape_shape, + std::initializer_list crops_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list block_shape_data, + std::initializer_list crops_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor block_shape_tensor = + makeInputTensor(block_shape_shape, block_shape_data, memory_manager.get()); + Tensor crops_tensor = + makeInputTensor(crops_shape, crops_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template class BatchToSpaceNDTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(BatchToSpaceNDTest, DataTypes); + +TYPED_TEST(BatchToSpaceNDTest, Simple) +{ + Check(/*input_shape=*/{4, 2, 2, 1}, /*block_shape_shape=*/{2}, /*crops_shape=*/{2, 2}, + /*output_shape=*/{1, 4, 4, 1}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*block_shape_data=*/{2, 2}, /*crops_data=*/{0, 0, 0, 0}, + /*output_data=*/{1, 5, 2, 6, 9, 13, 10, 14, 3, 7, 4, 8, 11, 15, 12, 16}); +} + +TEST(BatchToSpaceNDTest, Invalid_Shape_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor( + {3, 2, 2, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, memory_manager.get()); + Tensor block_shape_tensor = makeInputTensor({2}, {2, 2}, memory_manager.get()); + Tensor crops_tensor = makeInputTensor({2, 2}, {0, 0, 0, 0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(BatchToSpaceNDTest, Invalid_Crops_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor( + {4, 2, 2, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, memory_manager.get()); + Tensor block_shape_tensor = makeInputTensor({2}, {2, 2}, memory_manager.get()); + Tensor crops_tensor = makeInputTensor({2, 2}, {0, 0, -1, 0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/BinaryOpCommon.h b/compiler/luci-micro/luci-interpreter/src/kernels/BinaryOpCommon.h new file mode 100644 index 0000000..2d2842a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/BinaryOpCommon.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H +#define LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H + +#include "tensorflow/lite/kernels/internal/common.h" +#include "tensorflow/lite/kernels/internal/types.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +// Derived from tensorflow/lite/kernels/internal/reference/maximum_minimum.h (v2.3.0). +template +void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape, + const T *input1_data, + const tflite::RuntimeShape &unextended_input2_shape, + const T *input2_data, + const tflite::RuntimeShape &unextended_output_shape, T *output_data, + Op op) +{ + if (unextended_input1_shape == unextended_input2_shape) + { + const int flat_size = tflite::MatchingElementsSize( + unextended_input1_shape, unextended_input2_shape, unextended_output_shape); + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = op(input1_data[i], input2_data[i]); + } + } + else + { + assert(unextended_input1_shape.DimensionsCount() <= N); + assert(unextended_input2_shape.DimensionsCount() <= N); + assert(unextended_output_shape.DimensionsCount() <= N); + + tflite::NdArrayDesc desc1{}; + tflite::NdArrayDesc desc2{}; + tflite::NdArrayDesc output_desc{}; + tflite::NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, + &desc1, &desc2); + tflite::CopyDimsToDesc(tflite::RuntimeShape::ExtendedShape(N, unextended_output_shape), + &output_desc); + + auto fn = [&](int indexes[N]) { + output_data[SubscriptToIndex(output_desc, indexes)] = + op(input1_data[SubscriptToIndex(desc1, indexes)], + input2_data[SubscriptToIndex(desc2, indexes)]); + }; + tflite::NDOpsHelper(output_desc, fn); + } +} + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/src/kernels/CMakeLists.txt new file mode 100644 index 0000000..9f4ba0e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/CMakeLists.txt @@ -0,0 +1,43 @@ +set(SOURCES + BinaryOpCommon.h + Utils.h + Utils.cpp + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/TestMemoryManager.h" + ${LUCI_INTERPRETER_SOURCE_DIR}/TestMemoryManager.cpp + "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/SimpleMemoryManager.h" + ${LUCI_INTERPRETER_SOURCE_DIR}/SimpleMemoryManager.cpp) + +macro(REGISTER_KERNEL NODE) + list(APPEND SOURCES "${NODE}.h") + list(APPEND SOURCES "${NODE}.cpp") +endmacro(REGISTER_KERNEL) + +include(${KERNEL_REGISTER_FILE}) + +add_library(${LUCI_INTERPRETER_KERNELS} STATIC ${SOURCES}) +if (NOT NNCC_LIBRARY_NO_PIC) + set_target_properties(${LUCI_INTERPRETER_KERNELS} PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif(NOT NNCC_LIBRARY_NO_PIC) +target_include_directories(${LUCI_INTERPRETER_KERNELS} PUBLIC ${LUCI_INTERPRETER_SOURCE_DIR}) + +target_link_libraries(${LUCI_INTERPRETER_KERNELS} PUBLIC ${LUCI_INTERPRETER_CORE}) +target_link_libraries(${LUCI_INTERPRETER_KERNELS} PRIVATE nncc_common) + +add_pal_to_target(${LUCI_INTERPRETER_KERNELS}) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +macro(REGISTER_KERNEL NODE) + list(APPEND TEST_SOURCES "${NODE}.test.cpp") +endmacro(REGISTER_KERNEL) + +include(${KERNEL_REGISTER_FILE}) + +list(APPEND TEST_SOURCES TestUtils.h TestUtils.cpp) + +GTest_AddTest(${LUCI_INTERPRETER_KERNELS}_test ${TEST_SOURCES}) +target_link_libraries(${LUCI_INTERPRETER_KERNELS}_test ${LUCI_INTERPRETER_KERNELS}) diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Cast.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.cpp new file mode 100644 index 0000000..39ee725 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Cast.h" +#include "kernels/Utils.h" + +namespace +{ + +using namespace luci_interpreter; +using namespace luci_interpreter::kernels; + +template +void cast_data(const InT *in_data, OutT *out_data, uint32_t elements_count) +{ + std::transform(in_data, in_data + elements_count, out_data, + [](InT a) { return static_cast(a); }); +} + +template void cast_from_pointer_to_tensor(const InT *in_data, Tensor *out_tensor) +{ + auto const out_type = out_tensor->element_type(); + auto const elements_count = out_tensor->shape().num_elements(); + + switch (out_type) + { + case loco::DataType::U8: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::U16: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::U32: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::U64: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::S8: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::S16: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::S32: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::S64: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::FLOAT32: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + case loco::DataType::BOOL: + cast_data(in_data, getTensorData(out_tensor), elements_count); + break; + default: + throw std::runtime_error("Unsupported output type."); + } +} + +void cast_from_tensor_to_tensor(const Tensor *in_tensor, Tensor *out_tensor) +{ + auto in_type = in_tensor->element_type(); + + switch (in_type) + { + case loco::DataType::U8: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::U16: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::U32: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::U64: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::S8: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::S16: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::S32: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::S64: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::FLOAT32: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + case loco::DataType::BOOL: + cast_from_pointer_to_tensor(getTensorData(in_tensor), out_tensor); + break; + default: + throw std::runtime_error("Unsupported input type."); + } +} + +} // namespace + +namespace luci_interpreter +{ +namespace kernels +{ + +Cast::Cast(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Cast::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() != loco::DataType::Unknown); + LUCI_INTERPRETER_CHECK(output()->element_type() != loco::DataType::Unknown); + + const Shape &shape = input()->shape(); + output()->resize(shape); +} + +void Cast::execute() const +{ + assert(input()->shape().num_elements() == output()->shape().num_elements()); + + cast_from_tensor_to_tensor(input(), output()); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Cast.h b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.h new file mode 100644 index 0000000..f0bd020 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_CAST_H +#define LUCI_INTERPRETER_KERNELS_CAST_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Cast : public Kernel +{ +public: + Cast(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_CAST_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Cast.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.test.cpp new file mode 100644 index 0000000..4713ad3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Cast.test.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Cast.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list shape, std::initializer_list input_data, + std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType input_type = getElementType(); + constexpr DataType output_type = getElementType(); + + Tensor input_tensor = makeInputTensor(shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(output_type); + + Cast kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), shape); +} + +template +void CheckBoolTo(std::initializer_list shape, std::initializer_list input_data, + std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType input_type = loco::DataType::BOOL; + constexpr DataType output_type = getElementType(); + std::vector::Type> input_data_converted; + for (auto elem : input_data) + { + input_data_converted.push_back(elem); + } + + Tensor input_tensor = + makeInputTensor(shape, input_data_converted, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(output_type); + + Cast kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), shape); +} + +template class CastTest : public ::testing::Test +{ +}; + +using IntDataTypes = + ::testing::Types; +TYPED_TEST_SUITE(CastTest, IntDataTypes); + +TYPED_TEST(CastTest, FloatToInt) +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1.0f, 9.0f, 7.0f, 3.0f, // + }, + /*output_data=*/ + { + 1, 9, 7, 3, // + }); + SUCCEED(); +} + +TYPED_TEST(CastTest, IntToFloat) +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1, 9, 7, 3, // + }, + /*output_data=*/ + { + 1.0f, 9.0f, 7.0f, 3.0f, // + }); + SUCCEED(); +} + +template void check_int() +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1, 9, 7, 3, // + }, + /*output_data=*/ + { + 1, 9, 7, 3, // + }); + SUCCEED(); +} + +TYPED_TEST(CastTest, IntToInt) +{ + check_int(); + check_int(); + check_int(); + check_int(); + check_int(); + check_int(); + check_int(); + check_int(); + SUCCEED(); +} + +TYPED_TEST(CastTest, IntToBool) +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1, 0, 7, 0, // + }, + /*output_data=*/ + { + true, false, true, false, // + }); + SUCCEED(); +} + +TYPED_TEST(CastTest, BoolToInt) +{ + CheckBoolTo(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + true, false, false, true, // + }, + /*output_data=*/ + { + 1, 0, 0, 1, // + }); + SUCCEED(); +} + +TEST(CastTest, FloatToBool) +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1.0f, 0.0f, 7.0f, 0.0f, // + }, + /*output_data=*/ + { + true, false, true, false, // + }); + SUCCEED(); +} + +TEST(CastTest, BoolToFloat) +{ + CheckBoolTo(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + true, false, false, true, // + }, + /*output_data=*/ + { + 1.0f, 0.0f, 0.0f, 1.0f, // + }); + SUCCEED(); +} + +TEST(CastTest, FloatToFloat) +{ + Check(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + 1.0f, 0.0f, 7.0f, 0.0f, // + }, + /*output_data=*/ + { + 1.0f, 0.0f, 7.0f, 0.0f, // + }); + SUCCEED(); +} + +TEST(CastTest, BoolToBool) +{ + CheckBoolTo(/*shape=*/{1, 1, 1, 4}, + /*input_data=*/ + { + true, true, false, false, // + }, + /*output_data=*/ + { + true, true, false, false, // + }); + SUCCEED(); +} + +TEST(CastTest, UnsupportedType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({1, 1, 2, 4}, + { + 1, 2, 7, 8, // + 1, 9, 7, 3, // + }, + memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::Unknown); + + Cast kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); + SUCCEED(); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.cpp new file mode 100644 index 0000000..46ee594 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Concatenation.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Concatenation::Concatenation(std::vector inputs, Tensor *output, + const ConcatenationParams ¶ms) + : KernelWithParams(std::move(inputs), {output}, params) +{ +} + +void Concatenation::configure() +{ + const int num_inputs = _inputs.size(); + LUCI_INTERPRETER_CHECK(num_inputs > 0); + const Tensor *t0 = _inputs[0]; + + // TODO: Support concat with fused activation function + LUCI_INTERPRETER_CHECK(params().activation == luci::FusedActFunc::NONE); + + int axis = _params.axis; + if (axis < 0) + axis += t0->shape().num_dims(); + LUCI_INTERPRETER_CHECK(axis >= 0 && axis < t0->shape().num_dims()); + + int32_t sum_axis = t0->shape().dim(axis); + for (int i = 1; i < num_inputs; ++i) + { + const Tensor *tensor = _inputs[i]; + LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type()); + LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims()); + for (int d = 0; d < t0->shape().num_dims(); ++d) + { + if (d == axis) + { + sum_axis += tensor->shape().dim(axis); + } + else + { + LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d)); + } + } + } + + Shape output_shape = t0->shape(); + output_shape.dim(axis) = sum_axis; + + // If input tensors are INT8 type then quantization parameters of all input tensors and the output + // should be the same + for (auto current_tensor : _inputs) + { + if (current_tensor->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(current_tensor->quantized_dimension() == + output()->quantized_dimension()); + + LUCI_INTERPRETER_CHECK(current_tensor->zero_points().size() == + current_tensor->scales().size()); + LUCI_INTERPRETER_CHECK(current_tensor->zero_points() == output()->zero_points()); + LUCI_INTERPRETER_CHECK(current_tensor->scales() == output()->scales()); + } + } + output()->resize(output_shape); +} + +void Concatenation::execute() const +{ + switch (_inputs[0]->element_type()) + { + case DataType::FLOAT32: + evalGeneric(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S8: + evalGeneric(); + break; + case DataType::S32: + evalGeneric(); + break; + case DataType::S64: + evalGeneric(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void Concatenation::evalGeneric() const +{ + int axis = _params.axis; + if (axis < 0) + axis += output()->shape().num_dims(); + + VectorOfTensors inputs(_inputs); + tflite::ConcatenationParams params{}; + params.axis = axis; + params.inputs_count = _inputs.size(); + tflite::reference_ops::Concatenation(params, inputs.shapes(), inputs.data(), + getTensorShape(output()), getTensorData(output())); +} + +void Concatenation::evalQuantized() const +{ + int axis = _params.axis; + if (axis < 0) + axis += output()->shape().num_dims(); + + VectorOfQuantizedTensors inputs(_inputs); + tflite::ConcatenationParams params{}; + params.axis = axis; + params.input_zeropoint = inputs.zero_point(); + params.input_scale = inputs.scale(); + params.inputs_count = _inputs.size(); + params.output_zeropoint = output()->zero_point(); + params.output_scale = output()->scale(); + + tflite::reference_ops::ConcatenationWithScaling(params, inputs.shapes(), inputs.data(), + getTensorShape(output()), + getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.h b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.h new file mode 100644 index 0000000..b48c8ed --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_CONCATENATION_H +#define LUCI_INTERPRETER_KERNELS_CONCATENATION_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Concatenation : public KernelWithParams +{ +public: + Concatenation(std::vector inputs, Tensor *output, + const ConcatenationParams ¶ms); + + const Tensor *input(int index) const { return _inputs[index]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void evalGeneric() const; + void evalQuantized() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_CONCATENATION_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.test.cpp new file mode 100644 index 0000000..f893b38 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Concatenation.test.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Concatenation.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ConcatenationTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(ConcatenationTest, Float) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({2, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + // Try different 'axis' and expect different results. + { + params.axis = 0; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + kernel.configure(); + for (auto t : kernel.getOutputTensors()) + { + _memory_manager->allocate_memory(*t); + } + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + } + { + params.axis = -2; // Same as '0'. + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + } + { + params.axis = 1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})); + } + { + params.axis = -1; // Same as '1'. + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})); + } +} + +TEST_F(ConcatenationTest, Input_Number_Check_NEG) +{ + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Invalid_Axis_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({2, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = -3; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Mismatching_Input_Type_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({2, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Mismatching_Input_Dimension_Num_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({1, 2, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Mismatching_Input_Dimension_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12, 13, 14, 15}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({3, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Int8_Mismatching_Input_Type_NEG) +{ + std::vector input1_data{1, 2, 3, 4}; + std::vector input2_data{5, 6, 7, 8}; + Tensor input1_tensor = makeInputTensor({2, 2}, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({2, 2}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Int8_Mismatching_Input_Output_Quant_Params_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + int quantized_dimension = 3; + std::vector scales{0.1, 0.2, 0.3}; + std::vector zero_points{1, -1, 1}; + + Tensor input1_tensor = makeInputTensor( + {1, 1, 2, 3}, scales, zero_points, quantized_dimension, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + {1, 1, 2, 3}, scales, zero_points, quantized_dimension, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, scales.at(0), zero_points.at(0)); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ConcatenationTest, Int8_Mismatching_Zero_Point_NEG) +{ + std::vector input1_data{1, 2, 3, 4}; + std::vector input2_data{5, 6, 7, 8}; + float scale = 0.1; + int32_t zero_point_1 = 1; + int32_t zero_point_2 = -1; + + Tensor input1_tensor = + makeInputTensor({2, 2}, scale, zero_point_1, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({2, 2}, scale, zero_point_2, input2_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::S8, scale, zero_point_1); + ConcatenationParams params{}; + + params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +// TODO: Remove this test when concat w/ fused_activation is supported +TEST_F(ConcatenationTest, With_Fused_Activation_NEG) +{ + std::vector input1_data{1, 2, 3, 4, 5, 6}; + std::vector input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = + makeInputTensor({2, 3}, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({2, 3}, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = 1; + params.activation = luci::FusedActFunc::RELU; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.cpp new file mode 100644 index 0000000..234f954 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Conv2D.h" + +#include "kernels/Utils.h" + +#include "PALConv2d.h" + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Conv2D::Conv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output, + Tensor *scratchpad, const Conv2DParams ¶ms) + : KernelWithParams({input, filter, bias}, {output, scratchpad}, params) +{ +} + +void Conv2D::configure() +{ + // TensorFlow Lite (as of v2.2.0) supports the following combinations of types: + // | input filter bias output | + // ----+---------------------------+ + // (1) | float float float float | + // (2) | float int8 float float | hybrid + // (3) | uint8 uint8 int32 uint8 | quantized + // (4) | int8 int8 int32 int8 | quantized per channel + // + // We only support (1), (3) and (4) for now, and additionally the following: + // | input filter bias output | + // ----+---------------------------+ + // (5) | int16 int16 int64 int16 | + // + if (input()->element_type() == DataType::FLOAT32 && filter()->element_type() == DataType::FLOAT32) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::FLOAT32); + } + else if (input()->element_type() == DataType::U8 && filter()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32); + } + else if (input()->element_type() == DataType::S8 && filter()->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32); + LUCI_INTERPRETER_CHECK(filter()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(filter()->scales().size() == + static_cast(filter()->shape().dim(0))); + for (auto zerop : filter()->zero_points()) + { + LUCI_INTERPRETER_CHECK(zerop == 0); + } + } + else if (input()->element_type() == DataType::S16 && filter()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S64); + } + else + { + throw std::runtime_error("Unsupported type."); + } + LUCI_INTERPRETER_CHECK(output()->element_type() == input()->element_type()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t output_depth = filter_shape.dim(0); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + LUCI_INTERPRETER_CHECK(filter_shape.dim(3) == input_shape.dim(3)); + + LUCI_INTERPRETER_CHECK(bias() == nullptr || (bias()->shape().num_dims() == 1 && + bias()->shape().dim(0) == output_depth)); + + const int32_t output_height = + computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, + _params.dilation_height_factor); + const int32_t output_width = + computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, + _params.dilation_width_factor); + + _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor, + input_height, filter_height, output_height); + _padding_width = computePadding(_params.stride_width, _params.dilation_width_factor, input_width, + filter_width, output_width); + + output()->resize({batches, output_height, output_width, output_depth}); + + // Allocate tensor for scratchpad, if needed. + tflite::ConvParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + auto scratchpad = getOutputTensors()[1]; + luci_interpreter_pal::SetupScratchpadTensor(scratchpad, input()->element_type(), params, + getTensorShape(input()), getTensorShape(filter()), + getTensorShape(output())); + + switch (_params.activation) + { + case Activation::NONE: + case Activation::RELU: + case Activation::RELU6: + case Activation::RELU_N1_TO_1: + break; + default: + throw std::runtime_error("Unsupported fused activation"); + } +} + +void Conv2D::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + if (filter()->element_type() == DataType::FLOAT32) + { + evalFloat(); + break; + } + throw std::runtime_error("Unsupported type."); + case DataType::U8: + if (filter()->scales().size() == 1) + { + evalQuantized(); + } + else if (filter()->scales().size() > 1) + { + LUCI_INTERPRETER_CHECK(filter()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(filter()->scales().size() == + static_cast(filter()->shape().dim(0))); + evalQuantizedPerChannel(); + } + break; + case DataType::S8: + evalQuantizedS8PerChannel(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Conv2D::evalFloat() const +{ + float activation_min{}; + float activation_max{}; + calculateActivationRange(_params.activation, &activation_min, &activation_max); + + tflite::ConvParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + + auto scratchpad = getOutputTensors()[1]; + float *scratchpad_data = nullptr; + if (scratchpad->is_allocatable()) + scratchpad_data = scratchpad->data(); + + luci_interpreter_pal::Conv(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(filter()), getTensorData(filter()), + getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output()), + getTensorShape(scratchpad), scratchpad_data); +} + +void Conv2D::evalQuantized() const +{ + const auto input_scale = static_cast(input()->scale()); + const auto filter_scale = static_cast(filter()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const double real_multiplier = input_scale * filter_scale / output_scale; + int32_t output_multiplier{}; + int output_shift{}; + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::ConvParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + // The kernel expects input and filter zero points to be negated. + params.input_offset = -input()->zero_point(); // Note the '-'. + params.weights_offset = -filter()->zero_point(); // Note the '-'. + params.output_offset = output()->zero_point(); + params.output_multiplier = output_multiplier; + params.output_shift = output_shift; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + auto scratchpad = getOutputTensors()[1]; + luci_interpreter_pal::Conv(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(filter()), getTensorData(filter()), + getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output()), + getTensorShape(scratchpad), getTensorData(scratchpad)); +} + +void Conv2D::evalQuantizedPerChannel() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t output_depth = filter_shape.dim(0); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + const int32_t dilation_height_factor = _params.dilation_height_factor; + const int32_t dilation_width_factor = _params.dilation_width_factor; + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + const std::vector effective_output_scale = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + const std::vector multipliers_raw = + quantizeMultipliers(effective_output_scale); + BroadcastableWrapper quant_multipliers(multipliers_raw); + + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t out_y = 0; out_y < output_height; ++out_y) + { + for (int32_t out_x = 0; out_x < output_width; ++out_x) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + const int32_t in_y_origin = out_y * stride_height - _padding_height; + const int32_t in_x_origin = out_x * stride_width - _padding_width; + int32_t acc = 0; + for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int32_t in_y = in_y_origin + dilation_height_factor * filter_y; + const int32_t in_x = in_x_origin + dilation_width_factor * filter_x; + if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) + { + for (int32_t in_c = 0; in_c < input_depth; ++in_c) + { + const uint8_t input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + const uint8_t filter_val = + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + acc += static_cast(input_val - input()->zero_point()) * + static_cast(filter_val - filter()->zero_points()[out_c]); + } + } + } + } + if (bias_data) + { + acc += bias_data[out_c]; + } + + int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( + acc, quant_multipliers[out_c].multiplier, quant_multipliers[out_c].shift); + + scaled_acc += output()->zero_point(); + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc; + } + } + } + } +} + +void Conv2D::evalQuantizedS8PerChannel() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::ConvParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + // The kernel expects filter zero points to be negated. + params.input_offset = -input()->zero_point(); // Note the '-'. + params.weights_offset = 0; // Unused in tflite code + params.output_offset = output()->zero_point(); + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + const std::vector effective_output_scales = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + std::vector quant_multipliers = + quantizeMultipliers(effective_output_scales); + + std::vector shifts; + std::transform(quant_multipliers.begin(), quant_multipliers.end(), std::back_inserter(shifts), + [](ChannelQuantMultipliers cm) { return cm.shift; }); + std::vector multipliers; + std::transform(quant_multipliers.begin(), quant_multipliers.end(), + std::back_inserter(multipliers), + [](ChannelQuantMultipliers cm) { return cm.multiplier; }); + + auto scratchpad = getOutputTensors()[1]; + int8_t *scratchpad_data = nullptr; + if (scratchpad->is_allocatable()) + scratchpad_data = scratchpad->data(); + + luci_interpreter_pal::ConvPerChannel( + params, multipliers.data(), shifts.data(), getTensorShape(input()), + getTensorData(input()), getTensorShape(filter()), getTensorData(filter()), + getTensorShape(bias()), getTensorData(bias()), getTensorShape(output()), + getTensorData(output()), getTensorShape(scratchpad), scratchpad_data); +} + +void Conv2D::evalQuantizedS16() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t output_depth = filter_shape.dim(0); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + const int32_t dilation_height_factor = _params.dilation_height_factor; + const int32_t dilation_width_factor = _params.dilation_width_factor; + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + const std::vector effective_output_scale = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + const std::vector multipliers_raw = + quantizeMultipliers(effective_output_scale); + BroadcastableWrapper multipliers(multipliers_raw); + + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t out_y = 0; out_y < output_height; ++out_y) + { + for (int32_t out_x = 0; out_x < output_width; ++out_x) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + const int32_t in_y_origin = out_y * stride_height - _padding_height; + const int32_t in_x_origin = out_x * stride_width - _padding_width; + int64_t acc = 0; + for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int32_t in_y = in_y_origin + dilation_height_factor * filter_y; + const int32_t in_x = in_x_origin + dilation_width_factor * filter_x; + if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) + { + for (int32_t in_c = 0; in_c < input_depth; ++in_c) + { + const int16_t input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + const int16_t filter_val = + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + acc += static_cast(input_val) * static_cast(filter_val); + } + } + } + } + if (bias_data) + { + acc += bias_data[out_c]; + } + + int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( + acc, multipliers[out_c].multiplier, multipliers[out_c].shift); + + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + + output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc; + } + } + } + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.h b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.h new file mode 100644 index 0000000..330bf3a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_CONV2D_H +#define LUCI_INTERPRETER_KERNELS_CONV2D_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class Conv2D : public KernelWithParams +{ +public: + Conv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output, + Tensor *scratchpad, const Conv2DParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *filter() const { return _inputs[1]; } + const Tensor *bias() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedPerChannel() const; + void evalQuantizedS8PerChannel() const; + void evalQuantizedS16() const; + +private: + int32_t _padding_height{}; + int32_t _padding_width{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_CONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.test.cpp new file mode 100644 index 0000000..0fe6ef7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Conv2D.test.cpp @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Conv2D.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class Conv2DTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(Conv2DTest, Float) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(im2col); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 11, 16, 7, 20, // row = 0 + 0, 40, 0, 44, // row = 1 + }; + std::vector ref_output_shape{1, 2, 2, 2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, FloatPointwise) +{ + Shape input_shape{1, 2, 2, 2}; + Shape filter_shape{2, 1, 1, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, // row = 0, col = 0 + 3, 4, // row = 0, col = 1 + 5, 6, // row = 1, col = 0 + 7, 8, // row = 1, col = 1 + }; + std::vector filter_data{ + -1, 2, // out = 0 + -3, 4, // out = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 1; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(im2col); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 4, 7, 6, 9, // row = 0 + 8, 11, 10, 13, // row = 1 + }; + std::vector ref_output_shape{1, 2, 2, 2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, FloatCheck) +{ + Shape input_shape{2, 2, 4, 1}; + Shape filter_shape{3, 2, 2, 1}; + Shape bias_shape{3}; + std::vector input_data{ + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 + }; + std::vector filter_data{ + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter + }; + std::vector bias_data{1, 2, 3}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + std::vector ref_output_data{ + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right + }; + std::vector ref_output_shape{2, 1, 2, 3}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, Uint8) +{ + std::vector input_data{ + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 + }; + std::vector filter_data{ + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter + }; + std::vector bias_data{1, 2, 3}; + + std::pair input_quant_param = quantizationParams(-63.5, 64); + std::pair output_quant_param = quantizationParams(-127, 128); + + Tensor input_tensor = + makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor({3, 2, 2, 1}, input_quant_param.first, input_quant_param.second, + filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor( + {3}, input_quant_param.first * input_quant_param.first, 0, bias_data, _memory_manager.get()); + Tensor im2col(DataType::U8, Shape({}), {}, ""); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + std::vector ref_output_data{ + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right + }; + std::vector ref_output_shape{2, 1, 2, 3}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, Uint8_CWQ) +{ + const int output_channels = 3; + std::vector input_data{ + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 + }; + std::vector filter_data{ + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter + }; + std::vector bias_data{1, 2, 3}; + Shape filter_shape{output_channels, 2, 2, 1}; + + std::pair input_quant_param = quantizationParams(0, 4); + std::pair output_quant_param = quantizationParams(-127, 128); + + std::vector> filter_quant_params; + filter_quant_params.push_back(quantizationParams(0, 4)); + filter_quant_params.push_back(quantizationParams(-1, 1)); + filter_quant_params.push_back(quantizationParams(-1, 1)); + + std::vector filter_scales; + std::vector filter_zerops; + for (auto iter : filter_quant_params) + { + filter_scales.push_back(iter.first); + filter_zerops.push_back(iter.second); + } + + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); + std::vector zerop(output_channels, 0); + + Tensor input_tensor = + makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, + 0, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor({output_channels}, bias_scales, zerop, 0, + bias_data, _memory_manager.get()); + Tensor im2col(DataType::U8, Shape({}), {}, ""); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + std::vector ref_output_data{ + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right + }; + std::vector ref_output_shape{2, 1, 2, 3}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, SInt8_CWQ) +{ + const int output_channels = 3; + std::vector input_data{ + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 + }; + std::vector filter_data{ + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter + }; + std::vector bias_data{1, 2, 3}; + Shape filter_shape{output_channels, 2, 2, 1}; + + std::pair input_quant_param = quantizationParams(0, 4); + std::pair output_quant_param = quantizationParams(-127, 128); + + std::vector> filter_quant_params; + filter_quant_params.push_back(std::pair(0.5, 0)); + filter_quant_params.push_back(std::pair(0.25, 0)); + filter_quant_params.push_back(std::pair(0.125, 0)); + + std::vector filter_scales; + std::vector filter_zerops; + for (auto iter : filter_quant_params) + { + filter_scales.push_back(iter.first); + filter_zerops.push_back(iter.second); + } + + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); + std::vector zerop(output_channels, 0); + + Tensor input_tensor = + makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, + 0, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor({output_channels}, bias_scales, zerop, 0, + bias_data, _memory_manager.get()); + Tensor im2col(DataType::S8, Shape({}), {}, ""); + Tensor output_tensor = + makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + std::vector ref_output_data{ + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right + }; + std::vector ref_output_shape{2, 1, 2, 3}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(Conv2DTest, SInt16) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector ref_output_shape{1, 2, 2, 2}; + + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + std::vector ref_output_data{ + 11, 16, 7, 20, // row = 0 + 0, 40, 0, 44, // row = 1 + }; + + Tensor input_tensor = + makeInputTensor(input_shape, 0.25, 0, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, 0.2, 0, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, 0.25 * 0.2, 0, bias_data, _memory_manager.get()); + Tensor im2col(DataType::S16, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(Conv2DTest, SInt16_CWQ_weights) +{ + Shape input_shape{1, 2, 2, 2}; // Batch x H x W x C + Shape filter_shape{3, 1, 1, 2}; // Out channels x H x W x In Channels + Shape bias_shape{3}; + std::vector ref_output_shape{1, 2, 2, 3}; + + std::vector input_data{ + 1, 2, // row = 0, col 0 + 3, 4, // row = 0, col 1 + 5, 6, // row = 1, col 0 + 7, 8, // row = 1, col 1 + }; + std::vector filter_data{ + 4, -3, // out = 0 + 1, -3, // out = 1 + 5, -3, // out = 2 + }; + std::vector bias_data{1, 10, 5}; + std::vector ref_output_data{ + 0, 5, 4, // row 0, col 0 + 1, 1, 8, // row 0, col 1 + 3, 0, 12, // row 1, col 0 + 5, 0, 16, // row 1, col 1 + }; + + float input_scale = 0.25f; + float output_scale = 0.05f; + std::vector filter_scales = {0.25f, 0.2f, 0.1f}; + std::vector bias_scales; + for (int i = 0; i < filter_scales.size(); ++i) + bias_scales.push_back(filter_scales[i] * input_scale); + std::vector zerop = {0, 0, 0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_scale, 0, input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, zerop, 0, + filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, + _memory_manager.get()); + Tensor im2col(DataType::S16, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 1; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(im2col); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(Conv2DTest, Unsupported_Type_Configure_NEG) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(Conv2DTest, Invalid_Bias_Type_NEG) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(Conv2DTest, Invalid_Bias_Data_NEG) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{3}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2, 3}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(Conv2DTest, Invalid_Input_Shape_NEG) +{ + Shape input_shape{1, 4, 6, 1}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(Conv2DTest, Invalid_fused_act_tanh_NEG) +{ + Shape input_shape{1, 4, 3, 2}; + Shape filter_shape{2, 2, 2, 2}; + Shape bias_shape{2}; + std::vector input_data{ + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 + }; + std::vector filter_data{ + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 + }; + std::vector bias_data{1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Conv2DParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::TANH; + + Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.cpp new file mode 100644 index 0000000..3a9acd1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "DepthToSpace.h" +#include "Utils.h" +#include "PALDepthToSpace.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +DepthToSpace::DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpaceParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void DepthToSpace::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 || + output()->element_type() == DataType::U8) + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()) + const int block_size = params().block_size; + const int32_t input_height = input()->shape().dim(1); + const int32_t input_width = input()->shape().dim(2); + const int32_t input_channels = input()->shape().dim(3); + int32_t output_height = input_height * block_size; + int32_t output_width = input_width * block_size; + int32_t output_channels = input_channels / block_size / block_size; + + LUCI_INTERPRETER_CHECK(input_height == output_height / block_size); + LUCI_INTERPRETER_CHECK(input_width == output_width / block_size); + LUCI_INTERPRETER_CHECK(input_channels == output_channels * block_size * block_size); + + Shape output_shape(4); + output_shape.dim(0) = input()->shape().dim(0); + output_shape.dim(1) = output_height; + output_shape.dim(2) = output_width; + output_shape.dim(3) = output_channels; + + output()->resize(output_shape); +} + +void DepthToSpace::execute() const +{ + tflite::DepthToSpaceParams op_params; + op_params.block_size = params().block_size; + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::DepthToSpace(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::DepthToSpace(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported Type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.h b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.h new file mode 100644 index 0000000..63ce376 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H +#define LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class DepthToSpace : public KernelWithParams +{ +public: + DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpaceParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp new file mode 100644 index 0000000..88e6e07 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/DepthToSpace.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template class DepthToSpaceTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(DepthToSpaceTest, DataTypes); + +TYPED_TEST(DepthToSpaceTest, SimpleCase) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8}; + Shape input_shape{1, 1, 2, 4}; + std::vector output_data{1, 2, 5, 6, 3, 4, 7, 8}; + std::vector output_shape{1, 2, 4, 1}; + + Tensor input_tensor = + makeInputTensor()>(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(getElementType()); + + DepthToSpaceParams params{}; + params.block_size = 2; + + DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(DepthToSpaceTest, InvalidInputShape_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8}; + Shape input_shape{1, 2, 4}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DepthToSpaceParams params{}; + params.block_size = 2; + + DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(DepthToSpaceTest, InOutTypeMismatch_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8}; + Shape input_shape{1, 1, 2, 4}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + DepthToSpaceParams params{}; + params.block_size = 2; + + DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(DepthToSpaceTest, InvalidBlockSize_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8}; + Shape input_shape{1, 1, 2, 4}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DepthToSpaceParams params{}; + params.block_size = 3; + + DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.cpp new file mode 100644 index 0000000..c554c30 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/DepthwiseConv2D.h" + +#include "kernels/Utils.h" + +#include "PALDepthwiseConv2d.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +DepthwiseConv2D::DepthwiseConv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, + Tensor *output, Tensor *scratchpad, + const DepthwiseConv2DParams ¶ms) + : KernelWithParams({input, filter, bias}, {output, scratchpad}, params) +{ +} + +void DepthwiseConv2D::configure() +{ + // TensorFlow Lite (as of v2.2.0) supports the following combinations of types: + // | input filter bias output | + // ----+---------------------------+ + // (1) | float float float float | + // (2) | float int8 float float | hybrid + // (3) | uint8 uint8 int32 uint8 | quantized + // (4) | int8 int8 int32 int8 | quantized per channel + // (5) | int16 int8 int64 int16 | quantized per channel 16x8 + // + // We only support (1), (3) and (4) for now, and additionally the following: + // | input filter bias output | + // ----+---------------------------+ + // (5) | int16 int16 int64 int16 | + // + if (input()->element_type() == DataType::FLOAT32 && filter()->element_type() == DataType::FLOAT32) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::FLOAT32); + } + else if (input()->element_type() == DataType::U8 && filter()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32); + } + else if (input()->element_type() == DataType::S8 && filter()->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(filter()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(static_cast(filter()->shape().dim(3)) == + filter()->scales().size()); + for (auto zerop : filter()->zero_points()) + { + LUCI_INTERPRETER_CHECK(zerop == 0); + } + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32); + } + else if (input()->element_type() == DataType::S16 && filter()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S64); + } + else + { + throw std::runtime_error("Unsupported type."); + } + LUCI_INTERPRETER_CHECK(output()->element_type() == input()->element_type()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + // Filter format: [1, H, W, O]. + LUCI_INTERPRETER_CHECK(filter_shape.dim(0) == 1); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t channels_out = filter_shape.dim(3); + + LUCI_INTERPRETER_CHECK(bias() == nullptr || (bias()->shape().num_dims() == 1 && + bias()->shape().dim(0) == channels_out)); + + const int32_t output_height = + computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, + _params.dilation_height_factor); + const int32_t output_width = + computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, + _params.dilation_width_factor); + + _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor, + input_height, filter_height, output_height); + _padding_width = computePadding(_params.stride_width, _params.dilation_width_factor, input_width, + filter_width, output_width); + + output()->resize({batches, output_height, output_width, channels_out}); + + tflite::DepthwiseParams params{}; + + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + + auto scratchpad = getOutputTensors()[1]; + luci_interpreter_pal::SetupScratchpadTensor(scratchpad, params, input()->element_type(), + getTensorShape(input()), getTensorShape(filter()), + getTensorShape(output())); +} + +void DepthwiseConv2D::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + if (filter()->element_type() == DataType::FLOAT32) + { + evalFloat(); + break; + } + throw std::runtime_error("Unsupported type."); + case DataType::U8: + if (filter()->scales().size() == 1) + { + evalQuantized(); + } + else if (filter()->scales().size() > 1) + { + LUCI_INTERPRETER_CHECK(filter()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(filter()->scales().size() == + static_cast(filter()->shape().dim(3))); + evalQuantizedPerChannel(); + } + break; + case DataType::S8: + evalQuantizedS8PerChannel(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void DepthwiseConv2D::evalFloat() const +{ + float activation_min{}; + float activation_max{}; + calculateActivationRange(_params.activation, &activation_min, &activation_max); + + tflite::DepthwiseParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + params.depth_multiplier = _params.depth_multiplier; + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + + tflite::reference_ops::DepthwiseConv( + params, getTensorShape(input()), getTensorData(input()), getTensorShape(filter()), + getTensorData(filter()), getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output())); +} + +void DepthwiseConv2D::evalQuantizedPerChannel() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + const int32_t dilation_height_factor = _params.dilation_height_factor; + const int32_t dilation_width_factor = _params.dilation_width_factor; + const int32_t depth_multiplier = _params.depth_multiplier; + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + const std::vector effective_output_scales = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + std::vector quant_multipliers_raw = + quantizeMultipliers(effective_output_scales); + BroadcastableWrapper quant_multipliers(quant_multipliers_raw); + + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int in_channel = 0; in_channel < input_depth; ++in_channel) + { + for (int m = 0; m < depth_multiplier; ++m) + { + const int output_channel = m + in_channel * depth_multiplier; + const int in_x_origin = (out_x * stride_width) - _padding_width; + const int in_y_origin = (out_y * stride_height) - _padding_height; + int32 acc = 0; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + const int in_y = in_y_origin + dilation_height_factor * filter_y; + // Zero padding by omitting the areas outside the image. + const bool is_point_inside_image = + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + if (is_point_inside_image) + { + int32 input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_channel)]; + int32 filter_val = + filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, output_channel)]; + acc += (filter_val - filter()->zero_points()[output_channel]) * + (input_val - input()->zero_point()); + } + } + } + if (bias_data) + { + acc += bias_data[output_channel]; + } + int32_t output_multiplier = quant_multipliers[output_channel].multiplier; + int output_shift = quant_multipliers[output_channel].shift; + int32_t scaled_acc = + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + scaled_acc += output()->zero_point(); + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + output_data[calcOffset(output_shape, batch, out_y, out_x, output_channel)] = + static_cast(scaled_acc); + } + } + } + } + } +} + +void DepthwiseConv2D::evalQuantized() const +{ + const auto input_scale = static_cast(input()->scale()); + const auto filter_scale = static_cast(filter()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const double real_multiplier = input_scale * filter_scale / output_scale; + int32_t output_multiplier{}; + int output_shift{}; + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::DepthwiseParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + params.depth_multiplier = _params.depth_multiplier; + // The kernel expects input and filter zero points to be negated. + params.input_offset = -input()->zero_point(); // Note the '-'. + params.weights_offset = -filter()->zero_point(); // Note the '-'. + params.output_offset = output()->zero_point(); + params.output_multiplier = output_multiplier; + params.output_shift = output_shift; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + tflite::reference_ops::DepthwiseConv( + params, getTensorShape(input()), getTensorData(input()), getTensorShape(filter()), + getTensorData(filter()), getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output())); +} + +void DepthwiseConv2D::evalQuantizedS8PerChannel() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::DepthwiseParams params{}; + + params.padding_type = tflite::PaddingType::kSame; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.dilation_height_factor = _params.dilation_height_factor; + params.dilation_width_factor = _params.dilation_width_factor; + params.depth_multiplier = _params.depth_multiplier; + // The kernel expects input and filter zero points to be negated. + params.input_offset = -input()->zero_point(); // Note the '-'. + params.weights_offset = 0; + params.output_offset = output()->zero_point(); + params.output_multiplier = 1; // unused in tflite code + params.output_shift = 0; // unused in tflite code + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + const std::vector effective_output_scales = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + std::vector quant_multipliers = + quantizeMultipliers(effective_output_scales); + + std::vector shifts; + std::transform(quant_multipliers.begin(), quant_multipliers.end(), std::back_inserter(shifts), + [](ChannelQuantMultipliers cm) { return cm.shift; }); + std::vector multipliers; + std::transform(quant_multipliers.begin(), quant_multipliers.end(), + std::back_inserter(multipliers), + [](ChannelQuantMultipliers cm) { return cm.multiplier; }); + + auto scratchpad = getOutputTensors()[1]; + int8_t *scratchpad_data = nullptr; + if (scratchpad->is_allocatable()) + scratchpad_data = scratchpad->data(); + + luci_interpreter_pal::DepthwiseConvPerChannel( + params, multipliers.data(), shifts.data(), getTensorShape(input()), + getTensorData(input()), getTensorShape(filter()), getTensorData(filter()), + getTensorShape(bias()), getTensorData(bias()), getTensorShape(output()), + getTensorData(output()), getTensorShape(scratchpad), scratchpad_data); +} + +void DepthwiseConv2D::evalQuantizedS16() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + const int32_t dilation_height_factor = _params.dilation_height_factor; + const int32_t dilation_width_factor = _params.dilation_width_factor; + const int32_t depth_multiplier = _params.depth_multiplier; + + const std::vector effective_output_scales = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + std::vector quant_multipliers_raw = + quantizeMultipliers(effective_output_scales); + + BroadcastableWrapper quant_multipliers(quant_multipliers_raw); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t out_y = 0; out_y < output_height; ++out_y) + { + for (int32_t out_x = 0; out_x < output_width; ++out_x) + { + for (int32_t in_c = 0; in_c < input_depth; ++in_c) + { + for (int32_t m = 0; m < depth_multiplier; ++m) + { + const int32_t out_c = m + in_c * depth_multiplier; + const int32_t in_y_origin = out_y * stride_height - _padding_height; + const int32_t in_x_origin = out_x * stride_width - _padding_width; + int64_t acc = 0; + for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int32_t in_y = in_y_origin + dilation_height_factor * filter_y; + const int32_t in_x = in_x_origin + dilation_width_factor * filter_x; + if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) + { + const int16_t input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + const int16_t filter_val = + filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, out_c)]; + acc += static_cast(input_val) * static_cast(filter_val); + } + } + } + if (bias_data != nullptr) + { + acc += bias_data[out_c]; + } + + int32_t output_multiplier = quant_multipliers[out_c].multiplier; + int output_shift = quant_multipliers[out_c].shift; + int32_t scaled_acc = + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + + output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc; + } + } + } + } + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h new file mode 100644 index 0000000..3d1faf6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H +#define LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class DepthwiseConv2D : public KernelWithParams +{ +public: + DepthwiseConv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output, + Tensor *scratchpad, const DepthwiseConv2DParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *filter() const { return _inputs[1]; } + const Tensor *bias() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedPerChannel() const; + void evalQuantizedS8PerChannel() const; + void evalQuantizedS16() const; + +private: + int32_t _padding_height{}; + int32_t _padding_width{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp new file mode 100644 index 0000000..6b4673f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/DepthwiseConv2D.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class DepthwiseConv2DTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(DepthwiseConv2DTest, Float) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 2, 4}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(scratchpad); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 71, 0, 99, 0, // + 167, 0, 227, 28, // + }; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4})); +} + +TEST_F(DepthwiseConv2DTest, Uint8) +{ + std::vector input_data{ + 1, 2, 7, 8, // column 1 + 3, 4, 9, 10, // column 2 + 5, 6, 11, 12, // column 3 + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + + std::pair input_quant_param = quantizationParams(-63.5, 64); + std::pair output_quant_param = quantizationParams(-127, 128); + + Tensor input_tensor = + makeInputTensor({1, 3, 2, 2}, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor({1, 2, 2, 4}, input_quant_param.first, input_quant_param.second, + filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor( + {4}, input_quant_param.first * input_quant_param.first, 0, bias_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 1; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + std::vector ref_output_data{ + 71, -34, 99, -20, // + 91, -26, 127, -4, // + }; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4})); +} + +TEST_F(DepthwiseConv2DTest, SInt16) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 2, 4}; + Shape bias_shape{4}; + std::vector ref_output_shape{1, 2, 1, 4}; + + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + std::vector ref_output_data{ + 71, 0, 99, 0, // + 167, 0, 227, 28, // + }; + + Tensor input_tensor = + makeInputTensor(input_shape, 0.25, 0, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, 0.2, 0, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, 0.25 * 0.2, 0, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + Tensor scratchpad(DataType::S64, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(DepthwiseConv2DTest, SInt16_CWQ_weights) +{ + const int output_channels = 4; + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 2, output_channels}; + Shape bias_shape{4}; + std::vector ref_output_shape{1, 2, 1, output_channels}; + + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + std::vector ref_output_data{ + 71, 0, 99, 0, // + 167, 0, 227, 28, // + }; + + float input_scale = 0.25; + std::vector filter_scales{0.2f, 1.f, 0.5f, 0.1f}; + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_scales[i] * input_scale); + std::vector zerop(4, 0); + Tensor input_tensor = + makeInputTensor(input_shape, input_scale, 0, input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, zerop, 3, + filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + Tensor scratchpad(DataType::S16, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(DepthwiseConv2DTest, Uint8_CWQ_weights) +{ + const int output_channels = 4; + Shape input_shape{1, 3, 2, 2}; + Shape filter_shape{1, 2, 2, output_channels}; + Shape bias_shape{4}; + std::vector ref_output_shape{1, 2, 1, output_channels}; + + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + std::vector ref_output_data{ + 71, -34, 99, -20, // + 91, -26, 127, -4, // + }; + + std::pair input_quant_param = quantizationParams(0, 16); + std::pair output_quant_param = quantizationParams(-127, 128); + + std::vector> filter_quant_params; + filter_quant_params.push_back(quantizationParams(-9, 13)); + filter_quant_params.push_back(quantizationParams(-14, 10)); + filter_quant_params.push_back(quantizationParams(-11, 15)); + filter_quant_params.push_back(quantizationParams(-16, 12)); + + std::vector filter_scales; + std::vector filter_zerops; + for (auto iter : filter_quant_params) + { + filter_scales.push_back(iter.first); + filter_zerops.push_back(iter.second); + } + + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); + std::vector zerop(output_channels, 0); + + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, + 3, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, + _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + Tensor scratchpad(DataType::U8, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 1; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, output_quant_param.first)); +} + +TEST_F(DepthwiseConv2DTest, SInt8_CWQ_weights) +{ + const int output_channels = 4; + Shape input_shape{1, 3, 2, 2}; + Shape filter_shape{1, 2, 2, output_channels}; + Shape bias_shape{4}; + std::vector ref_output_shape{1, 2, 1, output_channels}; + + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + std::vector ref_output_data{ + 71, -34, 99, -20, // + 91, -26, 127, -4, // + }; + + std::pair input_quant_param = quantizationParams(-128, 127); + std::pair output_quant_param = quantizationParams(-127, 128); + + std::vector> filter_quant_params; + filter_quant_params.push_back(std::pair(0.5, 0)); + filter_quant_params.push_back(std::pair(0.25, 0)); + filter_quant_params.push_back(std::pair(1, 0)); + filter_quant_params.push_back(std::pair(0.125, 0)); + + std::vector filter_scales; + std::vector filter_zerops; + for (auto iter : filter_quant_params) + { + filter_scales.push_back(iter.first); + filter_zerops.push_back(iter.second); + } + + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); + std::vector zerop(output_channels, 0); + + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, + 3, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, + _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); + Tensor scratchpad(DataType::S8, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 1; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::NONE; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, output_quant_param.first)); +} + +TEST_F(DepthwiseConv2DTest, InvalidBiasType_NEG) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 2, 4}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DepthwiseConv2DTest, InOutTypeMismatch_NEG) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 2, 4}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + Tensor scratchpad(DataType::U8, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DepthwiseConv2DTest, InvalidInputShape_NEG) +{ + Shape input_shape{4, 2, 2}; + Shape filter_shape{2, 2, 4}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DepthwiseConv2DTest, InvalidFilterShape_NEG) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{2, 1, 2, 4}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DepthwiseConv2DTest, InvalidBiasDim_NEG) +{ + Shape input_shape{1, 4, 2, 2}; + Shape filter_shape{1, 2, 4, 2}; + Shape bias_shape{4}; + std::vector input_data{ + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // + }; + std::vector filter_data{ + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // + }; + std::vector bias_data{1, 2, 3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor filter_tensor = + makeInputTensor(filter_shape, filter_data, _memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + + DepthwiseConv2DParams params{}; + params.padding = Padding::VALID; + params.depth_multiplier = 2; + params.stride_height = 2; + params.stride_width = 1; + params.dilation_height_factor = 1; + params.dilation_width_factor = 1; + params.activation = Activation::RELU; + + DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &scratchpad, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.cpp new file mode 100644 index 0000000..96399e5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Dequantize.h" +#include "kernels/Utils.h" +#include "PALDequantize.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Dequantize::Dequantize(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Dequantize::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == loco::DataType::S8 || + input()->element_type() == loco::DataType::U8 || + input()->element_type() == loco::DataType::S16); + + LUCI_INTERPRETER_CHECK(input()->scales().size() == 1); + + if (input()->element_type() == loco::DataType::S16) + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0); + + LUCI_INTERPRETER_CHECK(output()->element_type() == loco::DataType::FLOAT32); + + output()->resize(input()->shape()); +} + +void Dequantize::execute() const +{ + tflite::DequantizationParams op_params; + op_params.zero_point = input()->zero_point(); + op_params.scale = input()->scale(); + + switch (input()->element_type()) + { + case loco::DataType::U8: + { + luci_interpreter_pal::Dequantize(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + } + case loco::DataType::S8: + { + luci_interpreter_pal::Dequantize(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + } + case loco::DataType::S16: + { + luci_interpreter_pal::Dequantize(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.h b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.h new file mode 100644 index 0000000..5565df0 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_DEQUANTIZE_H +#define LUCI_INTERPRETER_KERNELS_DEQUANTIZE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Dequantize : public Kernel +{ +public: + Dequantize(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_DEQUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.test.cpp new file mode 100644 index 0000000..0cab633 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Dequantize.test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Dequantize.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class DequantizeTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(DequantizeTest, Uint8) +{ + std::vector input_data{0, 1, 2, 3, 4, 251, 252, 253, 254, 255}; + + std::vector ref_output_data{-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}; + + Tensor input_tensor(loco::DataType::U8, {2, 5}, {{0.5}, {127}}, ""); + + _memory_manager->allocate_memory(input_tensor); + input_tensor.writeData(input_data.data(), input_data.size() * sizeof(uint8_t)); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Dequantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(DequantizeTest, Sint8) +{ + std::vector input_data{-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}; + + std::vector ref_output_data{-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}; + + Tensor input_tensor(loco::DataType::S8, {2, 5}, {{0.5}, {-1}}, ""); + + _memory_manager->allocate_memory(input_tensor); + input_tensor.writeData(input_data.data(), input_data.size() * sizeof(int8_t)); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Dequantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(DequantizeTest, Sint16) +{ + std::vector input_data{-129, -126, -125, -124, -123, 124, 125, 126, 127, 131}; + + std::vector ref_output_data{-64.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 65.5}; + + Tensor input_tensor(loco::DataType::S16, {2, 5}, {{0.5}, {0}}, ""); + + _memory_manager->allocate_memory(input_tensor); + input_tensor.writeData(input_data.data(), input_data.size() * sizeof(int16_t)); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Dequantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(DequantizeTest, InvalidInputType_NEG) +{ + std::vector input_data{-129, -126, -125, -124, -123, 124, 125, 126, 127, 131}; + + Tensor input_tensor = + makeInputTensor({2, 5}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Dequantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DequantizeTest, InvalidOutputType_NEG) +{ + std::vector input_data{-129, -126, -125, -124, -123, 124, 125, 126, 127, 131}; + + Tensor input_tensor(loco::DataType::S16, {2, 5}, {{0.5}, {0}}, ""); + + _memory_manager->allocate_memory(input_tensor); + input_tensor.writeData(input_data.data(), input_data.size() * sizeof(int16_t)); + + Tensor output_tensor = makeOutputTensor(DataType::S8, /*scale*/ 0.5, /*zero_point*/ -1); + + Dequantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DequantizeTest, InvalidInputZeroPoint_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({2, 5}, 0.5, -1, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Dequantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Div.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Div.cpp new file mode 100644 index 0000000..dd15322 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Div.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Div.h" + +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Div::Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams ¶ms) + : KernelWithParams({input1, input2}, {output}, params) +{ +} + +void Div::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); + + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Div::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Div::evalFloat() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastDivSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +template void Div::evalInteger() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastDivSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +void Div::evalQuantized() const +{ + const auto input1_scale = static_cast(input1()->scale()); + const auto input2_scale = static_cast(input2()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const double real_output_multiplier = input1_scale / (input2_scale * output_scale); + + int32_t output_multiplier{}; + int output_shift{}; + + quantizeMultiplier(real_output_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::ArithmeticParams params{}; + + params.input1_offset = -input1()->zero_point(); // Note the '-'. + params.input2_offset = -input2()->zero_point(); // Note the '-'. + params.output_offset = output()->zero_point(); + params.output_multiplier = output_multiplier; + params.output_shift = output_shift; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastDivSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Div.h b/compiler/luci-micro/luci-interpreter/src/kernels/Div.h new file mode 100644 index 0000000..c1bf3e1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Div.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_DIV_H +#define LUCI_INTERPRETER_KERNELS_DIV_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Div : public KernelWithParams +{ +public: + Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams ¶ms); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_DIV_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Div.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Div.test.cpp new file mode 100644 index 0000000..85cd8b9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Div.test.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Div.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class DivTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +float GetTolerance(float min, float max) +{ + const float kQuantizedStep = (max - min) / 255.0f; + const float kQuantizedTolerance = 2.0f * kQuantizedStep + kQuantizedStep * kQuantizedStep; + return kQuantizedTolerance; +} + +TEST_F(DivTest, Float) +{ + Shape base_shape = {2, 3, 1, 1}; + + std::vector output_shape = {2, 3, 1, 1}; + + std::vector input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f}; + std::vector input2_data{0.2f, 1.6f, 0.5f, 0.4f, 1.6f, 0.4f}; + std::vector test_outputs{1.5f, 1.4375f, 1.8f, 1.25f, 0.5f, 2.75f}; + + Tensor input1_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(base_shape, input2_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST_F(DivTest, FloatBroadcast) +{ + Shape input1_shape = {1, 3}; + Shape input2_shape = {3, 1}; + + std::vector input1_data{-0.3f, 2.3f, 0.9f}; + std::vector input2_data{0.2f, 1.6f, 0.5f}; + std::vector test_outputs{0.f, 11.5f, 4.5f, 0.f, 1.4375f, 0.5625f, 0.f, 4.6f, 1.8f}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); +} + +TEST_F(DivTest, Uint8) +{ + Shape base_shape = {1, 2, 2, 1}; + + std::vector output_shape = {1, 2, 2, 1}; + + std::vector input1_data = {-0.8f, -0.2f, 0.3f, 0.7f}; + std::vector input2_data = {-0.8f, 0.4f, 0.8f, 1.0f}; + std::vector test_outputs{1.0f, 0.f, 0.375f, 0.7f}; + + const float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + + std::pair quant_param = quantizationParams(-1.f, 1.f); + + Tensor input1_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, input2_data, _memory_manager.get()); + + Tensor output_tensor = + makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(test_outputs, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template void checkInteger(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + + std::vector> test_outputs = {{5, 6, 2, 0, 10, 3, // + 10, 0, 4, 5, 20, 0, // + 0, 0, 0, 2, 0, 0, // + 2, 0, 1, 10, 5, 0, // + 2, 3, 1, 0, 5, 1, // + 18, 20, 7, 0, 37, 10}, + {5, 6, 4, 5, 0, 0, 2, 0, 1, 0, 37, 10}, + {5, 7, 4, 6, 2, 3, 10, 0, 8, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 1, 0, + 0, 0, 5, 9, 1, 1, 0, 0, 37, 50, 7, 10}, + {5, 7, 8, 0, 0, 0, 0, 10, 5, 9, 7, 10}}; + std::vector input1_data{20, 30, 40, -17, -4, -7, 11, -31, 10, 19, 75, 100}; + std::vector input2_data{4, 5, 10, -3, 2, 10}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } +} + +TEST_F(DivTest, SInt64) +{ + checkInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(DivTest, SInt32) +{ + checkInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(DivTest, Input_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(DivTest, Invalid_Input_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U64); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(DivTest, Invalid_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + DivParams params{}; + params.activation = Activation::RELU; + + Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Elu.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.cpp new file mode 100644 index 0000000..697d63b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Elu.h" +#include "kernels/Utils.h" + +#include "PALElu.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Elu::Elu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Elu::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void Elu::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::Elu(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Elu.h b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.h new file mode 100644 index 0000000..c844ab5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_ELU_H +#define LUCI_INTERPRETER_KERNELS_ELU_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Elu : public Kernel +{ +public: + Elu(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_ELU_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Elu.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.test.cpp new file mode 100644 index 0000000..814499c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Elu.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Elu.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Elu kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + (void)output_shape; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +TEST(EluTest, SimpleElu) +{ + Check( + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // + }, + /*output_data=*/ + { + 0.0, -0.997521, 2.0, -0.981684, // + 3.0, -0.864665, 10.0, -0.0951626, // + }); +} + +TEST(EluTest, InOutTypeMismatch_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Elu kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Equal.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.cpp new file mode 100644 index 0000000..a57e127 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Equal.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Equal::Equal(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void Equal::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void Equal::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Equal::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowEqual(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::Equal(op_params, getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), output_data); + } +} + +template void Equal::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::EqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +void Equal::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowEqualWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::EqualWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Equal.h b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.h new file mode 100644 index 0000000..c9be32c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_EQUAL_H +#define LUCI_INTERPRETER_KERNELS_EQUAL_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Equal : public Kernel +{ +public: + Equal(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_EQUAL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Equal.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.test.cpp new file mode 100644 index 0000000..5870e54 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Equal.test.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Equal.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class EqualTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(EqualTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, true, false, // Row 1 + false, true, false, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(EqualTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + 0.9, 0.7, 0.5, // Row 4 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + false, true, false, // Row 1 + false, false, false, // Row 2 + false, false, false, // Row 3 + true, true, true, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value, -2, max_value}; + + std::vector ref_output_data{true, false, true}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -2, -3, // Row 3 + min_value, -2, max_value, // Row 4 + }; + + std::vector y_data{ + min_value, -2, max_value, // Row 1 + }; + + std::vector ref_output_data{ + true, false, false, // Row 1 + false, false, true, // Row 2 + false, true, false, // Row 3 + true, true, true, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(EqualTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(EqualTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(EqualTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.5, 0.55, 0.5, // Row 1 + -1, 0, 0.05, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, true, false, false, // Row 1 + false, true, true, false, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + + std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 2); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(EqualTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + -1, 0.05, 0, 1, // Row 4 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + false, false, false, false, // Row 1 + false, false, true, false, // Row 2 + false, false, false, false, // Row 3 + true, true, true, true, // Row 4 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 4, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(EqualTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(EqualTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(EqualTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(EqualTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(EqualTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Equal kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Exp.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.cpp new file mode 100644 index 0000000..e7c560a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Exp.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Exp::Exp(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Exp::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void Exp::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Exp::evalFloat() const +{ + const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); + tflite::reference_ops::Exp(getTensorData(input()), size, getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Exp.h b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.h new file mode 100644 index 0000000..4291773 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_EXP_H +#define LUCI_INTERPRETER_KERNELS_EXP_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Exp : public Kernel +{ +public: + Exp(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_EXP_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Exp.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.test.cpp new file mode 100644 index 0000000..a159d9d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Exp.test.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Exp.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(ExpTest, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + Shape input_shape{1, 1, 7}; + std::vector input_data{0.0f, 1.0f, -1.0f, 100.0f, -100.0f, 0.01f, -0.01f}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Exp kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_shape{1, 1, 7}; + std::vector ref_output_data{std::exp(0.0f), std::exp(1.0f), std::exp(-1.0f), + std::exp(100.0f), std::exp(-100.0f), std::exp(0.01f), + std::exp(-0.01f)}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.cpp new file mode 100644 index 0000000..ba35c99 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/ExpandDims.h" +#include "kernels/Utils.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ExpandDims::ExpandDims(const Tensor *input, const Tensor *axis, Tensor *output) + : Kernel({input, axis}, {output}) +{ +} + +void ExpandDims::configure() +{ + int32_t axis_value; + + switch (axis()->element_type()) + { + case loco::DataType::S32: + axis_value = *getTensorData(axis()); + break; + case loco::DataType::S64: + axis_value = static_cast(*getTensorData(axis())); + break; + default: + throw std::runtime_error("Unsupported type."); + } + + const auto input_shape = input()->shape(); + + if (axis_value < 0) + { + axis_value += input_shape.num_dims() + 1; + } + + LUCI_INTERPRETER_CHECK(axis_value <= input_shape.num_dims() and axis_value >= 0); + + Shape output_shape(input_shape.num_dims() + 1); + for (int32_t i = 0; i < output_shape.num_dims(); ++i) + { + if (i < axis_value) + { + output_shape.dim(i) = input_shape.dim(i); + } + else if (i == axis_value) + { + output_shape.dim(i) = 1; + } + else + { + LUCI_INTERPRETER_CHECK(i >= 1); + output_shape.dim(i) = input_shape.dim(i - 1); + } + } + + output()->resize(output_shape); +} + +void ExpandDims::execute() const +{ + // Just copy input to output + const auto *input_data = input()->data(); + auto *output_data = output()->data(); + + const size_t element_size = getDataTypeSize(input()->element_type()); + const int32_t num_elements = input()->shape().num_elements(); + std::memcpy(output_data, input_data, num_elements * element_size); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.h b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.h new file mode 100644 index 0000000..e510b11 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_EXPAND_DIMS_H +#define LUCI_INTERPRETER_KERNELS_EXPAND_DIMS_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ExpandDims : public Kernel +{ +public: + ExpandDims(const Tensor *input, const Tensor *axis, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *axis() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_EXPAND_DIMS_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp new file mode 100644 index 0000000..df9eacc --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ExpandDims.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ExpandDimsTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(ExpandDimsTest, PositiveAxis) +{ + std::vector input_data{-1, 1, -2, 2}; + std::initializer_list input_shape = {2, 2}; + + std::initializer_list axis_value = {0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(input_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2})); +} + +TEST_F(ExpandDimsTest, NegAxis) +{ + std::vector input_data{-1, 1, -2, 2}; + std::initializer_list input_shape = {2, 2}; + + std::initializer_list axis_value = {-1}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(input_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 2, 1})); +} + +TEST_F(ExpandDimsTest, InvalidAxisType_NEG) +{ + std::vector input_data{-1, 1, -2, 2}; + std::initializer_list input_shape = {2, 2}; + + std::initializer_list axis_value = {1.0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ExpandDimsTest, InvalidAxisValue_NEG) +{ + std::vector input_data{-1, 1, -2, 2}; + std::initializer_list input_shape = {2, 2}; + + std::initializer_list axis_value = {3}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Fill.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.cpp new file mode 100644 index 0000000..e09d633 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Fill.h" +#include "kernels/Utils.h" +#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Fill::Fill(const Tensor *dims, const Tensor *value, Tensor *output) + : Kernel({dims, value}, {output}) +{ +} + +template void Fill::configureShape() +{ + const auto dims_data = getTensorData(dims()); + Shape output_shape(dims()->shape().dim(0)); + + for (int i = 0; i < output_shape.num_dims(); ++i) + { + T data = dims_data[i]; + if (data < 0) + throw std::runtime_error("Fill dimensions must be >= 0"); + + output_shape.dim(i) = data; + } + + output()->resize(output_shape); +} + +void Fill::configure() +{ + const auto dims_shape = dims()->shape(); + const auto value_shape = value()->shape(); + + // Make sure the 1st input tensor is 1-D + LUCI_INTERPRETER_CHECK(dims_shape.num_dims() == 1); + + // Make sure the 1st input tensor is int32 or int64 + LUCI_INTERPRETER_CHECK(dims()->element_type() == DataType::S32 or + dims()->element_type() == DataType::S64); + + // Make sure the 2nd input tensor is a scalar + LUCI_INTERPRETER_CHECK(value_shape.num_dims() == 0) + + // Check zero point and scale for S16 and S8 + if (value()->element_type() == loco::DataType::S16 or + value()->element_type() == loco::DataType::S8) + { + LUCI_INTERPRETER_CHECK(value()->scale() == output()->scale()); + LUCI_INTERPRETER_CHECK(value()->zero_point() == output()->zero_point()); + + if (value()->element_type() == loco::DataType::S16) + LUCI_INTERPRETER_CHECK(value()->zero_point() == 0); + } + // Resize output + switch (dims()->element_type()) + { + case DataType::S32: + configureShape(); + break; + case DataType::S64: + configureShape(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Fill::execute() const +{ + switch (output()->element_type()) + { + case DataType::S8: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S16: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S32: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::S64: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::FLOAT32: + tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), + getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Fill.h b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.h new file mode 100644 index 0000000..184f0cb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_FILL_H +#define LUCI_INTERPRETER_KERNELS_FILL_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Fill : public Kernel +{ +public: + Fill(const Tensor *dims, const Tensor *value, Tensor *output); + + const Tensor *dims() const { return _inputs[0]; } + const Tensor *value() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void configureShape(); +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_FILL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Fill.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.test.cpp new file mode 100644 index 0000000..cf56df5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Fill.test.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Fill.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class FillTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +template void runFillIntKernel(IMemoryManager *memory_manager) +{ + Shape dims_shape{2}; + + std::vector dims_data = {2, 3}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); + Tensor value = makeInputTensor
(/*scalar*/ {}, value_data, memory_manager); + + Tensor output_tensor = makeOutputTensor(DT); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + + std::vector ref_output_shape{2, 3}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +template void runFillQuantIntKernel(IMemoryManager *memory_manager) +{ + Shape dims_shape{2}; + + std::vector dims_data = {2, 3}; + std::vector value_data = {5}; + + int32_t zero_point = 0; + + if (DT == loco::DataType::S8) + zero_point = 1; + + Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); + Tensor value = makeInputTensor
(/*scalar*/ {}, /*scale*/ 0.25, /*zero_point*/ zero_point, + value_data, memory_manager); + + Tensor output_tensor = makeOutputTensor(DT, /*scale*/ 0.25, /*zero_point*/ zero_point); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + + std::vector ref_output_shape{2, 3}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FillTest, FillInt) +{ + // Run for int32_t input + runFillIntKernel(_memory_manager.get()); + // Run for int64_t input + runFillIntKernel(_memory_manager.get()); + // Run for int8_t input + runFillQuantIntKernel(_memory_manager.get()); + // Run for int16_t input + runFillQuantIntKernel(_memory_manager.get()); + + SUCCEED(); +} + +TEST_F(FillTest, FillFloat) +{ + Shape dims_shape{3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = + makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{5, 5, 5, 5, 5, 5, 5, 5}; + + std::vector ref_output_shape{2, 2, 2}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FillTest, Invalid_Input_Shape_NEG) +{ + Shape dims_shape{1, 3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = + makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(FillTest, Invalid_Value_Shape_NEG) +{ + Shape dims_shape{3}; + + std::vector dims_data = {2, 2, 2}; + std::vector value_data = {5}; + + Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); + Tensor value = makeInputTensor({1}, value_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + Fill kernel(&dims, &value, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Floor.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.cpp new file mode 100644 index 0000000..e3c4246 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Floor.h" +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Floor::Floor(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Floor::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void Floor::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Floor::evalFloat() const +{ + tflite::reference_ops::Floor(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Floor.h b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.h new file mode 100644 index 0000000..ca3ad59 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_H +#define LUCI_INTERPRETER_KERNELS_FLOOR_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Floor : public Kernel +{ +public: + Floor(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_FLOOR_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Floor.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.test.cpp new file mode 100644 index 0000000..30076fb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Floor.test.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Floor.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class FloorTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(FloorTest, SimpleFloat) +{ + std::initializer_list input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0.2, 8.6, 2.4, 4.3, // Row 1 + 3, 7.1, 10.5, -0.9, // Row 2 + }; + + std::initializer_list ref_output_shape{1, 2, 4, 1}; + std::vector ref_output_data{ + 0, 8, 2, 4, // Row 1 + 3, 7, 10, -1, // Row 2 + }; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Floor kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorTest, Input_Output_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Floor kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.cpp new file mode 100644 index 0000000..a7a10a3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/FloorDiv.h" +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +FloorDiv::FloorDiv(const Tensor *input, const Tensor *alpha, Tensor *output) + : Kernel({input, alpha}, {output}) +{ +} + +void FloorDiv::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(y()->element_type() == output()->element_type()); + + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void FloorDiv::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void FloorDiv::evalFloat() const +{ + auto FloorDivFunc = [](float x, float y) -> float { + return std::floor(static_cast(x) / static_cast(y)); + }; + + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + + // Check the denominator + for (int i = 0; i < getTensorShape(y()).FlatSize(); ++i) + { + LUCI_INTERPRETER_CHECK(y_data[i] != 0); + } + + if (x()->shape() != y()->shape()) + { + tflite::reference_ops::BroadcastBinaryFunction4DSlow( + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } + else + { + tflite::reference_ops::BinaryFunction( + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.h b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.h new file mode 100644 index 0000000..e9c47d8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H +#define LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class FloorDiv : public Kernel +{ +public: + FloorDiv(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.test.cpp new file mode 100644 index 0000000..3e1b5f1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FloorDiv.test.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/FloorDiv.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class FloorDivTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(FloorDivTest, FloatSimple) +{ + Shape x_shape{2, 3}; + std::vector x_data{ + 0.5, 2.4, 3.1, // Row 1 + 1.9, -1.9, -2.8, // Row 2 + }; + + Shape y_shape = x_shape; + std::vector y_data{ + 2.0, 0.5, 3.0, // Row 1 + 1.0, -1.0, -2.0, // Row 2 + }; + + std::vector ref_output_shape{2, 3}; + std::vector ref_output_data{ + 0, 4, 1, // Row 1 + 1, 1, 1, // Row 2 + }; + + Tensor x_tensor = makeInputTensor(x_shape, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor(y_shape, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorDivTest, FloatBroadcast) +{ + Shape x_shape{1, 3}; + std::vector x_data{ + 0.5, 2.4, -3.1, // Row 1 + }; + + Shape y_shape{3, 3}; + std::vector y_data{ + 1.0, 1.0, 1.0, // Row 1 + 2.0, -0.5, -2.0, // Row 2 + 0.3, 0.7, 0.9, // Row 3 + }; + + std::vector ref_output_shape{3, 3}; + std::vector ref_output_data{ + 0, 2, -4, // Row 1 + 0, -5, 1, // Row 2 + 1, 3, -4, // Row 3 + }; + + Tensor x_tensor = makeInputTensor(x_shape, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor(y_shape, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorDivTest, DivByZero_NEG) +{ + Shape shape{3}; + std::vector x_data{1, 0, -1}; + std::vector y_data{0, 0, 0}; + + Tensor x_tensor = makeInputTensor(shape, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor(shape, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(FloorDivTest, Input_Output_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorDivTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.cpp new file mode 100644 index 0000000..bd2bb2f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/FullyConnected.h" + +#include "kernels/Utils.h" + +#include "PALFullyConnected.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +FullyConnected::FullyConnected(const Tensor *input, const Tensor *weights, const Tensor *bias, + Tensor *output, const FullyConnectedParams ¶ms) + : KernelWithParams({input, weights, bias}, {output}, params) +{ +} + +void FullyConnected::configure() +{ + if (weights()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::U8); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::U8); + LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::S32) + } + else if (weights()->element_type() == DataType::FLOAT32) + { + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::FLOAT32) + } + else if (weights()->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::S8); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::S8); + LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::S32) + } + else + { + throw std::runtime_error("Unsupported type."); + } + + const Shape &input_shape = input()->shape(); + const Shape &weights_shape = weights()->shape(); + + LUCI_INTERPRETER_CHECK(weights_shape.num_dims() == 2); + LUCI_INTERPRETER_CHECK(bias() == nullptr || + bias()->shape().num_elements() == weights_shape.dim(0)); + + LUCI_INTERPRETER_CHECK(input_shape.num_elements() % weights_shape.dim(1) == 0); + const int32_t batch_size = input_shape.num_elements() / weights_shape.dim(1); + const int32_t num_units = weights_shape.dim(0); + + if (bias()) + LUCI_INTERPRETER_CHECK(bias()->shape().num_elements() == weights()->shape().dim(0)); + + if (params().keep_num_dims == false) + { + output()->resize({batch_size, num_units}); + } + else + { + luci_interpreter::Shape output_shape(input_shape.num_dims()); + for (int i = 0; i < input_shape.num_dims(); ++i) + output_shape.dim(i) = input_shape.dim(i); + output_shape.dim(input_shape.num_dims() - 1) = num_units; + output()->resize(output_shape); + } +} + +void FullyConnected::execute() const +{ + switch (input()->element_type()) + { + case DataType::U8: + evalQuantized(); + break; + case DataType::S8: + evalQuantizedS8(); + break; + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void FullyConnected::evalFloat() const +{ + float activation_min{}; + float activation_max{}; + calculateActivationRange(_params.activation, &activation_min, &activation_max); + + tflite::FullyConnectedParams params{}; + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + params.weights_format = tflite::FullyConnectedWeightsFormat::kDefault; + + tflite::reference_ops::FullyConnected( + params, getTensorShape(input()), getTensorData(input()), getTensorShape(weights()), + getTensorData(weights()), getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output())); +} + +void FullyConnected::evalQuantized() const +{ + double real_multiplier = 0.0; + int output_shift; + int32_t output_activation_min; + int32_t output_activation_max; + int32_t output_multiplier; + real_multiplier = + getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale()); + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + calculateActivationRangeQuantized(params().activation, output(), &output_activation_min, + &output_activation_max); + + int32_t input_offset = -input()->zero_point(); + int32_t filter_offset = -weights()->zero_point(); + int32_t output_offset = output()->zero_point(); + + tflite::FullyConnectedParams op_params{}; + op_params.input_offset = input_offset; + op_params.weights_offset = filter_offset; + op_params.output_offset = output_offset; + op_params.output_multiplier = output_multiplier; + op_params.output_shift = output_shift; + op_params.quantized_activation_min = output_activation_min; + op_params.quantized_activation_max = output_activation_max; + op_params.lhs_cacheable = false; + op_params.rhs_cacheable = false; + tflite::reference_ops::FullyConnected( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(weights()), + getTensorData(weights()), getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output())); +} + +void FullyConnected::evalQuantizedS8() const +{ + double real_multiplier = 0.0; + int output_shift; + int32_t output_activation_min; + int32_t output_activation_max; + int32_t output_multiplier; + real_multiplier = + getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale()); + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + calculateActivationRangeQuantized(params().activation, output(), &output_activation_min, + &output_activation_max); + + int32_t input_offset = -input()->zero_point(); + int32_t filter_offset = -weights()->zero_point(); + int32_t output_offset = output()->zero_point(); + + tflite::FullyConnectedParams op_params{}; + op_params.input_offset = input_offset; + op_params.weights_offset = filter_offset; + op_params.output_offset = output_offset; + op_params.output_multiplier = output_multiplier; + op_params.output_shift = output_shift; + op_params.quantized_activation_min = output_activation_min; + op_params.quantized_activation_max = output_activation_max; + op_params.lhs_cacheable = false; + op_params.rhs_cacheable = false; + luci_interpreter_pal::FullyConnected( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(weights()), + getTensorData(weights()), getTensorShape(bias()), getTensorData(bias()), + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.h b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.h new file mode 100644 index 0000000..2a7c068 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H +#define LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class FullyConnected : public KernelWithParams +{ +public: + FullyConnected(const Tensor *input, const Tensor *weights, const Tensor *bias, Tensor *output, + const FullyConnectedParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *weights() const { return _inputs[1]; } + const Tensor *bias() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedS8() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp new file mode 100644 index 0000000..4474cc4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/FullyConnected.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list weights_shape, + std::initializer_list bias_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list weights_data, + std::initializer_list bias_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, weights_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list weights_shape, + std::initializer_list bias_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list weights_data, + std::initializer_list bias_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + const float quantized_tolerance = getTolerance(-127, 128, 255); + std::pair input_quant_param = quantizationParams(-63.5, 64); + std::pair output_quant_param = quantizationParams(-127, 128); + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, input_quant_param.first, input_quant_param.second, + weights_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, input_quant_param.first * input_quant_param.first, 0, + bias_data, memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, quantized_tolerance)); +} + +template <> +void Check( + std::initializer_list input_shape, std::initializer_list weights_shape, + std::initializer_list bias_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list weights_data, + std::initializer_list bias_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + const float quantized_tolerance = getTolerance(-127, 128, 255); + std::pair input_quant_param = quantizationParams(-63.5, 64); + std::pair output_quant_param = quantizationParams(-127, 128); + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, input_quant_param.first, input_quant_param.second, + weights_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, input_quant_param.first * input_quant_param.first, 0, + bias_data, memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, quantized_tolerance)); +} + +template class FullyConnectedTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(FullyConnectedTest, DataTypes); + +TYPED_TEST(FullyConnectedTest, Simple) +{ + Check({3, 2, 2, 1}, {3, 6}, {3}, {2, 3}, + { + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 + }, + { + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 + }, + {-1, -5, -8}, + { + 0, 0, 32, // batch = 0 + 22, 11, 47, // batch = 1 + }); +} + +TEST(FullyConnectedTest, InvalidBiasType_NEG) +{ + Shape input_shape{3, 2, 2, 1}; + std::vector input_data{ + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 + }; + Shape weights_shape{3, 6}; + std::vector weights_data{ + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 + }; + Shape bias_shape{3}; + std::vector bias_data{-1, -5, -8}; + + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, weights_data, memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(FullyConnectedTest, InvalidWeightShapeDim_NEG) +{ + Shape input_shape{3, 2, 2, 1}; + std::vector input_data{ + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 + }; + Shape weights_shape{1, 3, 6}; + std::vector weights_data{ + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 + }; + Shape bias_shape{3}; + std::vector bias_data{-1, -5, -8}; + + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, weights_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(FullyConnectedTest, BiasElementNumWeightDimMismatch_NEG) +{ + Shape input_shape{3, 2, 2, 1}; + std::vector input_data{ + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 + }; + Shape weights_shape{6, 3}; + std::vector weights_data{ + -3, -7, 4, // unit = 0 + -4, -6, 4, // unit = 1 + 3, 5, 2, // unit = 2 + 3, -3, -8, // unit = 3 + -3, 7, 4, // unit = 4 + 9, 0, -5, // unit = 5 + }; + Shape bias_shape{3}; + std::vector bias_data{-1, -5, -8}; + + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor weights_tensor = + makeInputTensor(weights_shape, weights_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor(bias_shape, bias_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FullyConnectedParams params{}; + params.activation = Activation::RELU; + + FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Gather.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.cpp new file mode 100644 index 0000000..f125666 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2021 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Gather.h" +#include "kernels/Utils.h" +#include "PALGather.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Gather::Gather(const Tensor *params, const Tensor *indices, Tensor *output, + const GatherParams &gparams) + : KernelWithParams({params, indices}, {output}, gparams) +{ +} + +void Gather::configure() +{ + if (params()->element_type() == DataType::FLOAT32) + { + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32); + } + else + { + throw std::runtime_error("Unsupported type."); + } + + LUCI_INTERPRETER_CHECK(indices()->element_type() == DataType::S32 || + indices()->element_type() == DataType::S64); + + // refer tensorflow/lite/kernels/gather.cc + + const Shape ¶ms_shape = params()->shape(); + const Shape &indices_shape = indices()->shape(); + + int axis = _params.axis; + if (axis < 0) + { + axis += params_shape.num_dims(); + } + LUCI_INTERPRETER_CHECK(0 <= axis && axis < params_shape.num_dims()); + + int batch_dims = _params.batch_dims; + // batch_dims should be in range: [-rank(indices), rank(indices)]. + // Negative batch_dims is added with rank of positions. + if (batch_dims < 0) + { + batch_dims += indices_shape.num_dims(); + } + LUCI_INTERPRETER_CHECK(batch_dims <= axis); + LUCI_INTERPRETER_CHECK(0 <= batch_dims && batch_dims < params_shape.num_dims()); + LUCI_INTERPRETER_CHECK(batch_dims <= indices_shape.num_dims()); + for (int i = 0; i < batch_dims; ++i) + { + LUCI_INTERPRETER_CHECK(params_shape.dim(i) == indices_shape.dim(i)); + } + + const int num_dimensions = params_shape.num_dims() + indices_shape.num_dims() - 1 - batch_dims; + + Shape output_shape(num_dimensions); + int output_index = 0; + for (int i = 0; i < axis; ++i) + { + output_shape.dim(output_index++) = params_shape.dim(i); + } + for (int i = batch_dims; i < indices_shape.num_dims(); ++i) + { + output_shape.dim(output_index++) = indices_shape.dim(i); + } + for (int i = axis + 1; i < params_shape.num_dims(); ++i) + { + output_shape.dim(output_index++) = params_shape.dim(i); + } + output()->resize(output_shape); +} + +void Gather::execute() const +{ + switch (params()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Gather::evalFloat() const +{ + assert(indices()->element_type() == DataType::S32 || indices()->element_type() == DataType::S64); + + const auto params_data = getTensorData(params()); + auto output_data = getTensorData(output()); + + tflite::GatherParams tparams; + tparams.axis = _params.axis; + tparams.batch_dims = _params.batch_dims; + + if (indices()->element_type() == DataType::S32) + { + const auto indices_data = getTensorData(indices()); + + luci_interpreter_pal::Gather(tparams, getTensorShape(params()), params_data, + getTensorShape(indices()), indices_data, + getTensorShape(output()), output_data); + } + else + { + const auto indices_data = getTensorData(indices()); + + luci_interpreter_pal::Gather(tparams, getTensorShape(params()), params_data, + getTensorShape(indices()), indices_data, + getTensorShape(output()), output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Gather.h b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.h new file mode 100644 index 0000000..cc02d64 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_GATHER_H +#define LUCI_INTERPRETER_KERNELS_GATHER_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Gather : public KernelWithParams +{ +public: + Gather(const Tensor *params, const Tensor *indices, Tensor *output, const GatherParams &gparams); + + const Tensor *params() const { return _inputs[0]; } + const Tensor *indices() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_GATHER_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Gather.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.test.cpp new file mode 100644 index 0000000..4b3dda7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Gather.test.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Gather.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class GatherTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(GatherTest, Simple) +{ + std::vector params_data{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; + std::vector indices_data{1, 0, 1, 5}; + std::vector ref_output_data{2.f, 1.f, 2.f, 6.f}; + + Tensor params_tensor = + makeInputTensor({1, 6}, params_data, _memory_manager.get()); + Tensor indices_tensor = makeInputTensor({4}, indices_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + GatherParams gparams; + + gparams.axis = 1; + gparams.batch_dims = 0; + + Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4})); +} + +TEST_F(GatherTest, Simple_Batch) +{ + Shape params_shape = {3, 5}; + Shape indices_shape = {3, 2}; + std::vector params_data{0., 0., 1., 0., 2., 3., 0., 0., 0., 4., 0., 5., 0., 6., 0.}; + std::vector indices_data{2, 4, 0, 4, 1, 3}; + std::vector ref_output_data{1., 2., 3., 4., 5., 6.}; + + Tensor params_tensor = + makeInputTensor(params_shape, params_data, _memory_manager.get()); + Tensor indices_tensor = + makeInputTensor(indices_shape, indices_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + GatherParams gparams; + + gparams.axis = 1; + gparams.batch_dims = 1; + + Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 2})); +} + +TEST_F(GatherTest, Simple_NEG) +{ + Tensor params_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor indices_tensor = makeInputTensor({1}, {0}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + GatherParams gparams; + + Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GatherTest, Axis_NEG) +{ + Tensor params_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor indices_tensor = makeInputTensor({1}, {0}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + GatherParams gparams; + + gparams.axis = 100; + gparams.batch_dims = 0; + + Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GatherTest, Batch_NEG) +{ + std::vector params_data{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; + std::vector indices_data{1, 0, 1, 5}; + std::vector ref_output_data{2.f, 1.f, 2.f, 6.f}; + + Tensor params_tensor = + makeInputTensor({1, 6}, params_data, _memory_manager.get()); + Tensor indices_tensor = makeInputTensor({4}, indices_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + GatherParams gparams; + + gparams.axis = 0; + gparams.batch_dims = 1; + + Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Greater.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.cpp new file mode 100644 index 0000000..5ccae3c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Greater.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Greater::Greater(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void Greater::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void Greater::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Greater::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreater(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::Greater(op_params, getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), output_data); + } +} + +template void Greater::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreaterNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::GreaterNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +void Greater::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreaterWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::GreaterWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Greater.h b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.h new file mode 100644 index 0000000..065f76d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_GREATER_H +#define LUCI_INTERPRETER_KERNELS_GREATER_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Greater : public Kernel +{ +public: + Greater(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_GREATER_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Greater.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.test.cpp new file mode 100644 index 0000000..a480801 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Greater.test.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Greater.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class GreaterTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(GreaterTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, false, true, // Row 1 + true, false, false, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(GreaterTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + false, false, true, // Row 1 + true, false, false, // Row 2 + false, false, true, // Row 3 + }; + + Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); +} + +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value + 1, -2, max_value}; + + std::vector ref_output_data{false, true, false}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -4, -3, // Row 3 + min_value, -2, max_value, // Row 4 + }; + + std::vector y_data{ + min_value + 1, -2, max_value - 1, // Row 1 + }; + + std::vector ref_output_data{ + false, true, false, // Row 1 + true, true, true, // Row 2 + true, false, false, // Row 3 + false, false, true, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(GreaterTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(GreaterTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(GreaterTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, false, true, true, // Row 1 + true, false, true, false, // Row 2 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterTest, Uint8QuantizedRescale) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, false, true, true, // Row 1 + true, false, true, false, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 3); + + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + true, false, true, false, // Row 1 + true, true, false, false, // Row 2 + true, false, true, false, // Row 3 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Greater kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.cpp new file mode 100644 index 0000000..27e42c9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/GreaterEqual.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +GreaterEqual::GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output) + : Kernel({x, y}, {output}) +{ +} + +void GreaterEqual::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void GreaterEqual::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void GreaterEqual::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreaterEqual(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::GreaterEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), output_data); + } +} + +template void GreaterEqual::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreaterEqualNoScaling( + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } + else + { + tflite::reference_ops::GreaterEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } +} + +void GreaterEqual::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowGreaterEqualWithScaling( + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } + else + { + tflite::reference_ops::GreaterEqualWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.h b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.h new file mode 100644 index 0000000..e333c30 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H +#define LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class GreaterEqual : public Kernel +{ +public: + GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp new file mode 100644 index 0000000..35bf88e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/GreaterEqual.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class GreaterEqualTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(GreaterEqualTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, true, true, // Row 1 + true, true, false, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(GreaterEqualTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + false, true, true, // Row 1 + true, false, false, // Row 2 + false, false, true, // Row 3 + }; + + Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); +} +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value + 1, -2, max_value}; + + std::vector ref_output_data{false, true, true}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -4, -3, // Row 3 + min_value, -2, max_value - 1, // Row 4 + }; + + std::vector y_data{ + min_value + 1, -2, max_value - 1, // Row 1 + }; + + std::vector ref_output_data{ + false, true, false, // Row 1 + true, true, true, // Row 2 + true, false, false, // Row 3 + false, true, true, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(GreaterEqualTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(GreaterEqualTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(GreaterEqualTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, true, true, true, // Row 1 + true, false, true, false, // Row 2 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterEqualTest, Uint8QuantizedRescale) +{ + std::vector x_data{ + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.5, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + false, true, true, true, // Row 1 + true, false, true, false, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); + + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterEqualTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + true, false, true, false, // Row 1 + true, true, true, false, // Row 2 + true, false, true, false, // Row 3 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(GreaterEqualTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterEqualTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterEqualTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterEqualTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(GreaterEqualTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/If.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/If.cpp new file mode 100644 index 0000000..971708b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/If.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/If.h" +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +static std::vector joinInputs(const Tensor *cond, + const std::vector &inputs) +{ + std::vector result{cond}; + result.insert(result.cend(), inputs.cbegin(), inputs.cend()); + return result; +} + +If::If(const Tensor *cond, const std::vector &inputs, std::vector outputs, + RuntimeGraph *then_graph, RuntimeGraph *else_graph) + : Kernel(joinInputs(cond, inputs), std::move(outputs)), _then_graph(then_graph), + _else_graph(else_graph) +{ +} + +void If::configure() +{ + LUCI_INTERPRETER_CHECK(cond()->element_type() == DataType::BOOL); + LUCI_INTERPRETER_CHECK(cond()->shape().num_elements() == 1); + + for (RuntimeGraph *graph : {_then_graph, _else_graph}) + { + (void)graph; + LUCI_INTERPRETER_CHECK(graph->getInputTensors().size() == getInputTensors().size() - 1); + LUCI_INTERPRETER_CHECK(graph->getOutputTensors().size() == getOutputTensors().size()); + } +} + +void If::execute() const +{ + const bool cond_value = cond()->data()[0]; + + RuntimeGraph *active_graph = cond_value ? _then_graph : _else_graph; + const auto &graph_inputs = active_graph->getInputTensors(); + const auto &graph_outputs = active_graph->getOutputTensors(); + + // Copy kernel inputs to active graph inputs. + for (size_t i = 0; i < getInputTensors().size() - 1; ++i) + { + LUCI_INTERPRETER_CHECK(graph_inputs[i]->element_type() == input(i)->element_type()); + graph_inputs[i]->resize(input(i)->shape()); + + const int32_t num_elements = input(i)->shape().num_elements(); + const std::size_t element_size = getDataTypeSize(input(i)->element_type()); + // TODO: Think about how allocate memory for output in main graph + active_graph->configureAllocations(graph_inputs[i]); + std::memcpy(graph_inputs[i]->data(), input(i)->data(), num_elements * element_size); + } + + active_graph->execute(); + + // Copy graph outputs to kernel outputs. + for (size_t i = 0; i < getOutputTensors().size(); ++i) + { + LUCI_INTERPRETER_CHECK(graph_outputs[i]->element_type() == output(i)->element_type()); + output(i)->resize(graph_outputs[i]->shape()); + // TODO: Think about how allocate memory for output in main graph + active_graph->configureAllocations(output(i)); + + const int32_t num_elements = output(i)->shape().num_elements(); + const std::size_t element_size = getDataTypeSize(output(i)->element_type()); + std::memcpy(output(i)->data(), graph_outputs[i]->data(), + num_elements * element_size); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/If.h b/compiler/luci-micro/luci-interpreter/src/kernels/If.h new file mode 100644 index 0000000..fa6ab37 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/If.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_IF_H +#define LUCI_INTERPRETER_KERNELS_IF_H + +#include "core/Kernel.h" +#include "core/RuntimeGraph.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class If : public Kernel +{ +public: + If(const Tensor *cond, const std::vector &inputs, std::vector outputs, + RuntimeGraph *then_graph, RuntimeGraph *else_graph); + + const Tensor *cond() const { return _inputs[0]; } + const Tensor *input(int index) const { return _inputs[1 + index]; } + Tensor *output(int index) const { return _outputs[index]; } + + void configure() override; + void execute() const override; + +private: + RuntimeGraph *const _then_graph; + RuntimeGraph *const _else_graph; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_IF_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/If.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/If.test.cpp new file mode 100644 index 0000000..c5f4faf --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/If.test.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "core/RuntimeModule.h" +#include "kernels/Add.h" +#include "kernels/If.h" +#include "kernels/Mul.h" +#include "kernels/TestUtils.h" + +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class IfTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +RuntimeGraph *buildAddSubgraph(RuntimeModule *module, IMemoryManager *memory_manager) +{ + RuntimeGraph *graph = module->addGraph(memory_manager); + Tensor *input1 = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + Tensor *input2 = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + Tensor *output = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + + memory_manager->allocate_memory(*input1); + memory_manager->allocate_memory(*input2); + memory_manager->allocate_memory(*output); + + graph->setInputTensors({input1, input2}); + graph->setOutputTensors({output}); + + AddParams params{}; + params.activation = Activation::NONE; + graph->addKernel(std::make_unique(input1, input2, output, params)); + + return graph; +} + +RuntimeGraph *buildMulSubgraph(RuntimeModule *module, IMemoryManager *memory_manager) +{ + RuntimeGraph *graph = module->addGraph(memory_manager); + Tensor *input1 = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + Tensor *input2 = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + Tensor *output = graph->addTensor( + std::make_unique(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + + memory_manager->allocate_memory(*input1); + memory_manager->allocate_memory(*input2); + memory_manager->allocate_memory(*output); + + graph->setInputTensors({input1, input2}); + graph->setOutputTensors({output}); + + MulParams params{}; + params.activation = Activation::NONE; + graph->addKernel(std::make_unique(input1, input2, output, params)); + + return graph; +} + +TEST_F(IfTest, CondTrue) +{ + Tensor cond = makeInputTensor({1}, {true}, _memory_manager.get()); + Tensor input1 = makeInputTensor({2}, {5, 7}, _memory_manager.get()); + Tensor input2 = makeInputTensor({1, 2}, {1, 2}, _memory_manager.get()); + Tensor output = makeOutputTensor(DataType::FLOAT32); + + RuntimeModule module(nullptr); + RuntimeGraph *then_graph = buildAddSubgraph(&module, _memory_manager.get()); + RuntimeGraph *else_graph = buildMulSubgraph(&module, _memory_manager.get()); + + If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph); + kernel.configure(); + _memory_manager->allocate_memory(output); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output), FloatArrayNear({6, 9})); +} + +TEST_F(IfTest, CondFalse) +{ + Tensor cond = makeInputTensor({1}, {false}, _memory_manager.get()); + Tensor input1 = makeInputTensor({2}, {5, 7}, _memory_manager.get()); + Tensor input2 = makeInputTensor({1, 2}, {1, 2}, _memory_manager.get()); + Tensor output = makeOutputTensor(DataType::FLOAT32); + + RuntimeModule module(nullptr); + RuntimeGraph *then_graph = buildAddSubgraph(&module, _memory_manager.get()); + RuntimeGraph *else_graph = buildMulSubgraph(&module, _memory_manager.get()); + + If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph); + kernel.configure(); + _memory_manager->allocate_memory(output); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output), FloatArrayNear({5, 14})); +} + +TEST_F(IfTest, InvalidCondType_NEG) +{ + Tensor cond = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input1 = makeInputTensor({2}, {5, 7}, _memory_manager.get()); + Tensor input2 = makeInputTensor({1, 2}, {1, 2}, _memory_manager.get()); + Tensor output = makeOutputTensor(DataType::FLOAT32); + + RuntimeModule module(nullptr); + RuntimeGraph *then_graph = buildAddSubgraph(&module, _memory_manager.get()); + RuntimeGraph *else_graph = buildMulSubgraph(&module, _memory_manager.get()); + + If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(IfTest, InvalidCondElementNum_NEG) +{ + Tensor cond = makeInputTensor({2}, {false, true}, _memory_manager.get()); + Tensor input1 = makeInputTensor({2}, {5, 7}, _memory_manager.get()); + Tensor input2 = makeInputTensor({1, 2}, {1, 2}, _memory_manager.get()); + Tensor output = makeOutputTensor(DataType::FLOAT32); + + RuntimeModule module(nullptr); + RuntimeGraph *then_graph = buildAddSubgraph(&module, _memory_manager.get()); + RuntimeGraph *else_graph = buildMulSubgraph(&module, _memory_manager.get()); + + If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.cpp new file mode 100644 index 0000000..22a329b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/InstanceNorm.h" + +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +InstanceNorm::InstanceNorm(const Tensor *input, const Tensor *gamma, const Tensor *beta, + Tensor *output, const InstanceNormParams ¶ms) + : KernelWithParams({input, gamma, beta}, {output}, params) +{ +} + +void InstanceNorm::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(gamma()->element_type() == input()->element_type()); + LUCI_INTERPRETER_CHECK(gamma()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(gamma()->shape().dim(0) == input()->shape().dim(3) || + gamma()->shape().dim(0) == 1); + LUCI_INTERPRETER_CHECK(beta()->element_type() == input()->element_type()); + LUCI_INTERPRETER_CHECK(beta()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(beta()->shape().dim(0) == input()->shape().dim(3) || + beta()->shape().dim(0) == 1); + output()->resize(input()->shape()); +} + +void InstanceNorm::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void InstanceNorm::evalFloat() const +{ + float activation_min, activation_max; + calculateActivationRange(params().activation, &activation_min, &activation_max); + auto input_shape = getTensorShape(input()); + auto output_shape = getTensorShape(output()); + const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); + const int32_t heights = tflite::MatchingDim(input_shape, 1, output_shape, 1); + const int32_t widths = tflite::MatchingDim(input_shape, 2, output_shape, 2); + const int32_t channels = tflite::MatchingDim(input_shape, 3, output_shape, 3); + const float *input_data = getTensorData(input()); + const float *gamma_data = getTensorData(gamma()); + auto gamma_shape = getTensorShape(gamma()); + bool single_gamma = gamma_shape.DimensionsCount() == 1 && gamma_shape.Dims(0) == 1; + const float *beta_data = getTensorData(beta()); + auto beta_shape = getTensorShape(beta()); + bool single_beta = beta_shape.DimensionsCount() == 1 && beta_shape.Dims(0) == 1; + float *output_data = getTensorData(output()); + for (int32_t batch = 0; batch < batches; batch++) + { + for (int32_t channel = 0; channel < channels; channel++) + { + double sum = 0.0f; + double square_sum = 0.0f; + int32_t size = heights * widths; + for (int32_t height = 0; height < heights; height++) + { + for (int32_t width = 0; width < widths; width++) + { + double input_val = input_data[tflite::Offset(input_shape, batch, height, width, channel)]; + sum += input_val; + square_sum += (input_val * input_val); + } + } + double mean = sum / size; + double var = square_sum / size - mean * mean; + + double gamma = single_gamma ? gamma_data[0] : gamma_data[channel]; + double beta = single_beta ? beta_data[0] : beta_data[channel]; + double a = gamma / (std::sqrt(var + params().epsilon)); + double b = -mean * a + beta; + + for (int32_t height = 0; height < heights; height++) + { + for (int32_t width = 0; width < widths; width++) + { + double input_value = + input_data[tflite::Offset(output_shape, batch, height, width, channel)]; + double output_value = input_value * a + b; + output_data[tflite::Offset(output_shape, batch, height, width, channel)] = + tflite::ActivationFunctionWithMinMax((float)output_value, activation_min, + activation_max); + } + } + } + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.h b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.h new file mode 100644 index 0000000..a70a84e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_INSTANCENORM_H +#define LUCI_INTERPRETER_KERNELS_INSTANCENORM_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class InstanceNorm : public KernelWithParams +{ +public: + InstanceNorm(const Tensor *input, const Tensor *gamma, const Tensor *beta, Tensor *output, + const InstanceNormParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *gamma() const { return _inputs[1]; } + const Tensor *beta() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_INSTANCENORM_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp new file mode 100644 index 0000000..04400c3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/InstanceNorm.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class InstanceNormTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(InstanceNormTest, Simple) +{ + Tensor input_tensor = + makeInputTensor({1, 2, 2, 1}, {1, 1, 1, 1}, _memory_manager.get()); + Tensor gamma_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor beta_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + InstanceNormParams params{}; + params.epsilon = 0.1f; + params.activation = Activation::NONE; + + InstanceNorm kernel(&input_tensor, &gamma_tensor, &beta_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear({2, 2, 2, 2})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1})); +} + +TEST_F(InstanceNormTest, Single_gamma_beta) +{ + Tensor input_tensor = + makeInputTensor({1, 2, 1, 2}, {1, 1, 1, 1}, _memory_manager.get()); + Tensor gamma_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor beta_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + InstanceNormParams params{}; + params.epsilon = 0.1f; + params.activation = Activation::NONE; + + InstanceNorm kernel(&input_tensor, &gamma_tensor, &beta_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear({2, 2, 2, 2})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 2})); +} + +TEST_F(InstanceNormTest, Wrong_gamma_beta_dim_NEG) +{ + Tensor input_tensor = + makeInputTensor({1, 2, 1, 2}, {1, 1, 1, 1}, _memory_manager.get()); + Tensor gamma_tensor = makeInputTensor({3}, {1, 1, 1}, _memory_manager.get()); + Tensor beta_tensor = makeInputTensor({3}, {2, 2, 2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + InstanceNormParams params{}; + params.epsilon = 0.1f; + params.activation = Activation::NONE; + + InstanceNorm kernel(&input_tensor, &gamma_tensor, &beta_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.cpp new file mode 100644 index 0000000..6422295 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/L2Normalize.h" +#include "kernels/Utils.h" + +#include "PALL2Normalize.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +L2Normalize::L2Normalize(const Tensor *input, Tensor *output, const L2NormParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void L2Normalize::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= 4); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 || + output()->element_type() == DataType::U8); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (output()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(output()->scale() == (1. / 128.)); + LUCI_INTERPRETER_CHECK(output()->zero_point() == 128); + } + LUCI_INTERPRETER_CHECK(params().activation == Activation::NONE); + output()->resize(input()->shape()); +} + +void L2Normalize::execute() const +{ + switch (output()->element_type()) + { + case DataType::FLOAT32: + eval(0); + break; + case DataType::U8: + eval(input()->zero_point()); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void L2Normalize::eval(int32_t zero_point) const +{ + tflite::L2NormalizationParams op_params{}; + op_params.input_zero_point = zero_point; + luci_interpreter_pal::L2Normalization(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.h b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.h new file mode 100644 index 0000000..6c7dac6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H +#define LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class L2Normalize : public KernelWithParams +{ +public: + L2Normalize(const Tensor *input, Tensor *output, const L2NormParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void eval(int32_t zero_point) const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.test.cpp new file mode 100644 index 0000000..6f960e8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Normalize.test.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/L2Normalize.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + L2NormParams params{}; + params.activation = Activation::NONE; + + L2Normalize kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::pair quant_param = + quantizationParams(std::min(input_data) < 0 ? std::min(input_data) : 0.f, + std::max(input_data) > 0 ? std::max(input_data) : 0.f); + + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 128., 128); + + L2NormParams params{}; + params.activation = Activation::NONE; + + L2Normalize kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template class L2NormalizeTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(L2NormalizeTest, DataTypes); + +TYPED_TEST(L2NormalizeTest, Simple) +{ + Check({1, 1, 1, 6}, {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, + {-0.55, 0.3, 0.35, 0.6, -0.35, 0.05}); +} + +TEST(L2NormalizeTest, ActivationType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}; + + Tensor input_tensor = + makeInputTensor({1, 1, 1, 6}, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + L2NormParams params{}; + params.activation = Activation::RELU6; + + L2Normalize kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(L2NormalizeTest, InvalidOutputQuantParam_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}; + + Tensor input_tensor = + makeInputTensor({1, 1, 1, 6}, 1. / 64., 127, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 64., 127); + + L2NormParams params{}; + params.activation = Activation::NONE; + + L2Normalize kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.cpp new file mode 100644 index 0000000..5a88808 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/L2Pool2D.h" + +#include "kernels/Utils.h" + +#include "PALL2Pool2D.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +L2Pool2D::L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void L2Pool2D::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + int batches = input()->shape().dim(0); + int height = input()->shape().dim(1); + int width = input()->shape().dim(2); + int channels_out = input()->shape().dim(3); + + // Matching GetWindowedOutputSize in TensorFlow. + auto padding = params().padding; + int out_width, out_height; + out_width = computeOutputSize(padding, width, params().filter_width, params().stride_width, 1); + out_height = + computeOutputSize(padding, height, params().filter_height, params().stride_height, 1); + _padding_width = + computePadding(params().stride_width, 1, width, params().filter_width, out_width); + _padding_height = + computePadding(params().stride_height, 1, height, params().filter_height, out_height); + + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32); + output()->resize({batches, out_height, out_width, channels_out}); +} + +void L2Pool2D::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + float activation_min, activation_max; + calculateActivationRange(params().activation, &activation_min, &activation_max); + tflite::PoolParams op_params; + op_params.stride_height = params().stride_height; + op_params.stride_width = params().stride_width; + op_params.filter_height = params().filter_height; + op_params.filter_width = params().filter_width; + op_params.padding_values.height = _padding_height; + op_params.padding_values.width = _padding_width; + op_params.float_activation_min = activation_min; + op_params.float_activation_max = activation_max; + luci_interpreter_pal::L2Pool(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.h b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.h new file mode 100644 index 0000000..d40f5f4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_L2POOL2D_H +#define LUCI_INTERPRETER_KERNELS_L2POOL2D_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class L2Pool2D : public KernelWithParams +{ +public: + L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + int32_t _padding_height = 0; + int32_t _padding_width = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_L2POOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp new file mode 100644 index 0000000..7245456 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/L2Pool2D.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class L2Pool2DTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(L2Pool2DTest, FloatNone) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{3.5, 6.5}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatRelu) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + -1, -6, 2, 4, // + -3, -2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::RELU; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{3.53553, 6.5}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatRelu1) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + -0.1, -0.6, 2, 4, // + -0.3, -0.2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::RELU_N1_TO_1; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.353553, 1.0}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatRelu6) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + -0.1, -0.6, 2, 4, // + -0.3, -0.2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::RELU6; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.353553, 6.0}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatPaddingSame) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::SAME; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{3.5, 6.5}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatPaddingSameStride) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::SAME; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 1; + params.stride_width = 1; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{3.5, 6.0, 6.5, 5.70088, 2.54951, 7.2111, 8.63134, 7.0}; + // NOTE with NEON+ruy, error is #1=-1.14441e-05, #6=-1.81198e-05 + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, 1.0e-4f)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, FloatPaddingValidStride) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 1; + params.stride_width = 1; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{3.5, 6.0, 6.5}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + // TODO make a Shape checking of output_tensor. +} + +TEST_F(L2Pool2DTest, InvalidInputShape_NEG) +{ + Shape input_shape{1, 2, 4}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 1; + params.stride_width = 1; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(L2Pool2DTest, InvalidInputOutputType_NEG) +{ + Shape input_shape{1, 2, 4}; + std::vector input_data{ + 0, 6, 2, 4, // + 3, 2, 10, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.activation = Activation::NONE; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 1; + params.stride_width = 1; + + L2Pool2D kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.cpp new file mode 100644 index 0000000..3833a55 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LeakyRelu.h" + +#include "kernels/Utils.h" + +#include + +#include "PALLeakyRelu.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +LeakyRelu::LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void LeakyRelu::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (input()->element_type() == DataType::U8) + { + double alpha_multiplier = input()->scale() * params().alpha / output()->scale(); + quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha); + double identity_multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); + } + output()->resize(input()->shape()); +} + +void LeakyRelu::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void LeakyRelu::evalFloat() const +{ + tflite::LeakyReluParams op_params{}; + op_params.alpha = params().alpha; + luci_interpreter_pal::LeakyRelu(op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void LeakyRelu::evalQuantized() const +{ + tflite::LeakyReluParams op_params{}; + op_params.input_offset = input()->zero_point(); + op_params.output_offset = output()->zero_point(); + op_params.output_multiplier_alpha = _output_multiplier_alpha; + op_params.output_shift_alpha = _output_shift_alpha; + op_params.output_multiplier_identity = _output_multiplier_identity; + op_params.output_shift_identity = _output_shift_identity; + + tflite::reference_ops::QuantizeLeakyRelu( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(output()), + getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.h b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.h new file mode 100644 index 0000000..e66f404 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LEAKYRELU_H +#define LUCI_INTERPRETER_KERNELS_LEAKYRELU_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LeakyRelu : public KernelWithParams +{ +public: + LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + +private: + int32_t _output_multiplier_alpha = 0; + int _output_shift_alpha = 0; + int32_t _output_multiplier_identity = 0; + int _output_shift_identity = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LEAKYRELU_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp new file mode 100644 index 0000000..0f6263b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LeakyRelu.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data, + float alpha) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + LeakyReluParams params{}; + params.alpha = alpha; + + LeakyRelu kernel(&input_tensor, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list output_data, float alpha) +{ + std::unique_ptr memory_manager = std::make_unique(); + const float quantized_tolerance = getTolerance(-8, 127.f / 16.f, 255); + std::pair quant_param = quantizationParams(-8, 127.f / 16.f); + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + LeakyReluParams params{}; + params.alpha = alpha; + + LeakyRelu kernel(&input_tensor, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, quantized_tolerance)); +} + +template class LeakReluTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(LeakReluTest, DataTypes); + +TYPED_TEST(LeakReluTest, Simple) +{ + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -0.5f, -1.0f, // Row 2 + }, + /*alpha=*/0.5f); + + SUCCEED(); +} + +TEST(LeakReluTest, IvalidInputOutputType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({2, 3}, + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + LeakyReluParams params{}; + params.alpha = 0.5f; + + LeakyRelu kernel(&input_tensor, &output_tensor, params); + + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Less.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Less.cpp new file mode 100644 index 0000000..8d26ff2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Less.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Less.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Less::Less(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void Less::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void Less::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Less::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLess(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::Less(op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } +} + +template void Less::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLessNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::LessNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +void Less::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLessWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::LessWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Less.h b/compiler/luci-micro/luci-interpreter/src/kernels/Less.h new file mode 100644 index 0000000..e27bb68 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Less.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LESS_H +#define LUCI_INTERPRETER_KERNELS_LESS_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Less : public Kernel +{ +public: + Less(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LESS_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Less.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Less.test.cpp new file mode 100644 index 0000000..8c59633 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Less.test.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Less.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LessTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LessTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, false, false, // Row 1 + false, false, true, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(LessTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + true, false, false, // Row 1 + false, true, true, // Row 2 + true, true, false, // Row 3 + }; + + Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); +} + +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value + 1, -2, max_value}; + + std::vector ref_output_data{true, false, false}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -4, -3, // Row 3 + min_value, -2, max_value, // Row 4 + }; + + std::vector y_data{ + min_value + 1, -2, max_value - 1, // Row 1 + }; + + std::vector ref_output_data{ + true, false, true, // Row 1 + false, false, false, // Row 2 + false, true, true, // Row 3 + true, false, false, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(LessTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(LessTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(LessTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, false, false, false, // Row 1 + false, true, false, true, // Row 2 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessTest, Uint8QuantizedRescale) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, false, false, false, // Row 1 + false, true, false, true, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); + + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + false, true, false, true, // Row 1 + false, false, false, true, // Row 2 + false, true, false, true, // Row 3 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Less kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.cpp new file mode 100644 index 0000000..b474bc4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LessEqual.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +LessEqual::LessEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void LessEqual::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void LessEqual::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void LessEqual::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLessEqual(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::LessEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), output_data); + } +} + +template void LessEqual::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLessEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::LessEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +void LessEqual::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowLessEqualWithScaling( + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } + else + { + tflite::reference_ops::LessEqualWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.h b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.h new file mode 100644 index 0000000..f82ea90 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H +#define LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LessEqual : public Kernel +{ +public: + LessEqual(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.test.cpp new file mode 100644 index 0000000..b2e2fa7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LessEqual.test.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LessEqual.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LessEqualTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LessEqualTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, true, false, // Row 1 + false, true, true, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(LessEqualTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + true, true, false, // Row 1 + false, true, true, // Row 2 + true, true, false, // Row 3 + }; + + Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); +} + +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value + 1, -2, max_value}; + + std::vector ref_output_data{true, false, true}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -4, -3, // Row 3 + min_value, -2, max_value, // Row 4 + }; + + std::vector y_data{ + min_value + 1, -2, max_value - 1, // Row 1 + }; + + std::vector ref_output_data{ + true, false, true, // Row 1 + false, false, false, // Row 2 + false, true, true, // Row 3 + true, true, false, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(LessEqualTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(LessEqualTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(LessEqualTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, true, false, false, // Row 1 + false, true, false, true, // Row 2 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessEqualTest, Uint8QuantizedRescale) +{ + std::vector x_data{ + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, true, false, false, // Row 1 + false, true, false, true, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); + + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessEqualTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + false, true, false, true, // Row 1 + false, false, true, true, // Row 2 + false, true, false, true, // Row 3 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(LessEqualTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessEqualTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessEqualTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessEqualTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(LessEqualTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp new file mode 100644 index 0000000..a2bf442 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LocalResponseNormalization.h" + +#include "kernels/Utils.h" + +#include "PALLocalResponseNormalization.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +LocalResponseNormalization::LocalResponseNormalization( + const Tensor *input, Tensor *output, const LocalResponseNormalizationParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void LocalResponseNormalization::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void LocalResponseNormalization::execute() const +{ + switch (output()->element_type()) + { + case DataType::FLOAT32: + tflite::LocalResponseNormalizationParams op_params; + op_params.range = params().radius; + op_params.bias = params().bias; + op_params.alpha = params().alpha; + op_params.beta = params().beta; + luci_interpreter_pal::LocalResponseNormalization( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h new file mode 100644 index 0000000..60408a1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H +#define LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LocalResponseNormalization : public KernelWithParams +{ +public: + LocalResponseNormalization(const Tensor *input, Tensor *output, + const LocalResponseNormalizationParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp new file mode 100644 index 0000000..4a9d473 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LocalResponseNormalization.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LocalResponseNormalizationTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LocalResponseNormalizationTest, SameAsL2Norm) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LocalResponseNormalizationParams params{}; + params.radius = 20; + params.bias = 0.0; + params.alpha = 1.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({-0.55, 0.3, 0.35, 0.6, -0.35, 0.05})); +} + +TEST_F(LocalResponseNormalizationTest, WithAlpha) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LocalResponseNormalizationParams params{}; + params.radius = 20; + params.bias = 0.0; + params.alpha = 4.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({-0.275, 0.15, 0.175, 0.3, -0.175, 0.025})); +} + +TEST_F(LocalResponseNormalizationTest, WithBias) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LocalResponseNormalizationParams params{}; + params.radius = 20; + params.bias = 9.0; + params.alpha = 4.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({-0.22, 0.12, 0.14, 0.24, -0.14, 0.02})); +} + +TEST_F(LocalResponseNormalizationTest, SmallRadius) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LocalResponseNormalizationParams params{}; + params.radius = 2; + params.bias = 9.0; + params.alpha = 4.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + FloatArrayNear({-0.264926, 0.125109, 0.140112, 0.267261, -0.161788, 0.0244266})); +} + +TEST_F(LocalResponseNormalizationTest, InvalidInputDimension_NEG) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LocalResponseNormalizationParams params{}; + params.radius = 20; + params.bias = 0.0; + params.alpha = 1.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LocalResponseNormalizationTest, InvalidInputOutputType_NEG) +{ + Tensor input_tensor = makeInputTensor( + {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + LocalResponseNormalizationParams params{}; + params.radius = 20; + params.bias = 0.0; + params.alpha = 1.0; + params.beta = 0.5; + + LocalResponseNormalization kernel(&input_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.cpp new file mode 100644 index 0000000..79c3153 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LogSoftmax.h" + +#include "kernels/Utils.h" + +#include + +#include "PALLogSoftmax.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +LogSoftmax::LogSoftmax(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void LogSoftmax::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (input()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(output()->scale() == 16. / 256); + LUCI_INTERPRETER_CHECK(output()->zero_point() == 255); + + tflite::SoftmaxParams params{}; + + params.table = _table; + params.beta = 1.0; + luci_interpreter_pal::PopulateSoftmaxLookupTable(¶ms, input()->scale(), params.beta); + } + output()->resize(input()->shape()); +} + +void LogSoftmax::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void LogSoftmax::evalFloat() const +{ + tflite::SoftmaxParams params{}; + tflite::reference_ops::LogSoftmax(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void LogSoftmax::evalQuantized() const +{ + const auto input_shape = getTensorShape(input()); + const auto output_shape = getTensorShape(output()); + const auto input_scale = input()->scale(); + uint8_t *output_data = getTensorData(output()); + const uint8_t *input_data = getTensorData(input()); + const float beta = 1.0; + + tflite::SoftmaxParams params{}; + + params.table = const_cast(_table); + params.zero_point = output()->zero_point(); + params.scale = output()->scale(); + + luci_interpreter_pal::InitializeParams(¶ms, input_scale, beta); + luci_interpreter_pal::LogSoftmax(params, input_scale, input_shape, input_data, output_shape, + output_data); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.h b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.h new file mode 100644 index 0000000..18477fb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H +#define LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LogSoftmax : public Kernel +{ +public: + LogSoftmax(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + + float _table[256]; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.test.cpp new file mode 100644 index 0000000..50dcd5c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogSoftmax.test.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogSoftmax.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LogSoftmaxTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LogSoftmaxTest, Float) +{ + Shape input_shape{2, 4}; + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + LogSoftmax kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + -4.14297, -10.14297, -2.14297, -.142971, // + -7.00104, -12.00104, -.00104087, -9.00104, // + }; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(LogSoftmaxTest, Uint8) +{ + float kMin = -10; + float kMax = 10; + float kLogSoftmaxQuantizedTolerance = 16. / 256; + std::pair quant_param = quantizationParams(kMin, kMax); + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }; + Tensor input_tensor = makeInputTensor({2, 4}, quant_param.first, quant_param.second, + input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255); + + LogSoftmax kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + -4.14297, -10.14297, -2.14297, -.142971, // + -7.00104, -12.00104, -.00104087, -9.00104, // + }; + std::vector ref_output_shape{2, 4}; + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kLogSoftmaxQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({189, 93, 221, 253, 142, 63, 255, 111})); +} + +TEST_F(LogSoftmaxTest, InvalidInputOutputType_NEG) +{ + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }; + Tensor input_tensor = + makeInputTensor({2, 4}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255); + + LogSoftmax kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LogSoftmaxTest, InvalidOutputQuantParam_NEG) +{ + std::pair quant_param = quantizationParams(-10, 10); + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }; + Tensor input_tensor = makeInputTensor({2, 4}, quant_param.first, quant_param.second, + input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 20. / 256, 255); + + LogSoftmax kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.cpp new file mode 100644 index 0000000..8e72632 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogicalAnd.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +LogicalAnd::LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void LogicalAnd::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void LogicalAnd::execute() const +{ + switch (input1()->element_type()) + { + case DataType::BOOL: + evalLogicalAnd(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +inline void LogicalAnd::evalLogicalAnd() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), + [](bool x, bool y) { return x && y; }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.h b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.h new file mode 100644 index 0000000..46b8899 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOGICALAND_H +#define LUCI_INTERPRETER_KERNELS_LOGICALAND_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LogicalAnd : public Kernel +{ +public: + LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + inline void evalLogicalAnd() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOGICALAND_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp new file mode 100644 index 0000000..21b7951 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogicalAnd.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LogicalAndTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LogicalAndTest, Basic) +{ + Shape input_shape{1, 1, 1, 4}; + Tensor input_tensor1 = + makeInputTensor(input_shape, {true, false, false, true}, _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, {true, false, true, false}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAre(true, false, false, false)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); +} + +TEST_F(LogicalAndTest, Broadcast) +{ + Tensor input_tensor1 = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, + _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor({1, 1, 1, 1}, {true}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAre(true, false, false, true)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); +} + +TEST_F(LogicalAndTest, MismatchInputType_NEG) +{ + Tensor input1_tensor = + makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LogicalAndTest, InputTypeInvalid_NEG) +{ + Tensor input1_tensor = + makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1, 1, 1, 1}, {0}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.cpp new file mode 100644 index 0000000..65ab961 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/LogicalNot.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +LogicalNot::LogicalNot(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void LogicalNot::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void LogicalNot::execute() const +{ + switch (input()->element_type()) + { + case DataType::BOOL: + evalLogicalNot(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +inline void LogicalNot::evalLogicalNot() const +{ + const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); + bool *output_data = getTensorData(output()); + const bool *input_data = getTensorData(input()); + for (int i = 0; i < size; ++i) + { + output_data[i] = !input_data[i]; + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.h b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.h new file mode 100644 index 0000000..1608faf --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOGICALNOT_H +#define LUCI_INTERPRETER_KERNELS_LOGICALNOT_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LogicalNot : public Kernel +{ +public: + LogicalNot(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + inline void evalLogicalNot() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOGICALNOT_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.test.cpp new file mode 100644 index 0000000..3cbf27f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalNot.test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogicalNot.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LogicalNotTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LogicalNotTest, Basic) +{ + Shape input_shape{1, 1, 1, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, {true, false, false, true}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalNot kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAre(false, true, true, false)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); +} + +TEST_F(LogicalNotTest, OutputTypeInvalid_NEG) +{ + Tensor input_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + LogicalNot kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LogicalNotTest, InputTypeInvalid_NEG) +{ + Tensor input_tensor = + makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalNot kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.cpp new file mode 100644 index 0000000..f289ca6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogicalOr.h" + +#include "kernels/Utils.h" +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +LogicalOr::LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void LogicalOr::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(input1()->element_type() == DataType::BOOL); + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void LogicalOr::execute() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), + [](bool x, bool y) { return x || y; }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.h b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.h new file mode 100644 index 0000000..8860648 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOGICALOR_H +#define LUCI_INTERPRETER_KERNELS_LOGICALOR_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class LogicalOr : public Kernel +{ +public: + LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOGICALOR_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp new file mode 100644 index 0000000..d65a69a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/LogicalOr.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LogicalOrTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LogicalOrTest, Basic) +{ + Tensor input1_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, + _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, true, false}, + _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAre(true, false, true, true)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); +} + +TEST_F(LogicalOrTest, Broadcast) +{ + Tensor input1_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, + _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAre(true, false, false, true)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); +} + +TEST_F(LogicalOrTest, MismatchInputType_NEG) +{ + Tensor input1_tensor = + makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::S32); + + LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(LogicalOrTest, InputTypeInvalid_NEG) +{ + Tensor input1_tensor = + makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1, 1, 1, 1}, {0}, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.cpp new file mode 100644 index 0000000..58e4f18 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Logistic.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Logistic::Logistic(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Logistic::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (input()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(output()->scale() == 1. / 256); + populateLookupTable(); + } + output()->resize(input()->shape()); +} + +void Logistic::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Logistic::evalFloat() const +{ + tflite::reference_ops::Logistic(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void Logistic::evalQuantized() const +{ + const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); + uint8_t *output_data = getTensorData(output()); + const uint8_t *input_data = getTensorData(input()); + for (int i = 0; i < size; ++i) + { + output_data[i] = getTableValue(input_data[i]); + } +} + +void Logistic::populateLookupTable() +{ + const auto input_scale = static_cast(input()->scale()); + const auto input_zero_point = static_cast(input()->zero_point()); + const auto output_scale = static_cast(output()->scale()); + const auto output_zero_point = static_cast(output()->zero_point()); + const float inverse_scale = 1 / output_scale; + int32_t maxval = std::numeric_limits::max(); + int32_t minval = std::numeric_limits::min(); + for (int32_t val = minval; val <= maxval; ++val) + { + const float dequantized = input_scale * (val - input_zero_point); + const float transformed = 1.0f / (1.0f + std::exp(-dequantized)); + const float rescaled = std::round(transformed * inverse_scale); + const int32_t quantized = static_cast(rescaled + output_zero_point); + setTableValue(static_cast(std::max(std::min(maxval, quantized), minval)), + static_cast(val)); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.h b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.h new file mode 100644 index 0000000..31de6ad --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_LOGISTIC_H +#define LUCI_INTERPRETER_KERNELS_LOGISTIC_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Logistic : public Kernel +{ +public: + Logistic(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void populateLookupTable(); + void setTableValue(uint8_t value, uint8_t idx) { _table[idx] = value; }; + uint8_t getTableValue(uint8_t idx) const { return _table[idx]; }; + +private: + uint8_t _table[256]{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_LOGISTIC_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.test.cpp new file mode 100644 index 0000000..5a1ea66 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Logistic.test.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Logistic.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor()>(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(getElementType()); + + Logistic kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::pair input_quant_param = + quantizationParams(std::min(input_data), std::max(input_data)); + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0); + + Logistic kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale() * 2)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template class LogisticTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(LogisticTest, DataTypes); + +TYPED_TEST(LogisticTest, Simple) +{ + Check( + {89}, {89}, + {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636, + -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000, + -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364, + -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727, + -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091, + -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455, + -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818, + -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818, + 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455, + 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091, + 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727, + 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364, + 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000, + 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636, + 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000}, + {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198, + 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786, + 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065, + 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576, + 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562, + 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805, + 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241, + 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759, + 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195, + 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438, + 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424, + 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935, + 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214, + 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802, + 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021}); +} + +TEST(LogisticTest, IvalidInputOutputType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape = {1}; + std::vector input_data{10}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0); + + Logistic kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(LogisticTest, IvalidQuantParam_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Shape input_shape = {2}; + std::vector input_data{-10, 10}; + std::pair input_quant_param = quantizationParams(-10, 10); + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 255, 0); + + Logistic kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.cpp new file mode 100644 index 0000000..8d9760f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/MaxPool2D.h" + +#include "kernels/Utils.h" + +#include +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +MaxPool2D::MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void MaxPool2D::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + assert(input()->shape().num_dims() == 4); + const Shape &input_shape = input()->shape(); + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t depth = input_shape.dim(3); + + const int32_t output_height = + computeOutputSize(_params.padding, input_height, _params.filter_height, _params.stride_height); + const int32_t output_width = + computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); + + _padding_height = + computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); + _padding_width = + computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); + + output()->resize({batches, output_height, output_width, depth}); + if (input()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); + LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point()); + } + else if (input()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + } +} + +void MaxPool2D::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalSInt16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void MaxPool2D::evalFloat() const +{ + float activation_min{}; + float activation_max{}; + calculateActivationRange(_params.activation, &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + + tflite::reference_ops::MaxPool(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void MaxPool2D::evalQuantized() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + tflite::reference_ops::MaxPool(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void MaxPool2D::evalSInt16() const +{ + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::PoolParams params{}; + params.padding_values.height = _padding_height; + params.padding_values.width = _padding_width; + params.stride_height = _params.stride_height; + params.stride_width = _params.stride_width; + params.filter_height = _params.filter_height; + params.filter_width = _params.filter_width; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + tflite::reference_integer_ops::MaxPool( + params, getTensorShape(input()), getTensorData(input()), // + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.h b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.h new file mode 100644 index 0000000..bb76663 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H +#define LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class MaxPool2D : public KernelWithParams +{ +public: + MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalSInt16() const; + +private: + int32_t _padding_height{}; + int32_t _padding_width{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp new file mode 100644 index 0000000..44f2a22 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/MaxPool2D.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MaxPool2DTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MaxPool2DTest, Float) +{ + Shape input_shape{1, 3, 5, 1}; + std::vector input_data{ + 1, -1, 0, -2, 2, // + -7, -6, -5, -4, -3, // + 5, 4, 3, 6, 7, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + MaxPool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 1, 2, // + 5, 6, // + }; + std::initializer_list ref_output_shape{1, 2, 2, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MaxPool2DTest, Uint8) +{ + std::pair quant_param = quantizationParams(-15.9375, 15.9375); + std::vector input_data{ + 0, -6, 12, 4, // + -3, -2, 10, 7, // + }; + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 2; + params.stride_height = 2; + params.stride_width = 2; + params.activation = Activation::RELU6; + + MaxPool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.0, 6.0}; + std::initializer_list ref_output_shape{1, 1, 2, 1}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MaxPool2DTest, SInt16) +{ + Shape input_shape{1, 3, 5, 1}; + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector input_data{ + 1, -1, 0, -2, 2, // + -7, -6, -5, -4, -3, // + 5, 4, 3, 6, 7, // + }; + std::vector ref_output_data{ + 1, 2, // + 5, 6, // + }; + + Tensor input_tensor = + makeInputTensor(input_shape, 0.2, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0); + + Pool2DParams params{}; + params.padding = Padding::VALID; + params.filter_height = 2; + params.filter_width = 3; + params.stride_height = 1; + params.stride_width = 2; + params.activation = Activation::RELU6; + + MaxPool2D kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.cpp new file mode 100644 index 0000000..b102b5e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Maximum.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Maximum::Maximum(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void Maximum::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()) + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()) + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Maximum::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalMaximum(); + break; + case DataType::U8: + evalMaximum(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template inline void Maximum::evalMaximum() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), + [](T x, T y) { return std::max(x, y); }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.h b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.h new file mode 100644 index 0000000..3c99e69 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MAXIMUM_H +#define LUCI_INTERPRETER_KERNELS_MAXIMUM_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Maximum : public Kernel +{ +public: + Maximum(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template inline void evalMaximum() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MAXIMUM_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.test.cpp new file mode 100644 index 0000000..e4a505b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Maximum.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Maximum.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MaximumTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MaximumTest, Float) +{ + Shape input_shape{3, 1, 2}; + std::vector input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43}; + Tensor input_tensor1 = + makeInputTensor(input_shape, input_data1, _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, input_data2, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{1.0, 0.0, 1.0, 12.0, -2.0, -1.43}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(MaximumTest, Uint8) +{ + Shape input_shape{3, 1, 2}; + std::vector input_data1{1, 0, 2, 11, 2, 23}; + std::vector input_data2{0, 0, 1, 12, 255, 1}; + Tensor input_tensor1 = + makeInputTensor(input_shape, input_data1, _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, input_data2, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_shape{2, 4}; + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({1, 0, 2, 12, 255, 23})); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mean.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.cpp new file mode 100644 index 0000000..8e65e0d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Mean.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +static void resolveAxes(const int32_t *axes_data, int num_axes, tflite::MeanParams *params) +{ + params->axis_count = num_axes; + for (int i = 0; i < num_axes; ++i) + { + params->axis[i] = static_cast(axes_data[i]); + } + for (int i = num_axes; i < 4; ++i) + { + params->axis[i] = 1; + } +} + +// Returns the number of axes that will be reduced. Removes duplicates. +static int getAxisReductionCount(const int32_t *axes_data, int num_axes, int input_num_dims) +{ + int reduction_count = num_axes; + for (int i = 0; i < num_axes; ++i) + { + int current = axes_data[i] >= 0 ? axes_data[i] : axes_data[i] + input_num_dims; + assert(current >= 0 && current < input_num_dims); + for (int j = 0; j < i; j++) + { + int previous = axes_data[j] >= 0 ? axes_data[j] : axes_data[j] + input_num_dims; + // This checks for duplicate axis + if (current == previous) + { + --reduction_count; + break; + } + } + } + return reduction_count; +} + +static Shape getOutputShape(const Shape &input_shape, const int32_t *axes_data, int num_axes, + bool keep_dims) +{ + int input_num_dims = input_shape.num_dims(); + if (input_num_dims == 0) + { + return Shape(0); + } + + if (keep_dims) + { + Shape output_shape(input_num_dims); + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + is_axis = true; + break; + } + } + if (is_axis) + { + output_shape.dim(idx) = 1; + } + else + { + output_shape.dim(idx) = input_shape.dim(idx); + } + } + return output_shape; + } + else + { + int num_reduce_axes = getAxisReductionCount(axes_data, num_axes, input_num_dims); + Shape output_shape(input_num_dims - num_reduce_axes); + int num_skip_axes = 0; + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + ++num_skip_axes; + is_axis = true; + break; + } + } + if (!is_axis) + { + output_shape.dim(idx - num_skip_axes) = input_shape.dim(idx); + } + } + return output_shape; + } +} + +Mean::Mean(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, Tensor *temp_sum, const ReducerParams ¶ms) + : KernelWithParams({input, axes}, {output, temp_index, resolved_axes, temp_sum}, + params) +{ +} + +void Mean::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32); + if (input()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + } + + const Shape &input_shape = input()->shape(); + int input_num_dims = input_shape.num_dims(); + + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + assert(num_axes <= 4); + + Shape output_shape = getOutputShape(input_shape, axes_data, num_axes, _params.keep_dims); + output()->resize(output_shape); + + tflite::MeanParams params{}; + resolveAxes(axes_data, num_axes, ¶ms); + _need_temporaries = !( + _params.keep_dims && input_num_dims == 4 && params.axis_count == 2 && + ((params.axis[0] == 1 && params.axis[1] == 2) || (params.axis[0] == 2 && params.axis[1] == 1))); + if (_need_temporaries) + { + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + auto temp_sum = getOutputTensors()[3]; + + temp_index->resize(Shape(input_num_dims)); + resolved_axes->resize(Shape(num_axes)); + temp_sum->resize(output()->shape()); + } + else + { + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + auto temp_sum = getOutputTensors()[3]; + + temp_index->set_allocatable(false); + resolved_axes->set_allocatable(false); + temp_sum->set_allocatable(false); + } +} + +void Mean::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Mean::evalFloat() const +{ + const Shape &input_shape = input()->shape(); + int input_num_dims = input_shape.num_dims(); + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + + tflite::MeanParams params{}; + resolveAxes(axes_data, num_axes, ¶ms); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + auto temp_sum = getOutputTensors()[3]; + + // Defer to specialized implementation for 4D Mean across axes 1 & 2. + if (_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 && + ((params.axis[0] == 1 && params.axis[1] == 2) || + (params.axis[0] == 2 && params.axis[1] == 1))) + { + tflite::reference_ops::Mean(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Mean(getTensorData(input()), getTensorShape(input()).DimsData(), + input()->shape().num_dims(), getTensorData(output()), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), + axes_data, num_axes, _params.keep_dims, + getTensorData(temp_index), getTensorData(resolved_axes), + getTensorData(temp_sum)); + } +} + +void Mean::evalQuantized() const +{ + const Shape &input_shape = input()->shape(); + int input_num_dims = input_shape.num_dims(); + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + + tflite::MeanParams params{}; + resolveAxes(axes_data, num_axes, ¶ms); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + auto temp_sum = getOutputTensors()[3]; + + // Defer to specialized implementation for 4D Mean across axes 1 & 2. + if (_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 && + ((params.axis[0] == 1 && params.axis[1] == 2) || + (params.axis[0] == 2 && params.axis[1] == 1))) + { + tflite::reference_ops::Mean(params, getTensorShape(input()), getTensorData(input()), + input()->zero_point(), input()->scale(), getTensorShape(output()), + getTensorData(output()), output()->zero_point(), + output()->scale()); + } + else if (input()->zero_point() == output()->zero_point() && input()->scale() == output()->scale()) + { + tflite::reference_ops::Mean(getTensorData(input()), getTensorShape(input()).DimsData(), + input()->shape().num_dims(), getTensorData(output()), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), + axes_data, num_axes, _params.keep_dims, + getTensorData(temp_index), getTensorData(resolved_axes), + getTensorData(temp_sum)); + } + else + { + tflite::reference_ops::QuantizedMeanOrSum<>( + getTensorData(input()), input()->zero_point(), input()->scale(), + getTensorShape(input()).DimsData(), input()->shape().num_dims(), + getTensorData(output()), output()->zero_point(), output()->scale(), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, + _params.keep_dims, getTensorData(temp_index), getTensorData(resolved_axes), + getTensorData(temp_sum), + /*compute_sum=*/false); + } +} + +void Mean::evalQuantizedS16() const +{ + const auto *input_data = getTensorData(input()); + auto *output_data = getTensorData(output()); + + const Shape &input_shape = input()->shape(); + const Shape &output_shape = output()->shape(); + + const auto *axes_data = getTensorData(axes()); + const int num_axes = axes()->shape().num_elements(); + + constexpr int32_t output_min = -std::numeric_limits::max(); + constexpr int32_t output_max = std::numeric_limits::max(); + + // Defer to specialized implementation for 4D Mean across axes 1 & 2. + if (_params.keep_dims && input_shape.num_dims() == 4 && num_axes == 2 && + ((axes_data[0] == 1 && axes_data[1] == 2) || (axes_data[0] == 2 && axes_data[1] == 1))) + { + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t depth = input_shape.dim(3); + assert(output_shape.num_dims() == 4); + assert(output_shape.dim(0) == batches); + assert(output_shape.dim(1) == 1); + assert(output_shape.dim(2) == 1); + assert(output_shape.dim(3) == depth); + + const double real_multiplier = + static_cast(input()->scale()) / static_cast(output()->scale()); + + int32_t output_multiplier{}; + int output_shift{}; + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + + const int32_t num_elements_in_axes = input_height * input_width; + + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t c = 0; c < depth; ++c) + { + int32_t acc = 0; + for (int32_t in_y = 0; in_y < input_height; ++in_y) + { + for (int32_t in_x = 0; in_x < input_width; ++in_x) + { + acc += input_data[calcOffset(input_shape, batch, in_y, in_x, c)]; + } + } + int32_t scaled_acc = + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + // Divide by the number of elements rounding to the nearest integer. + scaled_acc = scaled_acc > 0 + ? (scaled_acc + num_elements_in_axes / 2) / num_elements_in_axes + : (scaled_acc - num_elements_in_axes / 2) / num_elements_in_axes; + + scaled_acc = std::max(scaled_acc, output_min); + scaled_acc = std::min(scaled_acc, output_max); + + output_data[calcOffset(output_shape, batch, 0, 0, c)] = scaled_acc; + } + } + } + else + { + throw std::runtime_error("Unsupported configuration."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mean.h b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.h new file mode 100644 index 0000000..ed07ae5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MEAN_H +#define LUCI_INTERPRETER_KERNELS_MEAN_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class Mean : public KernelWithParams +{ +public: + Mean(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, Tensor *temp_sum, const ReducerParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *axes() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedS16() const; + +private: + bool _need_temporaries = false; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MEAN_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mean.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.test.cpp new file mode 100644 index 0000000..d2c0093 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mean.test.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Mean.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MeanTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MeanTest, FloatKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{0, 2}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = true; + + Mean kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{10.5, 12.5, 14.5}; + std::initializer_list ref_output_shape{1, 3, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MeanTest, FloatKeepDims4DMean) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{1, 2}; + Tensor input_tensor = + makeInputTensor({2, 2, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = true; + + Mean kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{6, 7, 18, 19}; + std::initializer_list ref_output_shape{2, 1, 1, 2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MeanTest, FloatNotKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{1, 0, -3, -3}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({4}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = false; + + Mean kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{12, 13}; + std::initializer_list ref_output_shape{2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MeanTest, Uint8KeepDims) +{ + float kQuantizedTolerance = getTolerance(-1.0, 1.0, 255); + std::vector input_data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + + std::vector axis_data{1}; + Tensor input_tensor = makeInputTensor({3, 2}, quant_param.first, quant_param.second, + input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::U8, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + ReducerParams params{}; + params.keep_dims = true; + + Mean kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.3, 0.35, 0.55}; + std::initializer_list ref_output_shape{3, 1}; + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MeanTest, Uint8NotKeepDims) +{ + float kQuantizedTolerance = getTolerance(-1.0, 1.0, 255); + std::vector input_data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + + std::vector axis_data{1}; + Tensor input_tensor = makeInputTensor( + {1, 3, 2}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({1}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + ReducerParams params{}; + params.keep_dims = false; + + Mean kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.4, 0.4}; + std::initializer_list ref_output_shape{1, 2}; + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MeanTest, SInt16KeepDims4D) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector axes_data{1, 2}; + std::vector ref_output_data{6, 7, 18, 19}; + + Tensor input_tensor = + makeInputTensor({2, 2, 3, 2}, 0.25, 0, input_data, _memory_manager.get()); + Tensor axes_tensor = makeInputTensor({2}, axes_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor temp_sum(DataType::FLOAT32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0); + + ReducerParams params{}; + params.keep_dims = true; + + Mean kernel(&input_tensor, &axes_tensor, &output_tensor, &temp_index, &resolved_axes, &temp_sum, + params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(temp_sum); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 1, 1, 2})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.cpp new file mode 100644 index 0000000..5d3dcde --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Minimum.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Minimum::Minimum(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void Minimum::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()) + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()) + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Minimum::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalMinimum(); + break; + case DataType::U8: + evalMinimum(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template inline void Minimum::evalMinimum() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), + [](T x, T y) { return std::min(x, y); }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.h b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.h new file mode 100644 index 0000000..5ff4035 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MINIMUM_H +#define LUCI_INTERPRETER_KERNELS_MINIMUM_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Minimum : public Kernel +{ +public: + Minimum(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template inline void evalMinimum() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MINIMUM_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.test.cpp new file mode 100644 index 0000000..9a14364 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Minimum.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Minimum.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MinimumTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MinimumTest, Float) +{ + Shape input_shape{3, 1, 2}; + std::vector input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43}; + Tensor input_tensor1 = + makeInputTensor(input_shape, input_data1, _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, input_data2, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{-1.0, 0.0, -1.0, 11.0, -3.0, -1.44}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(MinimumTest, Uint8) +{ + Shape input_shape{3, 1, 2}; + std::vector input_data1{1, 0, 2, 11, 2, 23}; + std::vector input_data2{0, 0, 1, 12, 255, 1}; + Tensor input_tensor1 = + makeInputTensor(input_shape, input_data1, _memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, input_data2, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_shape{2, 4}; + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({0, 0, 1, 11, 2, 1})); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.cpp new file mode 100644 index 0000000..bae1eac --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/MirrorPad.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +MirrorPad::MirrorPad(const Tensor *input, const Tensor *paddings, Tensor *output, + const MirrorPadParams ¶ms) + : KernelWithParams({input, paddings}, {output}, params) +{ +} + +void MirrorPad::configure() +{ + const Shape &input_shape = input()->shape(); + const int num_dims = input_shape.num_dims(); + + if (num_dims > 4) + throw std::runtime_error("Unsupported number of dimensions."); + + assert(output()->element_type() == input()->element_type()); + assert(paddings()->element_type() == DataType::S32); + // Paddings shape should be [N, 2]. + assert(paddings()->shape().num_dims() == 2); + assert(paddings()->shape().dim(0) == num_dims); + assert(paddings()->shape().dim(1) == 2); + + Shape output_shape(num_dims); + const auto *paddings_data = getTensorData(paddings()); + for (int i = 0; i < num_dims; ++i) + { + const int32_t padding_before = paddings_data[i * 2]; + const int32_t padding_after = paddings_data[i * 2 + 1]; + assert(padding_before >= 0 && padding_after >= 0); + output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after; + } + + output()->resize(output_shape); +} + +template +inline void MirrorPadImpl(const Tensor &input, const Tensor &paddings, MirrorPadMode mode, + Tensor &output); + +void MirrorPad::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + { + MirrorPadImpl(*input(), *paddings(), params().mode, *output()); + break; + } + case DataType::U8: + { + assert(output()->zero_point() >= std::numeric_limits::min()); + assert(output()->zero_point() <= std::numeric_limits::max()); + + MirrorPadImpl(*input(), *paddings(), params().mode, *output()); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } +} + +template +inline void MirrorPadImpl(const Tensor &input, const Tensor &paddings, MirrorPadMode mode, + Tensor &output) +{ + auto const input_dims = input.shape().num_dims(); + auto const input_data = input.data(); + auto const paddings_data = paddings.data(); + auto const output_data = output.data(); + + auto const input_b = input_dims > 3 ? input.shape().dim(input_dims - 4) : 1; + auto const input_h = input_dims > 2 ? input.shape().dim(input_dims - 3) : 1; + auto const input_w = input_dims > 1 ? input.shape().dim(input_dims - 2) : 1; + auto const input_d = input.shape().dim(input_dims - 1); + + auto const input_h_offset = input_d * input_w; + auto const input_b_offset = input_h_offset * input_h; + + auto const output_b = input_dims > 3 ? output.shape().dim(input_dims - 4) : 1; + auto const output_h = input_dims > 2 ? output.shape().dim(input_dims - 3) : 1; + auto const output_w = input_dims > 1 ? output.shape().dim(input_dims - 2) : 1; + auto const output_d = output.shape().dim(input_dims - 1); + + auto const left_b_pad = paddings_data[2 * (input_dims - 4)]; + auto const left_h_pad = paddings_data[2 * (input_dims - 3)]; + auto const left_w_pad = paddings_data[2 * (input_dims - 2)]; + auto const left_d_pad = paddings_data[2 * (input_dims - 1)]; + + auto const right_b_pad = paddings_data[2 * (input_dims - 4) + 1]; + auto const right_h_pad = paddings_data[2 * (input_dims - 3) + 1]; + auto const right_w_pad = paddings_data[2 * (input_dims - 2) + 1]; + auto const right_d_pad = paddings_data[2 * (input_dims - 1) + 1]; + + const auto positive_mod = [](auto a, auto b) { return (a % b + b) % b; }; + const auto offset_index = [input_d, input_h_offset, input_b_offset](auto d, auto w, auto h, + auto b) { + return d + w * input_d + h * input_h_offset + b * input_b_offset; + }; + + const auto symmetric_dim = [&positive_mod](auto i, auto left_pad, auto input) { + bool reflected = (((i < left_pad ? i + 1 - input : i) - left_pad) / input & 1) == 1; + return positive_mod(reflected ? input + left_pad - i - 1 : i - left_pad, input); + }; + + const T *in_ptr = input_data; + T *out_ptr = output_data; + + for (int32_t b = 0; b < output_b; ++b) + { + for (int32_t h = 0; h < output_h; ++h) + { + for (int32_t w = 0; w < output_w; ++w) + { + for (int32_t d = 0; d < output_d; ++d) + { + if (b < left_b_pad || b >= output_b - right_b_pad || // + h < left_h_pad || h >= output_h - right_h_pad || // + w < left_w_pad || w >= output_w - right_w_pad || // + d < left_d_pad || d >= output_d - right_d_pad) + { + if (mode == MirrorPadMode::REFLECT) + { + *out_ptr++ = input_data[offset_index( + positive_mod(d - left_d_pad, input_d), positive_mod(w - left_w_pad, input_w), + positive_mod(h - left_h_pad, input_h), positive_mod(b - left_b_pad, input_b))]; + } + else + { + *out_ptr++ = input_data[offset_index( + symmetric_dim(d, left_d_pad, input_d), symmetric_dim(w, left_w_pad, input_w), + symmetric_dim(h, left_h_pad, input_h), symmetric_dim(b, left_b_pad, input_b))]; + } + } + else + { + *out_ptr++ = *in_ptr++; + } + } + } + } + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.h b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.h new file mode 100644 index 0000000..d3e6e85 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MIRROR_PAD_H +#define LUCI_INTERPRETER_KERNELS_MIRROR_PAD_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class MirrorPad : public KernelWithParams +{ +public: + MirrorPad(const Tensor *input, const Tensor *paddings, Tensor *output, + const MirrorPadParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *paddings() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MIRROR_PAD_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp new file mode 100644 index 0000000..740d8cb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/MirrorPad.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MirrorPadTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + void Execute(const Tensor &input, const Tensor &padding, Tensor &output, MirrorPadMode mode) + { + MirrorPadParams params{}; + params.mode = mode; + + MirrorPad kernel(&input, &padding, &output, params); + kernel.configure(); + _memory_manager->allocate_memory(output); + kernel.execute(); + } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MirrorPadTest, FloatReflect) +{ + Shape input_shape = {1, 2, 2, 1}; + Shape padding_shape = {4, 2}; + + std::vector input_data{1.0f, 2.0f, // + 3.0f, 4.0f}; // + std::vector padding_data{0, 0, 2, 1, 1, 2, 0, 0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor padding_tensor = + makeInputTensor(padding_shape, padding_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::REFLECT); + + std::vector ref_output_data{2.0f, 1.0f, 2.0f, 1.0f, 2.0f, // + 4.0f, 3.0f, 4.0f, 3.0f, 4.0f, // + 2.0f, 1.0f, 2.0f, 1.0f, 2.0f, // + 4.0f, 3.0f, 4.0f, 3.0f, 4.0f, // + 2.0f, 1.0f, 2.0f, 1.0f, 2.0f}; // + std::initializer_list ref_output_shape{1, 5, 5, 1}; + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MirrorPadTest, FloatSymmetric) +{ + Shape input_shape = {1, 2, 2, 1}; + Shape padding_shape = {4, 2}; + + std::vector input_data{1.0f, 2.0f, // + 3.0f, 4.0f}; // + std::vector padding_data{0, 0, 2, 1, 1, 2, 0, 0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor padding_tensor = + makeInputTensor(padding_shape, padding_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::SYMMETRIC); + + std::vector ref_output_data{3.0, 3.0, 4.0, 4.0, 3.0, // + 1.0, 1.0, 2.0, 2.0, 1.0, // + 1.0, 1.0, 2.0, 2.0, 1.0, // + 3.0, 3.0, 4.0, 4.0, 3.0, // + 3.0, 3.0, 4.0, 4.0, 3.0}; // + std::initializer_list ref_output_shape{1, 5, 5, 1}; + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MirrorPadTest, FloatSymmetric2Dim) +{ + Shape input_shape = {3, 1}; + Shape padding_shape = {2, 2}; + + std::vector input_data{1.0f, 2.0f, 3.0f}; + std::vector padding_data{1, 2, 0, 0}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor padding_tensor = + makeInputTensor(padding_shape, padding_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::SYMMETRIC); + + std::vector ref_output_data{1.0, 1.0, 2.0, 3.0, 3.0, 2.0}; + std::initializer_list ref_output_shape{6, 1}; + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MirrorPadTest, Uint8Reflect) +{ + Shape input_shape = {1, 2, 3, 1}; + Shape padding_shape = {4, 2}; + + float quant_tolerance = getTolerance(0.0f, 6.0f, 255); + std::pair quant_param = quantizationParams(0.0f, 6.0f); + + std::vector input_data{1.0f, 2.0f, 3.0f, // + 4.0f, 5.0f, 6.0f}; // + std::vector padding_data{0, 0, 2, 1, 1, 3, 0, 0}; + + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + + Tensor padding_tensor = + makeInputTensor(padding_shape, padding_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::REFLECT); + + std::vector ref_output_data{ + 3.0f, 1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f, // + 6.0f, 4.0f, 5.0f, 6.0f, 4.0f, 5.0f, 6.0f, // + 3.0f, 1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f, // + 6.0f, 4.0f, 5.0f, 6.0f, 4.0f, 5.0f, 6.0f, // + 3.0f, 1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f, // + }; + std::initializer_list ref_output_shape{1, 5, 7, 1}; + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, quant_tolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MirrorPadTest, Uint8Symmetric) +{ + Shape input_shape = {1, 2, 3, 1}; + Shape padding_shape = {4, 2}; + + float quant_tolerance = getTolerance(0.0f, 6.0f, 255); + std::pair quant_param = quantizationParams(0.0f, 6.0f); + + std::vector input_data{1.0f, 2.0f, 3.0f, // + 4.0f, 5.0f, 6.0f}; // + std::vector padding_data{0, 0, 2, 1, 1, 3, 0, 0}; + + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + + Tensor padding_tensor = + makeInputTensor(padding_shape, padding_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::SYMMETRIC); + + std::vector ref_output_data{ + 4.0f, 4.0f, 5.0f, 6.0f, 6.0f, 5.0f, 4.0f, // + 1.0f, 1.0f, 2.0f, 3.0f, 3.0f, 2.0f, 1.0f, // + 1.0f, 1.0f, 2.0f, 3.0f, 3.0f, 2.0f, 1.0f, // + 4.0f, 4.0f, 5.0f, 6.0f, 6.0f, 5.0f, 4.0f, // + 4.0f, 4.0f, 5.0f, 6.0f, 6.0f, 5.0f, 4.0f, // + }; + std::initializer_list ref_output_shape{1, 5, 7, 1}; + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, quant_tolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(MirrorPadTest, UnsupportedDim_NEG) +{ + Tensor input_tensor = + makeInputTensor({1, 1, 1, 1, 1}, {1.0f}, _memory_manager.get()); + Tensor padding_tensor = + makeInputTensor({5, 2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + EXPECT_ANY_THROW(Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::REFLECT)); +} + +TEST_F(MirrorPadTest, InvalidInputType_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor padding_tensor = makeInputTensor({1, 2}, {0, 0}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + EXPECT_ANY_THROW(Execute(input_tensor, padding_tensor, output_tensor, MirrorPadMode::REFLECT)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mul.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.cpp new file mode 100644 index 0000000..531fb4f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Mul.h" + +#include "kernels/BinaryOpCommon.h" +#include "kernels/Utils.h" + +#include "PALMul.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Mul::Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams ¶ms) + : KernelWithParams({input1, input2}, {output}, params) +{ +} + +void Mul::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == input1()->element_type()); + if (input1()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(input1()->zero_points().size() == 1 && + input2()->zero_points().size() == 1) + LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 && + output()->zero_point() == 0); + } + + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Mul::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Mul::evalFloat() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + luci_interpreter_pal::BroadcastMul4DSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + luci_interpreter_pal::Mul(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +template void Mul::evalInteger() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + luci_interpreter_pal::BroadcastMul4DSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + luci_interpreter_pal::Mul(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +void Mul::evalQuantizedS16() const +{ + const auto input1_scale = static_cast(input1()->scale()); + const auto input2_scale = static_cast(input2()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const double real_multiplier = input1_scale * input2_scale / output_scale; + + int32_t output_multiplier; + int output_shift; + quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + auto fn = [output_multiplier, output_shift, activation_min, activation_max](int16_t input1_val, + int16_t input2_val) { + int32_t output = static_cast(input1_val) * static_cast(input2_val); + output = tflite::MultiplyByQuantizedMultiplier(output, output_multiplier, output_shift); + output = std::max(output, activation_min); + output = std::min(output, activation_max); + return static_cast(output); + }; + + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), fn); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mul.h b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.h new file mode 100644 index 0000000..c0cf817 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_MUL_H +#define LUCI_INTERPRETER_KERNELS_MUL_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class Mul : public KernelWithParams +{ +public: + Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams ¶ms); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantizedS16() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MUL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Mul.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.test.cpp new file mode 100644 index 0000000..fc0e606 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Mul.test.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Mul.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class MulTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(MulTest, Float) +{ + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> test_outputs = { + {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, + 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, + 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, + 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, + 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, + {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; + std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) + << "With shape number " << i; + } + // Re-run with exchanged inputs. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = + makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) + << "With shape number " << i; + } +} + +template void checkInteger(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + + dtype max_value = std::numeric_limits::max(); + dtype res_max = max_value - max_value % 10; + + std::vector> test_outputs = { + {8, 0, 20, 0, 4, 30, // + 16, 0, 40, 3, 8, 0, // + 0, 0, 0, 6, 0, 0, // + 4, 0, 10, 9, 2, 0, // + 40, 0, 100, 0, 20, 150, // + 28, 0, 70, 0, 14, res_max}, + {8, 0, 40, 3, 0, 0, 4, 0, 100, 0, 14, res_max}, + {8, 12, 0, 0, 20, 30, 16, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 2, 0, 10, 0, 0, 0, 20, 30, 100, 150, 0, 0, 14, max_value / 10 * 2, + 70, res_max}, + {8, 12, 0, 0, 0, 0, 0, 9, 20, 30, 70, res_max}}; + std::vector input1_data{2, 3, 4, -1, -3, -2, 1, -3, 10, 15, 7, max_value / 10}; + std::vector input2_data{4, 0, 10, -3, 2, 10}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } + // Re-run with exchanged inputs. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor input2_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } +} + +TEST_F(MulTest, SInt64) +{ + checkInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(MulTest, SInt32) +{ + checkInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(MulTest, SInt16) +{ + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> ref_output_shapes{ + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + + std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + std::vector> ref_outputs = { + {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, + 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, + 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, + 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, + 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, + {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, 3.0 / 32767, 0, input1_data, + _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(test_shapes[i], 1.0 / 32767, 0, + input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); + const float tolerance = output_tensor.scale() * 2; + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), + ::testing::ElementsAreArray(ref_output_shapes[i])) + << "With shape number " << i; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) + << "With shape number " << i; + } + // Re-run with exchanged inputs and different scales. + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(test_shapes[i], 2.0 / 32767, 0, + input2_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(base_shape, 4.0 / 32767, 0, input1_data, + _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 3.0 / 32767, 0); + const float tolerance = output_tensor.scale() * 2; + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), + ::testing::ElementsAreArray(ref_output_shapes[i])) + << "With shape number " << i; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) + << "With shape number " << i; + } +} + +TEST_F(MulTest, Input_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(MulTest, Invalid_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(MulTest, Invalid_Input_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U64); + + MulParams params{}; + params.activation = Activation::RELU; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(MulTest, Invalid_Quantization_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + MulParams params{}; + params.activation = Activation::NONE; + + Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Neg.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.cpp new file mode 100644 index 0000000..c6fe08a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Neg.h" +#include "kernels/Utils.h" + +#include "PALNeg.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Neg::Neg(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Neg::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + output()->resize(input()->shape()); +} + +void Neg::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Neg::evalFloat() const +{ + luci_interpreter_pal::Negate(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Neg.h b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.h new file mode 100644 index 0000000..69fa1a1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_NEG_H +#define LUCI_INTERPRETER_KERNELS_NEG_H + +#include "core/Kernel.h" +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class Neg : public Kernel +{ +public: + Neg(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_NEG_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Neg.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.test.cpp new file mode 100644 index 0000000..8b2bc1a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Neg.test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Neg.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + Neg kernel(&input_tensor, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(NegTest, FloatSimple) +{ + Check(/*input_shape=*/{2, 3}, + /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, -1.0f, -3.0f, // Row 1 + -1.0f, 1.0f, 2.0f, // Row 2 + }); + + SUCCEED(); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.cpp new file mode 100644 index 0000000..54e5eee --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/NotEqual.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +NotEqual::NotEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void NotEqual::configure() +{ + LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + + if (x()->element_type() == DataType::U8) + { + quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); + quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); + } + output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); +} + +void NotEqual::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void NotEqual::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowNotEqual(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::NotEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), output_data); + } +} + +template void NotEqual::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowNotEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } + else + { + tflite::reference_ops::NotEqualNoScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } +} + +void NotEqual::evalQuantized() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + auto output_data = getTensorData(output()); + + tflite::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -x()->zero_point(); // Note the '-' + op_params.input1_shift = _x_shift; + op_params.input1_multiplier = _x_multiplier; + op_params.input2_offset = -y()->zero_point(); // Note the '-' + op_params.input2_shift = _y_shift; + op_params.input2_multiplier = _y_multiplier; + op_params.is_broadcast = x()->shape() != y()->shape(); + + if (op_params.is_broadcast) + { + tflite::reference_ops::Broadcast4DSlowNotEqualWithScaling( + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); + } + else + { + tflite::reference_ops::NotEqualWithScaling(op_params, getTensorShape(x()), x_data, + getTensorShape(y()), y_data, + getTensorShape(output()), output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.h b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.h new file mode 100644 index 0000000..d2aafe8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H +#define LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class NotEqual : public Kernel +{ +public: + NotEqual(const Tensor *x, const Tensor *y, Tensor *output); + + const Tensor *x() const { return _inputs[0]; } + const Tensor *y() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; + +private: + int32_t _x_multiplier = 0; + int _x_shift = 0; + int32_t _y_multiplier = 0; + int _y_shift = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.test.cpp new file mode 100644 index 0000000..45bf402 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/NotEqual.test.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/NotEqual.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class NotEqualTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(NotEqualTest, FloatSimple) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, false, true, // Row 1 + true, false, true, // Row 2 + }; + + Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(NotEqualTest, FloatBroardcast) +{ + std::vector x_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + 0.9, 0.7, 0.5, // Row 4 + }; + + std::vector y_data{ + 0.9, 0.7, 0.5, // Row 1 + }; + + std::vector ref_output_data{ + true, false, true, // Row 1 + true, true, true, // Row 2 + true, true, true, // Row 3 + false, false, false, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +template +void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{min_value, 2, max_value}; + + std::vector y_data{min_value, -2, max_value}; + + std::vector ref_output_data{false, true, false}; + + Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); +} + +template +void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + dtype min_value = std::numeric_limits::min(); + dtype max_value = std::numeric_limits::max(); + std::vector x_data{ + min_value, 2, 3, // Row 1 + 4, 5, max_value, // Row 2 + -1, -2, -3, // Row 3 + min_value, -2, max_value, // Row 4 + }; + + std::vector y_data{ + min_value, -2, max_value, // Row 1 + }; + + std::vector ref_output_data{ + false, true, true, // Row 1 + true, true, false, // Row 2 + true, false, true, // Row 3 + false, false, false, // Row 4 + }; + + Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); + Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); +} + +TEST_F(NotEqualTest, Int32) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(NotEqualTest, Int64) +{ + checkIntegerSimple(_memory_manager.get()); + checkIntegerBroadcast(_memory_manager.get()); + SUCCEED(); +} + +// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. +const float F_MIN = -128.0 / 128.0; +const float F_MAX = 127.0 / 128.0; + +TEST_F(NotEqualTest, Uint8Quantized) +{ + std::vector x_data{ + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 + }; + + std::vector y_data{ + 0.9, 0.5, 0.55, 0.5, // Row 1 + -1, 0, 0.05, 1, // Row 2 + }; + + std::vector ref_output_data{ + true, false, true, true, // Row 1 + true, false, false, true, // Row 2 + }; + + std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); + + std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 2); + Tensor y_tensor = makeInputTensor( + {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(NotEqualTest, Uint8QuantizedBroadcast) +{ + std::vector x_data{ + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + -1, 0.05, 0, 1, // Row 4 + }; + + std::vector y_data{ + -1, 0.05, 0, 1, // Row 1 + }; + + std::vector ref_output_data{ + true, true, true, true, // Row 1 + true, true, false, true, // Row 2 + true, true, true, true, // Row 3 + false, false, false, false, // Row 4 + }; + + std::pair quant_param = quantizationParams(F_MIN, F_MAX); + Tensor x_tensor = makeInputTensor( + {1, 4, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); + Tensor y_tensor = makeInputTensor( + {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); +} + +TEST_F(NotEqualTest, Input_Type_Mismatch_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(NotEqualTest, Input_Output_Type_NEG) +{ + Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(NotEqualTest, Float_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(NotEqualTest, Int32_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(NotEqualTest, Int64_Broadcast_NEG) +{ + Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.cpp new file mode 100644 index 0000000..4d3e5f2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/OneHot.h" +#include "kernels/Utils.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +namespace +{ + +template +void OneHotComputeImpl(const Tensor *indices_tensor, const Tensor *on_value_tensor, + const Tensor *off_value_tensor, int32_t depth, int32_t axis, + Tensor *output_tensor) +{ + // define input shape and correct axis + auto const &input_shape = indices_tensor->shape(); + axis = axis == -1 ? input_shape.num_dims() : axis; + + // TODO support other integer input types + auto const *indices = getTensorData(indices_tensor); + auto const on_value = getTensorData(on_value_tensor)[0]; + auto const off_value = getTensorData(off_value_tensor)[0]; + auto *output = getTensorData(output_tensor); + + // prefix_dim_size == # of elements before the axis + // depth == # of elements per axis + // suffix_dim_size == # of elements after the axis + auto prefix_dim_size = 1; + for (int32_t i = 0; i < axis; ++i) + { + prefix_dim_size *= input_shape.dim(i); + } + assert(prefix_dim_size > 0); + auto const suffix_dim_size = input_shape.num_elements() / prefix_dim_size; + + // View the indices as a matrix of size: + // prefix_dim_size x suffix_dim_size + // View the output as a matrix of size: + // prefix_dim_size x depth x suffix_dim_size + // Then the output is: + // output(i, j, k) == (indices(i, k) == j) ? on : off + for (int32_t i = 0; i < prefix_dim_size; ++i) + for (int32_t j = 0; j < depth; ++j) + for (int32_t k = 0; k < suffix_dim_size; ++k, ++output) + *output = indices[i * suffix_dim_size + k] == j ? on_value : off_value; +} + +} // namespace + +OneHot::OneHot(const Tensor *indices, const Tensor *depth, const Tensor *on_value, + const Tensor *off_value, Tensor *output, const OneHotParams ¶ms) + : KernelWithParams({indices, depth, on_value, off_value}, {output}, params) +{ + // Do nothing +} + +void OneHot::configure() +{ + // check types + LUCI_INTERPRETER_CHECK(indices()->element_type() == DataType::S32); + LUCI_INTERPRETER_CHECK(depth()->element_type() == DataType::S32); + LUCI_INTERPRETER_CHECK(on_value()->element_type() == off_value()->element_type()); + LUCI_INTERPRETER_CHECK(output()->element_type() == on_value()->element_type()); + + // check shape dependent parameters + LUCI_INTERPRETER_CHECK(on_value()->shape().num_elements() == 1); + LUCI_INTERPRETER_CHECK(off_value()->shape().num_elements() == 1); + LUCI_INTERPRETER_CHECK(depth()->shape().num_elements() == 1); + LUCI_INTERPRETER_CHECK(params().axis >= -1 && params().axis <= indices()->shape().num_dims()); + + // define parameters that affect the output shape + auto const depth_value = getTensorData(depth())[0]; + auto const &input_shape = indices()->shape(); + auto const input_dims = input_shape.num_dims(); + auto const axis = params().axis == -1 ? input_dims : params().axis; + + // define output shape + Shape output_shape(input_shape.num_dims() + 1); + { + for (int32_t d = 0; d < axis; ++d) + output_shape.dim(d) = input_shape.dim(d); + + output_shape.dim(axis) = depth_value; + + for (int32_t d = axis + 1; d < output_shape.num_dims(); ++d) + output_shape.dim(d) = input_shape.dim(d - 1); + } + + // reshape output + output()->resize(output_shape); +} + +void OneHot::execute() const +{ + auto const depth_value = getTensorData(depth())[0]; + auto const axis = params().axis; + + switch (output()->element_type()) + { + case loco::DataType::FLOAT32: + OneHotComputeImpl(indices(), on_value(), off_value(), depth_value, axis, output()); + break; + case loco::DataType::U8: + OneHotComputeImpl(indices(), on_value(), off_value(), depth_value, axis, output()); + break; + case loco::DataType::S16: + OneHotComputeImpl(indices(), on_value(), off_value(), depth_value, axis, output()); + break; + default: + // TODO Support other data types + throw std::runtime_error("Not supported, yet!"); + break; + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.h b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.h new file mode 100644 index 0000000..572f857 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_ONEHOT_H +#define LUCI_INTERPRETER_KERNELS_ONEHOT_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class OneHot : public KernelWithParams +{ +public: + OneHot(const Tensor *indices, const Tensor *depth, const Tensor *on_value, + const Tensor *off_value, Tensor *output, const OneHotParams ¶ms); + + const Tensor *indices() const { return _inputs[0]; } + const Tensor *depth() const { return _inputs[1]; } + const Tensor *on_value() const { return _inputs[2]; } + const Tensor *off_value() const { return _inputs[3]; } + + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_ONEHOT_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.test.cpp new file mode 100644 index 0000000..45b6968 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/OneHot.test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/OneHot.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list depth_data, + std::initializer_list on_value_data, std::initializer_list off_value_data, + int32_t axis, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + constexpr auto input_type = getElementType(); + constexpr auto output_type = getElementType(); + + Tensor input_tensor = makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor depth_tensor = makeInputTensor({}, depth_data, memory_manager.get()); + Tensor on_value_tensor = makeInputTensor({}, on_value_data, memory_manager.get()); + Tensor off_value_tensor = makeInputTensor({}, off_value_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(output_type); + + OneHotParams params{}; + params.axis = axis; + + OneHot kernel(&input_tensor, &depth_tensor, &on_value_tensor, &off_value_tensor, &output_tensor, + params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); +} + +template class OneHotTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(OneHotTest, DataTypes); + +TYPED_TEST(OneHotTest, BasicPattern) +{ + // axis 0 + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{4, 2, 3}, + /*input_data=*/ + { + 0, 3, 5, // + 7, 3, 0, // + }, + /*depth_data=*/{4}, /*on_value_data=*/{1}, /*off_value_data=*/{0}, + /*axis=*/0, + /*output_data=*/ + { + 1, 0, 0, // + 0, 0, 1, // + + 0, 0, 0, // + 0, 0, 0, // + + 0, 0, 0, // + 0, 0, 0, // + + 0, 1, 0, // + 0, 1, 0, // + }); + // axis 1 + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 4, 3}, + /*input_data=*/ + { + 0, 3, 5, // + 7, 3, 0, // + }, + /*depth_data=*/{4}, /*on_value_data=*/{1}, /*off_value_data=*/{0}, + /*axis=*/1, + /*output_data=*/ + { + 1, 0, 0, // + 0, 0, 0, // + 0, 0, 0, // + 0, 1, 0, // + + 0, 0, 1, // + 0, 0, 0, // + 0, 0, 0, // + 0, 1, 0, // + }); + // axis -1 + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3, 4}, + /*input_data=*/ + { + 0, 3, 5, // + 7, 3, 0, // + }, + /*depth_data=*/{4}, /*on_value_data=*/{1}, /*off_value_data=*/{0}, + /*axis=*/-1, + /*output_data=*/ + { + 1, 0, 0, 0, // + 0, 0, 0, 1, // + 0, 0, 0, 0, // + + 0, 0, 0, 0, // + 0, 0, 0, 1, // + 1, 0, 0, 0, // + }); +} + +TEST(OneHotTest, UnsupportedInputType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + // input type should be integer + Tensor input_tensor = makeInputTensor({1}, {0}, memory_manager.get()); + + Tensor depth_tensor = makeInputTensor({}, {1}, memory_manager.get()); + Tensor on_value_tensor = makeInputTensor({}, {1.0}, memory_manager.get()); + Tensor off_value_tensor = makeInputTensor({}, {0.0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + OneHotParams params = {-1}; + + OneHot kernel(&input_tensor, &depth_tensor, &on_value_tensor, &off_value_tensor, &output_tensor, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(OneHotTest, OutputTypeMismatch_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {0}, memory_manager.get()); + Tensor depth_tensor = makeInputTensor({}, {1}, memory_manager.get()); + + // type of on_value, off_value and output_tensor should be same + Tensor on_value_tensor = makeInputTensor({}, {1.0}, memory_manager.get()); + Tensor off_value_tensor = makeInputTensor({}, {0.0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + OneHotParams params = {-1}; + + OneHot kernel(&input_tensor, &depth_tensor, &on_value_tensor, &off_value_tensor, &output_tensor, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(OneHotTest, InvalidAxis_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {0}, memory_manager.get()); + Tensor depth_tensor = makeInputTensor({}, {1}, memory_manager.get()); + Tensor on_value_tensor = makeInputTensor({}, {1.0}, memory_manager.get()); + Tensor off_value_tensor = makeInputTensor({}, {0.0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + // axis should be in [-1, input_shape.rank] + OneHotParams params = {-2}; + + OneHot kernel(&input_tensor, &depth_tensor, &on_value_tensor, &off_value_tensor, &output_tensor, + params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.cpp new file mode 100644 index 0000000..5a6b05c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/PRelu.h" + +#include "kernels/BinaryOpCommon.h" +#include "kernels/Utils.h" + +#include +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +PRelu::PRelu(const Tensor *input, const Tensor *alpha, Tensor *output) + : Kernel({input, alpha}, {output}) +{ +} + +PRelu::~PRelu() +{ + // Destructor declared to delete vector of alpha quantized data properly +} + +void PRelu::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(alpha()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(input()->scales().size() <= 1); + LUCI_INTERPRETER_CHECK(output()->scales().size() <= 1); + + if (input()->element_type() == DataType::U8) + { + LUCI_INTERPRETER_CHECK(alpha()->scales().size() <= 1); // remove when CWQ kernel arrives + _alpha_multipliers.resize(1); + double alpha_multiplier = input()->scale() * alpha()->scale() / output()->scale(); + quantizeMultiplier(alpha_multiplier, &_alpha_multipliers[0].multiplier, + &_alpha_multipliers[0].shift); + double identity_multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); + } + else if (input()->element_type() == DataType::S16) + { + // Common check for correctness of quant params + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + for (size_t channel = 0; channel < alpha()->zero_points().size(); ++channel) + { + LUCI_INTERPRETER_CHECK(alpha()->zero_points()[channel] == 0); + } + // PRelu specific checks for CWQ + LUCI_INTERPRETER_CHECK(alpha()->quantized_dimension() == alpha()->shape().num_dims() - 1); + LUCI_INTERPRETER_CHECK(static_cast(alpha()->scales().size()) == + alpha()->shape().dim(alpha()->quantized_dimension())); + LUCI_INTERPRETER_CHECK(alpha()->shape().num_elements() == + input()->shape().dim(input()->shape().num_dims() - 1)); + + // all dimension of alpha except last one should be size 1 + for (int dim = 0; dim < alpha()->shape().num_dims() - 1; ++dim) + { + LUCI_INTERPRETER_CHECK(alpha()->shape().dim(dim) == 1); + } + + std::vector real_multipliers = + getQuantizedConvolutionMultiplers(input()->scale(), alpha()->scales(), output()->scale()); + + _alpha_multipliers = quantizeMultipliers(real_multipliers); + + double identity_multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); + } + output()->resize(calculateShapeForBroadcast(input()->shape(), alpha()->shape())); +} + +void PRelu::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void PRelu::evalFloat() const +{ + const auto input_data = getTensorData(input()); + const auto alpha_data = getTensorData(alpha()); + const auto size = getTensorShape(input()).FlatSize(); + auto output_data = getTensorData(output()); + + auto PReluFunc = [](float input, float alpha) { return input >= 0.0 ? input : input * alpha; }; + + if (input()->shape() != alpha()->shape()) + { + tflite::reference_ops::BroadcastBinaryFunction4DSlow( + getTensorShape(input()), getTensorData(input()), getTensorShape(alpha()), + getTensorData(alpha()), getTensorShape(output()), getTensorData(output()), + PReluFunc); + } + else + { + for (auto i = decltype(size){0}; i < size; ++i) + { + if (input_data[i] >= 0) + output_data[i] = input_data[i]; + else + output_data[i] = input_data[i] * alpha_data[i]; + } + } +} + +void PRelu::evalQuantized() const +{ + tflite::PreluParams op_params{}; + + op_params.input_offset = -input()->zero_point(); // Note the '-'. + op_params.alpha_offset = -alpha()->zero_point(); // Note the '-'. + op_params.output_offset = output()->zero_point(); + op_params.output_shift_1 = _output_shift_identity; + op_params.output_multiplier_1 = _output_multiplier_identity; + op_params.output_shift_2 = _alpha_multipliers[0].shift; + op_params.output_multiplier_2 = _alpha_multipliers[0].multiplier; + + if (input()->shape() != alpha()->shape()) + { + tflite::reference_ops::BroadcastPrelu4DSlow( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(alpha()), + getTensorData(alpha()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Prelu( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(alpha()), + getTensorData(alpha()), getTensorShape(output()), getTensorData(output())); + } +} + +static inline int16_t evalElemS16PRelu(int16_t input_val, int16_t alpha_val, + const ChannelQuantMultipliers &identity_mult, + const ChannelQuantMultipliers &alpha_mult) +{ + constexpr int32_t quantized_min = std::numeric_limits::min(); + constexpr int32_t quantized_max = std::numeric_limits::max(); + + const int32_t output_val = + input_val >= 0 + ? tflite::MultiplyByQuantizedMultiplier(static_cast(input_val), + identity_mult.multiplier, identity_mult.shift) + : tflite::MultiplyByQuantizedMultiplier(static_cast(input_val * alpha_val), + alpha_mult.multiplier, alpha_mult.shift); + const int32_t clamped_output = std::min(quantized_max, std::max(quantized_min, output_val)); + return clamped_output; +} + +void PRelu::evalQuantizedS16() const +{ + // Note that this kernel assumes alpha is CWQ + tflite::RuntimeShape input_shape = getTensorShape(input()); + const int16_t *input_data = input()->data(); + const int16_t *alpha_data = alpha()->data(); + int16_t *output_data = output()->data(); + + const ChannelQuantMultipliers pos_mult{_output_shift_identity, _output_multiplier_identity}; + + const int last_dim = input()->shape().num_dims() - 1; + + int32_t outer_dims_size = 1; + for (int i = 0; i < last_dim; ++i) + outer_dims_size *= input_shape.Dims(i); + int32_t quant_dim_size = input_shape.Dims(last_dim); + + for (int32_t outer_dims = 0; outer_dims < outer_dims_size; ++outer_dims) + for (int32_t quant_channel = 0; quant_channel < quant_dim_size; ++quant_channel) + { + const ChannelQuantMultipliers &neg_mult = _alpha_multipliers[quant_channel]; + size_t offset = static_cast(outer_dims) * static_cast(quant_dim_size); + offset += quant_channel; + + output_data[offset] = + evalElemS16PRelu(input_data[offset], alpha_data[quant_channel], pos_mult, neg_mult); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.h b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.h new file mode 100644 index 0000000..f7735d4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_PRELU_H +#define LUCI_INTERPRETER_KERNELS_PRELU_H + +#include "core/Kernel.h" +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class ChannelQuantMultipliers; + +class PRelu : public Kernel +{ +public: + PRelu(const Tensor *input, const Tensor *alpha, Tensor *output); + + ~PRelu(); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *alpha() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedS16() const; + +private: + std::vector _alpha_multipliers; + // TODO merge this into one ChannelQuantMultiplier object + int32_t _output_multiplier_identity = 0; + int _output_shift_identity = 0; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PRELU_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.test.cpp new file mode 100644 index 0000000..6d97382 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PRelu.test.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/PRelu.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list alpha_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list alpha_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor(alpha_shape, alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(PReluTest, FloatSimple) +{ + Check(/*input_shape=*/{2, 3}, /*alpha_shape=*/{2, 3}, + /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*alpha_data=*/ + { + 0.0f, 0.5f, 0.1f, // Row 1 + 0.0f, 0.5f, 0.1f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -0.5f, -0.2f, // Row 2 + }); + + SUCCEED(); +} + +TEST(PReluTest, FloatBroadcast) +{ + Check(/*input_shape=*/{1, 2, 2, 3}, /*alpha_shape=*/{1, 1, 3}, + /*output_shape=*/{1, 2, 2, 3}, + /*input_data=*/ + { + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 1.0f, 1.0f, 1.0f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -2.0f, -2.0f, -2.0f, // Row 2, Column 2 + }, + /*alpha_data=*/ + {0.0f, 1.0f, 2.0f}, + /*output_data=*/ + { + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 1.0f, 1.0f, 1.0f, // Row 1, Column 2 + 0.0f, -1.0f, -2.0f, // Row 2, Column 1 + 0.0f, -2.0f, -4.0f, // Row 2, Column 2 + }); + + SUCCEED(); +} + +float GetTolerance(float min, float max) { return (max - min) / 255.0; } + +TEST(PReluTest, Uint8Simple) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f}; + std::vector alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f}; + std::vector ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f}; + + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + + Tensor input_tensor = makeInputTensor( + {1, 2, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor( + {1, 2, 3, 1}, quant_param.first, quant_param.second, alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1})); + + SUCCEED(); +} + +TEST(PReluTest, Uint8Broadcast) +{ + std::vector input_data{ + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -0.25f, -0.25f, -0.25f, // Row 2, Column 2 + }; + std::vector alpha_data{0.0f, 0.5f, -0.5f}; + std::vector ref_output_data{ + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + 0.0f, -0.5f, 0.5f, // Row 2, Column 1 + 0.0f, -0.125f, 0.125f // Row 2, Column 2 + }; + std::vector ref_quant_output_data{ + 128, 128, 128, // Row 1, Column 1 + 192, 192, 192, // Row 1, Column 2 + 128, 64, 192, // Row 2, Column 1 + 128, 112, 144 // Row 2, Column 2 + }; + float kQuantizedTolerance = 2 * (1. / 256); + const float kMin = -1; + const float kMax = 127.f / 128.f; + std::pair quant_param = quantizationParams(kMin, kMax); + + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor( + {1, 2, 2, 3}, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor( + {1, 1, 3}, quant_param.first, quant_param.second, alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3})); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_quant_output_data)); +} + +TEST(PReluTest, SInt16_LWQ_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + // Rewrite this test in case layer-wise quantization for sint16 is supported + std::vector input_data(6); // data is not important + std::vector alpha_data(6); + + Tensor input_tensor = + makeInputTensor({1, 2, 3, 1}, 0.1, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor({1, 2, 3, 1}, 0.1, 0, alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, SInt16_CWQ_Simple) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{-0.8f, 0.2f, 0.9f, -0.7f, 0.1f, -0.4f}; + std::vector alpha_data{0.5f, 0.25f}; + std::vector ref_output_data{-0.4f, 0.2f, 0.9f, -0.175f, 0.1f, -0.1f}; + + std::vector alpha_scales{0.05f, 0.025f}; + std::vector zerop{0, 0}; + Tensor input_tensor = + makeInputTensor({1, 1, 3, 2}, 0.1, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor({2}, alpha_scales, zerop, 0, alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.025, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 3, 2})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(PReluTest, SInt16_CWQ_spatial_alpha_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data(6); // data is not important + std::vector alpha_data(6); + + std::vector alpha_scales{0.25f, 0.05f}; + std::vector zerop{0, 0}; + Tensor input_tensor = + makeInputTensor({1, 1, 3, 2}, 0.1, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1, 1, 3, 2}, alpha_scales, zerop, 3, + alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, SInt16_CWQ_wrong_dim_quant_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data(6); // data is not important + std::vector alpha_data(6); + + std::vector alpha_scales{0.25f}; + std::vector zerop{0}; + Tensor input_tensor = + makeInputTensor({1, 1, 3, 2}, 0.1, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1, 1, 1, 2}, alpha_scales, zerop, 1, + alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, SInt16_CWQ_uneven_shape1) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{-0.8f, 0.2f, 0.9f, -0.7f, 0.1f, -0.4f}; + std::vector alpha_data{0.5f, 0.25f}; + std::vector ref_output_data{-0.4f, 0.2f, 0.9f, -0.175f, 0.1f, -0.1f}; + + std::vector alpha_scales{0.05f, 0.025f}; + std::vector zerop{0, 0}; + Tensor input_tensor = + makeInputTensor({1, 1, 3, 2}, 0.1, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1, 1, 2}, alpha_scales, zerop, 2, + alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.025, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 3, 2})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(PReluTest, SInt16_CWQ_uneven_shape2) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{ + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -0.25f, -0.25f, -0.25f, // Row 2, Column 2 + }; + std::vector alpha_data{0.0f, 0.5f, -0.5f}; + std::vector ref_output_data{ + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + 0.0f, -0.5f, 0.5f, // Row 2, Column 1 + 0.0f, -0.125f, 0.125f // Row 2, Column 2 + }; + + std::vector alpha_scales{1.f, 0.05f, 0.1f}; + std::vector zerop{0, 0, 0}; + Tensor input_tensor = + makeInputTensor({1, 2, 2, 3}, 0.01, 0, input_data, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1, 1, 1, 3}, alpha_scales, zerop, 3, + alpha_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.001, 0); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(PReluTest, Input_Output_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({1}, {1.f}, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1}, {1.f}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, Input_Alpha_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({1}, {1.f}, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, Invalid_Input_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor alpha_tensor = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST(PReluTest, Input_Output_U8_CWQ_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector scales{1.f, 1.f}; + std::vector zerop{0, 0}; + std::vector dummy_data(4, 0.f); + Tensor input_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + Tensor output_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, Input_Output_S16_CWQ_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector scales{1.f, 1.f}; + std::vector zerop{0, 0}; + std::vector dummy_data(4, 0.f); + Tensor input_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + Tensor output_tensor = + makeInputTensor({2, 2}, scales, zerop, 0, dummy_data, memory_manager.get()); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PReluTest, Mixing_U8_S16_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector dummy_data(4, 0.f); + Tensor input_tensor = + makeInputTensor({2, 2}, 1.f, 0, dummy_data, memory_manager.get()); + Tensor alpha_tensor = + makeInputTensor({2, 2}, 1.f, 0, dummy_data, memory_manager.get()); + Tensor output_tensor = + makeInputTensor({2, 2}, 1.f, 0, dummy_data, memory_manager.get()); + + PRelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pack.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.cpp new file mode 100644 index 0000000..42aab33 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Pack.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Pack::Pack(std::vector inputs, Tensor *output, const PackParams ¶ms) + : KernelWithParams(std::move(inputs), {output}, params) +{ +} + +void Pack::configure() +{ + LUCI_INTERPRETER_CHECK(_inputs.size() == static_cast(params().values_count)); + const Tensor *t0 = _inputs[0]; + const int dimension_size = t0->shape().num_dims() + 1; + int axis = params().axis; + if (axis < 0) + { + axis += dimension_size; + } + LUCI_INTERPRETER_CHECK(axis >= 0 && axis <= t0->shape().num_dims()); + + if (t0->element_type() != DataType::S32 && t0->element_type() != DataType::FLOAT32 && + t0->element_type() != DataType::U8 && t0->element_type() != DataType::S8 && + t0->element_type() != DataType::S16 && t0->element_type() != DataType::S64) + { + throw std::runtime_error("Unsupported type."); + } + + for (uint32_t i = 1; i < _inputs.size(); ++i) + { + const Tensor *tensor = _inputs[i]; + LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type()); + LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims()); + for (int d = 0; d < t0->shape().num_dims(); ++d) + { + LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d)); + } + } + + Shape output_shape(dimension_size); + int i = 0; + for (int index = 0; index < dimension_size; ++index) + { + if (index == axis) + { + output_shape.dim(index) = params().values_count; + } + else + { + output_shape.dim(index) = t0->shape().dim(i++); + } + } + + if (t0->element_type() == DataType::U8 || t0->element_type() == DataType::S8 || + t0->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(output()->zero_point() == t0->zero_point()); + LUCI_INTERPRETER_CHECK(output()->scale() == t0->scale()); + // Guarantee input/output quantization params match as we do not support + // packing quantized tensors. + for (int i = 0; i < params().values_count; i++) + { + LUCI_INTERPRETER_CHECK(_inputs[i]->zero_point() == t0->zero_point()); + LUCI_INTERPRETER_CHECK(_inputs[i]->scale() == t0->scale()); + } + } + + output()->resize(output_shape); +} + +void Pack::execute() const +{ + switch (_inputs[0]->element_type()) + { + case DataType::FLOAT32: + evalGeneric(); + break; + case DataType::U8: + evalGeneric(); + break; + case DataType::S8: + evalGeneric(); + break; + case DataType::S16: + evalGeneric(); + break; + case DataType::S32: + evalGeneric(); + break; + case DataType::S64: + evalGeneric(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void Pack::evalGeneric() const +{ + const Tensor *t0 = _inputs[0]; + const int dimension_size = t0->shape().num_dims() + 1; + int axis = params().axis; + if (axis < 0) + { + axis += dimension_size; + } + + VectorOfTensors inputs(_inputs); + tflite::PackParams params{}; + params.axis = axis; + params.inputs_count = _inputs.size(); + tflite::reference_ops::Pack(params, inputs.shapes(), inputs.data(), getTensorShape(output()), + getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pack.h b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.h new file mode 100644 index 0000000..4a2fcfd --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_PACK_H +#define LUCI_INTERPRETER_KERNELS_PACK_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Pack : public KernelWithParams +{ +public: + Pack(std::vector inputs, Tensor *output, const PackParams ¶ms); + + const Tensor *input(int index) const { return _inputs[index]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void evalGeneric() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PACK_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pack.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.test.cpp new file mode 100644 index 0000000..d16320b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pack.test.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Pack.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::vector> input_shapes, + std::initializer_list output_shape, std::vector> input_datas, + std::initializer_list output_data, int32_t axis) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + std::vector inputs(input_datas.size()); + std::vector tmp_inputs; + for (int i = 0; i < input_datas.size(); i++) + { + if (std::is_same::value || std::is_same::value || + std::is_same::value) + { + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {}, "")); + memory_manager->allocate_memory(tmp_inputs[i]); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } + else if (std::is_same::value || std::is_same::value) + { + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f / 255}, {128}}, "")); + memory_manager->allocate_memory(tmp_inputs[i]); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } + else + { + assert((std::is_same::value) && "unexpected dtype is tested"); + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f}, {0}}, "")); + memory_manager->allocate_memory(tmp_inputs[i]); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } + } + for (int i = 0; i < input_datas.size(); i++) + { + inputs[i] = &tmp_inputs[i]; + } + + Tensor output_tensor = makeOutputTensor(element_type); + if (std::is_same::value || std::is_same::value) + { + output_tensor = makeOutputTensor(element_type, 1.0f / 255, 128); + } + else if (std::is_same::value) + { + output_tensor = makeOutputTensor(element_type, 1.0f, 0); + } + + PackParams params{}; + params.axis = axis; + params.values_count = input_datas.size(); + Pack kernel(inputs, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template class PackTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(PackTest, DataTypes); + +TYPED_TEST(PackTest, ThreeInputs) +{ + Check(/*input_shapes=*/{{2}, {2}, {2}}, + /*output_shape=*/{3, 2}, + /*input_datas=*/ + {{1, 4}, {2, 5}, {3, 6}}, + /*output_data=*/ + {1, 4, 2, 5, 3, 6}, /*axis=*/0); + + SUCCEED(); +} + +TYPED_TEST(PackTest, NegAxis) +{ + Check(/*input_shapes=*/{{2}, {2}, {2}}, + /*output_shape=*/{2, 3}, + /*input_datas=*/ + {{1, 4}, {2, 5}, {3, 6}}, + /*output_data=*/ + {1, 2, 3, 4, 5, 6}, /*axis=*/-1); + + SUCCEED(); +} + +TEST(Pack, MismatchingInputValuesCount_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input1_data{1, 4}; + std::vector input2_data{2, 5}; + std::vector input3_data{3, 6}; + Tensor input1_tensor = makeInputTensor({2}, input1_data, memory_manager.get()); + Tensor input2_tensor = makeInputTensor({2}, input2_data, memory_manager.get()); + Tensor input3_tensor = makeInputTensor({2}, input3_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + PackParams params{}; + { + params.axis = 0; + params.values_count = 2; + + Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); + } +} + +TEST(Pack, InvalidInputAxis_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input1_data{1, 4}; + std::vector input2_data{2, 5}; + std::vector input3_data{3, 6}; + Tensor input1_tensor = makeInputTensor({2}, input1_data, memory_manager.get()); + Tensor input2_tensor = makeInputTensor({2}, input2_data, memory_manager.get()); + Tensor input3_tensor = makeInputTensor({2}, input3_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + PackParams params{}; + { + params.axis = 2; + params.values_count = 3; + + Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); + } +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pad.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.cpp new file mode 100644 index 0000000..c07f6e3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Pad.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Pad::Pad(const Tensor *input, const Tensor *paddings, Tensor *output) + : Kernel({input, paddings}, {output}) +{ +} + +void Pad::configure() +{ + const Shape &input_shape = input()->shape(); + const int num_dims = input_shape.num_dims(); + + if (num_dims > 4) + throw std::runtime_error("Unsupported number of dimensions."); + + assert(output()->element_type() == input()->element_type()); + assert(paddings()->element_type() == DataType::S32); + // Paddings shape should be [N, 2]. + assert(paddings()->shape().num_dims() == 2); + assert(paddings()->shape().dim(0) == num_dims); + assert(paddings()->shape().dim(1) == 2); + + Shape output_shape(num_dims); + const auto *paddings_data = getTensorData(paddings()); + for (int i = 0; i < num_dims; ++i) + { + const int32_t padding_before = paddings_data[i * 2]; + const int32_t padding_after = paddings_data[i * 2 + 1]; + assert(padding_before >= 0 && padding_after >= 0); + output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after; + } + + output()->resize(output_shape); +} + +void Pad::execute() const +{ + const int num_dims = input()->shape().num_dims(); + + tflite::PadParams params{}; + params.left_padding_count = num_dims; + params.right_padding_count = num_dims; + + const auto *paddings_data = getTensorData(paddings()); + for (int i = num_dims - 1; i >= 0; --i) + { + params.left_padding[i] = paddings_data[i * 2]; + params.right_padding[i] = paddings_data[i * 2 + 1]; + } + + switch (input()->element_type()) + { + case DataType::FLOAT32: + { + const float pad_value = 0.0f; + tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), + &pad_value, getTensorShape(output()), + getTensorData(output())); + break; + } + case DataType::U8: + { + assert(output()->zero_point() >= std::numeric_limits::min()); + assert(output()->zero_point() <= std::numeric_limits::max()); + const auto pad_value = static_cast(output()->zero_point()); + tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), + &pad_value, getTensorShape(output()), + getTensorData(output())); + break; + } + case DataType::S8: + { + assert(output()->zero_point() >= std::numeric_limits::min()); + assert(output()->zero_point() <= std::numeric_limits::max()); + const auto pad_value = static_cast(output()->zero_point()); + tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), + &pad_value, getTensorShape(output()), + getTensorData(output())); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pad.h b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.h new file mode 100644 index 0000000..e05b47f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_PAD_H +#define LUCI_INTERPRETER_KERNELS_PAD_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Pad : public Kernel +{ +public: + Pad(const Tensor *input, const Tensor *paddings, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *paddings() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PAD_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pad.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.test.cpp new file mode 100644 index 0000000..dd3ce94 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pad.test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Pad.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +float GetTolerance(float min, float max) { return (max - min) / 255.0; } + +TEST(Pad, Uint8) +{ + std::unique_ptr memory_manager = std::make_unique(); + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + std::vector input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3}; + std::vector paddings_data{0, 0, 0, 2, 1, 3, 0, 0}; + Tensor input_tensor = makeInputTensor( + {1, 2, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({4, 2}, paddings_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0, -0.8, 0.2, 0.9, 0, 0, 0, 0, 0.7, 0.1, -0.3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1})); +} + +TEST(Pad, Int8) +{ + std::unique_ptr memory_manager = std::make_unique(); + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + std::vector input_data{-0.2, 0.4, 0.5, -0.7, -0.1, -0.9, 0.7, 0.1, 0.2}; + std::vector paddings_data{0, 0, 1, 2, 2, 1, 0, 0}; + Tensor input_tensor = makeInputTensor( + {1, 3, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({4, 2}, paddings_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, quant_param.first, quant_param.second); + + Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0, 0, 0, 0, 0, 0, 0, 0, -0.2, 0.4, 0.5, 0, + 0, 0, -0.7, -0.1, -0.9, 0, 0, 0, 0.7, 0.1, 0.2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 6, 6, 1})); +} + +TEST(Pad, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6}; + std::vector paddings_data{1, 0, 0, 2, 0, 3, 0, 0}; + Tensor input_tensor = + makeInputTensor({1, 2, 3, 1}, input_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({4, 2}, paddings_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 4, 5, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::initializer_list ref_output_shape{2, 4, 6, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.cpp new file mode 100644 index 0000000..197cdaa --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/PadV2.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +PadV2::PadV2(const Tensor *input, const Tensor *paddings, const Tensor *constant_values, + Tensor *output) + : Kernel({input, paddings, constant_values}, {output}) +{ +} + +void PadV2::configure() +{ + const Shape &input_shape = input()->shape(); + const int num_dims = input_shape.num_dims(); + + if (num_dims > 4) + throw std::runtime_error("Unsupported number of dimensions."); + + assert(output()->element_type() == input()->element_type()); + assert(paddings()->element_type() == DataType::S32); + assert(constant_values()->element_type() == output()->element_type()); + // Paddings shape should be [N, 2]. + assert(paddings()->shape().num_dims() == 2); + assert(paddings()->shape().dim(0) == num_dims); + assert(paddings()->shape().dim(1) == 2); + // Constant values elements number should be 1. + assert(constant_values()->shape().num_elements() == 1); + + Shape output_shape(num_dims); + const auto *paddings_data = getTensorData(paddings()); + for (int i = 0; i < num_dims; ++i) + { + const int32_t padding_before = paddings_data[i * 2]; + const int32_t padding_after = paddings_data[i * 2 + 1]; + assert(padding_before >= 0 && padding_after >= 0); + output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after; + } + + output()->resize(output_shape); +} + +void PadV2::execute() const +{ + const int num_dims = input()->shape().num_dims(); + + tflite::PadParams params{}; + params.left_padding_count = num_dims; + params.right_padding_count = num_dims; + + const auto *paddings_data = getTensorData(paddings()); + for (int i = num_dims - 1; i >= 0; --i) + { + params.left_padding[i] = paddings_data[i * 2]; + params.right_padding[i] = paddings_data[i * 2 + 1]; + } + + switch (input()->element_type()) + { + case DataType::FLOAT32: + { + const auto pad_value = getTensorData(constant_values())[0]; + tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), + &pad_value, getTensorShape(output()), + getTensorData(output())); + break; + } + case DataType::U8: + { + assert(output()->zero_point() >= std::numeric_limits::min()); + assert(output()->zero_point() <= std::numeric_limits::max()); + const auto pad_value = getTensorData(constant_values())[0]; + tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), + &pad_value, getTensorShape(output()), + getTensorData(output())); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.h b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.h new file mode 100644 index 0000000..48a31f5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_PAD_V2_H +#define LUCI_INTERPRETER_KERNELS_PAD_V2_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class PadV2 : public Kernel +{ +public: + PadV2(const Tensor *input, const Tensor *paddings, const Tensor *constant_values, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *paddings() const { return _inputs[1]; } + const Tensor *constant_values() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PAD_V2_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.test.cpp new file mode 100644 index 0000000..41efaff --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/PadV2.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/PadV2.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +float GetTolerance(float min, float max) { return (max - min) / 255.0; } + +TEST(PadV2, Uint8) +{ + std::unique_ptr memory_manager = std::make_unique(); + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::pair quant_param = quantizationParams(-1.0f, 1.0f); + std::vector input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3}; + std::vector paddings_data{0, 0, 0, 2, 1, 3, 0, 0}; + std::vector constant_values_data{0.5}; + Tensor input_tensor = makeInputTensor( + {1, 2, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({4, 2}, paddings_data, memory_manager.get()); + Tensor constant_values = makeInputTensor( + {1}, quant_param.first, quant_param.second, constant_values_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + PadV2 kernel(&input_tensor, &paddings_tensor, &constant_values, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data = { + 0.5, -0.8, 0.2, 0.9, 0.5, 0.5, 0.5, 0.5, 0.7, 0.1, -0.3, 0.5, 0.5, 0.5, // + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}; // + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(ref_output_data, kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1})); +} + +TEST(PadV2, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + std::vector input_data{1, 2, 3, 4, 5, 6}; + std::vector paddings_data{1, 0, 0, 2, 0, 3, 0, 0}; + std::vector constant_values_data{7}; + Tensor input_tensor = + makeInputTensor({1, 2, 3, 1}, input_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({4, 2}, paddings_data, memory_manager.get()); + Tensor constant_values = + makeInputTensor({1}, constant_values_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + PadV2 kernel(&input_tensor, &paddings_tensor, &constant_values, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 1, 2, 3, 7, 7, 7, 4, 5, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + std::initializer_list ref_output_shape{2, 4, 6, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pow.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.cpp new file mode 100644 index 0000000..722c640 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Pow.h" +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Pow::Pow(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void Pow::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); + + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Pow::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + eval(); + break; + case DataType::S32: + eval(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void Pow::eval() const +{ + tflite::ArithmeticParams params{}; + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastPow4DSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Pow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pow.h b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.h new file mode 100644 index 0000000..8ff865e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_POW_H +#define LUCI_INTERPRETER_KERNELS_POW_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Pow : public Kernel +{ +public: + Pow(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void eval() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_POW_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Pow.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.test.cpp new file mode 100644 index 0000000..0e85811 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Pow.test.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Pow.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class PowTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(PowTest, SimplePow) +{ + std::initializer_list base_shape = {1, 1, 3, 2}; + + std::vector input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f}; + std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + std::vector test_outputs{0.786f, 1.2838f, 1.043f, 0.7071f, 0.8f, 1.08956f}; + + Tensor input1_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(base_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape)); +} + +TEST_F(PowTest, FloatBroadcastPow) +{ + std::initializer_list input1_shape = {1, 3}; + std::initializer_list input2_shape = {3, 1}; + + std::vector input1_data{0.3f, 2.3f, 0.9f}; + std::vector input2_data{0.2f, 0.3f, 0.4f}; + std::vector test_outputs{0.786f, 1.18126f, 0.9791f, 0.6968f, 1.28386f, + 0.96888f, 0.6178f, 1.3953f, 0.9587f}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); +} + +TEST_F(PowTest, IntPow) +{ + std::initializer_list base_shape = {1, 3}; + + std::vector input_data{2, 3, 4}; + std::vector test_outputs{4, 27, 256}; + + Tensor input1_tensor = + makeInputTensor(base_shape, input_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(base_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(test_outputs)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape)); +} + +TEST_F(PowTest, Input_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.0f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {1.0f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(PowTest, Input_Type_Mismatch_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.0f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {4}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(PowTest, Invalid_Input_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Pow kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.cpp new file mode 100644 index 0000000..0c8544a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Quantize.h" +#include "kernels/Utils.h" +#include "PALQuantize.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +namespace +{ + +template void call_requantize(const Tensor *input, Tensor *output) +{ + int32_t multiplier; + int shift; + + const double effective_output_scale = input->scale() / output->scale(); + quantizeMultiplier(effective_output_scale, &multiplier, &shift); + + const auto input_shape = getTensorShape(input); + const auto output_shape = getTensorShape(output); + const auto size = tflite::MatchingFlatSize(input_shape, output_shape); + + const auto input_data = getTensorData(input); + + switch (output->element_type()) + { + case loco::DataType::S8: + luci_interpreter_pal::Requantize(input_data, size, multiplier, shift, input->zero_point(), + output->zero_point(), getTensorData(output)); + break; + case loco::DataType::U8: + luci_interpreter_pal::Requantize(input_data, size, multiplier, shift, input->zero_point(), + output->zero_point(), getTensorData(output)); + break; + case loco::DataType::S16: + luci_interpreter_pal::Requantize(input_data, size, multiplier, shift, input->zero_point(), + output->zero_point(), getTensorData(output)); + break; + default: + throw std::runtime_error("Unsupported quantized type, yet!"); + } +} + +} // namespace + +Quantize::Quantize(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Quantize::configure() +{ + + if (input()->element_type() == loco::DataType::S16) + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0); + + switch (input()->element_type()) + { + case loco::DataType::FLOAT32: + { + LUCI_INTERPRETER_CHECK(output()->element_type() == loco::DataType::U8 || + output()->element_type() == loco::DataType::S8 || + output()->element_type() == loco::DataType::S16); + break; + } + case loco::DataType::S16: + case loco::DataType::S8: + case loco::DataType::U8: + { + LUCI_INTERPRETER_CHECK(output()->element_type() == loco::DataType::S8 || + output()->element_type() == loco::DataType::U8 || + output()->element_type() == loco::DataType::S16); + if (output()->element_type() == loco::DataType::S16) + { + LUCI_INTERPRETER_CHECK(output()->zero_point() == 0); + } + break; + } + default: + throw std::runtime_error("Unsupported type"); + } + + output()->resize(input()->shape()); +} + +void Quantize::execute() const +{ + switch (input()->element_type()) + { + case loco::DataType::FLOAT32: + { + tflite::QuantizationParams op_params; + op_params.zero_point = output()->zero_point(); + op_params.scale = output()->scale(); + const auto input_data = getTensorData(input()); + + switch (output()->element_type()) + { + case loco::DataType::S8: + { + luci_interpreter_pal::Quantize(op_params, getTensorShape(input()), input_data, + getTensorShape(output()), getTensorData(output())); + break; + } + case loco::DataType::U8: + { + luci_interpreter_pal::Quantize(op_params, getTensorShape(input()), input_data, + getTensorShape(output()), + getTensorData(output())); + break; + } + case loco::DataType::S16: + { + luci_interpreter_pal::Quantize(op_params, getTensorShape(input()), input_data, + getTensorShape(output()), + getTensorData(output())); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } + break; + } + case loco::DataType::S16: + { + call_requantize(input(), output()); + break; + } + case loco::DataType::S8: + { + call_requantize(input(), output()); + break; + } + case loco::DataType::U8: + { + call_requantize(input(), output()); + break; + } + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.h b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.h new file mode 100644 index 0000000..006c536 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_QUANTIZE_H +#define LUCI_INTERPRETER_KERNELS_QUANTIZE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Quantize : public Kernel +{ +public: + Quantize(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_QUANTIZE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.test.cpp new file mode 100644 index 0000000..22e67fe --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Quantize.test.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Quantize.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class QuantizeTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(QuantizeTest, FloatUint8) +{ + std::vector input_data{-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}; + + std::vector ref_output_data{0, 1, 2, 3, 4, 251, 252, 253, 254, 255}; + + Tensor input_tensor = + makeInputTensor({2, 5}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, /*scale*/ 0.5, /*zero_point*/ 127); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(QuantizeTest, FloatInt8) +{ + std::vector input_data{-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}; + + std::vector ref_output_data{-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}; + + Tensor input_tensor = + makeInputTensor({2, 5}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, /*scale*/ 0.5, /*zero_point*/ -1); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(QuantizeTest, FloatInt16) +{ + std::vector input_data{-63.5, -63, -3, -2, -1, 1, 2, 3, 63.5, 64}; + + std::vector ref_output_data{-12700, -12600, -600, -400, -200, + 200, 400, 600, 12700, 12800}; + + Tensor input_tensor = + makeInputTensor({2, 5}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, /*scale*/ 0.005, /*zero_point*/ 0); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 5})); +} + +TEST_F(QuantizeTest, Int16Int16) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::vector ref_output_data{2, 4, 6, 8, 10, 12, 14, 16, 18, 20}; + + Tensor input_tensor = makeInputTensor( + {1, 1, 2, 5}, /*scale*/ 1.0, /*zero_point*/ 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, /*scale*/ 0.5, /*zero_point*/ 0); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 5})); +} + +TEST_F(QuantizeTest, Int8Int8) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::vector ref_output_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; + + Tensor input_tensor = makeInputTensor( + {1, 1, 2, 5}, /*scale*/ 0.5, /*zero_point*/ -1, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, /*scale*/ 0.5, /*zero_point*/ -1); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 5})); +} + +TEST_F(QuantizeTest, Uint8Uint8) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::vector ref_output_data{129, 131, 133, 135, 137, 139, 141, 143, 145, 147}; + + Tensor input_tensor = makeInputTensor( + {1, 1, 2, 5}, /*scale*/ 0.5, /*zero_point*/ 127, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, /*scale*/ 0.5, /*zero_point*/ 127); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 5})); +} + +TEST_F(QuantizeTest, Int16Int8) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::vector ref_output_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; + + Tensor input_tensor = makeInputTensor( + {1, 1, 2, 5}, /*scale*/ 1.0, /*zero_point*/ 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, /*scale*/ 0.5, /*zero_point*/ -1); + + Quantize kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 5})); +} + +TEST_F(QuantizeTest, InvalidInputType_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8, /*scale*/ 0.5, /*zero_point*/ -1); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(QuantizeTest, InvalidOutputTypeForFloatInput_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(QuantizeTest, InvalidOutputTypeForInt16Input_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(QuantizeTest, InvalidOutputTypeForInt8Input_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(QuantizeTest, InvalidOutputTypeForUint8Input_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(QuantizeTest, InvalidInputZeroPoint_NEG) +{ + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + Tensor input_tensor = + makeInputTensor({1, 1, 2, 5}, 0.5, -1, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + + Quantize kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.cpp new file mode 100644 index 0000000..747ec6c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Relu.h" +#include "kernels/Utils.h" + +#include "PALRelu.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Relu::Relu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Relu::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (input()->element_type() == DataType::S16) + { + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + } + + if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16) + { + double multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift); + } + output()->resize(input()->shape()); +} + +void Relu::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Relu::evalFloat() const +{ + const auto input_data = getTensorData(input()); + const auto input_shape = getTensorShape(input()); + auto output_data = getTensorData(output()); + auto output_shape = getTensorShape(output()); + + luci_interpreter_pal::Relu(input_shape, input_data, output_shape, output_data); +} + +void Relu::evalQuantized() const +{ + tflite::ReluParams params; + params.input_offset = input()->zero_point(); + params.output_offset = output()->zero_point(); + params.output_multiplier = _output_multiplier; + params.output_shift = _output_shift; + + params.quantized_activation_min = + std::max(static_cast(std::numeric_limits::min()), params.output_offset); + params.quantized_activation_max = static_cast(std::numeric_limits::max()); + + luci_interpreter_pal::ReluX(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void Relu::evalQuantizedS16() const +{ + const auto *input_data = getTensorData(input()); + auto *output_data = getTensorData(output()); + + constexpr int32_t output_min = 0; + constexpr int32_t output_max = std::numeric_limits::max(); + + const int32_t num_elements = input()->shape().num_elements(); + + for (int32_t i = 0; i < num_elements; ++i) + { + const int32_t input_val = input_data[i]; + int32_t output_val = + tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift); + output_val = std::max(output_val, output_min); + output_val = std::min(output_val, output_max); + output_data[i] = static_cast(output_val); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu.h b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.h new file mode 100644 index 0000000..b813f0c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RELU_H +#define LUCI_INTERPRETER_KERNELS_RELU_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Relu : public Kernel +{ +public: + Relu(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedS16() const; + +private: + int32_t _output_multiplier{0}; + int32_t _output_shift{0}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RELU_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.test.cpp new file mode 100644 index 0000000..bd32e3c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu.test.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Relu.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ReluTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(ReluTest, FloatSimple) +{ + std::vector input_data{ + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }; + + std::vector ref_output_data{ + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, 0.0f, 0.0f, // Row 2 + }; + + Tensor input_tensor = + makeInputTensor({2, 3}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Relu kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(ReluTest, Uint8Quantized) +{ + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 7, 1, // + }; + // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. + const float f_min = (-128.0 / 128.0) * 8; + const float f_max = (127.0 / 128.0) * 8; + + std::pair quant_param = quantizationParams(f_min, f_max); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Relu kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({128, 128, 160, 192, 176, 128, 240, 144})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1})); +} + +TEST_F(ReluTest, Uint8Requantized) +{ + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 7, 1, // + }; + + // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. + const float in_min = (-128.0 / 128.0) * 8; + const float in_max = (127.0 / 128.0) * 8; + const float out_min = (0.0 / 256.0) * 8; + const float out_max = (255.0 / 256.0) * 8; + + std::pair quant_input = quantizationParams(in_min, in_max); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_input.first, quant_input.second, input_data, _memory_manager.get()); + + std::pair quant_output = quantizationParams(out_min, out_max); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); + + Relu kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({0, 0, 64, 128, 96, 0, 224, 32})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1})); +} + +TEST_F(ReluTest, SInt16) +{ + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 7, 1, // + }; + std::vector ref_output_data{ + 0, 0, 2, 4, // + 3, 0, 7, 1, // + }; + + Tensor input_tensor = + makeInputTensor({1, 2, 4, 1}, 0.5, 0, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.25, 0); + + Relu kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(ReluTest, Input_Output_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Relu kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(ReluTest, Invalid_Input_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Relu kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.cpp new file mode 100644 index 0000000..07205ed --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Relu6.h" +#include "kernels/Utils.h" + +#include "PALRelu6.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Relu6::Relu6(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Relu6::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + if (input()->element_type() == DataType::U8) + { + double multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift); + } + output()->resize(input()->shape()); +} + +void Relu6::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Relu6::evalFloat() const +{ + const auto input_data = getTensorData(input()); + const auto input_shape = getTensorShape(input()); + auto output_data = getTensorData(output()); + auto output_shape = getTensorShape(output()); + + luci_interpreter_pal::Relu6(input_shape, input_data, output_shape, output_data); +} + +void Relu6::evalQuantized() const +{ + tflite::ReluParams params; + params.input_offset = input()->zero_point(); + params.output_offset = output()->zero_point(); + params.output_multiplier = _output_multiplier; + params.output_shift = _output_shift; + + params.quantized_activation_min = + std::max(static_cast(std::numeric_limits::min()), params.output_offset); + params.quantized_activation_max = + std::min(static_cast(std::numeric_limits::max()), + params.output_offset + static_cast(roundf(6.f / output()->scale()))); + + luci_interpreter_pal::ReluX(params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.h b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.h new file mode 100644 index 0000000..f5030b5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RELU6_H +#define LUCI_INTERPRETER_KERNELS_RELU6_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Relu6 : public Kernel +{ +public: + Relu6(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + +private: + int32_t _output_multiplier{0}; + int32_t _output_shift{0}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RELU6_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.test.cpp new file mode 100644 index 0000000..af7b3f3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Relu6.test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Relu6.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class Relu6Test : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(Relu6Test, FloatSimple) +{ + std::vector input_data{ + 0.0f, 1.0f, 3.0f, // Row 1 + 7.0f, -1.0f, -2.0f, // Row 2 + }; + + std::vector ref_output_data{ + 0.0f, 1.0f, 3.0f, // Row 1 + 6.0f, 0.0f, 0.0f, // Row 2 + }; + + Tensor input_tensor = + makeInputTensor({2, 3}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Relu6 kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(Relu6Test, Uint8Quantized) +{ + // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. + const float f_min = (-128.0 / 128.0) * 10; + const float f_max = (127.0 / 128.0) * 10; + const float tolerance = (f_max - f_min) / 255.0; + + std::vector input_data{ + 0, -6, 2, 8, // + -2, 3, 7, 1, // + }; + + std::pair quant_param = quantizationParams(f_min, f_max); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + + Relu6 kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({128, 128, 154, 205, 128, 166, 205, 141})); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance)); +} + +TEST_F(Relu6Test, Uint8Requantized) +{ + // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. + const float in_min = (-128.0 / 128.0) * 10; + const float in_max = (127.0 / 128.0) * 10; + const float out_min = (0.0 / 256.0) * 0; + const float out_max = (255.0 / 256.0) * 6; + const float tolerance = (in_max - in_min) / 255.0; + + std::vector input_data{ + 0, -6, 2, 8, // + -2, 3, 7, 1, // + }; + + std::pair quant_input = quantizationParams(in_min, in_max); + Tensor input_tensor = makeInputTensor( + {1, 2, 4, 1}, quant_input.first, quant_input.second, input_data, _memory_manager.get()); + + std::pair quant_output = quantizationParams(out_min, out_max); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); + + Relu6 kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray({0, 0, 87, 255, 0, 127, 255, 43})); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance)); +} + +TEST_F(Relu6Test, Input_Output_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Relu6 kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(Relu6Test, Invalid_Input_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Relu6 kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.cpp new file mode 100644 index 0000000..61d3300 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Reshape.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +static Shape extractShapeFromTensor(const Tensor *tensor) +{ + assert(tensor->element_type() == DataType::S32); + Shape shape(tensor->shape().num_elements()); + const auto *shape_data = tensor->data(); + for (int i = 0; i < tensor->shape().num_elements(); ++i) + { + shape.dim(i) = shape_data[i]; + } + return shape; +} + +static void resolveUnknownDimension(const Shape &input_shape, Shape *output_shape) +{ + const int32_t num_input_elements = input_shape.num_elements(); + int32_t num_output_elements = 1; + int unknown_dim_index = -1; + for (int i = 0; i < output_shape->num_dims(); ++i) + { + const int32_t value = output_shape->dim(i); + if (value == -1) + { + assert(unknown_dim_index == -1); + unknown_dim_index = i; + } + else + { + num_output_elements *= value; + } + } + if (unknown_dim_index != -1) + { + output_shape->dim(unknown_dim_index) = num_input_elements / num_output_elements; + num_output_elements *= output_shape->dim(unknown_dim_index); + } + assert(num_output_elements == num_input_elements); +} + +Reshape::Reshape(const Tensor *input, const Tensor *shape, Tensor *output) + : Kernel({input, shape}, {output}) +{ +} + +void Reshape::configure() +{ + Shape output_shape = extractShapeFromTensor(shape()); + resolveUnknownDimension(input()->shape(), &output_shape); + output()->resize(output_shape); +} + +void Reshape::execute() const +{ + const auto *input_data = input()->data(); + auto *output_data = output()->data(); + + const size_t element_size = getDataTypeSize(input()->element_type()); + const int32_t num_elements = input()->shape().num_elements(); + std::memcpy(output_data, input_data, num_elements * element_size); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.h b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.h new file mode 100644 index 0000000..99b947f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RESHAPE_H +#define LUCI_INTERPRETER_KERNELS_RESHAPE_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Reshape : public Kernel +{ +public: + Reshape(const Tensor *input, const Tensor *shape, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *shape() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RESHAPE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.test.cpp new file mode 100644 index 0000000..c2ff3ea --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Reshape.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Reshape.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ReshapeTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +// TODO Test types other than FLOAT32. + +TEST_F(ReshapeTest, Regular) +{ + Shape input_shape{1, 2, 2, 3}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape shape_shape{2}; + std::vector shape_data{3, 4}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor shape_tensor = + makeInputTensor(shape_shape, shape_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); +} + +TEST_F(ReshapeTest, UnknownDimension) +{ + Shape input_shape{2, 1, 2, 3}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape shape_shape{3}; + std::vector shape_data{2, -1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor shape_tensor = + makeInputTensor(shape_shape, shape_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp new file mode 100644 index 0000000..e2ddd6a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ResizeBilinear.h" + +#include "kernels/Utils.h" + +#include "PALResizeBilinear.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ResizeBilinear::ResizeBilinear(const Tensor *input, const Tensor *size, Tensor *output, + const ResizeBilinearParams ¶ms) + : KernelWithParams({input, size}, {output}, params) +{ +} + +void ResizeBilinear::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32); + if (params().half_pixel_centers && params().align_corners) + throw std::runtime_error("If half_pixel_centers is True, align_corners must be False."); + LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2); + Shape output_shape(4); + output_shape.dim(0) = input()->shape().dim(0); + output_shape.dim(1) = getTensorData(size())[0]; + output_shape.dim(2) = getTensorData(size())[1]; + output_shape.dim(3) = input()->shape().dim(3); + output()->resize(output_shape); +} + +void ResizeBilinear::execute() const +{ + tflite::ResizeBilinearParams op_params{}; + op_params.align_corners = params().align_corners; + op_params.half_pixel_centers = params().half_pixel_centers; + switch (output()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::ResizeBilinear( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), + getTensorData(size()), getTensorShape(output()), getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::ResizeBilinear( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), + getTensorData(size()), getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.h b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.h new file mode 100644 index 0000000..b7bdc2a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H +#define LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ResizeBilinear : public KernelWithParams +{ +public: + ResizeBilinear(const Tensor *input, const Tensor *shape, Tensor *output, + const ResizeBilinearParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *size() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp new file mode 100644 index 0000000..933a112 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved + * + * 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 "kernels/ResizeBilinear.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list size_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list size_data, std::initializer_list output_data, + bool align_corners, bool half_pixel_centers) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeBilinearParams params{}; + params.align_corners = align_corners; + params.half_pixel_centers = half_pixel_centers; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list size_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list size_data, + std::initializer_list output_data, bool align_corners, + bool half_pixel_centers) +{ + // On TFlite example use Uint8 value it self, so this means quant param scale 1.0f and zero + // point 0. + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, 1.0, 0, input_data, memory_manager.get()); + Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0, 0); + + ResizeBilinearParams params{}; + params.align_corners = align_corners; + params.half_pixel_centers = half_pixel_centers; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); +} + +template class ResizeBilinearTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(ResizeBilinearTest, DataTypes); + +TYPED_TEST(ResizeBilinearTest, SimpleTest) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + {3, 3}, + { + 3, 5, 6, // + 7, 9, 10, // + 9, 11, 12, // + 4, 8, 10, // + 8, 12, 14, // + 10, 14, 16, // + }, + false, false); + SUCCEED(); +} + +TEST(ResizeBilinearTest, HalfPixelCenterFloatTest) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 1, 2, // + 3, 4, // + 1, 2, // + 3, 4 // + }, + {3, 3}, + { + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // + }, + false, true); + SUCCEED(); +} + +TEST(ResizeBilinearTest, HalfPixelCenterUint8Test) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 12, 16 // + }, + {3, 3}, + { + 2, 4, 6, // + 6, 7, 9, // + 9, 10, 12, // + 4, 7, 10, // + 8, 10, 13, // + 12, 14, 16, // + }, + false, true); + SUCCEED(); +} + +TEST(ResizeBilinearTest, InputShapeInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({2}, {3, 3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeBilinearParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(ResizeBilinearTest, SizeShapeInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({2, 1}, {3, 3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeBilinearParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(ResizeBilinearTest, SizeDimInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({3}, {3, 3, 1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeBilinearParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(ResizeBilinearTest, InvalidParams_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({2}, {3, 3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeBilinearParams params{}; + params.align_corners = true; + params.half_pixel_centers = true; + + ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp new file mode 100644 index 0000000..306cefb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ResizeNearestNeighbor.h" + +#include "kernels/Utils.h" + +#include +#include "PALResizeNearestNeighbor.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ResizeNearestNeighbor::ResizeNearestNeighbor(const Tensor *input, const Tensor *size, + Tensor *output, + const ResizeNearestNeighborParams ¶ms) + : KernelWithParams({input, size}, {output}, params) +{ +} + +void ResizeNearestNeighbor::configure() +{ + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32); + LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2); + Shape output_shape(4); + output_shape.dim(0) = input()->shape().dim(0); + output_shape.dim(1) = getTensorData(size())[0]; + output_shape.dim(2) = getTensorData(size())[1]; + output_shape.dim(3) = input()->shape().dim(3); + output()->resize(output_shape); +} + +void ResizeNearestNeighbor::execute() const +{ + tflite::ResizeNearestNeighborParams op_params{}; + op_params.align_corners = params().align_corners; + op_params.half_pixel_centers = params().half_pixel_centers; + switch (output()->element_type()) + { + case DataType::FLOAT32: + tflite::reference_ops::ResizeNearestNeighbor( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), + getTensorData(size()), getTensorShape(output()), getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::ResizeNearestNeighbor( + op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), + getTensorData(size()), getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h new file mode 100644 index 0000000..137d031 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H +#define LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ResizeNearestNeighbor : public KernelWithParams +{ +public: + ResizeNearestNeighbor(const Tensor *input, const Tensor *shape, Tensor *output, + const ResizeNearestNeighborParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *size() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp new file mode 100644 index 0000000..7ade02a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved + * + * 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 "kernels/ResizeNearestNeighbor.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list size_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list size_data, std::initializer_list output_data, + bool align_corners, bool half_pixel_centers) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeNearestNeighborParams params{}; + params.align_corners = align_corners; + params.half_pixel_centers = half_pixel_centers; + + ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +template <> +void Check(std::initializer_list input_shape, + std::initializer_list size_shape, + std::initializer_list output_shape, + std::initializer_list input_data, + std::initializer_list size_data, + std::initializer_list output_data, bool align_corners, + bool half_pixel_centers) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::pair quant_param = + quantizationParams(std::min(input_data) < 0 ? std::min(input_data) : 0.f, + std::max(input_data) > 0 ? std::max(input_data) : 0.f); + Tensor input_tensor = makeInputTensor( + input_shape, quant_param.first, quant_param.second, input_data, memory_manager.get()); + Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.first); + + ResizeNearestNeighborParams params{}; + params.align_corners = align_corners; + params.half_pixel_centers = half_pixel_centers; + + ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); +} + +template class ResizeNearestNeighborTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(ResizeNearestNeighborTest, DataTypes); + +TYPED_TEST(ResizeNearestNeighborTest, SimpleTest) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + {3, 3}, + { + 3, 3, 6, // + 3, 3, 6, // + 9, 9, 12, // + 4, 4, 10, // + 4, 4, 10, // + 10, 10, 16, // + }, + false, false); +} + +TYPED_TEST(ResizeNearestNeighborTest, AlignCenterTest) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + {3, 3}, + { + 3, 6, 6, // + 9, 12, 12, // + 9, 12, 12, // + 4, 10, 10, // + 10, 16, 16, // + 10, 16, 16, // + }, + true, false); +} + +TYPED_TEST(ResizeNearestNeighborTest, HalfPixelCenterTest) +{ + Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + {3, 3}, + { + 3, 6, 6, // + 9, 12, 12, // + 9, 12, 12, // + 4, 10, 10, // + 10, 16, 16, // + 10, 16, 16, // + }, + false, true); +} + +TEST(ResizeNearestNeighborTest, InputShapeInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({2}, {3, 3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeNearestNeighborParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(ResizeNearestNeighborTest, SizeShapeInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({2, 1}, {3, 3}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeNearestNeighborParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(ResizeNearestNeighborTest, SizeDimInvalid_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, + { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // + }, + memory_manager.get()); + Tensor size_tensor = makeInputTensor({3}, {3, 3, 1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ResizeNearestNeighborParams params{}; + params.align_corners = false; + params.half_pixel_centers = false; + + ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.cpp new file mode 100644 index 0000000..1b6a5cc --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/ReverseV2.h" +#include "kernels/Utils.h" +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +ReverseV2::ReverseV2(const Tensor *input, const Tensor *axes, Tensor *output) + : Kernel({input, axes}, {output}) +{ +} + +void ReverseV2::configure() +{ + assert(axes()->shape().num_dims() == 1); + assert(input()->shape().num_dims() >= axes()->shape().num_elements()); + if (input()->element_type() != DataType::S32 && input()->element_type() != DataType::FLOAT32 && + input()->element_type() != DataType::U8 && input()->element_type() != DataType::S16 && + input()->element_type() != DataType::S64) + { + throw std::runtime_error("Unsupported input type."); + } + if (axes()->element_type() != DataType::S32) + { + throw std::runtime_error("Unsupported axes type."); + } + if (axes()->shape().num_elements() > 1) + { + throw std::runtime_error("Current implementation does not support more than 1 axis."); + } + int axis_value = getTensorData(axes())[0]; + if (axis_value < 0 || axis_value >= input()->shape().num_dims()) + { + throw std::runtime_error("Invalid axes value"); + } + assert(input()->element_type() == output()->element_type()); + + output()->resize(input()->shape()); +} + +void ReverseV2::execute() const +{ + int axis_value = getTensorData(axes())[0]; + switch (output()->element_type()) + { + case DataType::FLOAT32: + tflite::reference_ops::Reverse(axis_value, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + tflite::reference_ops::Reverse( + axis_value, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported output type"); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.h b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.h new file mode 100644 index 0000000..51211c7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_REVERSE_H +#define LUCI_INTERPRETER_KERNELS_REVERSE_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ReverseV2 : public Kernel +{ +public: + ReverseV2(const Tensor *input, const Tensor *axes, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *axes() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_REVERSE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.test.cpp new file mode 100644 index 0000000..c0025fa --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/ReverseV2.test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/ReverseV2.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template class ReverseV2Test : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(ReverseV2Test, DataTypes); + +TYPED_TEST(ReverseV2Test, MultiDimensions) +{ + std::unique_ptr memory_manager = std::make_unique(); + + // TypeParam + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; + Shape input_shape{4, 3, 2}; + std::vector axis_data{1}; + Shape axis_shape{1}; + + std::vector output_data{5, 6, 3, 4, 1, 2, 11, 12, 9, 10, 7, 8, + 17, 18, 15, 16, 13, 14, 23, 24, 21, 22, 19, 20}; + std::vector output_shape{4, 3, 2}; + + Tensor input_tensor = + makeInputTensor()>(input_shape, input_data, memory_manager.get()); + Tensor axis_tensor = makeInputTensor(axis_shape, axis_data, memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(getElementType()); + + ReverseV2 kernel = ReverseV2(&input_tensor, &axis_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.cpp new file mode 100644 index 0000000..6dd92dc --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Rsqrt.h" +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Rsqrt::Rsqrt(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Rsqrt::configure() +{ + if (input()->element_type() != output()->element_type()) + { + throw std::runtime_error("Input/output tensor data type mismatch."); + } + output()->resize(input()->shape()); +} + +void Rsqrt::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Rsqrt::evalFloat() const +{ + auto in = getTensorData(input()); + auto out = getTensorData(output()); + auto size = getTensorShape(input()).FlatSize(); + for (auto i = in; i != in + size; ++i) + { + *out = 1.f / std::sqrt(*i); + ++out; + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.h b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.h new file mode 100644 index 0000000..adc5bcf --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_RSQRT_H +#define LUCI_INTERPRETER_KERNELS_RSQRT_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Rsqrt : public Kernel +{ +public: + Rsqrt(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_RSQRT_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp new file mode 100644 index 0000000..3c64942 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Rsqrt.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Rsqrt kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(RsqrtTest, SimpleRsqrt) +{ + Check( + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 5, 4, 8, 2, // + 6, 7.5, 9, 0.3, // + }, + /*output_data=*/ + { + 0.44721360, 0.5, 0.35355339, 0.70710678, // + 0.40824829, 0.36514837, 0.33333333, 1.8257419, // + }); +} + +TEST(RsqrtTest, Input_Output_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {1.f}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Rsqrt kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(RsqrtTest, Invalid_Input_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Rsqrt kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.cpp new file mode 100644 index 0000000..40d79aa --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/SVDF.h" +#include "kernels/Utils.h" +#include "PALSVDF.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +namespace +{ +TfLiteFusedActivation get_tflite_activation(Activation activation) +{ + switch (activation) + { + case luci::FusedActFunc::RELU: + return kTfLiteActRelu; + case luci::FusedActFunc::RELU6: + return kTfLiteActRelu6; + case luci::FusedActFunc::RELU_N1_TO_1: + return kTfLiteActReluN1To1; + case luci::FusedActFunc::TANH: + return kTfLiteActTanh; + case luci::FusedActFunc::SIGN_BIT: + return kTfLiteActSignBit; + case luci::FusedActFunc::NONE: + return kTfLiteActNone; + default: + throw std::runtime_error("Unsupported activation type"); + } +} +} // namespace + +SVDF::SVDF(const Tensor *input, const Tensor *weight_feature, const Tensor *weight_time, + const Tensor *bias, const Tensor *input_activation_state, Tensor *output, + Tensor *scratchpad_activation_state, Tensor *scratchpad_1, Tensor *scratchpad_2, + Tensor *scratchpad_3, Tensor *scratchpad_4, Tensor *scratchpad_5, Tensor *scratchpad_6, + const SVDFParams ¶ms) + : KernelWithParams({input, weight_feature, weight_time, bias, input_activation_state}, + {output, scratchpad_activation_state, scratchpad_1, scratchpad_2, + scratchpad_3, scratchpad_4, scratchpad_5, scratchpad_6}, + params) +{ + // Do nothing +} + +void SVDF::configure() +{ + const Shape &input_shape = input()->shape(); + const Shape &weight_features_shape = weight_feature()->shape(); + const Shape &weight_time_shape = weight_time()->shape(); + + // Validate Input Tensor: + LUCI_INTERPRETER_CHECK(input()->element_type() == loco::DataType::FLOAT32 || + input()->element_type() == loco::DataType::S8); + LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 2); + + // Validate inputs and output types + if (input()->element_type() == loco::DataType::S8) + { + LUCI_INTERPRETER_CHECK(weight_feature()->element_type() == loco::DataType::S8); + LUCI_INTERPRETER_CHECK(weight_time()->element_type() == loco::DataType::S16 || + weight_time()->element_type() == loco::DataType::S8); + if (bias()) + LUCI_INTERPRETER_CHECK(bias()->element_type() == loco::DataType::S32); + + LUCI_INTERPRETER_CHECK(input_activation_state()->element_type() == loco::DataType::S16 || + input_activation_state()->element_type() == loco::DataType::S8); + LUCI_INTERPRETER_CHECK(output()->element_type() == loco::DataType::S8); + + // Note: now tflite support only ReLU activation for integer SVDF + LUCI_INTERPRETER_CHECK(params().activation == luci::FusedActFunc::RELU); + } + else if (weight_feature()->element_type() == loco::DataType::FLOAT32) + { + LUCI_INTERPRETER_CHECK(weight_feature()->element_type() == loco::DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(weight_time()->element_type() == loco::DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(input_activation_state()->element_type() == loco::DataType::FLOAT32); + if (bias()) + LUCI_INTERPRETER_CHECK(bias()->element_type() == loco::DataType::FLOAT32); + LUCI_INTERPRETER_CHECK(output()->element_type() == loco::DataType::FLOAT32); + } + else if ((weight_feature()->element_type() == loco::DataType::U8 || + weight_feature()->element_type() == loco::DataType::S8) && + input()->element_type() == loco::DataType::FLOAT32) + { + // TODO:: support hybrid SVDF op + throw std::runtime_error("Hybrid type is not currently supported"); + } + else + { + throw std::runtime_error("Unsupported type."); + } + + // Check all the parameters of tensor match within themselves and match the + // input configuration. + const int rank = params().svdf_rank; + const int batch_size = input_shape.dim(0); + const int num_filters = weight_features_shape.dim(0); + LUCI_INTERPRETER_CHECK(rank != 0); + LUCI_INTERPRETER_CHECK(num_filters % rank == 0); + + const int num_units = num_filters / rank; + const int memory_size = weight_time_shape.dim(1); + + // Validate Weight_Feature Input Tensor: + LUCI_INTERPRETER_CHECK(weight_features_shape.num_dims() == 2); + LUCI_INTERPRETER_CHECK(weight_features_shape.dim(1) == input_shape.dim(1)); + + // Validate Weight_Time Input Tensor: + LUCI_INTERPRETER_CHECK(weight_time_shape.num_dims() == 2); + LUCI_INTERPRETER_CHECK(weight_time_shape.dim(0) == num_filters); + + // Validate Bias + if (bias()) + LUCI_INTERPRETER_CHECK(bias()->shape().dim(0) == num_units); + + // Validate Input Activation State + LUCI_INTERPRETER_CHECK(input_activation_state()->shape().num_dims() == 2); + LUCI_INTERPRETER_CHECK(input_activation_state()->shape().dim(0) == batch_size); + LUCI_INTERPRETER_CHECK(input_activation_state()->shape().dim(1) == memory_size * num_filters); + + // Resize scratchpad_state to input_activation_state + auto scratchpad_activation_state = getOutputTensors()[1]; + scratchpad_activation_state->resize({batch_size, memory_size * num_filters}); + + // Resize output tensor + output()->resize({batch_size, num_units}); + + luci_interpreter_pal::SetupScratchpadTensor( + input()->element_type(), weight_feature()->element_type(), getOutputTensors()[2], + getOutputTensors()[3], getOutputTensors()[4], getOutputTensors()[5], getOutputTensors()[6], + getOutputTensors()[7], input_shape, weight_time_shape, batch_size, num_filters, num_units); +} + +void SVDF::execute() const +{ + switch (weight_feature()->element_type()) + { + case loco::DataType::FLOAT32: + evalFloat(); + break; + case loco::DataType::S8: + { + if (input()->element_type() == loco::DataType::S8) + evalInteger(); + else + // TODO:: support hybrid SVDF op + throw std::runtime_error("Hybrid type is not currently supported"); + break; + } + default: + throw std::runtime_error("Unsupported type"); + } +} + +void SVDF::evalInteger() const +{ + const auto effective_scale_1 = static_cast(input()->scale() * weight_feature()->scale() / + input_activation_state()->scale()); + const auto effective_scale_2 = static_cast(input_activation_state()->scale() * + weight_time()->scale() / output()->scale()); + + int32_t effective_scale_1_a; + int effective_scale_1_b; + int32_t effective_scale_2_a; + int effective_scale_2_b; + + tflite::QuantizeMultiplier(effective_scale_1, &effective_scale_1_a, &effective_scale_1_b); + tflite::QuantizeMultiplier(effective_scale_2, &effective_scale_2_a, &effective_scale_2_b); + + TfLiteSVDFParams params_svdf{}; + params_svdf.asymmetric_quantize_inputs = params().asymmetric_quantize_inputs; + params_svdf.rank = params().svdf_rank; + params_svdf.activation = get_tflite_activation(params().activation); + + auto scratchpad_activation_state = getOutputTensors()[1]; + // Note: it is expected that activation_state input variable tensor reset to zero, + // also expected that this variable tensor doesn't have buffer + auto scratchpad_data = getTensorData(scratchpad_activation_state); + std::fill_n(scratchpad_data, scratchpad_activation_state->shape().num_elements(), 0); + + auto scratchpad = getOutputTensors()[2]; + auto output_temp = getOutputTensors()[3]; + + int32_t input_zp = input()->zero_point(); + int32_t output_zp = output()->zero_point(); + luci_interpreter_pal::IntegerSVDF( + params_svdf, getTensorShape(input()), getTensorData(input()), + getTensorShape(weight_feature()), getTensorData(weight_feature()), + getTensorShape(weight_time()), getTensorData(weight_time()), getTensorShape(bias()), + getTensorData(bias()), scratchpad_data, getTensorShape(output()), + getTensorData(output()), getTensorData(scratchpad), + getTensorData(output_temp), effective_scale_1_a, effective_scale_1_b, + effective_scale_2_a, effective_scale_2_b, input_zp, output_zp); +} + +void SVDF::evalFloat() const +{ + TfLiteSVDFParams params_svdf{}; + params_svdf.asymmetric_quantize_inputs = params().asymmetric_quantize_inputs; + params_svdf.rank = params().svdf_rank; + params_svdf.activation = get_tflite_activation(params().activation); + + auto scratchpad_activation_state = getOutputTensors()[1]; + // Note: it is expected that activation_state input variable tensor reset to zero, + // also expected that this variable tensor doesn't have buffer + auto scratchpad_data = getTensorData(scratchpad_activation_state); + std::fill_n(scratchpad_data, scratchpad_activation_state->shape().num_elements(), 0); + + auto scratchpad_1 = getOutputTensors()[2]; + + luci_interpreter_pal::FloatSVDF( + params_svdf, getTensorShape(input()), getTensorData(input()), + getTensorShape(weight_feature()), getTensorData(weight_feature()), + getTensorShape(weight_time()), getTensorData(weight_time()), getTensorShape(bias()), + getTensorData(bias()), getTensorData(scratchpad_1), scratchpad_data, + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.h b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.h new file mode 100644 index 0000000..335a6cd --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SVDF_H +#define LUCI_INTERPRETER_KERNELS_SVDF_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SVDF : public KernelWithParams +{ +public: + SVDF(const Tensor *input, const Tensor *weight_feature, const Tensor *weight_time, + const Tensor *bias, const Tensor *input_activation_state, Tensor *output, + Tensor *scratchpad_activation_state, Tensor *scratchpad_1, Tensor *scratchpad_2, + Tensor *scratchpad_3, Tensor *scratchpad_4, Tensor *scratchpad_5, Tensor *scratchpad_6, + const SVDFParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *weight_feature() const { return _inputs[1]; } + const Tensor *weight_time() const { return _inputs[2]; } + const Tensor *bias() const { return _inputs[3]; } + const Tensor *input_activation_state() const { return _inputs[4]; } + + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalInteger() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SVDF_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.test.cpp new file mode 100644 index 0000000..82bd9b0 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SVDF.test.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/SVDF.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class SVDFTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(SVDFTest, FullIntegerTest) +{ + const int32_t batches = 2; + const int32_t input_size = 3; + const int32_t units = 4; + const int32_t memory_size = 10; + const int32_t rank = 1; + const int32_t num_filters = units * rank; + + Shape input_shape{batches, input_size}; + Shape weight_feature_shape{num_filters, input_size}; + Shape weight_time_shape{num_filters, memory_size}; + Shape bias_shape{units}; + Shape activation_state_shape{batches, memory_size * num_filters}; + + std::vector input_data{0.49837467, 0.19278903, 0.26584083, + 0.17660543, 0.52949083, -0.77931279}; + + std::vector weight_feature_data{-0.31930989, -0.36118156, 0.0079667, 0.37613347, + 0.22197971, 0.12416199, 0.27901134, 0.27557442, + 0.3905206, -0.36137494, -0.06634006, -0.10640851}; + + std::vector weight_time_data{ + -0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156, + 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199, + + 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518, + -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296, + + -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236, + 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846, + + -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166, + -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657}; + + std::vector bias_data{-0.0976817, 0.15294972, 0.39635518, -0.02702999}; + + std::pair input_quant_param = quantizationParams(-1, 1); + std::pair weight_feature_quant_param = quantizationParams(-0.5, 0.5); + std::pair weight_time_quant_param = quantizationParams(-1, 1); + std::pair bias_quant_param = quantizationParams(-512, 512); + std::pair activation_state_quant_param = quantizationParams(-16, 16); + + std::pair output_quant_param = quantizationParams(-0.5, 0.5); + + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor weight_feature_tensor = makeInputTensor( + weight_feature_shape, weight_feature_quant_param.first, weight_feature_quant_param.second, + weight_feature_data, _memory_manager.get()); + Tensor weight_time_tensor = makeInputTensor( + weight_time_shape, weight_time_quant_param.first, weight_time_quant_param.second, + weight_time_data, _memory_manager.get()); + Tensor bias_tensor = makeInputTensor( + bias_shape, bias_quant_param.first, bias_quant_param.second, bias_data, _memory_manager.get()); + Tensor activation_state_tensor = makeOutputTensor( + DataType::S16, activation_state_quant_param.first, activation_state_quant_param.second); + activation_state_tensor.resize(activation_state_shape); + Tensor output_tensor = + makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); + + Tensor scratchpad_activation_state(DataType::S16, Shape({}), {}, ""); + Tensor scratchpad_1(DataType::S32, Shape({}), {}, ""); + Tensor scratchpad_2(DataType::S32, Shape({}), {}, ""); + Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_4(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_5(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_6(DataType::FLOAT32, Shape({}), {}, ""); + + SVDFParams params{}; + params.activation = Activation::RELU; + params.asymmetric_quantize_inputs = false; + params.svdf_rank = rank; + + SVDF kernel(&input_tensor, &weight_feature_tensor, &weight_time_tensor, &bias_tensor, + &activation_state_tensor, &output_tensor, &scratchpad_activation_state, &scratchpad_1, + &scratchpad_2, &scratchpad_3, &scratchpad_4, &scratchpad_5, &scratchpad_6, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad_activation_state); + _memory_manager->allocate_memory(scratchpad_1); + _memory_manager->allocate_memory(scratchpad_2); + _memory_manager->allocate_memory(scratchpad_3); + _memory_manager->allocate_memory(scratchpad_4); + _memory_manager->allocate_memory(scratchpad_5); + _memory_manager->allocate_memory(scratchpad_6); + kernel.execute(); + + std::vector ref_output_data{-9, 24, 31, 1, -10, 10, -3, 0}; + + std::vector ref_output_shape{batches, units}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(SVDFTest, FloatTest) +{ + const int32_t batches = 2; + const int32_t input_size = 3; + const int32_t units = 4; + const int32_t memory_size = 10; + const int32_t rank = 1; + const int32_t num_filters = units * rank; + + Shape input_shape{batches, input_size}; + Shape weight_feature_shape{num_filters, input_size}; + Shape weight_time_shape{num_filters, memory_size}; + Shape activation_state_shape{batches, memory_size * num_filters}; + + std::vector input_data{0.12609188, -0.46347019, -0.89598465, + 0.35867718, 0.36897406, 0.73463392}; + + std::vector weight_feature_data{-0.31930989, -0.36118156, 0.0079667, 0.37613347, + 0.22197971, 0.12416199, 0.27901134, 0.27557442, + 0.3905206, -0.36137494, -0.06634006, -0.10640851}; + + std::vector weight_time_data{ + -0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156, + 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199, + + 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518, + -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296, + + -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236, + 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846, + + -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166, + -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor weight_feature_tensor = makeInputTensor( + weight_feature_shape, weight_feature_data, _memory_manager.get()); + Tensor weight_time_tensor = + makeInputTensor(weight_time_shape, weight_time_data, _memory_manager.get()); + Tensor activation_state_tensor = makeOutputTensor(DataType::FLOAT32); + activation_state_tensor.resize(activation_state_shape); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Tensor scratchpad_activation_state(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_4(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_5(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_6(DataType::FLOAT32, Shape({}), {}, ""); + + SVDFParams params{}; + params.activation = Activation::NONE; + params.asymmetric_quantize_inputs = false; + params.svdf_rank = rank; + + SVDF kernel(&input_tensor, &weight_feature_tensor, &weight_time_tensor, nullptr, + &activation_state_tensor, &output_tensor, &scratchpad_activation_state, &scratchpad_1, + &scratchpad_2, &scratchpad_3, &scratchpad_4, &scratchpad_5, &scratchpad_6, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + _memory_manager->allocate_memory(scratchpad_activation_state); + _memory_manager->allocate_memory(scratchpad_1); + _memory_manager->allocate_memory(scratchpad_2); + _memory_manager->allocate_memory(scratchpad_3); + _memory_manager->allocate_memory(scratchpad_4); + _memory_manager->allocate_memory(scratchpad_5); + _memory_manager->allocate_memory(scratchpad_6); + kernel.execute(); + + std::vector ref_output_data{0.014899, -0.0517661, -0.143725, -0.00271883, + -0.03004015, 0.09565311, 0.1587342, 0.00784263}; + + std::vector ref_output_shape{batches, units}; + const float tolerance = 1e-5; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, tolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(SVDFTest, Unsupported_Type_Configure_NEG) +{ + const int32_t batches = 2; + const int32_t input_size = 3; + const int32_t units = 4; + const int32_t memory_size = 10; + const int32_t rank = 1; + const int32_t num_filters = units * rank; + + Shape input_shape{batches, input_size}; + Shape weight_feature_shape{num_filters, input_size}; + Shape weight_time_shape{num_filters, memory_size}; + Shape activation_state_shape{batches, memory_size * num_filters}; + + std::vector input_data{0, 1, 3, 4, 4, -2}; + + std::vector weight_feature_data{-0.31930989, -0.36118156, 0.0079667, 0.37613347, + 0.22197971, 0.12416199, 0.27901134, 0.27557442, + 0.3905206, -0.36137494, -0.06634006, -0.10640851}; + + std::vector weight_time_data{ + -0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156, + 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199, + + 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518, + -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296, + + -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236, + 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846, + + -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166, + -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor weight_feature_tensor = makeInputTensor( + weight_feature_shape, weight_feature_data, _memory_manager.get()); + Tensor weight_time_tensor = + makeInputTensor(weight_time_shape, weight_time_data, _memory_manager.get()); + Tensor activation_state_tensor = makeOutputTensor(DataType::FLOAT32); + activation_state_tensor.resize(activation_state_shape); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Tensor scratchpad_activation_state(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_4(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_5(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_6(DataType::FLOAT32, Shape({}), {}, ""); + + SVDFParams params{}; + params.activation = Activation::NONE; + params.asymmetric_quantize_inputs = false; + params.svdf_rank = rank; + + SVDF kernel(&input_tensor, &weight_feature_tensor, &weight_time_tensor, nullptr, + &activation_state_tensor, &output_tensor, &scratchpad_activation_state, &scratchpad_1, + &scratchpad_2, &scratchpad_3, &scratchpad_4, &scratchpad_5, &scratchpad_6, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(SVDFTest, Invalid_Input_Shape_NEG) +{ + const int32_t batches = 2; + const int32_t right_input_size = 3; + const int32_t wrong_input_size = 4; + const int32_t units = 4; + const int32_t memory_size = 10; + const int32_t rank = 1; + const int32_t num_filters = units * rank; + + Shape input_shape{batches, wrong_input_size}; + Shape weight_feature_shape{num_filters, right_input_size}; + Shape weight_time_shape{num_filters, memory_size}; + Shape activation_state_shape{batches, memory_size * num_filters}; + + std::vector input_data{0, 1, 3, 2, 4, 4, -2, 1}; + + std::vector weight_feature_data{-0.31930989, -0.36118156, 0.0079667, 0.37613347, + 0.22197971, 0.12416199, 0.27901134, 0.27557442, + 0.3905206, -0.36137494, -0.06634006, -0.10640851}; + + std::vector weight_time_data{ + -0.31930989, 0.37613347, 0.27901134, -0.36137494, -0.36118156, + 0.22197971, 0.27557442, -0.06634006, 0.0079667, 0.12416199, + + 0.3905206, -0.10640851, -0.0976817, 0.15294972, 0.39635518, + -0.02702999, 0.39296314, 0.15785322, 0.21931258, 0.31053296, + + -0.36916667, 0.38031587, -0.21580373, 0.27072677, 0.23622236, + 0.34936687, 0.18174365, 0.35907319, -0.17493086, 0.324846, + + -0.10781813, 0.27201805, 0.14324132, -0.23681851, -0.27115166, + -0.01580888, -0.14943552, 0.15465137, 0.09784451, -0.0337657}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor weight_feature_tensor = makeInputTensor( + weight_feature_shape, weight_feature_data, _memory_manager.get()); + Tensor weight_time_tensor = + makeInputTensor(weight_time_shape, weight_time_data, _memory_manager.get()); + Tensor activation_state_tensor = makeOutputTensor(DataType::FLOAT32); + activation_state_tensor.resize(activation_state_shape); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Tensor scratchpad_activation_state(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_4(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_5(DataType::FLOAT32, Shape({}), {}, ""); + Tensor scratchpad_6(DataType::FLOAT32, Shape({}), {}, ""); + + SVDFParams params{}; + params.activation = Activation::NONE; + params.asymmetric_quantize_inputs = false; + params.svdf_rank = rank; + + SVDF kernel(&input_tensor, &weight_feature_tensor, &weight_time_tensor, nullptr, + &activation_state_tensor, &output_tensor, &scratchpad_activation_state, &scratchpad_1, + &scratchpad_2, &scratchpad_3, &scratchpad_4, &scratchpad_5, &scratchpad_6, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Shape.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.cpp new file mode 100644 index 0000000..0429fe1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Shape.h" +#include "kernels/Utils.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +ShapeKernel::ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void ShapeKernel::configure() +{ + LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::S32 or + output()->element_type() == DataType::S64); + const auto input_shape = input()->shape(); + + Shape output_shape(1); + output_shape.dim(0) = input_shape.num_dims(); + + output()->resize(output_shape); +} + +void ShapeKernel::execute() const +{ + switch (params().out_type) + { + case DataType::S32: + evalInt(); + break; + case DataType::S64: + evalInt(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template void ShapeKernel::evalInt() const +{ + const auto input_shape = input()->shape(); + + auto output_data = getTensorData(output()); + + for (int i = 0; i < input_shape.num_dims(); ++i) + { + output_data[i] = input_shape.dim(i); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Shape.h b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.h new file mode 100644 index 0000000..cfaadec --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SHAPE_H +#define LUCI_INTERPRETER_KERNELS_SHAPE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ShapeKernel : public KernelWithParams +{ +public: + ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void evalInt() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SHAPE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Shape.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.test.cpp new file mode 100644 index 0000000..4763e01 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Shape.test.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Shape.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class ShapeTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +template void runShapeKernel(loco::DataType dataType, IMemoryManager *memory_manager) +{ + Shape input_shape{1, 3, 1, 3, 5}; + + Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); + Tensor output_tensor = makeOutputTensor(dataType); + + ShapeParams params{}; + params.out_type = dataType; + + ShapeKernel kernel(&input_tensor, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{1, 3, 1, 3, 5}; + EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + + std::vector ref_output_shape{5}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(ShapeTest, OutTypeInt) +{ + + // Run for int32_t output + runShapeKernel(loco::DataType::S32, _memory_manager.get()); + // Run for int64_t output + runShapeKernel(loco::DataType::S64, _memory_manager.get()); + + SUCCEED(); +} + +TEST_F(ShapeTest, Invalid_Output_Type_NEG) +{ + Shape input_shape{1, 3}; + + Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); + Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); + + ShapeParams params{}; + params.out_type = loco::DataType::FLOAT32; + + ShapeKernel kernel(&input_tensor, &output_tensor, params); + + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Slice.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.cpp new file mode 100644 index 0000000..2fe2c54 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Slice.h" +#include "Utils.h" +#include "PALSlice.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ +const int max_dim = 4; + +Slice::Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output) + : Kernel({input, begin, size}, {output}) +{ +} + +template +Shape calculateOutputShape(const Tensor *input, const Tensor *begin, const Tensor *size) +{ + Shape output_shape = Shape(input->shape().num_dims()); + for (int idx = 0; idx < input->shape().num_dims(); idx++) + { + T size_value = getTensorData(size)[idx]; + if (size_value < 0) + { + if (size_value != -1) + { + throw std::runtime_error("Invalid size."); + } + size_value = input->shape().dim(idx) - getTensorData(begin)[idx]; + } + else + { + if (input->shape().dim(idx) < getTensorData(begin)[idx] + size_value) + { + throw std::runtime_error("Invalid begin and size."); + } + } + output_shape.dim(idx) = static_cast(size_value); + } + return output_shape; +} + +template +void getBeginAndSizeVectors(int dimensions, const Tensor *begin, const Tensor *size, + std::vector *begins, std::vector *sizes) +{ + for (int idx = dimensions - 1; idx >= 0; --idx) + { + begins->push_back(getTensorData(begin)[idx]); + sizes->push_back(getTensorData(size)[idx]); + } +} + +void Slice::configure() +{ + assert(input()->element_type() == output()->element_type()); + assert(begin()->element_type() == DataType::S32 || begin()->element_type() == DataType::S64); + assert(size()->element_type() == DataType::S32 || size()->element_type() == DataType::S64); + assert(begin()->shape().num_dims() == 1); + assert(size()->shape().num_dims() == 1); + assert(input()->shape().num_dims() <= max_dim); + + if (begin()->element_type() == DataType::S32) + { + output()->resize(calculateOutputShape(input(), begin(), size())); + } + else if (begin()->element_type() == DataType::S64) + { + output()->resize(calculateOutputShape(input(), begin(), size())); + } + else + { + throw std::runtime_error("Unsupported type."); + } +} + +void Slice::execute() const +{ + std::vector begins; + begins.reserve(max_dim); + std::vector sizes; + sizes.reserve(max_dim); + if (begin()->element_type() == DataType::S32) + { + getBeginAndSizeVectors(input()->shape().num_dims(), begin(), size(), &begins, &sizes); + } + else if (begin()->element_type() == DataType::S64) + { + getBeginAndSizeVectors(input()->shape().num_dims(), begin(), size(), &begins, &sizes); + } + else + { + throw std::runtime_error("Unsupported begin type."); + } + for (int i = input()->shape().num_dims(); i < max_dim; ++i) + { + begins.push_back(0); + sizes.push_back(1); + } + + assert(begins.size() == 4); + assert(sizes.size() == 4); + tflite::SliceParams op_params{}; + op_params.begin_count = 4; + op_params.size_count = 4; + for (int i = 0; i < 4; i++) + { + op_params.begin[i] = begins[3 - i]; + op_params.size[i] = sizes[3 - i]; + } + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::Slice(op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::Slice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::S8: + luci_interpreter_pal::Slice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported input type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Slice.h b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.h new file mode 100644 index 0000000..23c3596 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SLICE_H +#define LUCI_INTERPRETER_KERNELS_SLICE_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Slice : public Kernel +{ +public: + Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *begin() const { return _inputs[1]; } + const Tensor *size() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SLICE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Slice.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.test.cpp new file mode 100644 index 0000000..5179829 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Slice.test.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Slice.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template class SliceTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SliceTest, DataTypes); + +TYPED_TEST(SliceTest, SimpleTest) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::vector input_data{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6}; + Shape input_shape{3, 2, 3, 1}; + std::vector begin_data{1, 0, 0, 0}; + Shape begin_shape{4}; + std::vector size_data{2, 1, -1, 1}; + Shape size_shape{4}; + std::vector output_data{3, 3, 3, 5, 5, 5}; + std::vector output_shape{2, 1, 3, 1}; + + Tensor input_tensor = + makeInputTensor()>(input_shape, input_data, memory_manager.get()); + Tensor begin_tensor = + makeInputTensor(begin_shape, begin_data, memory_manager.get()); + Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); + + Tensor output_tensor = makeOutputTensor(getElementType()); + + Slice kernel(&input_tensor, &begin_tensor, &size_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.cpp new file mode 100644 index 0000000..c230aaa --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Softmax.h" + +#include "kernels/Utils.h" + +#include +#include "PALSoftmax.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Softmax::Softmax(const Tensor *input, Tensor *output, const SoftmaxParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void Softmax::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= 1); + if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S8) + { + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::S8 || output()->zero_point() == 0); + LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::U8 || + output()->zero_point() == std::numeric_limits::min()); + tflite::SoftmaxParams op_params{}; + op_params.table = _table; + luci_interpreter_pal::PopulateSoftmaxLookupTable(&op_params, input()->scale(), params().beta); + } + output()->resize(input()->shape()); +} + +void Softmax::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S8: + evalQuantized(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Softmax::evalFloat() const +{ + tflite::SoftmaxParams op_params{}; + op_params.beta = params().beta; + + tflite::reference_ops::Softmax(op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +template void Softmax::evalQuantized() const +{ + tflite::SoftmaxParams op_params{}; + op_params.table = const_cast(_table); + op_params.zero_point = output()->zero_point(); + op_params.scale = output()->scale(); + luci_interpreter_pal::InitializeParams(&op_params, input()->scale(), params().beta); + luci_interpreter_pal::Softmax(op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.h b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.h new file mode 100644 index 0000000..1f281df --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SOFTMAX_H +#define LUCI_INTERPRETER_KERNELS_SOFTMAX_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Softmax : public KernelWithParams +{ +public: + Softmax(const Tensor *input, Tensor *output, const SoftmaxParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalQuantized() const; + + float _table[256]; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SOFTMAX_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.test.cpp new file mode 100644 index 0000000..08e7067 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Softmax.test.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Softmax.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template constexpr loco::DataType toLocoDataType(); + +template <> constexpr loco::DataType toLocoDataType() { return loco::DataType::FLOAT32; } + +template <> constexpr loco::DataType toLocoDataType() { return loco::DataType::U8; } + +template <> constexpr loco::DataType toLocoDataType() { return loco::DataType::S8; } + +template ::value, bool> = true> +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor()>(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(toLocoDataType()); + + SoftmaxParams params{}; + params.beta = 0.1; + + Softmax kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template ::value, bool> = true> +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::pair input_quant_param = + quantizationParams(std::min(std::min(input_data), 0.f), + std::max(std::max(input_data), 0.f)); + std::pair output_quant_param = + quantizationParams(std::min(std::min(output_data), 0.f), + std::max(std::max(output_data), 0.f)); + Tensor input_tensor = makeInputTensor()>(input_shape, input_quant_param.first, + input_quant_param.second, input_data, + memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(toLocoDataType(), output_quant_param.first, output_quant_param.second); + + SoftmaxParams params{}; + params.beta = 0.1; + + Softmax kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); +} + +template class SoftmaxTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SoftmaxTest, DataTypes); + +TYPED_TEST(SoftmaxTest, Simple) +{ + Check({2, 1, 2, 3}, {2, 1, 2, 3}, + { + 5, -9, 8, // + -7, 2, -4, // + 1, -2, 9, // + 3, -6, -1, // + }, + { + 0.38514, 0.09497, 0.51989, // + 0.20792, 0.51141, 0.28067, // + 0.25212, 0.18678, 0.56110, // + 0.48149, 0.19576, 0.32275, // + }); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.cpp new file mode 100644 index 0000000..630cd38 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/SpaceToBatchND.h" +#include "kernels/Utils.h" + +#include "PALSpaceToBatchND.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +const int kInputMinDimensionNum = 3; +const int kInputMaxDimensionNum = 4; + +} // namespace + +SpaceToBatchND::SpaceToBatchND(const Tensor *input, const Tensor *block_shape, + const Tensor *paddings, Tensor *output) + : Kernel({input, block_shape, paddings}, {output}) +{ +} + +void SpaceToBatchND::configure() +{ + const auto *block_shape_data = block_shape()->data(); + const auto *paddings_data = paddings()->data(); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= kInputMinDimensionNum); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= kInputMaxDimensionNum); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + int spatial_dims_num = input()->shape().num_dims() - 2; + + LUCI_INTERPRETER_CHECK(block_shape()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(block_shape()->shape().dim(0) == spatial_dims_num); + + LUCI_INTERPRETER_CHECK(paddings()->shape().num_dims() == 2); + LUCI_INTERPRETER_CHECK(paddings()->shape().dim(0) == spatial_dims_num); + LUCI_INTERPRETER_CHECK(paddings()->shape().dim(1) == 2); + + Shape output_shape = Shape(input()->shape().num_dims()); + int output_batch_size = input()->shape().dim(0); + for (int i = 0; i < spatial_dims_num; ++i) + { + int final_dim_size = + (input()->shape().dim(i + 1) + paddings_data[i * 2] + paddings_data[i * 2 + 1]); + LUCI_INTERPRETER_CHECK(final_dim_size % block_shape_data[i] == 0); + output_shape.dim(i + 1) = final_dim_size / block_shape_data[i]; + output_batch_size = output_batch_size * block_shape_data[i]; + } + output_shape.dim(0) = output_batch_size; + output_shape.dim(input()->shape().num_dims() - 1) = + input()->shape().dim(input()->shape().num_dims() - 1); + output()->resize(output_shape); +} + +void SpaceToBatchND::execute() const +{ + switch (input()->element_type()) + { + tflite::SpaceToBatchParams op_params; + case DataType::FLOAT32: + op_params.output_offset = 0; + luci_interpreter_pal::SpaceToBatchND( + op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(block_shape()), getTensorData(block_shape()), + getTensorShape(paddings()), getTensorData(paddings()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + op_params.output_offset = output()->zero_point(); + luci_interpreter_pal::SpaceToBatchND( + op_params, getTensorShape(input()), getTensorData(input()), + getTensorShape(block_shape()), getTensorData(block_shape()), + getTensorShape(paddings()), getTensorData(paddings()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.h b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.h new file mode 100644 index 0000000..0893003 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H +#define LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SpaceToBatchND : public Kernel +{ +public: + SpaceToBatchND(const Tensor *input, const Tensor *block_shape, const Tensor *paddings, + Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *block_shape() const { return _inputs[1]; } + const Tensor *paddings() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp new file mode 100644 index 0000000..3a8b0a8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/SpaceToBatchND.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, + std::initializer_list block_shape_shape, + std::initializer_list paddings_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list block_shape_data, + std::initializer_list paddings_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor block_shape_tensor = + makeInputTensor(block_shape_shape, block_shape_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor(paddings_shape, paddings_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template <> +void Check( + std::initializer_list input_shape, std::initializer_list block_shape_shape, + std::initializer_list paddings_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list block_shape_data, + std::initializer_list paddings_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::pair input_quant_param = + quantizationParams(std::min(input_data), std::max(input_data)); + Tensor input_tensor = + makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, + input_data, memory_manager.get()); + Tensor block_shape_tensor = + makeInputTensor(block_shape_shape, block_shape_data, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor(paddings_shape, paddings_data, memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::U8, input_quant_param.first, input_quant_param.second); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template class SpaceToBatchNDTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SpaceToBatchNDTest, DataTypes); + +TYPED_TEST(SpaceToBatchNDTest, Simple) +{ + Check(/*input_shape=*/{1, 5, 2, 1}, /*block_shape_shape=*/{2}, + /*paddings_shape=*/{2, 2}, + /*output_shape=*/{6, 2, 2, 1}, + /*input_data=*/{-1.0, 0.2, -0.3, 0.4, -0.5, 0.6, -0.7, 0.8, -0.9, 1.0}, + /*block_shape_data=*/{3, 2}, /*paddings_data=*/{1, 0, 2, 0}, + /*output_data=*/{0, 0, 0, -0.5, 0, 0, 0, 0.6, 0, -1.0, 0, -0.7, + 0, 0.2, 0, 0.8, 0, -0.3, 0, -0.9, 0, 0.4, 0, 1.0}); +} + +TEST(SpaceToBatchNDTest, Invalid_Shape_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor( + {1, 3, 3, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}, memory_manager.get()); + Tensor block_shape_tensor = makeInputTensor({2}, {2, 2}, memory_manager.get()); + Tensor paddings_tensor = + makeInputTensor({2, 2}, {0, 0, 0, 0}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp new file mode 100644 index 0000000..7c29e8c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "SpaceToDepth.h" +#include "Utils.h" +#include "PALSpaceToDepth.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +SpaceToDepth::SpaceToDepth(const Tensor *input, Tensor *output, const SpaceToDepthParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void SpaceToDepth::configure() +{ + assert(input()->shape().num_dims() == 4); + assert(output()->element_type() == DataType::FLOAT32 || + output()->element_type() == DataType::U8 || output()->element_type() == DataType::S8 || + output()->element_type() == DataType::S32 || output()->element_type() == DataType::S64); + assert(input()->element_type() == output()->element_type()); + + const int block_size = params().block_size; + const int32_t input_height = input()->shape().dim(1); + const int32_t input_width = input()->shape().dim(2); + int32_t output_height = input_height / block_size; + int32_t output_width = input_width / block_size; + + assert(input_height == output_height * block_size); + assert(input_width == output_width * block_size); + + Shape output_shape(4); + output_shape.dim(0) = input()->shape().dim(0); + output_shape.dim(1) = output_height; + output_shape.dim(2) = output_width; + output_shape.dim(3) = input()->shape().dim(3) * block_size * block_size; + + output()->resize(output_shape); +} + +void SpaceToDepth::execute() const +{ + tflite::SpaceToDepthParams op_params{}; + op_params.block_size = params().block_size; + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::SpaceToDepth(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + luci_interpreter_pal::SpaceToDepth(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.h b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.h new file mode 100644 index 0000000..e66316b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H +#define LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +class SpaceToDepth : public KernelWithParams +{ +public: + SpaceToDepth(const Tensor *input, Tensor *output, const SpaceToDepthParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp new file mode 100644 index 0000000..4af4886 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/SpaceToDepth.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template class SpaceToDepthTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SpaceToDepthTest, DataTypes); + +TYPED_TEST(SpaceToDepthTest, SimpleCase) +{ + std::unique_ptr memory_manager = std::make_unique(); + + constexpr DataType element_type = getElementType(); + std::vector input_data{1, 5, 6, 7, 2, 3, 4, 8}; + Shape input_shape{1, 2, 2, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + std::vector output_data{1, 5, 6, 7, 2, 3, 4, 8}; + std::vector output_shape{1, 1, 1, 8}; + Tensor output_tensor = makeOutputTensor(element_type); + + SpaceToDepthParams params{}; + params.block_size = 2; + + SpaceToDepth kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Split.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Split.cpp new file mode 100644 index 0000000..1a563f3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Split.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Split.h" + +#include "Utils.h" + +#include "PALSplit.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +Split::Split(const Tensor *axis, const Tensor *input, std::vector outputs) + : Kernel({axis, input}, std::move(outputs)) +{ +} + +void Split::configure() +{ + assert(axis()->shape().num_elements() == 1); + _axis_value = getTensorData(axis())[0]; + if (_axis_value < 0) + _axis_value += input()->shape().num_dims(); + assert(_axis_value >= 0 && _axis_value < input()->shape().num_dims()); + + const int32_t input_size = input()->shape().dim(_axis_value); + assert(input_size % _outputs.size() == 0); + const int32_t slice_size = input_size / _outputs.size(); + + Shape output_shape = input()->shape(); + output_shape.dim(_axis_value) = slice_size; + for (Tensor *output : _outputs) + { + output->resize(output_shape); + } +} + +void Split::execute() const +{ + tflite::SplitParams params{}; + params.num_split = _outputs.size(); + params.axis = _axis_value; + +#define TF_LITE_SPLIT(scalar) \ + { \ + VectorOfTensors all_outputs(_outputs); \ + luci_interpreter_pal::Split(params, getTensorShape(input()), getTensorData(input()), \ + all_outputs.shapes(), all_outputs.data()); \ + } + + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_SPLIT(float); + break; + case DataType::U8: + TF_LITE_SPLIT(uint8_t); + break; + default: + throw std::runtime_error("Unsupported type."); + } +#undef TF_LITE_SPLIT +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Split.h b/compiler/luci-micro/luci-interpreter/src/kernels/Split.h new file mode 100644 index 0000000..9542b1e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Split.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_H +#define LUCI_INTERPRETER_KERNELS_SPLIT_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Split : public Kernel +{ +public: + Split(const Tensor *axis, const Tensor *input, std::vector outputs); + + const Tensor *axis() const { return _inputs[0]; } + const Tensor *input() const { return _inputs[1]; } + Tensor *output(int index) const { return _outputs[index]; } + + void configure() override; + void execute() const override; + +private: + int32_t _axis_value{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SPLIT_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Split.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Split.test.cpp new file mode 100644 index 0000000..283cd9a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Split.test.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Split.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(int axis, int num_splits, std::initializer_list input_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::vector> output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + constexpr DataType element_type = getElementType(); + Tensor axis_tensor = makeInputTensor({}, {axis}, memory_manager.get()); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + + std::vector output_tensors; + output_tensors.reserve(num_splits); + for (int i = 0; i < num_splits; ++i) + { + output_tensors.emplace_back(makeOutputTensor(element_type)); + } + + std::vector output_tensor_ptrs(num_splits); + for (int i = 0; i < num_splits; ++i) + { + output_tensor_ptrs[i] = &output_tensors[i]; + } + + Split kernel(&axis_tensor, &input_tensor, std::move(output_tensor_ptrs)); + kernel.configure(); + for (int i = 0; i < num_splits; ++i) + { + memory_manager->allocate_memory(output_tensors[i]); + } + kernel.execute(); + + for (int i = 0; i < num_splits; ++i) + { + EXPECT_THAT(extractTensorData(output_tensors[i]), + ::testing::ElementsAreArray(output_data[i])); + } +} + +template class SplitTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SplitTest, DataTypes); + +TYPED_TEST(SplitTest, FourDimensional) +{ + Check(/*axis=*/0, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 3, 4, 5, 6, 7, 8}, // + {9, 10, 11, 12, 13, 14, 15, 16}, // + }); + Check( + /*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 3, 4, 9, 10, 11, 12}, // + {5, 6, 7, 8, 13, 14, 15, 16}, // + }); + Check( + /*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 5, 6, 9, 10, 13, 14}, // + {3, 4, 7, 8, 11, 12, 15, 16}, // + }); + Check( + /*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 3, 5, 7, 9, 11, 13, 15}, // + {2, 4, 6, 8, 10, 12, 14, 16}, // + }); +} + +TYPED_TEST(SplitTest, OneDimensional) +{ + Check( + /*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8}, + {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); +} + +TYPED_TEST(SplitTest, NegativeAxis) +{ + Check( + /*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 3, 4, 5, 6, 7, 8}, // + {9, 10, 11, 12, 13, 14, 15, 16}, + }); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.cpp new file mode 100644 index 0000000..aa68208 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "SplitV.h" + +#include "Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +SplitV::SplitV(const Tensor *input, const Tensor *size_splits, const Tensor *axis, + std::vector outputs) + : Kernel({input, size_splits, axis}, std::move(outputs)) +{ +} + +void SplitV::configure() +{ + assert(axis()->shape().num_elements() == 1); + _axis_value = getTensorData(axis())[0]; + if (_axis_value < 0) + _axis_value += input()->shape().num_dims(); + assert(_axis_value >= 0 && _axis_value < input()->shape().num_dims()); + + auto num_split = static_cast(_outputs.size()); + auto sizes_data = getTensorData(size_splits()); + + assert(size_splits()->shape().num_dims() == 1); + + int32_t sum = 0; + const auto num_dims_size_spits = size_splits()->shape().dim(0); + int32_t count_neg_dim = 0; + + for (int32_t i = 0; i < num_dims_size_spits - 1; ++i) + { + if (sizes_data[i] != -1) + { + sum += sizes_data[i]; + } + else + { + count_neg_dim++; + } + } + assert(count_neg_dim < 2); + assert(size_splits()->shape().num_elements() == num_split); + + auto output_shape = input()->shape(); + for (int32_t i = 0; i < num_split; ++i) + { + if (sizes_data[i] == -1) + { + output_shape.dim(_axis_value) = input()->shape().dim(_axis_value) - sum; + } + else + { + output_shape.dim(_axis_value) = sizes_data[i]; + } + _outputs[i]->resize(output_shape); + } +} + +void SplitV::execute() const +{ + tflite::SplitParams params{}; + params.num_split = _outputs.size(); + params.axis = _axis_value; + +#define TF_LITE_SPLIT(scalar) \ + { \ + VectorOfTensors all_outputs(_outputs); \ + tflite::optimized_ops::Split(params, getTensorShape(input()), getTensorData(input()), \ + all_outputs.shapes(), all_outputs.data()); \ + } + + switch (input()->element_type()) + { + case DataType::FLOAT32: + TF_LITE_SPLIT(float); + break; + case DataType::U8: + TF_LITE_SPLIT(uint8_t); + break; + case DataType::S16: + TF_LITE_SPLIT(int16_t); + break; + default: + throw std::runtime_error("Unsupported type."); + } +#undef TF_LITE_SPLIT +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.h b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.h new file mode 100644 index 0000000..92f6288 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_V_H +#define LUCI_INTERPRETER_KERNELS_SPLIT_V_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SplitV : public Kernel +{ +public: + SplitV(const Tensor *input, const Tensor *size_splits, const Tensor *axis, + std::vector outputs); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *size_splits() const { return _inputs[1]; } + const Tensor *axis() const { return _inputs[2]; } + Tensor *output(int index) const { return _outputs[index]; } + + void configure() override; + void execute() const override; + +private: + int32_t _axis_value{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SPLIT_V_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.test.cpp new file mode 100644 index 0000000..035bc21 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SplitV.test.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/SplitV.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(int axis, std::initializer_list splits_size, + std::initializer_list input_shape, std::initializer_list input_data, + std::vector> output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + + auto num_splits = static_cast(splits_size.size()); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor sizes_tensor = + makeInputTensor({num_splits}, splits_size, memory_manager.get()); + Tensor axis_tensor = makeInputTensor({}, {axis}, memory_manager.get()); + + std::vector output_tensors; + output_tensors.reserve(num_splits); + for (int i = 0; i < num_splits; ++i) + { + output_tensors.emplace_back(makeOutputTensor(element_type)); + } + + std::vector output_tensor_ptrs(num_splits); + for (int i = 0; i < num_splits; ++i) + { + output_tensor_ptrs[i] = &output_tensors[i]; + } + + SplitV kernel(&input_tensor, &sizes_tensor, &axis_tensor, std::move(output_tensor_ptrs)); + kernel.configure(); + for (int i = 0; i < num_splits; ++i) + { + memory_manager->allocate_memory(output_tensors[i]); + } + kernel.execute(); + + for (int i = 0; i < num_splits; ++i) + { + auto tmp = extractTensorData(output_tensors[i]); + EXPECT_THAT(extractTensorData(output_tensors[i]), + ::testing::ElementsAreArray(output_data[i])); + } +} + +template class SplitVTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SplitVTest, DataTypes); + +TYPED_TEST(SplitVTest, ThreeDimensional) +{ + Check( + /*axis=*/0, /*splits_size=*/{1, 2}, {3, 3, 3}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + { + {1, 2, 3, 4, 5, 6, 7, 8, 9}, // + {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27} // + }); + Check( + /*axis=*/1, /*splits_size=*/{1, 2}, {3, 3, 3}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + { + {1, 2, 3, 10, 11, 12, 19, 20, 21}, // + {4, 5, 6, 7, 8, 9, 13, 14, 15, 16, 17, 18, 22, 23, 24, 25, 26, 27} // + }); + Check( + /*axis=*/2, /*splits_size=*/{1, 2}, {3, 3, 3}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + { + {1, 4, 7, 10, 13, 16, 19, 22, 25}, // + {2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27} // + }); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.cpp new file mode 100644 index 0000000..46e9fc9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Sqrt.h" +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Sqrt::Sqrt(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Sqrt::configure() +{ + if (input()->element_type() != output()->element_type()) + { + throw std::runtime_error("Input/output tensor data type mismatch."); + } + output()->resize(input()->shape()); +} + +void Sqrt::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Sqrt::evalFloat() const +{ + auto in = getTensorData(input()); + auto out = getTensorData(output()); + auto size = getTensorShape(input()).FlatSize(); + for (auto i = in; i != in + size; ++i) + { + *out = std::sqrt(*i); + ++out; + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.h b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.h new file mode 100644 index 0000000..4034655 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SQRT_H +#define LUCI_INTERPRETER_KERNELS_SQRT_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Sqrt : public Kernel +{ +public: + Sqrt(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SQRT_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.test.cpp new file mode 100644 index 0000000..96835fb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sqrt.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Sqrt.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Sqrt kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(SqrtTest, SimpleSqrt) +{ + Check( + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 0, 8, 2, 4, // + 3, 7, 10, 0.3, // + }, + /*output_data=*/ + { + 0.0, 2.8284271, 1.4142136, 2, // + 1.7320508, 2.6457513, 3.1622777, 0.54772256, // + }); +} + +TEST(SqrtTest, Input_Output_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {1.f}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Sqrt kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(SqrtTest, Invalid_Input_Type_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Tensor input_tensor = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Sqrt kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Square.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Square.cpp new file mode 100644 index 0000000..bc71905 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Square.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Square.h" +#include "kernels/Utils.h" + +#include +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Square::Square(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Square::configure() +{ + if (input()->element_type() != output()->element_type()) + { + throw std::runtime_error("Input/output tensor data type mismatch."); + } + output()->resize(input()->shape()); +} + +void Square::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Square::evalFloat() const +{ + auto in = getTensorData(input()); + auto out = getTensorData(output()); + auto size = getTensorShape(input()).FlatSize(); + for (auto i = in; i != in + size; ++i) + { + *out = (*i) * (*i); + ++out; + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Square.h b/compiler/luci-micro/luci-interpreter/src/kernels/Square.h new file mode 100644 index 0000000..73ed5a7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Square.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SQUARE_H +#define LUCI_INTERPRETER_KERNELS_SQUARE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Square : public Kernel +{ +public: + Square(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SQUARE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Square.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Square.test.cpp new file mode 100644 index 0000000..51662de --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Square.test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Square.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(SquareTest, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape{3, 1, 2}; + std::vector input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data1, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Square kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{1.0, 0.0, 1.0, 121.0, 4.0, 2.0736}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.cpp new file mode 100644 index 0000000..3bafeba --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/SquaredDifference.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +SquaredDifference::SquaredDifference(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void SquaredDifference::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()) + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()) + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void SquaredDifference::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalSquaredDifference(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template inline void SquaredDifference::evalSquaredDifference() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output()), [](T x, T y) { + const T difference = x - y; + return difference * difference; + }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.h b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.h new file mode 100644 index 0000000..9327caf --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H +#define LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SquaredDifference : public Kernel +{ +public: + SquaredDifference(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template inline void evalSquaredDifference() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.test.cpp new file mode 100644 index 0000000..2819c01 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/SquaredDifference.test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/SquaredDifference.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(SquaredDifferenceTest, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape{3, 1, 2}; + std::vector input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43}; + Tensor input_tensor1 = + makeInputTensor(input_shape, input_data1, memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape, input_data2, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SquaredDifference kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{4.0, 0.0, 4.0, 1.0, 1.0, 0.0001}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(SquaredDifferenceTest, FloatBroadcast) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape1{3, 1, 2}; + Shape input_shape2{1}; + std::vector input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector input_data2{1.0}; + Tensor input_tensor1 = + makeInputTensor(input_shape1, input_data1, memory_manager.get()); + Tensor input_tensor2 = + makeInputTensor(input_shape2, input_data2, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SquaredDifference kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{0.0, 1.0, 4.0, 100.0, 9.0, 5.9536}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.cpp new file mode 100644 index 0000000..4a75518 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Squeeze.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Squeeze::Squeeze(const Tensor *input, Tensor *output, const SqueezeParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void Squeeze::configure() +{ + int input_num_dims = input()->shape().num_dims(); + int num_squeeze_dims = params().squeeze_dims.size(); + assert(input_num_dims <= 8); + bool should_squeeze[8] = {false}; + int num_squeezed_dims = 0; + if (num_squeeze_dims == 0) + { + for (int idx = 0; idx < input_num_dims; ++idx) + { + if (input()->shape().dim(idx) == 1) + { + should_squeeze[idx] = true; + ++num_squeezed_dims; + } + } + } + else + { + for (int idx = 0; idx < num_squeeze_dims; ++idx) + { + int current = params().squeeze_dims[idx] < 0 ? params().squeeze_dims[idx] + input_num_dims + : params().squeeze_dims[idx]; + assert(current >= 0 && current < input_num_dims && input()->shape().dim(current) == 1); + if (!should_squeeze[current]) + ++num_squeezed_dims; + should_squeeze[current] = true; + } + } + Shape output_shape(input_num_dims - num_squeezed_dims); + for (int in_idx = 0, out_idx = 0; in_idx < input_num_dims; ++in_idx) + { + if (!should_squeeze[in_idx]) + { + output_shape.dim(out_idx++) = input()->shape().dim(in_idx); + } + } + output()->resize(output_shape); +} + +void Squeeze::execute() const +{ + assert(input()->shape().num_elements() == output()->shape().num_elements()); + + const auto *input_data = input()->data(); + auto *output_data = output()->data(); + std::memcpy(output_data, input_data, + getDataTypeSize(input()->element_type()) * input()->shape().num_elements()); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.h b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.h new file mode 100644 index 0000000..687af51 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SQUEEZE_H +#define LUCI_INTERPRETER_KERNELS_SQUEEZE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Squeeze : public KernelWithParams +{ +public: + Squeeze(const Tensor *input, Tensor *output, const SqueezeParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SQUEEZE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.test.cpp new file mode 100644 index 0000000..1bc0b64 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Squeeze.test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Squeeze.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data, + std::initializer_list squeeze_dims) +{ + std::unique_ptr memory_manager = std::make_unique(); + + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + SqueezeParams params{}; + params.squeeze_dims = squeeze_dims; + + Squeeze kernel(&input_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template class SqueezeTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(SqueezeTest, DataTypes); + +TYPED_TEST(SqueezeTest, TotalTest) +{ + Check( + /*input_shape=*/{1, 24, 1}, /*output_shape=*/{24}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, + /*output_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, + {-1, 0}); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.cpp new file mode 100644 index 0000000..a8730d8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/StridedSlice.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +StridedSlice::StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, + const Tensor *strides, Tensor *output, const StridedSliceParams ¶ms) + : KernelWithParams({input, begin, end, strides}, {output}, params) +{ +} + +void StridedSlice::configure() +{ + assert(begin()->shape().num_dims() == 1); + assert(end()->shape().num_dims() == 1); + assert(strides()->shape().num_dims() == 1); + assert(input()->element_type() == output()->element_type()); + assert(begin()->element_type() == DataType::S32); + assert(end()->element_type() == DataType::S32); + assert(strides()->element_type() == DataType::S32); + assert(input()->shape().num_dims() <= 4); + if (params().ellipsis_mask != 0) + { + throw std::runtime_error("ellipsis_mask is not implemented yet."); + } + if (params().new_axis_mask != 0) + { + throw std::runtime_error("new_axis_mask is not implemented yet."); + } + if (input()->element_type() == DataType::U8) + { + assert(input()->scale() == output()->scale()); + assert(input()->zero_point() == output()->zero_point()); + } + tflite::StridedSliceParams op_params{}; + op_params.start_indices_count = input()->shape().num_dims(); + op_params.stop_indices_count = input()->shape().num_dims(); + op_params.strides_count = input()->shape().num_dims(); + + for (int i = 0; i < input()->shape().num_dims(); i++) + { + op_params.start_indices[i] = getTensorData(begin())[i]; + op_params.stop_indices[i] = getTensorData(end())[i]; + op_params.strides[i] = getTensorData(strides())[i]; + } + op_params.begin_mask = params().begin_mask; + op_params.ellipsis_mask = 0; + op_params.end_mask = params().end_mask; + op_params.new_axis_mask = 0; + op_params.shrink_axis_mask = params().shrink_axis_mask; + std::vector output_shape_vector; + for (int i = 0; i < input()->shape().num_dims(); i++) + { + int idx = input()->shape().num_dims() - i - 1; + int32_t stride = getTensorData(strides())[idx]; + assert(stride != 0); + int32_t begin = ::tflite::strided_slice::StartForAxis(op_params, getTensorShape(input()), idx); + int32_t end = + ::tflite::strided_slice::StopForAxis(op_params, getTensorShape(input()), idx, begin); + + const bool shrink_axis = params().shrink_axis_mask & (1 << idx); + if (shrink_axis) + { + end = begin + 1; + } + + int32_t dim_shape = std::ceil((end - begin) / static_cast(stride)); + dim_shape = dim_shape < 0 ? 0 : dim_shape; + if (!shrink_axis) + { + output_shape_vector.push_back(dim_shape); + } + } + Shape output_shape = Shape(output_shape_vector.size()); + for (size_t i = 0; i < output_shape_vector.size(); i++) + { + output_shape.dim(i) = output_shape_vector[output_shape_vector.size() - i - 1]; + } + output()->resize(output_shape); +} + +void StridedSlice::execute() const +{ + tflite::StridedSliceParams op_params{}; + op_params.start_indices_count = input()->shape().num_dims(); + op_params.stop_indices_count = input()->shape().num_dims(); + op_params.strides_count = input()->shape().num_dims(); + + for (int i = 0; i < input()->shape().num_dims(); i++) + { + op_params.start_indices[i] = getTensorData(begin())[i]; + op_params.stop_indices[i] = getTensorData(end())[i]; + op_params.strides[i] = getTensorData(strides())[i]; + } + op_params.begin_mask = params().begin_mask; + op_params.ellipsis_mask = 0; + op_params.end_mask = params().end_mask; + op_params.new_axis_mask = 0; + op_params.shrink_axis_mask = params().shrink_axis_mask; + + switch (input()->element_type()) + { + case DataType::FLOAT32: + tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::S32: + tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.h b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.h new file mode 100644 index 0000000..fc96893 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H +#define LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class StridedSlice : public KernelWithParams +{ +public: + StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, const Tensor *strides, + Tensor *output, const StridedSliceParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *begin() const { return _inputs[1]; } + const Tensor *end() const { return _inputs[2]; } + const Tensor *strides() const { return _inputs[3]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp new file mode 100644 index 0000000..399cdeb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/StridedSlice.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(StridedSliceTest, Float) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape{2, 3, 2}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape begin_shape{3}; + std::vector begin_data{0, 0, 0}; + Shape end_shape{3}; + std::vector end_data{1, 3, 2}; + Shape strides_shape{3}; + std::vector strides_data{1, 1, 1}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor begin_tensor = + makeInputTensor(begin_shape, begin_data, memory_manager.get()); + Tensor end_tensor = makeInputTensor(end_shape, end_data, memory_manager.get()); + Tensor strides_tensor = + makeInputTensor(strides_shape, strides_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + StridedSliceParams params{}; + params.begin_mask = 0; + params.end_mask = 0; + params.ellipsis_mask = 0; + params.new_axis_mask = 0; + params.shrink_axis_mask = 1; + + StridedSlice kernel(&input_tensor, &begin_tensor, &end_tensor, &strides_tensor, &output_tensor, + params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector output_shape{3, 2}; + std::vector output_data{1, 2, 3, 4, 5, 6}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(StridedSliceTest, Uint8) +{ + std::unique_ptr memory_manager = std::make_unique(); + + Shape input_shape{2, 3, 2}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape begin_shape{3}; + std::vector begin_data{0, 0, 0}; + Shape end_shape{3}; + std::vector end_data{1, 3, 2}; + Shape strides_shape{3}; + std::vector strides_data{1, 1, 1}; + Tensor input_tensor = + makeInputTensor(input_shape, 1.0f, 0, input_data, memory_manager.get()); + Tensor begin_tensor = + makeInputTensor(begin_shape, begin_data, memory_manager.get()); + Tensor end_tensor = makeInputTensor(end_shape, end_data, memory_manager.get()); + Tensor strides_tensor = + makeInputTensor(strides_shape, strides_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0f, 0); + + StridedSliceParams params{}; + params.begin_mask = 0; + params.end_mask = 0; + params.ellipsis_mask = 0; + params.new_axis_mask = 0; + params.shrink_axis_mask = 1; + + StridedSlice kernel(&input_tensor, &begin_tensor, &end_tensor, &strides_tensor, &output_tensor, + params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector output_shape{3, 2}; + std::vector output_data{1, 2, 3, 4, 5, 6}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sub.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.cpp new file mode 100644 index 0000000..24b6a72 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Sub.h" +#include "kernels/Utils.h" + +#include "PALSub.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Sub::Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams ¶ms) + : KernelWithParams({input1, input2}, {output}, params) +{ +} + +void Sub::configure() +{ + LUCI_INTERPRETER_CHECK(!(input1()->element_type() != input2()->element_type())) + LUCI_INTERPRETER_CHECK(!(input1()->element_type() != output()->element_type())) + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void Sub::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S64: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Sub::evalFloat() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastSubSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + luci_interpreter_pal::Sub(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +template void Sub::evalInteger() const +{ + tflite::ArithmeticParams params{}; + fillArithmeticActivationRange(params, _params.activation); + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastSubSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +void Sub::evalQuantized() const +{ + const auto input1_scale = static_cast(input1()->scale()); + const auto input2_scale = static_cast(input2()->scale()); + const auto output_scale = static_cast(output()->scale()); + + const int left_shift = 20; + const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); + const double real_input1_multiplier = input1_scale / twice_max_input_scale; + const double real_input2_multiplier = input2_scale / twice_max_input_scale; + const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); + + int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; + int input1_shift{}, input2_shift{}, output_shift{}; + quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); + quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); + quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + + tflite::ArithmeticParams params{}; + params.left_shift = left_shift; + // The kernel expects inputs' zero points to be negated. + params.input1_offset = -input1()->zero_point(); // Note the '-'. + params.input1_multiplier = input1_multiplier; + params.input1_shift = input1_shift; + params.input2_offset = -input2()->zero_point(); // Note the '-'. + params.input2_multiplier = input2_multiplier; + params.input2_shift = input2_shift; + params.output_offset = output()->zero_point(); + params.output_multiplier = output_multiplier; + params.output_shift = output_shift; + params.quantized_activation_min = activation_min; + params.quantized_activation_max = activation_max; + + const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( + getTensorShape(input1()), getTensorShape(input2()), ¶ms); + + if (need_broadcast) + { + tflite::reference_ops::BroadcastSubSlow( + params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), + getTensorData(input2()), getTensorShape(output()), getTensorData(output())); + } + else + { + tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData(input1()), + getTensorShape(input2()), getTensorData(input2()), + getTensorShape(output()), getTensorData(output())); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sub.h b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.h new file mode 100644 index 0000000..23952b3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SUB_H +#define LUCI_INTERPRETER_KERNELS_SUB_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Sub : public KernelWithParams +{ +public: + Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams ¶ms); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + template void evalInteger() const; + void evalQuantized() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SUB_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Sub.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.test.cpp new file mode 100644 index 0000000..9abafd4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Sub.test.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Sub.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; +using std::pair; +using std::vector; +using std::transform; +using std::initializer_list; + +class SubTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +// for quantized Add, the error shouldn't exceed step +float GetTolerance(float min, float max) +{ + float kQuantizedStep = (max - min) / 255.0; + return kQuantizedStep; +} + +TEST_F(SubTest, Uint8) +{ + Shape base_shape = {2, 3, 1, 2}; + vector base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + vector test_shapes = {{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + vector test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + vector> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + vector> output_data = { + {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f, + 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, + -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f}, + {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f}, + {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, + 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, + -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f}, + {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}}; + + float kQuantizedTolerance = GetTolerance(-3.f, 3.f); + pair quant_param = quantizationParams(-3.f, 3.f); + for (size_t i = 0; i < output_data.size(); ++i) + { + Tensor input1_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + + SubParams params{}; + params.activation = Activation::NONE; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data[i], kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + } + + // Inversion step for output_data, because subtract is not commutative operation + auto multiply = [](auto &i) { + transform(i.begin(), i.end(), i.begin(), [](auto &value) { return value * -1.0f; }); + }; + for_each(output_data.begin(), output_data.end(), multiply); + + // Re-run with exchanged inputs. + for (size_t i = 0; i < output_data.size(); ++i) + { + Tensor input1_tensor = makeInputTensor( + test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor( + base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + + SubParams params{}; + params.activation = Activation::NONE; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data[i], kQuantizedTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + } +} + +TEST_F(SubTest, Float) +{ + Shape base_shape = {2, 3, 1, 2}; + vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + vector> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + vector> test_outputs = { + {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f, + 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, + 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}}; + + vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, + 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; + vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = + makeInputTensor(base_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SubParams params{}; + params.activation = Activation::RELU; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) + << "With shape number " << i; + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + } +} + +template void CheckInteger(luci_interpreter::IMemoryManager *memory_manager) +{ + using dtype = typename loco::DataTypeImpl::Type; + Shape base_shape = {2, 3, 1, 2}; + std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + std::vector> test_outputs = { + {0, 1, 2, 3, 0, 0, 0, 0, 4, 1, 0, 0, 0, 0, 7, 0, 3, 0, + 0, 2, 4, 4, 0, 0, 3, 0, 10, 0, 6, 0, 3, 0, 10, 2, 6, 0}, + {0, 1, 4, 1, 3, 0, 0, 2, 10, 0, 6, 0}, + {0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 4, 3, 0, 0, 3, 0, 7, 0, + 2, 4, 0, 2, 0, 0, 8, 0, 6, 0, 1, 0, 8, 2, 6, 0, 1, 0}, + {0, 0, 0, 0, 7, 0, 2, 4, 6, 0, 1, 0}}; + std::vector input1_data{-1, 2, 1, 0, 4, -5, 1, 3, 7, -1, 7, 1}; + std::vector input2_data{4, 1, -3, -1, 1, 6}; + for (size_t i = 0; i < test_shapes.size(); ++i) + { + Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); + Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); + Tensor output_tensor = makeOutputTensor(DType); + + SubParams params{}; + params.activation = Activation::RELU; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) + << "With shape number " << i; + } +}; + +TEST_F(SubTest, SInt32) +{ + CheckInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(SubTest, SInt64) +{ + CheckInteger(_memory_manager.get()); + SUCCEED(); +} + +TEST_F(SubTest, Input_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SubParams params{}; + params.activation = Activation::RELU; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(SubTest, Invalid_Output_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + SubParams params{}; + params.activation = Activation::RELU; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(SubTest, Invalid_Input_Type_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U64); + + SubParams params{}; + params.activation = Activation::RELU; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(SubTest, Mismatching_Input_Int_Types_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + SubParams params{}; + params.activation = Activation::NONE; + + Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.cpp new file mode 100644 index 0000000..c4fa169 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Tanh.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +Tanh::Tanh(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Tanh::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + if (input()->element_type() == DataType::U8) + { + populateLookupTable(); + } + output()->resize(input()->shape()); +} + +void Tanh::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + evalQuantized(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Tanh::evalFloat() const +{ + tflite::reference_ops::Tanh(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); +} + +void Tanh::evalQuantized() const +{ + const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); + uint8_t *output_data = getTensorData(output()); + const uint8_t *input_data = getTensorData(input()); + for (int i = 0; i < size; ++i) + { + output_data[i] = getTableValue(input_data[i]); + } +} + +void Tanh::populateLookupTable() +{ + const auto input_scale = static_cast(input()->scale()); + const auto input_zero_point = static_cast(input()->zero_point()); + const auto output_scale = static_cast(output()->scale()); + const auto output_zero_point = static_cast(output()->zero_point()); + const float inverse_scale = 1 / output_scale; + int32_t maxval = std::numeric_limits::max(); + int32_t minval = std::numeric_limits::min(); + for (int32_t val = minval; val <= maxval; ++val) + { + const float dequantized = input_scale * (val - input_zero_point); + const float transformed = std::tanh(dequantized); + const float rescaled = std::round(transformed * inverse_scale); + const int32_t quantized = static_cast(rescaled + output_zero_point); + setTableValue(static_cast(std::max(std::min(maxval, quantized), minval)), + static_cast(val)); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.h b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.h new file mode 100644 index 0000000..8017c96 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_TANH_H +#define LUCI_INTERPRETER_KERNELS_TANH_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Tanh : public Kernel +{ +public: + Tanh(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void populateLookupTable(); + void setTableValue(uint8_t value, uint8_t idx) { _table[idx] = value; }; + uint8_t getTableValue(uint8_t idx) const { return _table[idx]; }; + +private: + uint8_t _table[256]{}; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_TANH_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.test.cpp new file mode 100644 index 0000000..bfae479 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Tanh.test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Tanh.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class TanhTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(TanhTest, Float) +{ + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, -6, 2, 4, // + 3, -2, 10, 1, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Tanh kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 0, -0.9999877, 0.9640275, 0.999329, // + 0.99505475, -0.9640275, 1, 0.7615941, // + }; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST_F(TanhTest, Uint8) +{ + float kMin = -1; + float kMax = 127.f / 128.f; + float kTanhTolerance = 2 * (1. / 256); + std::pair input_quant_param = quantizationParams(8 * kMin, 8 * kMax); + std::pair output_quant_param = quantizationParams(kMin, kMax); + std::vector input_data{ + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + }; + Tensor input_tensor = + makeInputTensor({2, 6, 4, 1}, input_quant_param.first, input_quant_param.second, + input_data, _memory_manager.get()); + Tensor output_tensor = + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + + Tanh kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{ + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + }; + std::vector ref_output_shape{2, 6, 4, 1}; + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data, kTanhTolerance)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(TanhTest, InputTypeInvalid_NEG) +{ + std::vector input_data{ + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + }; + Tensor input_tensor = + makeInputTensor({2, 6, 4, 1}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Tanh kernel(&input_tensor, &output_tensor); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(TanhTest, InputOutputMismatch_NEG) +{ + std::vector input_data{ + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + }; + Tensor input_tensor = + makeInputTensor({2, 6, 4, 1}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + Tanh kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.cpp new file mode 100644 index 0000000..4d983ad --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/TestUtils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ +namespace testing +{ + +using ::testing::FloatNear; +using ::testing::Matcher; + +Tensor makeOutputTensor(DataType element_type) { return Tensor(element_type, {}, {}, ""); } + +Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point) +{ + return Tensor(element_type, {}, {{scale}, {zero_point}}, ""); +} + +std::vector dequantizeTensorData(const Tensor &tensor) +{ + if (tensor.element_type() == DataType::U8) + { + std::vector data = extractTensorData(tensor); + return dequantize(data.data(), data.size(), tensor.scale(), tensor.zero_point()); + } + if (tensor.element_type() == DataType::S8) + { + std::vector data = extractTensorData(tensor); + return dequantize(data.data(), data.size(), tensor.scale(), tensor.zero_point()); + } + else if (tensor.element_type() == DataType::S16) + { + // S16 quantization is symmetric, so zero point should be zero. + for (auto zp : tensor.zero_points()) + { + (void)zp; + assert(zp == 0); + } + + std::vector data = extractTensorData(tensor); + if (tensor.scales().size() == 1) + { + return dequantize(data.data(), data.size(), tensor.scale(), 0); + } + + // quantize_dimension breaks shape into two parts: + // inner dimensions that contains continuous data with one quantization type + // outer dimensions that contains other dimensions + const Shape shape = tensor.shape(); + const int32_t quantized_dimension = tensor.quantized_dimension(); + assert(quantized_dimension < shape.num_dims()); + size_t outer_dims_size = 1; + int32_t quant_dim_size = shape.dim(quantized_dimension); + size_t inner_dims_size = 1; + assert(quant_dim_size == tensor.scales().size()); + + for (int i = 0; i < quantized_dimension; ++i) + outer_dims_size *= shape.dim(i); + for (int i = quantized_dimension + 1; i < shape.num_dims(); ++i) + inner_dims_size *= shape.dim(i); + + assert(shape.num_elements() == outer_dims_size * quant_dim_size * inner_dims_size); + + std::vector dequantized_data; + dequantized_data.reserve(shape.num_elements()); + for (size_t outer_it = 0; outer_it < outer_dims_size; ++outer_it) + for (int32_t channel = 0; channel < quant_dim_size; ++channel) + { + float scale = tensor.scales()[channel]; + size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); + std::vector part_dequantized_data = + dequantize(data.data() + offset, inner_dims_size, scale, 0); + dequantized_data.insert(dequantized_data.end(), part_dequantized_data.begin(), + part_dequantized_data.end()); + } + return dequantized_data; + } + else + { + throw std::runtime_error("Unsupported type."); + } +} + +Matcher> FloatArrayNear(const std::vector &values, float max_abs_error) +{ + std::vector> matchers; + matchers.reserve(values.size()); + for (const float v : values) + { + matchers.emplace_back(FloatNear(v, max_abs_error)); + } + return ElementsAreArray(matchers); +} + +std::vector extractTensorShape(const Tensor &tensor) +{ + std::vector result; + int dims = tensor.shape().num_dims(); + for (int i = 0; i < dims; i++) + { + result.push_back(tensor.shape().dim(i)); + } + return result; +} + +} // namespace testing +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.h b/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.h new file mode 100644 index 0000000..1f5a0c3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/TestUtils.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_TESTUTILS_H +#define LUCI_INTERPRETER_KERNELS_TESTUTILS_H + +#include "luci_interpreter/core/Tensor.h" +#include "luci_interpreter/MemoryManager.h" + +#include + +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ +namespace testing +{ + +template +std::vector quantize(const float *data, size_t num_elements, float scale, int32_t zero_point); + +template +Tensor makeInputTensor(const Shape &shape, const std::vector::Type> &data, + IMemoryManager *memory_manager) +{ + Tensor tensor(DT, shape, {}, ""); + memory_manager->allocate_memory(tensor); + tensor.writeData(data.data(), data.size() * sizeof(typename DataTypeImpl
::Type)); + return tensor; +} + +/** + * @brief Create layer-wise quantized tensor + * @tparam DT base integer data type, for example DataType::U8, DataType::S16, DataType::S64 + * @param shape desired tensor shape + * @param scale scale of quantized number + * @param zero_point zero point of quantized number, should be 0 for signed datatypes + * @param data floating point data for quantization + * @param memory_manager memory manager for allocating memory to tensor + * @return created tensor + */ +template +Tensor makeInputTensor(const Shape &shape, float scale, int32_t zero_point, + const std::vector &data, IMemoryManager *memory_manager) +{ + using NativeT = typename DataTypeImpl
::Type; + Tensor tensor(DT, shape, {{scale}, {zero_point}}, ""); + std::vector quantized_data = + quantize(data.data(), data.size(), scale, zero_point); + memory_manager->allocate_memory(tensor); + tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT)); + return tensor; +} + +/** + * @brief Create channel-wise quantized tensor + * @tparam DT base integer data type, for example DataType::U8, DataType::S16, DataType::S64 + * @param shape desired tensor shape + * @param scales scales of quantized number + * @param zero_points zero points of quantized number, should be 0 for signed datatypes + * @param quantize_dimension dimension to apply quantization along. Usually channels/output channels + * @param data floating point data for quantization + * @param memory_manager memory manager for allocating memory to tensor + * @return created tensor + */ +template +Tensor makeInputTensor(const Shape &shape, const std::vector &scales, + const std::vector &zero_points, int quantized_dimension, + const std::vector &data, IMemoryManager *memory_manager) +{ + using NativeT = typename DataTypeImpl
::Type; + assert(quantized_dimension < shape.num_dims()); + Tensor tensor(DT, shape, {scales, zero_points, quantized_dimension}, ""); + + // quantize_dimension breaks shape into two parts: + // inner dimensions that contains continuous data with one quantization type + // outer dimensions that contains other dimensions + size_t outer_dims_size = 1; + int32_t quant_dim_size = shape.dim(quantized_dimension); + size_t inner_dims_size = 1; + assert(quant_dim_size == scales.size()); + assert(quant_dim_size == zero_points.size()); + + for (int i = 0; i < quantized_dimension; ++i) + outer_dims_size *= shape.dim(i); + for (int i = quantized_dimension + 1; i < shape.num_dims(); ++i) + inner_dims_size *= shape.dim(i); + + assert(shape.num_elements() == outer_dims_size * quant_dim_size * inner_dims_size); + + std::vector quantized_data; + quantized_data.reserve(shape.num_elements()); + for (size_t outer_it = 0; outer_it < outer_dims_size; ++outer_it) + for (int32_t channel = 0; channel < quant_dim_size; ++channel) + { + int32_t zero_point = zero_points[channel]; + float scale = scales[channel]; + size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); + std::vector part_quantized_data = + quantize(data.data() + offset, inner_dims_size, scale, zero_point); + quantized_data.insert(quantized_data.end(), part_quantized_data.begin(), + part_quantized_data.end()); + } + assert(quantized_data.size() == shape.num_elements()); + memory_manager->allocate_memory(tensor); + tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT)); + return tensor; +} + +Tensor makeOutputTensor(DataType element_type); +Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point); + +std::vector extractTensorShape(const Tensor &tensor); + +// Returns the corresponding DataType given the type T. +template constexpr DataType getElementType() +{ + if (std::is_same::value) + return DataType::FLOAT32; + if (std::is_same::value) + return DataType::FLOAT64; + if (std::is_same::value) + return DataType::U8; + if (std::is_same::value) + return DataType::U16; + if (std::is_same::value) + return DataType::U32; + if (std::is_same::value) + return DataType::U64; + if (std::is_same::value) + return DataType::S8; + if (std::is_same::value) + return DataType::S16; + if (std::is_same::value) + return DataType::S32; + if (std::is_same::value) + return DataType::S64; + if (std::is_same::value) + return DataType::BOOL; + return DataType::Unknown; +} + +template std::vector extractTensorData(const Tensor &tensor) +{ + const auto *data_ptr = tensor.data(); + return std::vector(data_ptr, data_ptr + tensor.shape().num_elements()); +} + +std::vector dequantizeTensorData(const Tensor &tensor); + +// Array version of `::testing::FloatNear` matcher. +::testing::Matcher> FloatArrayNear(const std::vector &values, + float max_abs_error = 1.0e-5f); + +template +std::vector quantize(const float *data, size_t num_elements, float scale, int32_t zero_point) +{ + static_assert(std::is_integral::value, "Integral type expected."); + + float q_min{}, q_max{}; + if (std::is_signed::value) + { + q_min = -std::numeric_limits::max(); + q_max = std::numeric_limits::max(); + } + else + { + q_min = 0; + q_max = std::numeric_limits::max(); + } + + std::vector q; + for (size_t i = 0; i < num_elements; ++i) + { + const auto &f = data[i]; + q.push_back(static_cast( + std::max(q_min, std::min(q_max, std::round(zero_point + (f / scale)))))); + } + return q; +} + +template +std::vector dequantize(const T *data, size_t num_elements, float scale, int32_t zero_point) +{ + static_assert(std::is_integral::value, "Integral type expected."); + std::vector f; + for (size_t i = 0; i < num_elements; ++i) + { + const T &q = data[i]; + f.push_back(scale * (q - zero_point)); + } + return f; +} + +// NOTE Returns scale and zero point for _asymmetric_ range (both signed and unsigned). +template std::pair quantizationParams(float f_min, float f_max) +{ + static_assert(std::is_integral::value, "Integral type expected."); + int32_t zero_point = 0; + float scale = 0; + const T qmin = std::numeric_limits::lowest(); + const T qmax = std::numeric_limits::max(); + const float qmin_double = qmin; + const float qmax_double = qmax; + // 0 should always be a representable value. Let's assume that the initial + // min,max range contains 0. + assert(f_max >= 0); + assert(f_min <= 0); + if (f_min == f_max) + { + // Special case where the min,max range is a point. Should be {0}. + assert(f_max == 0); + assert(f_min == 0); + return {scale, zero_point}; + } + + // General case. + // + // First determine the scale. + scale = (f_max - f_min) / (qmax_double - qmin_double); + + // Zero-point computation. + // First the initial floating-point computation. The zero-point can be + // determined from solving an affine equation for any known pair + // (real value, corresponding quantized value). + // We know two such pairs: (rmin, qmin) and (rmax, qmax). + // The arithmetic error on the zero point computed from either pair + // will be roughly machine_epsilon * (sum of absolute values of terms) + // so we want to use the variant that adds the smaller terms. + const float zero_point_from_min = qmin_double - f_min / scale; + const float zero_point_from_max = qmax_double - f_max / scale; + + const float zero_point_from_min_error = std::abs(qmin_double) + std::abs(f_min / scale); + + const float zero_point_from_max_error = std::abs(qmax_double) + std::abs(f_max / scale); + + const float zero_point_double = zero_point_from_min_error < zero_point_from_max_error + ? zero_point_from_min + : zero_point_from_max; + + // Now we need to nudge the zero point to be an integer + // (our zero points are integer, and this is motivated by the requirement + // to be able to represent the real value "0" exactly as a quantized value, + // which is required in multiple places, for example in Im2col with SAME + // padding). + + T nudged_zero_point = 0; + if (zero_point_double < qmin_double) + { + nudged_zero_point = qmin; + } + else if (zero_point_double > qmax_double) + { + nudged_zero_point = qmax; + } + else + { + nudged_zero_point = static_cast(std::round(zero_point_double)); + } + + // The zero point should always be in the range of quantized value, + // // [qmin, qmax]. + assert(qmax >= nudged_zero_point); + assert(qmin <= nudged_zero_point); + zero_point = nudged_zero_point; + // finally, return the values + return {scale, zero_point}; +} + +inline float getTolerance(float min, float max, int quantize_steps) +{ + return ((max - min) / quantize_steps); +} + +} // namespace testing +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_TESTUTILS_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.cpp new file mode 100644 index 0000000..802d872 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Transpose.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Transpose::Transpose(const Tensor *input, const Tensor *perm, Tensor *output) + : Kernel({input, perm}, {output}) +{ +} + +void Transpose::configure() +{ + // Transpose op only supports 1D-4D input arrays. + int dims = input()->shape().num_dims(); + const int32_t *perm_data = getTensorData(perm()); + + assert(input()->shape().num_dims() <= 4); + assert(input()->element_type() == output()->element_type()); + + assert(perm()->shape().num_dims() == 1); + assert(perm()->shape().dim(0) == dims); + + Shape output_shape(dims); + for (int i = 0; i < dims; i++) + { + assert(perm_data[i] < dims && perm_data[i] >= 0); + output_shape.dim(i) = input()->shape().dim(perm_data[i]); + } + + output()->resize(output_shape); +} + +void Transpose::execute() const +{ + tflite::TransposeParams params{}; + const int32_t *perm_data = getTensorData(perm()); + const int32_t size = perm()->shape().dim(0); + params.perm_count = size; + for (int i = 0; i < size; i++) + params.perm[i] = perm_data[i]; + switch (input()->element_type()) + { + case DataType::FLOAT32: + tflite::reference_ops::Transpose(params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + case DataType::U8: + tflite::reference_ops::Transpose(params, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.h b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.h new file mode 100644 index 0000000..d6f89c3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_TRANSPOSE_H +#define LUCI_INTERPRETER_KERNELS_TRANSPOSE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Transpose : public Kernel +{ +public: + Transpose(const Tensor *input, const Tensor *perm, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *perm() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_TRANSPOSE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.test.cpp new file mode 100644 index 0000000..43be8f8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Transpose.test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Transpose.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list input_shape, std::initializer_list perm_shape, + std::initializer_list output_shape, std::initializer_list input_data, + std::initializer_list perm_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor perm_tensor = makeInputTensor(perm_shape, perm_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + Transpose kernel(&input_tensor, &perm_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); +} + +template class TransposeTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(TransposeTest, DataTypes); + +TYPED_TEST(TransposeTest, Small3D) +{ + Check(/*input_shape=*/{2, 3, 4}, /*perm_shape=*/{3}, /*output_shape=*/{4, 2, 3}, + /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, + /*perm_data=*/{2, 0, 1}, + /*output_data=*/{0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21, + 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23}); +} + +TYPED_TEST(TransposeTest, Large4D) +{ + Check( + /*input_shape=*/{2, 3, 4, 5}, /*perm_shape=*/{4}, /*output_shape=*/{4, 2, 3, 5}, + /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, + /*perm_data=*/{2, 0, 1, 3}, + /*output_data=*/{0, 1, 2, 3, 4, 20, 21, 22, 23, 24, 40, 41, 42, 43, 44, + 60, 61, 62, 63, 64, 80, 81, 82, 83, 84, 100, 101, 102, 103, 104, + 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 45, 46, 47, 48, 49, + 65, 66, 67, 68, 69, 85, 86, 87, 88, 89, 105, 106, 107, 108, 109, + 10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, + 70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114, + 15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59, + 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119}); +} + +TYPED_TEST(TransposeTest, Large2D) +{ + Check( + /*input_shape=*/{10, 12}, /*perm_shape=*/{2}, /*output_shape=*/{12, 10}, + /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, + /*perm_data=*/{1, 0}, + /*output_data=*/{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, + 61, 73, 85, 97, 109, 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, + 3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 4, 16, 28, 40, 52, + 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113, + 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, + 67, 79, 91, 103, 115, 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, + 9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 10, 22, 34, 46, 58, + 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119}); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.cpp new file mode 100644 index 0000000..1b5f9d9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/TransposeConv.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +TransposeConv::TransposeConv(const Tensor *output_shape, const Tensor *filter, const Tensor *input, + const Tensor *bias, Tensor *output, Tensor *scratch_tensor, + const TransposeConvParams ¶ms) + : KernelWithParams({output_shape, filter, input, bias}, + {output, scratch_tensor}, params) +{ +} + +TransposeConv::~TransposeConv() +{ + // Define destructor here, to delete vector of qunatized multipliers properly +} + +void TransposeConv::configure() +{ + assert(output_shape()->shape().num_dims() == 1); + assert(input()->shape().num_dims() == 4); + assert(filter()->shape().num_dims() == 4); + assert(input()->element_type() == DataType::FLOAT32 || input()->element_type() == DataType::U8 || + input()->element_type() == DataType::S16); + assert(input()->element_type() == output()->element_type()); + assert(input()->shape().dim(3) == filter()->shape().dim(3)); + + const int num_dims = output_shape()->shape().dim(0); + Shape out_shape(num_dims); + const auto *shape_data = getTensorData(output_shape()); + for (int i = 0; i < num_dims; i++) + out_shape.dim(i) = shape_data[i]; + output()->resize(out_shape); + + const int32_t filter_height = filter()->shape().dim(1); + const int32_t filter_width = filter()->shape().dim(2); + const int32_t output_height = out_shape.dim(1); + const int32_t output_width = out_shape.dim(2); + + const int32_t unused_output_height = + computeOutputSize(params().padding, output_height, filter_height, params().stride_height, 1); + const int32_t unused_output_width = + computeOutputSize(params().padding, output_width, filter_width, params().stride_width, 1); + + _padding_height = + computePadding(params().stride_height, 1, output_height, filter_height, unused_output_height); + _padding_width = + computePadding(params().stride_width, 1, output_width, filter_width, unused_output_width); + + if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16) + { + auto scratch_tensor = getOutputTensors()[1]; + scratch_tensor->resize(output()->shape()); + const std::vector real_multipliers = + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + + _quant_multipliers = quantizeMultipliers(real_multipliers); + } + else + { + auto scratch_tensor = getOutputTensors()[1]; + scratch_tensor->set_allocatable(false); + } +} + +void TransposeConv::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::U8: + if (filter()->scales().size() == 1) + { + evalQuantized(); + } + else if (filter()->scales().size() > 1) + { + LUCI_INTERPRETER_CHECK(filter()->shape().num_dims() == 4); + LUCI_INTERPRETER_CHECK(filter()->scales().size() == + static_cast(filter()->shape().dim(0))); + evalQuantizedPerChannel(); + } + break; + case DataType::S16: + evalQuantizedS16(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void TransposeConv::evalFloat() const +{ + tflite::ConvParams op_params{}; + op_params.padding_type = tflite::PaddingType::kSame; + op_params.padding_values.height = _padding_height; + op_params.padding_values.width = _padding_width; + op_params.stride_height = params().stride_height; + op_params.stride_width = params().stride_width; + tflite::reference_ops::TransposeConv(op_params, // + getTensorShape(input()), getTensorData(input()), // + getTensorShape(filter()), getTensorData(filter()), // + getTensorShape(bias()), getTensorData(bias()), // + getTensorShape(output()), getTensorData(output()), // + tflite::RuntimeShape(), nullptr); +} + +void TransposeConv::evalQuantized() const +{ + tflite::ConvParams op_params{}; + op_params.padding_type = tflite::PaddingType::kSame; + op_params.padding_values.height = _padding_height; + op_params.padding_values.width = _padding_width; + op_params.stride_height = params().stride_height; + op_params.stride_width = params().stride_width; + // The kernel expects input and filter zero points to be negated. + op_params.input_offset = -input()->zero_point(); // Note the '-'. + op_params.weights_offset = -filter()->zero_point(); // Note the '-'. + op_params.output_offset = output()->zero_point(); + op_params.output_multiplier = _quant_multipliers[0].multiplier; + op_params.output_shift = _quant_multipliers[0].shift; + op_params.quantized_activation_min = std::numeric_limits::min(); + op_params.quantized_activation_max = std::numeric_limits::max(); + + auto scratch_tensor = getOutputTensors()[1]; + + tflite::reference_ops::TransposeConv(op_params, // + getTensorShape(input()), getTensorData(input()), // + getTensorShape(filter()), getTensorData(filter()), // + getTensorShape(bias()), getTensorData(bias()), // + getTensorShape(output()), getTensorData(output()), // + tflite::RuntimeShape(), nullptr, // + getTensorData(scratch_tensor)); +} + +void TransposeConv::evalQuantizedPerChannel() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + auto scratch_tensor = getOutputTensors()[1]; + auto *scratch_data = getTensorData(scratch_tensor); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t output_depth = filter_shape.dim(0); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(Activation::NONE, output(), &activation_min, &activation_max); + + std::memset(scratch_data, 0, scratch_tensor->shape().num_elements() * sizeof(int32_t)); + + BroadcastableWrapper output_multipliers(_quant_multipliers); + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t in_y = 0; in_y < input_height; ++in_y) + { + for (int32_t in_x = 0; in_x < input_width; ++in_x) + { + for (int32_t in_c = 0; in_c < input_depth; ++in_c) + { + const int32_t out_y_origin = in_y * stride_height - _padding_height; + const int32_t out_x_origin = in_x * stride_width - _padding_width; + for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int32_t out_x = out_x_origin + filter_x; + const int32_t out_y = out_y_origin + filter_y; + if ((out_y >= 0 && out_y < output_height) && (out_x >= 0 && out_x < output_width)) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + const uint8_t input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + const uint8_t filter_val = + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] += + static_cast(input_val - input()->zero_point()) * + static_cast(filter_val - filter()->zero_points()[out_c]); + } + } + } + } + } + } + } + for (int32_t out_y = 0; out_y < output_height; ++out_y) + { + for (int32_t out_x = 0; out_x < output_width; ++out_x) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + int32_t acc = scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)]; + if (bias_data) + { + acc += bias_data[out_c]; + } + + int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( + acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); + + scaled_acc += output()->zero_point(); + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + + output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc; + } + } + } + } +} + +void TransposeConv::evalQuantizedS16() const +{ + const auto *input_data = getTensorData(input()); + const auto *filter_data = getTensorData(filter()); + const auto *bias_data = getTensorData(bias()); + auto *output_data = getTensorData(output()); + + auto scratch_tensor = getOutputTensors()[1]; + auto *scratch_data = getTensorData(scratch_tensor); + + const Shape &input_shape = input()->shape(); + const Shape &filter_shape = filter()->shape(); + const Shape &output_shape = output()->shape(); + + const int32_t batches = input_shape.dim(0); + const int32_t input_height = input_shape.dim(1); + const int32_t input_width = input_shape.dim(2); + const int32_t input_depth = input_shape.dim(3); + const int32_t output_depth = filter_shape.dim(0); + const int32_t filter_height = filter_shape.dim(1); + const int32_t filter_width = filter_shape.dim(2); + const int32_t output_height = output_shape.dim(1); + const int32_t output_width = output_shape.dim(2); + + const int32_t stride_height = _params.stride_height; + const int32_t stride_width = _params.stride_width; + + int32_t activation_min{}; + int32_t activation_max{}; + calculateActivationRangeQuantized(Activation::NONE, output(), &activation_min, &activation_max); + + std::memset(scratch_data, 0, scratch_tensor->shape().num_elements() * sizeof(int64_t)); + + BroadcastableWrapper output_multipliers(_quant_multipliers); + for (int32_t batch = 0; batch < batches; ++batch) + { + for (int32_t in_y = 0; in_y < input_height; ++in_y) + { + for (int32_t in_x = 0; in_x < input_width; ++in_x) + { + for (int32_t in_c = 0; in_c < input_depth; ++in_c) + { + const int32_t out_y_origin = in_y * stride_height - _padding_height; + const int32_t out_x_origin = in_x * stride_width - _padding_width; + for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int32_t out_x = out_x_origin + filter_x; + const int32_t out_y = out_y_origin + filter_y; + if ((out_y >= 0 && out_y < output_height) && (out_x >= 0 && out_x < output_width)) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + const int16_t input_val = + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + const int16_t filter_val = + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] += + static_cast(input_val) * static_cast(filter_val); + } + } + } + } + } + } + } + for (int32_t out_y = 0; out_y < output_height; ++out_y) + { + for (int32_t out_x = 0; out_x < output_width; ++out_x) + { + for (int32_t out_c = 0; out_c < output_depth; ++out_c) + { + int64_t acc = scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)]; + if (bias_data) + { + acc += bias_data[out_c]; + } + int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( + acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); + + scaled_acc = std::max(scaled_acc, activation_min); + scaled_acc = std::min(scaled_acc, activation_max); + + output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc; + } + } + } + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.h b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.h new file mode 100644 index 0000000..cea0cf3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H +#define LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class ChannelQuantMultipliers; + +class TransposeConv : public KernelWithParams +{ +public: + TransposeConv(const Tensor *output_shape, const Tensor *filter, const Tensor *input, + const Tensor *bias, Tensor *output, Tensor *scratch_tensor, + const TransposeConvParams ¶ms); + + ~TransposeConv(); + + const Tensor *output_shape() const { return _inputs[0]; } + const Tensor *filter() const { return _inputs[1]; } + const Tensor *input() const { return _inputs[2]; } + const Tensor *bias() const { return _inputs[3]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; + void evalQuantized() const; + void evalQuantizedPerChannel() const; + void evalQuantizedS16() const; + +private: + int32_t _padding_height{}; + int32_t _padding_width{}; + // The scaling factor from input to output (aka the 'real multiplier') can + // be represented as a fixed point multiplier plus a left shift. + std::vector _quant_multipliers; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp new file mode 100644 index 0000000..4856e1b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/TransposeConv.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(std::initializer_list output_shape_shape, + std::initializer_list weight_shape, std::initializer_list input_shape, + std::initializer_list bias_shape, std::initializer_list output_shape, + std::initializer_list output_shape_data, std::initializer_list weight_data, + std::initializer_list input_data, std::initializer_list bias_data, + std::initializer_list output_data, luci::Padding padding, int32_t stride_height, + int32_t stride_width) +{ + std::unique_ptr memory_manager = std::make_unique(); + + constexpr DataType element_type = getElementType(); + Tensor output_shape_tensor = + makeInputTensor(output_shape_shape, output_shape_data, memory_manager.get()); + Tensor weight_tensor = + makeInputTensor(weight_shape, weight_data, memory_manager.get()); + Tensor input_data_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + + DataType scratch_data_type = element_type == DataType::S16 ? DataType::S64 : DataType::S32; + Tensor scratch_tensor(scratch_data_type, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(element_type); + + TransposeConvParams params{}; + params.padding = padding; + params.stride_height = stride_height; + params.stride_width = stride_width; + + if (bias_data.size() != 0) + { + Tensor bias_tensor = + makeInputTensor()>(bias_shape, bias_data, memory_manager.get()); + TransposeConv kernel(&output_shape_tensor, &weight_tensor, &input_data_tensor, &bias_tensor, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + } + else + { + TransposeConv kernel(&output_shape_tensor, &weight_tensor, &input_data_tensor, nullptr, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + } + EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); +} + +TEST(TransposeConvTest, FloatSimple) +{ + Check( + /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1}, + /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, + /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*bias_data=*/{}, + /*output_data=*/{29, 62, 83, 75, 99, 192, 237, 198, 207, 372, 417, 330, 263, 446, 485, 365}, + /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); + + SUCCEED(); +} + +TEST(TransposeConvTest, FloatTwoFiltersTest) +{ + Check( + /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2}, + /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, + /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + /*bias_data=*/{}, + /*output_data=*/ + {184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760}, + /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); + + SUCCEED(); +} + +TEST(TransposeConvTest, SimpleBiasTest) +{ + Check( + /*output_shape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1}, + /*input_shape=*/{1, 2, 2, 1}, + /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 5, 5, 2}, + /*weight_data=*/{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}, + /*input_data=*/{1, 2, 3, 4}, + /*bias_data=*/{3, 4}, + /*output_data=*/{4, 6, 6, 8, 10, 14, 9, 12, 13, 16, 10, 12, 12, 14, 28, 32, 21, + 24, 25, 28, 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, 24, 28, 30, 34, + 64, 72, 39, 44, 47, 52, 42, 46, 48, 52, 106, 114, 63, 68, 71, 76}, + /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2); + + SUCCEED(); +} + +TEST(TransposeConvTest, UInt8) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::vector input_data{1, 2, 3, 4}; + std::vector filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}; + std::vector bias_data{3, 4}; + std::vector output_shape_data{1, 5, 5, 2}; + std::vector ref_output_data{ + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + }; + + // Choose quantization parameters carefully. + auto input_quant = quantizationParams(-8.0, 7.9375); // s = 1 / 16, zp = 128 + auto filter_quant = quantizationParams(-24.0, 39.75); // s = 1 / 4, zp = 96 + auto output_quant = quantizationParams(-64.0, 191.0); // s = 1, zp = 64 + + Tensor input_tensor = makeInputTensor( + {1, 2, 2, 1}, input_quant.first, input_quant.second, input_data, memory_manager.get()); + Tensor filter_tensor = makeInputTensor( + {2, 3, 3, 1}, filter_quant.first, filter_quant.second, filter_data, memory_manager.get()); + Tensor bias_tensor = makeInputTensor({2}, input_quant.first * filter_quant.first, + 0, bias_data, memory_manager.get()); + Tensor output_shape_tensor = + makeInputTensor({4}, output_shape_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second); + + DataType scratch_data_type = + input_tensor.element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + Tensor scratch_tensor(scratch_data_type, Shape({}), {}, ""); + + TransposeConvParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + + TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(TransposeConvTest, UInt8_CWQ) +{ + std::unique_ptr memory_manager = std::make_unique(); + + const int32_t output_channels = 2; + std::vector input_data{1, 2, 3, 4}; + std::vector filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}; + std::vector bias_data{3, 4}; + std::vector output_shape_data{1, 5, 5, 2}; + std::vector ref_output_data{ + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + }; + + // Choose quantization parameters carefully. + auto input_quant = quantizationParams(-8.0, 7.9375); // s = 1 / 16, zp = 128 + auto output_quant = quantizationParams(-64.0, 191.0); // s = 1, zp = 64 + + std::vector> filter_quant_params; + filter_quant_params.push_back(quantizationParams(0, 17)); + filter_quant_params.push_back(quantizationParams(0, 18)); + + std::vector filter_scales; + std::vector filter_zerops; + for (auto iter : filter_quant_params) + { + filter_scales.push_back(iter.first); + filter_zerops.push_back(iter.second); + } + + std::vector bias_scales; + for (int i = 0; i < output_channels; ++i) + bias_scales.push_back(filter_quant_params[i].first * input_quant.first); + std::vector zerop(output_channels, 0); + + Tensor input_tensor = makeInputTensor( + {1, 2, 2, 1}, input_quant.first, input_quant.second, input_data, memory_manager.get()); + Tensor filter_tensor = makeInputTensor( + {output_channels, 3, 3, 1}, filter_scales, filter_zerops, 0, filter_data, memory_manager.get()); + Tensor bias_tensor = makeInputTensor({output_channels}, bias_scales, zerop, 0, + bias_data, memory_manager.get()); + Tensor output_shape_tensor = + makeInputTensor({4}, output_shape_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second); + + DataType scratch_data_type = + input_tensor.element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + Tensor scratch_tensor(scratch_data_type, Shape({}), {}, ""); + + TransposeConvParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + + TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(TransposeConvTest, SInt16) +{ + std::unique_ptr memory_manager = std::make_unique(); + + std::vector input_data{1, 2, 3, 4}; + std::vector filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}; + std::vector bias_data{3, 4}; + std::vector output_shape_data{1, 5, 5, 2}; + std::vector ref_output_data{ + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + }; + + Tensor input_tensor = + makeInputTensor({1, 2, 2, 1}, 0.25, 0, input_data, memory_manager.get()); + Tensor filter_tensor = + makeInputTensor({2, 3, 3, 1}, 0.2, 0, filter_data, memory_manager.get()); + Tensor bias_tensor = + makeInputTensor({2}, 0.25 * 0.2, 0, bias_data, memory_manager.get()); + Tensor output_shape_tensor = + makeInputTensor({4}, output_shape_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + + DataType scratch_data_type = + input_tensor.element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + Tensor scratch_tensor(scratch_data_type, Shape({}), {}, ""); + + TransposeConvParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + + TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(TransposeConvTest, SInt16_CWQ_weights) +{ + std::unique_ptr memory_manager = std::make_unique(); + + const int output_channels = 2; + const Shape input_shape{1, 2, 2, 1}; + const Shape filter_shape{output_channels, 3, 3, 1}; + const Shape bias_shape{output_channels}; + std::vector output_shape_data{1, 5, 5, output_channels}; + + std::vector input_data{1, 2, 3, 4}; + std::vector filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}; + std::vector bias_data{3, 4}; + + std::vector ref_output_data{ + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + }; + + const float input_scale = 0.25; + const float output_scale = 0.5; + const std::vector filter_scales{0.2f, 0.5f}; + std::vector bias_scales{filter_scales[0] * input_scale, filter_scales[1] * input_scale}; + const std::vector zerop(2, 0); + + Tensor input_tensor = + makeInputTensor(input_shape, input_scale, 0, input_data, memory_manager.get()); + Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, zerop, 0, + filter_data, memory_manager.get()); + Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, + memory_manager.get()); + Tensor output_shape_tensor = + makeInputTensor({4}, output_shape_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0); + + DataType scratch_data_type = + input_tensor.element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + Tensor scratch_tensor(scratch_data_type, Shape({}), {}, ""); + + TransposeConvParams params{}; + params.padding = Padding::VALID; + params.stride_height = 2; + params.stride_width = 2; + + TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, + &output_tensor, &scratch_tensor, params); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + memory_manager->allocate_memory(scratch_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data)); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.cpp new file mode 100644 index 0000000..9127241 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "kernels/Unpack.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Unpack::Unpack(const Tensor *input, std::vector outputs, const UnpackParams ¶ms) + : KernelWithParams({input}, std::move(outputs), params) +{ +} + +void Unpack::configure() +{ + const Shape &input_shape = input()->shape(); + + int axis = _params.axis; + if (axis < 0) + axis += input()->shape().num_dims(); + assert(axis >= 0 && axis < input_shape.num_dims()); + + Shape output_shape(input_shape.num_dims() - 1); + int out_index = 0; + for (int in_index = 0; in_index < input_shape.num_dims(); ++in_index) + { + if (in_index != axis) + output_shape.dim(out_index++) = input_shape.dim(in_index); + } + + for (Tensor *output : _outputs) + { + assert(output->element_type() == input()->element_type()); + output->resize(output_shape); + } +} + +template void Unpack::executeImpl() const +{ + tflite::UnpackParams params{}; + params.axis = _params.axis; + params.num_split = _outputs.size(); + VectorOfTensors all_outputs(_outputs); + tflite::reference_ops::Unpack(params, getTensorShape(input()), getTensorData(input()), + **all_outputs.shapes(), all_outputs.data()); +} + +void Unpack::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + return executeImpl(); + case DataType::U8: + return executeImpl(); + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.h b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.h new file mode 100644 index 0000000..f4a44ec --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_UNPACK_H +#define LUCI_INTERPRETER_KERNELS_UNPACK_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Unpack : public KernelWithParams +{ +public: + Unpack(const Tensor *input, std::vector outputs, const UnpackParams ¶ms); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output(int index) const { return _outputs[index]; } + + void configure() override; + void execute() const override; + +private: + template void executeImpl() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_UNPACK_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.test.cpp new file mode 100644 index 0000000..9384ddc --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Unpack.test.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Unpack.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template +void Check(int axis, Shape input_shape, std::initializer_list input_data, + const std::vector> &exp_output_shape, + std::vector> exp_output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + const int num_outputs = input_shape.dim(axis < 0 ? axis + input_shape.num_dims() : axis); + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + std::vector output_tensors; + output_tensors.reserve(num_outputs); + for (int i = 0; i < num_outputs; ++i) + { + output_tensors.push_back(makeOutputTensor(element_type)); + } + + std::vector output_tensor_ptrs(num_outputs); + for (int i = 0; i < num_outputs; ++i) + { + output_tensor_ptrs[i] = &output_tensors[i]; + } + + UnpackParams params{}; + params.axis = axis; + + Unpack kernel(&input_tensor, std::move(output_tensor_ptrs), params); + kernel.configure(); + for (int i = 0; i < num_outputs; i++) + { + memory_manager->allocate_memory(output_tensors[i]); + } + kernel.execute(); + + for (int i = 0; i < num_outputs; ++i) + { + EXPECT_THAT(extractTensorData(output_tensors[i]), + ::testing::ElementsAreArray(exp_output_data[i])); + } +} + +template class UnpackTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types; +TYPED_TEST_SUITE(UnpackTest, DataTypes); + +TYPED_TEST(UnpackTest, ThreeOutputs) +{ + Check(/*axis=*/0, /*input_shape=*/{3, 2}, + /*input_data=*/{1, 2, 3, 4, 5, 6}, + /*exp_output_shape=*/{{2}, {2}, {2}}, + /*exp_output_data=*/{{1, 2}, {3, 4}, {5, 6}}); +} + +TYPED_TEST(UnpackTest, ThreeOutputsAxisOne) +{ + Check(/*axis=*/1, /*input_shape=*/{3, 2}, + /*input_data=*/{1, 2, 3, 4, 5, 6}, + /*exp_output_shape=*/{{3}, {3}}, + /*exp_output_data=*/{{1, 3, 5}, {2, 4, 6}}); +} + +TYPED_TEST(UnpackTest, ThreeOutputsNegativeAxisOne) +{ + Check(/*axis=*/-1, /*input_shape=*/{3, 2}, + /*input_data=*/{1, 2, 3, 4, 5, 6}, + /*exp_output_shape=*/{{3}, {3}}, + /*exp_output_data=*/{{1, 3, 5}, {2, 4, 6}}); +} + +TYPED_TEST(UnpackTest, ThreeOutputsNegativeAxisTwo) +{ + Check(/*axis=*/-2, /*input_shape=*/{3, 2}, + /*input_data=*/{1, 2, 3, 4, 5, 6}, + /*exp_output_shape=*/{{2}, {2}, {2}}, + /*exp_output_data=*/{{1, 2}, {3, 4}, {5, 6}}); +} + +TYPED_TEST(UnpackTest, OneOutput) +{ + Check(/*axis=*/0, /*input_shape=*/{1, 6}, + /*input_data=*/{1, 2, 3, 4, 5, 6}, + /*exp_output_shape=*/{{6}}, + /*exp_output_data=*/{{1, 2, 3, 4, 5, 6}}); +} + +TYPED_TEST(UnpackTest, ThreeDimensionsTwoOutputs) +{ + Check(/*axis=*/2, /*input_shape=*/{2, 2, 2}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8}, + /*exp_output_shape=*/{{2, 2}, {2, 2}}, + /*exp_output_data=*/{{1, 3, 5, 7}, {2, 4, 6, 8}}); +} + +TYPED_TEST(UnpackTest, FiveDimensionsTwoOutputs) +{ + Check( + /*axis=*/2, /*input_shape=*/{2, 2, 2, 2, 1}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*exp_output_shape=*/{{2, 2, 2, 1}, {2, 2, 2, 1}}, + /*exp_output_data=*/ + {{1, 2, 5, 6, 9, 10, 13, 14}, {3, 4, 7, 8, 11, 12, 15, 16}}); +} + +TYPED_TEST(UnpackTest, VectorToScalar) +{ + Check(/*axis=*/0, /*input_shape=*/{5}, + /*input_data=*/{1, 2, 3, 4, 5}, + /*exp_output_shape=*/{{}, {}, {}, {}, {}}, + /*exp_output_data=*/{{1}, {2}, {3}, {4}, {5}}); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Utils.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/Utils.cpp new file mode 100644 index 0000000..5d8e5db --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Utils.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/Utils.h" + +#include +#include +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +template +void calculateActivationRange(Activation activation, T *activation_min, T *activation_max) +{ + switch (activation) + { + case Activation::NONE: + *activation_min = std::numeric_limits::lowest(); + *activation_max = std::numeric_limits::max(); + break; + case Activation::RELU: + *activation_min = 0; + *activation_max = std::numeric_limits::max(); + break; + case Activation::RELU_N1_TO_1: + *activation_min = -1; + *activation_max = 1; + break; + case Activation::RELU6: + *activation_min = 0; + *activation_max = 6; + break; + default: + throw std::runtime_error("Unsupported activation."); + } +} + +template void calculateActivationRange(Activation activation, float *activation_min, + float *activation_max); +template void calculateActivationRange(Activation activation, int32_t *activation_min, + int32_t *activation_max); +template void calculateActivationRange(Activation activation, int64_t *activation_min, + int64_t *activation_max); + +static void calculateActivationRangeQuantizedImpl(Activation activation, int32_t qmin, int32_t qmax, + const Tensor *output, int32_t *activation_min, + int32_t *activation_max) +{ + const float scale = output->scale(); + const int32_t zero_point = output->zero_point(); + + auto quantize = [scale, zero_point](float x) { + return zero_point + static_cast(std::round(x / scale)); + }; + + switch (activation) + { + case Activation::NONE: + case Activation::TANH: + *activation_min = qmin; + *activation_max = qmax; + break; + case Activation::RELU: + *activation_min = std::max(qmin, quantize(0.0f)); + *activation_max = qmax; + break; + case Activation::RELU_N1_TO_1: + *activation_min = std::max(qmin, quantize(-1.0f)); + *activation_max = std::min(qmax, quantize(1.0f)); + break; + case Activation::RELU6: + *activation_min = std::max(qmin, quantize(0.0f)); + *activation_max = std::min(qmax, quantize(6.0f)); + break; + default: + throw std::runtime_error("Unsupported activation."); + } +} + +void calculateActivationRangeQuantized(Activation activation, const Tensor *output, + int32_t *activation_min, int32_t *activation_max) +{ + assert(output->zero_points().size() == 1); + int32_t qmin{}; + int32_t qmax{}; + switch (output->element_type()) + { + case DataType::U8: + qmin = 0; + qmax = std::numeric_limits::max(); + break; + case DataType::S8: + qmin = -std::numeric_limits::max(); + qmax = std::numeric_limits::max(); + break; + case DataType::S16: + // For now, assume that signed int16 type implies signed symmetric quantization. + assert(output->zero_point() == 0); + qmin = -std::numeric_limits::max(); + qmax = std::numeric_limits::max(); + break; + default: + throw std::runtime_error("Unsupported type."); + } + + calculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, activation_min, + activation_max); +} + +void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift) +{ + if (double_multiplier == 0.0) + { + *quantized_multiplier = 0; + *shift = 0; + return; + } + + const double q = std::frexp(double_multiplier, shift); + auto q_fixed = static_cast(std::round(q * (INT64_C(1) << 31))); + + if (q_fixed == (INT64_C(1) << 31)) + { + q_fixed /= 2; + ++*shift; + } + assert(q_fixed <= std::numeric_limits::max()); + // A shift amount smaller than -31 would cause all bits to be shifted out + // and thus all results would be zero. We implement that instead with + // q_fixed==0, so as to avoid hitting issues with right-shift + // operations with shift amounts greater than 31. Note that this happens + // roughly when abs(double_multiplier) < 2^-31 and the present handling means + // that we're effectively flushing tiny double_multiplier's to zero. + // We could conceivably handle values in the range (roughly) [32, 63] + // as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view + // the present handling is just doing 'flush denormals to zero'. We could + // reconsider and actually generate nonzero denormals if a need arises. + if (*shift < -31) + { + *shift = 0; + q_fixed = 0; + } + *quantized_multiplier = static_cast(q_fixed); +} + +void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier, + int *left_shift) +{ + assert(double_multiplier < 1.0); + assert(double_multiplier > 0.0); + int shift; + quantizeMultiplier(double_multiplier, quantized_multiplier, &shift); + assert(shift <= 0); + *left_shift = shift; +} + +Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape) +{ + const int num_input1_dims = input1_shape.num_dims(); + const int num_input2_dims = input2_shape.num_dims(); + const int num_out_dims = std::max(num_input1_dims, num_input2_dims); + Shape output_shape(num_out_dims); + + for (int i = 0; i < num_out_dims; ++i) + { + const int32_t input1_dim = i < num_input1_dims ? input1_shape.dim(num_input1_dims - i - 1) : 1; + const int32_t input2_dim = i < num_input2_dims ? input2_shape.dim(num_input2_dims - i - 1) : 1; + + bool need_broadcast = input1_dim != input2_dim; + bool can_broadcast = input1_dim == 1 || input2_dim == 1; + LUCI_INTERPRETER_CHECK(!need_broadcast || can_broadcast); + + output_shape.dim(num_out_dims - i - 1) = std::max(input1_dim, input2_dim); + } + + return output_shape; +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/Utils.h b/compiler/luci-micro/luci-interpreter/src/kernels/Utils.h new file mode 100644 index 0000000..ebeb20e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/Utils.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_UTILS_H +#define LUCI_INTERPRETER_KERNELS_UTILS_H + +#include "core/KernelParams.h" +#include "luci_interpreter/core/Tensor.h" + +#include + +#include +#include +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +#define LUCI_INTERPRETER_CHECK(cond) \ + if (!(cond)) \ + throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + +"(" + \ + std::string(#cond) + ") was not true."); + +inline int32_t computePadding(int32_t stride, int32_t dilation_rate, int32_t in_size, + int32_t filter_size, int32_t out_size) +{ + const int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1; + const int32_t padding = ((out_size - 1) * stride + effective_filter_size - in_size) / 2; + return padding > 0 ? padding : 0; +} + +inline int32_t computePaddingWithOffset(int32_t stride, int32_t dilation_rate, int32_t in_size, + int32_t filter_size, int32_t out_size, int32_t *offset) +{ + int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1; + int32_t total_padding = ((out_size - 1) * stride + effective_filter_size - in_size); + total_padding = total_padding > 0 ? total_padding : 0; + *offset = total_padding % 2; + return total_padding / 2; +} + +inline int32_t computeOutputSize(Padding padding, int32_t image_size, int32_t filter_size, + int32_t stride, int32_t dilation_rate = 1) +{ + const int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1; + switch (padding) + { + case Padding::SAME: + return (image_size + stride - 1) / stride; + case Padding::VALID: + return (image_size + stride - effective_filter_size) / stride; + default: + assert(false); + return 0; + } +} + +inline int32_t calcOffset(const Shape &shape, int32_t d0, int32_t d1, int32_t d2, int32_t d3) +{ + return ((d0 * shape.dim(1) + d1) * shape.dim(2) + d2) * shape.dim(3) + d3; +} + +template +void calculateActivationRange(Activation activation, T *activation_min, T *activation_max); + +void calculateActivationRangeQuantized(Activation activation, const Tensor *output, + int32_t *activation_min, int32_t *activation_max); + +template constexpr bool one_of_types() { return false; } + +// Checks if T is equal to one of {U,Other} types +template constexpr bool one_of_types() +{ + return std::is_same::value || one_of_types(); +} + +/** + * Fills activation min and max parameters depending on given data type and activation + * + * T is a template parameter, so after optimization this code left with only required if case + * + * @tparam T data type of arithmetic operation output tensor + * @param params tflite params to fill + * @param activation luci_interpreter::Activation of arithmetic operation + */ +template +void fillArithmeticActivationRange(tflite::ArithmeticParams &p, Activation act) +{ + static_assert(one_of_types(), "Unsupported dtype"); + + if (std::is_same::value) + calculateActivationRange(act, &p.float_activation_min, &p.float_activation_max); + if (std::is_same::value) + calculateActivationRange(act, &p.quantized_activation_min, &p.quantized_activation_max); + else + calculateActivationRange(act, &p.int64_activation_min, &p.int64_activation_max); +} + +// Decompose a double multiplier into a Q0.31 int32 representation of its +// significand, and shift representation of its exponent. +// +// Handles an arbitrary positive multiplier. The 'shift' output-value is +// basically the 'floating-point exponent' of the multiplier: +// Negative for a right-shift (when the multiplier is <1), positive for a +// left-shift (when the multiplier is >1) +void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift); + +// Decompose a double multiplier into a Q0.31 int32 representation of its +// significand, and shift representation of NEGATIVE its exponent --- +// this is intended as a RIGHT-shift. +// +// Restricted to the case where the multiplier < 1 (and non-negative). +void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier, + int *left_shift); + +Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape); + +inline double getQuantizedConvolutionMultipler(float input_scale, float filter_scale, + float output_scale) +{ + const double input_product_scale = static_cast(input_scale * filter_scale); + LUCI_INTERPRETER_CHECK(input_product_scale >= 0); + return input_product_scale / static_cast(output_scale); +} + +// TODO rename getQuantizedConvolutionMultiplers to something more general +// it is used for non conv operators too +inline std::vector getQuantizedConvolutionMultiplers(float input_scale, + const std::vector &filter_scale, + float output_scale) +{ + std::vector effective_output_scales; + size_t n = filter_scale.size(); + effective_output_scales.reserve(n); + for (size_t i = 0; i < n; ++i) + { + effective_output_scales.push_back( + getQuantizedConvolutionMultipler(input_scale, filter_scale[i], output_scale)); + } + return effective_output_scales; +} + +struct ChannelQuantMultipliers +{ + int shift; + int32_t multiplier; + ChannelQuantMultipliers() = default; +}; + +inline std::vector +quantizeMultipliers(const std::vector &effective_scale) +{ + size_t n = effective_scale.size(); + std::vector params(n); + for (size_t i = 0; i < n; ++i) + { + quantizeMultiplier(effective_scale[i], ¶ms[i].multiplier, ¶ms[i].shift); + } + return params; +} + +// Helper wrapper to hide broadcast logic +template class BroadcastableWrapper +{ +public: + BroadcastableWrapper(const std::vector &v) : _v(v), _stride(v.size() == 1 ? 0 : 1) {} + + T operator[](int idx) { return _v[idx * _stride]; } + +private: + const std::vector &_v; + int _stride; +}; + +inline tflite::RuntimeShape getTensorShape(const Tensor *tensor) +{ + if (tensor == nullptr) + return tflite::RuntimeShape(); + + const Shape &shape = tensor->shape(); + tflite::RuntimeShape runtime_shape(shape.num_dims()); + for (int i = 0; i < shape.num_dims(); ++i) + { + runtime_shape.SetDim(i, shape.dim(i)); + } + return runtime_shape; +} + +template const T *getTensorData(const Tensor *tensor) +{ + return tensor != nullptr ? tensor->data() : nullptr; +} + +template T *getTensorData(Tensor *tensor) +{ + return tensor != nullptr ? tensor->data() : nullptr; +} + +// A list of tensors in a format that can be used by kernels like split and +// concatenation. +template class VectorOfTensors +{ +public: + using ElementT = typename std::conditional::type; + using TensorT = typename std::conditional::type; + + // Build with the tensors in 'tensor_list'. + explicit VectorOfTensors(const std::vector &tensor_list) + { + const int num_tensors = tensor_list.size(); + + all_data_.reserve(num_tensors); + all_shape_.reserve(num_tensors); + all_shape_ptr_.reserve(num_tensors); + + for (TensorT *tensor : tensor_list) + { + all_data_.push_back(getTensorData(tensor)); + all_shape_.push_back(getTensorShape(tensor)); + } + + // Taking the pointer from inside a std::vector is only OK if the vector is + // never modified, so we populate all_shape in the previous loop and then we + // are free to grab iterators here. + for (tflite::RuntimeShape &shape : all_shape_) + { + all_shape_ptr_.push_back(&shape); + } + } + // Return a pointer to the data pointers of all tensors in the list. For + // example: + // float* const* f = v.data(); + // f[0][1] is the second element of the first tensor. + ElementT *const *data() const { return all_data_.data(); } + + // Return a pointer the shape pointers of all tensors in the list. For + // example: + // const RuntimeShape* const* d = v.dims(); + // dims[1] are the dimensions of the second tensor in the list. + const tflite::RuntimeShape *const *shapes() const { return all_shape_ptr_.data(); } + +private: + std::vector all_data_; + std::vector all_shape_; + std::vector all_shape_ptr_; +}; + +// A list of quantized tensors in a format that can be used by kernels like +// split and concatenation. +template class VectorOfQuantizedTensors : public VectorOfTensors +{ +public: + using typename VectorOfTensors::TensorT; + + // Build with the tensors in 'tensor_list'. + explicit VectorOfQuantizedTensors(const std::vector &tensor_list) + : VectorOfTensors(tensor_list) + { + for (TensorT *tensor : tensor_list) + { + zero_point_.push_back(tensor->zero_point()); + scale_.push_back(tensor->scale()); + } + } + + const float *scale() const { return scale_.data(); } + const int32_t *zero_point() const { return zero_point_.data(); } + +private: + std::vector zero_point_; + std::vector scale_; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_UTILS_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/While.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/While.cpp new file mode 100644 index 0000000..153bd1a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/While.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "kernels/While.h" +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +namespace +{ + +void copy(const std::vector &src, const std::vector &dst) +{ + for (size_t i = 0; i < src.size(); ++i) + { + LUCI_INTERPRETER_CHECK(dst[i]->element_type() == src[i]->element_type()); + dst[i]->resize(src[i]->shape()); + + const int32_t num_elements = src[i]->shape().num_elements(); + const std::size_t element_size = getDataTypeSize(src[i]->element_type()); + std::memcpy(dst[i]->data(), src[i]->data(), num_elements * element_size); + } +} + +void copy(const std::vector &src, const std::vector &dst) +{ + std::vector const_src; + for (const auto &t : src) + const_src.push_back(t); + copy(const_src, dst); +} + +// TODO: Think about how allocate memory for output in main graph +void configureTensorsAllocations(const std::vector &tensors, RuntimeGraph *run_graph) +{ + for (auto tensor : tensors) + run_graph->configureAllocations(tensor); +} + +} // namespace + +While::While(std::vector inputs, std::vector outputs, + RuntimeGraph *cond_graph, RuntimeGraph *body_graph) + : Kernel(std::move(inputs), std::move(outputs)), _cond_graph(cond_graph), _body_graph(body_graph) +{ +} + +void While::configure() +{ + LUCI_INTERPRETER_CHECK(_body_graph->getInputTensors().size() == getInputTensors().size()); + LUCI_INTERPRETER_CHECK(_body_graph->getOutputTensors().size() == getOutputTensors().size()); + LUCI_INTERPRETER_CHECK(_body_graph->getOutputTensors().size() == getInputTensors().size()); + + LUCI_INTERPRETER_CHECK(_cond_graph->getInputTensors().size() == getInputTensors().size()); + + const auto &cond_outputs = _cond_graph->getOutputTensors(); + LUCI_INTERPRETER_CHECK(cond_outputs.size() == 1) + LUCI_INTERPRETER_CHECK(cond_outputs[0]->element_type() == DataType::BOOL); +} + +/** + * @note Dynamic shape such as {1, 0, 8} may fail in tensor->data() + */ +void While::execute() const +{ + const auto &cond_inputs = _cond_graph->getInputTensors(); + const auto &cond_outputs = _cond_graph->getOutputTensors(); + + configureTensorsAllocations(cond_inputs, _cond_graph); + + copy(getInputTensors(), cond_inputs); + + const auto &body_inputs = _body_graph->getInputTensors(); + const auto &body_outputs = _body_graph->getOutputTensors(); + + configureTensorsAllocations(body_inputs, _body_graph); + + while (true) + { + _cond_graph->execute(); + + bool cond_value = cond_outputs[0]->data()[0]; + if (!cond_value) + break; + + copy(cond_inputs, body_inputs); + + _body_graph->execute(); + + copy(body_outputs, cond_inputs); + } + + copy(cond_inputs, getOutputTensors()); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/While.h b/compiler/luci-micro/luci-interpreter/src/kernels/While.h new file mode 100644 index 0000000..f758df3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/While.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_WHILE_H +#define LUCI_INTERPRETER_KERNELS_WHILE_H + +#include "core/Kernel.h" +#include "core/RuntimeGraph.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class While : public Kernel +{ +public: + While(std::vector inputs, std::vector outputs, RuntimeGraph *cond_graph, + RuntimeGraph *body_graph); + + const Tensor *input(int index) const { return _inputs[index]; } + Tensor *output(int index) const { return _outputs[index]; } + + void configure() override; + void execute() const override; + +private: + RuntimeGraph *const _cond_graph = nullptr; + RuntimeGraph *const _body_graph = nullptr; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_WHILE_H diff --git a/compiler/luci-micro/luci-interpreter/src/kernels/While.test.cpp b/compiler/luci-micro/luci-interpreter/src/kernels/While.test.cpp new file mode 100644 index 0000000..cb8f891 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/kernels/While.test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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 "core/RuntimeModule.h" +#include "kernels/Add.h" +#include "kernels/Less.h" +#include "kernels/While.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +RuntimeGraph *buildCondSubgraph(RuntimeModule *module, DataType dtype, Tensor *input_cond, + IMemoryManager *memory_manager) +{ + RuntimeGraph *graph = module->addGraph(memory_manager); + Tensor *input = + graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); + Tensor *output = + graph->addTensor(std::make_unique(DataType::BOOL, Shape{}, AffineQuantization{}, "")); + + memory_manager->allocate_memory(*input); + memory_manager->allocate_memory(*output); + + graph->setInputTensors({input}); + graph->setOutputTensors({output}); + + graph->addKernel(std::make_unique(input, input_cond, output)); + + return graph; +} + +RuntimeGraph *buildBodySubgraph(RuntimeModule *module, DataType dtype, Tensor *input_add, + IMemoryManager *memory_manager) +{ + RuntimeGraph *graph = module->addGraph(memory_manager); + Tensor *input = + graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); + Tensor *output = + graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); + + memory_manager->allocate_memory(*input); + memory_manager->allocate_memory(*output); + + graph->setInputTensors({input}); + graph->setOutputTensors({output}); + + AddParams params{}; + params.activation = Activation::NONE; + graph->addKernel(std::make_unique(input, input_add, output, params)); + + return graph; +} + +TEST(WhileTest, FloatLoop10) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input = makeInputTensor({1}, {1}, memory_manager.get()); + Tensor output = makeOutputTensor(DataType::FLOAT32); + + Tensor input_cond = makeInputTensor({1}, {10}, memory_manager.get()); + Tensor input_add = makeInputTensor({1}, {1}, memory_manager.get()); + + RuntimeModule module(nullptr); + RuntimeGraph *cond_graph = + buildCondSubgraph(&module, DataType::FLOAT32, &input_cond, memory_manager.get()); + RuntimeGraph *body_graph = + buildBodySubgraph(&module, DataType::FLOAT32, &input_add, memory_manager.get()); + + While kernel({&input}, {&output}, cond_graph, body_graph); + kernel.configure(); + memory_manager->allocate_memory(output); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output), FloatArrayNear({10})); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/CMakeLists.txt b/compiler/luci-micro/luci-interpreter/src/loader/CMakeLists.txt new file mode 100644 index 0000000..2927715 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/CMakeLists.txt @@ -0,0 +1,39 @@ +set(SOURCES + GraphLoader.h + GraphLoader.cpp + KernelBuilderHelper.h + KernelBuilderHelper.cpp + KernelBuilder.h + KernelBuilder.cpp + ModuleLoader.h + ModuleLoader.cpp + RuntimeToIR.h + nodes/Builders.h) + +# include kernel specific builders +macro(REGISTER_KERNEL NODE) + list(APPEND SOURCES "nodes/${NODE}.cpp") +endmacro(REGISTER_KERNEL) +include(${KERNEL_REGISTER_FILE}) + +add_library(${LUCI_INTERPRETER_LOADER} STATIC ${SOURCES}) +if (NOT NNCC_LIBRARY_NO_PIC) + set_target_properties(${LUCI_INTERPRETER_LOADER} PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif(NOT NNCC_LIBRARY_NO_PIC) +target_include_directories(${LUCI_INTERPRETER_LOADER} PUBLIC "${LUCI_INTERPRETER_PAL_DIR}") +target_include_directories(${LUCI_INTERPRETER_LOADER} PUBLIC "${LUCI_INTERPRETER_SOURCE_DIR}") + +target_link_libraries(${LUCI_INTERPRETER_LOADER} + PUBLIC luci_lang ${LUCI_INTERPRETER_CORE} + PRIVATE ${LUCI_INTERPRETER_KERNELS} nncc_common luci_plan) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +set(TEST_SOURCES KernelBuilder.test.cpp) + +GTest_AddTest(${LUCI_INTERPRETER_LOADER}_test ${TEST_SOURCES}) +target_link_libraries(${LUCI_INTERPRETER_LOADER}_test ${LUCI_INTERPRETER_LOADER}) diff --git a/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.cpp new file mode 100644 index 0000000..4020709 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "loader/GraphLoader.h" + +#include "loader/KernelBuilder.h" + +#include +#include + +namespace luci_interpreter +{ +namespace +{ + +template Shape getNodeShape(const NodeT *node) +{ + Shape shape(node->rank()); + for (uint32_t i = 0; i < node->rank(); ++i) + { + shape.dim(i) = node->dim(i).value(); + } + return shape; +} + +template const void *getNodeDataImpl(const luci::CircleConst *node, size_t *data_size) +{ + const size_t element_size = getDataTypeSize(DT); + const int32_t num_elements = node->size
(); + + *data_size = num_elements * element_size; + if (*data_size > 0) + { + // FIXME There is no good way to get the pointer to the data currently. + return &node->at
(0); + } + return nullptr; +} + +const void *getNodeData(const luci::CircleConst *node, size_t *data_size) +{ + switch (node->dtype()) + { + case DataType::U8: + return getNodeDataImpl(node, data_size); + case DataType::FLOAT32: + return getNodeDataImpl(node, data_size); + case DataType::S8: + return getNodeDataImpl(node, data_size); + case DataType::S16: + return getNodeDataImpl(node, data_size); + case DataType::S32: + return getNodeDataImpl(node, data_size); + case DataType::S64: + return getNodeDataImpl(node, data_size); + case DataType::BOOL: + return getNodeDataImpl(node, data_size); + default: + throw std::runtime_error("Unsupported type."); + } +} + +const void *getNodeData(const luci::CircleCustom *node, size_t *data_size) +{ + if (node->custom_code() != "CircleReferencingConst") + return nullptr; + + // helper struct which describes data loaded to custom_options of CircleReferencingConst node + // TODO move this struct to header + struct ConstDataReference + { + const uint8_t *data = nullptr; + uint32_t size = 0; + }; + + const auto &custom_options = node->custom_options(); + const auto &const_data_ref = *reinterpret_cast(custom_options.data()); + + *data_size = const_data_ref.size; + return const_data_ref.data; +} + +bool isExecutableNode(const luci::CircleNode *node) +{ + switch (node->opcode()) + { + // These nodes denote inputs / outputs of a graph. + case luci::CircleOpcode::CIRCLECONST: + case luci::CircleOpcode::CIRCLEINPUT: + case luci::CircleOpcode::CIRCLEOUTPUT: + case luci::CircleOpcode::CIRCLEOUTPUTEXCLUDE: + // The following nodes denote outputs of multiple-output nodes. + case luci::CircleOpcode::CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT: + case luci::CircleOpcode::CIRCLECUSTOMOUT: + case luci::CircleOpcode::CIRCLEIFOUT: + case luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT: + case luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT: + case luci::CircleOpcode::CIRCLESPLITOUT: + case luci::CircleOpcode::CIRCLESPLITVOUT: + case luci::CircleOpcode::CIRCLETOPKV2OUT: + case luci::CircleOpcode::CIRCLEUNIQUEOUT: + case luci::CircleOpcode::CIRCLEUNPACKOUT: + case luci::CircleOpcode::CIRCLEVARIABLE: + case luci::CircleOpcode::CIRCLEWHILEOUT: + return false; + // Custom nodes may be executable and non-executable + case luci::CircleOpcode::CUSTOM: + { + auto const custom_node = loco::must_cast(node); + + // TODO handle more non-executable Custom ops here + if (custom_node->custom_code() == "CircleReferencingConst") + return false; + + return true; + } + default: + return true; + } +} + +bool isTensorProducingNode(const luci::CircleNode *node) +{ + switch (node->opcode()) + { + // Output nodes do not produce tensors. + case luci::CircleOpcode::CIRCLEOUTPUT: + // The following nodes are multiple-output nodes. They do not produce tensors, the tensors + // are produced by the corresponding *Out nodes instead. + case luci::CircleOpcode::BIDIRECTIONAL_SEQUENCE_LSTM: + case luci::CircleOpcode::CUSTOM: + case luci::CircleOpcode::IF: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V4: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V5: + case luci::CircleOpcode::SPLIT: + case luci::CircleOpcode::SPLIT_V: + case luci::CircleOpcode::TOPK_V2: + case luci::CircleOpcode::UNIQUE: + case luci::CircleOpcode::UNPACK: + case luci::CircleOpcode::WHILE: + return false; + default: + return true; + } +} + +bool isSupportedCustomNode(const luci::CircleNode *node) +{ + const auto custom_node = loco::must_cast(node); + + // TODO handle more Custom ops here + if (custom_node->custom_code() == "CircleReferencingConst") + return true; + + return false; +} + +} // namespace + +GraphLoader::GraphLoader( + const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir, + const std::unordered_map &graph_to_runtime_graph, + std::unordered_map &node_to_tensor, IMemoryManager *memory_manager) + : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir), + _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor), + _memory_manager(memory_manager) +{ +} + +void GraphLoader::loadTensors() +{ + for (uint32_t i = 0; i < _graph->nodes()->size(); ++i) + { + const auto *node = loco::must_cast(_graph->nodes()->at(i)); + + if (node->opcode() == luci::CircleOpcode::CUSTOM && !isSupportedCustomNode(node)) + throw std::runtime_error("Unsupported Custom operator. " + node->name()); + + if (!isTensorProducingNode(node)) + continue; + + // Only Input, Const, Custom and Variable nodes have shapes. Shapes of intermediate tensors will + // be inferred. + Shape shape{}; + switch (node->opcode()) + { + case luci::CircleOpcode::CIRCLECONST: + case luci::CircleOpcode::CIRCLECUSTOMOUT: + case luci::CircleOpcode::CIRCLEINPUT: + case luci::CircleOpcode::CIRCLEVARIABLE: + shape = getNodeShape(node); + break; + default: + break; + } + + AffineQuantization quantization; + if (node->quantparam() != nullptr) + { + const luci::CircleQuantParam *params = node->quantparam(); + assert(params->scale.size() == params->zerop.size()); + quantization.scale.assign(params->scale.cbegin(), params->scale.cend()); + quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend()); + quantization.quantized_dimension = params->quantized_dimension; + } + + auto tensor = std::make_unique(node->dtype(), std::move(shape), std::move(quantization), + node->name()); + + // If node has execution plan then read memory offsets for nodes + // from the beginning of shared memory buffer. Used in Static Memory Manager. + if (luci::has_execution_plan(node)) + { + auto execution_plan = luci::get_execution_plan(node); + assert(!execution_plan.offsets().empty()); + tensor->set_offset(execution_plan.offsets().front()); + } + + if (const auto *const_node = dynamic_cast(node)) + { + size_t data_size{}; + const void *const_data = getNodeData(const_node, &data_size); + if (const_data != nullptr) + { + _memory_manager->allocate_memory(*tensor); + tensor->writeData(const_data, data_size); + } + } + else if (const auto *custom_out_node = dynamic_cast(node)) + { + const auto *custom_node = + loco::must_cast(custom_out_node->input()); + + if (custom_node->custom_code() == "CircleReferencingConst") + { + size_t data_size{}; + const void *const_data = getNodeData(custom_node, &data_size); + if (const_data != nullptr) + { + _memory_manager->allocate_memory(*tensor); + tensor->writeData(const_data, data_size); + } + } + } + + _node_to_tensor.emplace(node, tensor.get()); + _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node); + + _runtime_graph->addTensor(std::move(tensor)); + } +} + +void GraphLoader::initInputOutputTensors() const +{ + auto input_nodes = loco::input_nodes(_graph); + std::vector input_tensors(input_nodes.size()); + for (size_t i = 0; i < input_nodes.size(); ++i) + { + input_tensors[i] = _node_to_tensor.at(input_nodes[i]); + _memory_manager->allocate_memory(*input_tensors[i]); + } + _runtime_graph->setInputTensors(input_tensors); + + auto output_nodes = loco::output_nodes(const_cast(_graph)); + std::vector output_tensors(output_nodes.size()); + for (size_t i = 0; i < output_nodes.size(); ++i) + { + const auto *node = loco::must_cast(output_nodes[i]); + output_tensors[i] = _node_to_tensor.at(node->from()); + } + _runtime_graph->setOutputTensors(output_tensors); +} + +void GraphLoader::loadOperators() +{ + KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor); + + // Create kernels for executable nodes. This has to be done in execution order. + auto graph = const_cast(_graph); + + auto const graph_nodes = loco::all_nodes(graph); + + // Checking for execution plan in node annotations. + bool has_execution_annotation = true; + auto const checking_exec_plan = [&has_execution_annotation](auto const node) { + const auto *circle_node = loco::must_cast(node); + if (!luci::has_execution_plan(circle_node)) + has_execution_annotation = false; + }; + std::for_each(begin(graph_nodes), end(graph_nodes), checking_exec_plan); + + if (has_execution_annotation) + { + // Build ordered_nodes vector that stores the order of execution of graph nodes. + std::vector ordered_nodes(graph_nodes.size()); + + auto const filler = [&ordered_nodes](auto const node) { + const auto *circle_node = loco::must_cast(node); + auto const position = luci::get_execution_plan(circle_node).order_in_plan(); + ordered_nodes.at(position) = circle_node; + }; + std::for_each(begin(graph_nodes), end(graph_nodes), filler); + + for (auto node : ordered_nodes) + { + if (isExecutableNode(node)) + { + std::unique_ptr kernel = kernel_builder.build(node); + _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node); + _runtime_graph->addKernel(std::move(kernel)); + } + } + } + else + { + // If it is impossible to build the execution order plan, + // then we use the default postorder_traversal approach. + for (const loco::Node *loco_node : loco::postorder_traversal(loco::output_nodes(graph))) + { + const auto *node = loco::must_cast(loco_node); + if (isExecutableNode(node)) + { + std::unique_ptr kernel = kernel_builder.build(node); + _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node); + _runtime_graph->addKernel(std::move(kernel)); + } + } + } +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.h b/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.h new file mode 100644 index 0000000..fe066ec --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/GraphLoader.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_GRAPHLOADER_H +#define LUCI_INTERPRETER_LOADER_GRAPHLOADER_H + +#include "core/RuntimeGraph.h" +#include "loader/RuntimeToIR.h" +#include "luci_interpreter/MemoryManager.h" + +#include + +#include + +namespace luci_interpreter +{ + +class GraphLoader +{ +public: + GraphLoader(const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir, + const std::unordered_map &graph_to_runtime_graph, + std::unordered_map &node_to_tensor, + IMemoryManager *memory_manager); + + void loadTensors(); + void initInputOutputTensors() const; + void loadOperators(); + +private: + const loco::Graph *_graph; + RuntimeGraph *_runtime_graph; + RuntimeToIR &_runtime_to_ir; + IMemoryManager *_memory_manager; + + const std::unordered_map &_graph_to_runtime_graph; + std::unordered_map &_node_to_tensor; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_GRAPHLOADER_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.cpp b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.cpp new file mode 100644 index 0000000..8483a9a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "loader/KernelBuilder.h" +#include "loader/nodes/Builders.h" + +#include + +namespace luci_interpreter +{ + +#define CIRCLE_NODE(OPCODE, CLASS) CLASS, +#define CIRCLE_VNODE(OPCODE, CLASS) CLASS, + +// This enum is auxiliary. +// It is duplicate of luci::CircleOpcode but initialized with CLASS instead of OPCODE, +// because list of target operators is in format of CLASS names +enum class BuilderId +{ +#include + Size // casts to count of values in BuilderId enum +}; + +#undef CIRCLE_VNODE +#undef CIRCLE_NODE + +/** + * @brief Registry of kernel builders + * + * This class contains mapping from Opcodes to kernel builder functions + */ + +class KernelBuilderRegistry +{ +public: + using KernelBuilderFunc = std::unique_ptr(const luci::CircleNode *, + KernelBuilderHelper &); + + KernelBuilderRegistry() : _operator_builders(size_t(BuilderId::Size), nullptr) + { +#define REGISTER_KERNEL(name) \ + register_kernel_builder(BuilderId::Circle##name, build_kernel_Circle##name); + +#include "KernelsToBuild.lst" + +#undef REGISTER_KERNEL + } + + KernelBuilderFunc *get_kernel_builder_func(luci::CircleOpcode opcode) const + { + return _operator_builders.at(size_t(opcode)); + } + +private: + std::vector _operator_builders; + + void register_kernel_builder(BuilderId id, KernelBuilderFunc *func) + { + // Using BuilderId is a duplicate of luci::CirclreOpcode, + // size_t(id) is equal to size_t(corresponding operation opcode). + assert(size_t(id) < _operator_builders.size()); + _operator_builders[size_t(id)] = func; + } +}; + +KernelBuilder::KernelBuilder( + const std::unordered_map &graph_to_runtime_graph, + const std::unordered_map &node_to_tensor) + : KernelBuilderHelper(graph_to_runtime_graph, node_to_tensor) +{ + _builder_registry = std::make_unique(); +} + +KernelBuilder::~KernelBuilder() +{ + // Need to define in this CPP to hide KernelBuilderRegistry internals. + // This destructor deletes _builder_registry +} + +std::unique_ptr KernelBuilder::build(const luci::CircleNode *node) +{ + auto specific_builder = _builder_registry->get_kernel_builder_func(node->opcode()); + if (specific_builder != nullptr) + return specific_builder(node, *this); + + std::string msg = "Unsupported operator: "; + msg += std::to_string(static_cast(node->opcode())) + " " + std::string(node->name()); + throw std::invalid_argument(msg.c_str()); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.h b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.h new file mode 100644 index 0000000..b1f3833 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_KERNELBUILDER_H +#define LUCI_INTERPRETER_LOADER_KERNELBUILDER_H + +#include "loader/KernelBuilderHelper.h" + +#include "core/Kernel.h" +#include "core/RuntimeGraph.h" + +#include + +#include +#include + +namespace luci_interpreter +{ + +class KernelBuilderRegistry; + +class KernelBuilder : public KernelBuilderHelper +{ +public: + KernelBuilder( + const std::unordered_map &graph_to_runtime_graph, + const std::unordered_map &node_to_tensor); + + ~KernelBuilder(); + + std::unique_ptr build(const luci::CircleNode *node); + +private: + std::unique_ptr _builder_registry; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_KERNELBUILDER_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.test.cpp b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.test.cpp new file mode 100644 index 0000000..b221b69 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilder.test.cpp @@ -0,0 +1,1376 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "loader/GraphLoader.h" +#include "loader/KernelBuilder.h" +#include "luci_interpreter/SimpleMemoryManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +class KernelBuilderTest : public Test +{ +protected: + luci::CircleInput *createInputNode() { return createNode(); } + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; + + template NodeT *createNode(Args &&... args) + { + auto *node = _graph.nodes()->create(std::forward(args)...); + // The actual type does not matter for the purpose of the tests. + // NOTE The type is meaningless for nodes with multiple outputs (corresponding *Out nodes carry + // actual output types). + node->dtype(loco::DataType::FLOAT32); + return node; + } + + template NodeOutT *createNodeOut(loco::Node *node, int index) + { + auto *node_out = createNode(); + node_out->input(node); + node_out->index(index); + return node_out; + } + + template std::unique_ptr buildKernel(const luci::CircleNode *op) + { + std::unordered_map graph_to_runtime_graph; + + RuntimeGraph runtime_graph(nullptr, _memory_manager.get()); + graph_to_runtime_graph[&_graph] = &runtime_graph; + RuntimeToIR runtime_to_ir; + GraphLoader graph_loader(&_graph, &runtime_graph, runtime_to_ir, graph_to_runtime_graph, + _node_to_tensor, _memory_manager.get()); + graph_loader.loadTensors(); + + KernelBuilder kernel_builder(graph_to_runtime_graph, _node_to_tensor); + + auto kernel = kernel_builder.build(op); + return std::unique_ptr(dynamic_cast(kernel.release())); + } + + void checkTensor(const Tensor *tensor, const loco::Node *node) + { + EXPECT_THAT(tensor, Eq(_node_to_tensor.at(node))); + } + +private: + loco::Graph _graph; + std::unordered_map _node_to_tensor; +}; + +TEST_F(KernelBuilderTest, Add) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, ArgMax) +{ + auto *input = createInputNode(); + auto *axis = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->dimension(axis); + + op->output_type(loco::DataType::FLOAT32); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->axis(), axis); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().output_type, Eq(op->output_type())); +} + +TEST_F(KernelBuilderTest, AveragePool2D) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->value(input); + + op->padding(luci::Padding::SAME); + op->filter()->h(11); + op->filter()->w(13); + op->stride()->h(17); + op->stride()->w(19); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h())); + EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, BatchMatMul) +{ + auto *lhs = createInputNode(); + auto *rhs = createInputNode(); + + auto *op = createNode(); + op->x(lhs); + op->y(rhs); + op->adj_x(false); + op->adj_y(false); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), lhs); + checkTensor(kernel->y(), rhs); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().adj_x, Eq(op->adj_x())); + EXPECT_THAT(kernel->params().adj_y, Eq(op->adj_y())); +} + +TEST_F(KernelBuilderTest, Cast) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Concatenation) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(2); + op->values(0, input1); + op->values(1, input2); + op->axis(11); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(0), input1); + checkTensor(kernel->input(1), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().axis, Eq(op->axis())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Conv2D) +{ + auto *input = createInputNode(); + auto *filter = createInputNode(); + auto *bias = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->filter(filter); + op->bias(bias); + + op->padding(luci::Padding::SAME); + op->stride()->h(11); + op->stride()->w(13); + op->dilation()->h(17); + op->dilation()->w(19); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->filter(), filter); + checkTensor(kernel->bias(), bias); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().dilation_height_factor, Eq(op->dilation()->h())); + EXPECT_THAT(kernel->params().dilation_width_factor, Eq(op->dilation()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, DepthToSpace) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->input(input); + + op->block_size(11); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().block_size, Eq(op->block_size())); +} + +TEST_F(KernelBuilderTest, DepthwiseConv2D) +{ + auto *input = createInputNode(); + auto *filter = createInputNode(); + auto *bias = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->filter(filter); + op->bias(bias); + + op->padding(luci::Padding::SAME); + op->depthMultiplier(11); + op->stride()->h(13); + op->stride()->w(17); + op->dilation()->h(19); + op->dilation()->w(23); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->filter(), filter); + checkTensor(kernel->bias(), bias); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().depth_multiplier, Eq(op->depthMultiplier())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().dilation_height_factor, Eq(op->dilation()->h())); + EXPECT_THAT(kernel->params().dilation_width_factor, Eq(op->dilation()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Div) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Elu) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->features(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Exp) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Floor) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, FloorDiv) +{ + auto *x = createInputNode(); + auto *y = createInputNode(); + + auto *op = createNode(); + op->x(x); + op->y(y); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x); + checkTensor(kernel->y(), y); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Equal) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, FullyConnected) +{ + auto *input = createInputNode(); + auto *weights = createInputNode(); + auto *bias = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->weights(weights); + op->bias(bias); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->weights(), weights); + checkTensor(kernel->bias(), bias); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Greater) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, GreaterEqual) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, InstanceNorm) +{ + auto *input = createInputNode(); + auto *gamma = createInputNode(); + auto *beta = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->gamma(gamma); + op->beta(beta); + + op->epsilon(1e-05); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->gamma(), gamma); + checkTensor(kernel->beta(), beta); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().epsilon, Eq(op->epsilon())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, L2Normalize) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, L2Pool2D) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->value(input); + + op->padding(luci::Padding::SAME); + op->filter()->h(11); + op->filter()->w(13); + op->stride()->h(17); + op->stride()->w(19); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h())); + EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, LeakyRelu) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->features(input); + + op->alpha(11.0f); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().alpha, Eq(op->alpha())); +} + +TEST_F(KernelBuilderTest, Less) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, LessEqual) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, LocalResponseNormalization) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->input(input); + + op->radius(11); + op->bias(13.0f); + op->alpha(15.0f); + op->beta(17.0f); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().radius, Eq(op->radius())); + EXPECT_THAT(kernel->params().bias, Eq(op->bias())); + EXPECT_THAT(kernel->params().alpha, Eq(op->alpha())); + EXPECT_THAT(kernel->params().beta, Eq(op->beta())); +} + +TEST_F(KernelBuilderTest, LogicalAnd) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, LogicalNot) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, LogicalOr) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Logistic) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, LogSoftmax) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->logits(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Maximum) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, MaxPool2D) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->value(input); + + op->padding(luci::Padding::SAME); + op->filter()->h(11); + op->filter()->w(13); + op->stride()->h(17); + op->stride()->w(19); + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h())); + EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Mean) +{ + auto *input = createInputNode(); + auto *axes = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->reduction_indices(axes); + + op->keep_dims(true); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->axes(), axes); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().keep_dims, Eq(op->keep_dims())); +} + +TEST_F(KernelBuilderTest, Minimum) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Mul) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Neg) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, NotEqual) +{ + auto *x_input = createInputNode(); + auto *y_input = createInputNode(); + + auto *op = createNode(); + op->x(x_input); + op->y(y_input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->x(), x_input); + checkTensor(kernel->y(), y_input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, OneHot) +{ + auto *indices = createInputNode(); + auto *depth = createInputNode(); + auto *on_value = createInputNode(); + auto *off_value = createInputNode(); + auto axis = 1; + + auto *op = createNode(); + op->indices(indices); + op->depth(depth); + op->on_value(on_value); + op->off_value(off_value); + op->axis(axis); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->indices(), indices); + checkTensor(kernel->depth(), depth); + checkTensor(kernel->on_value(), on_value); + checkTensor(kernel->off_value(), off_value); + EXPECT_THAT(kernel->params().axis, Eq(op->axis())); +} + +TEST_F(KernelBuilderTest, Pad) +{ + auto *input = createInputNode(); + auto *paddings = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->paddings(paddings); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->paddings(), paddings); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, PadV2) +{ + auto *input = createInputNode(); + auto *paddings = createInputNode(); + auto *constant_values = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->paddings(paddings); + op->constant_values(constant_values); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->paddings(), paddings); + checkTensor(kernel->constant_values(), constant_values); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Pow) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, PRelu) +{ + auto *input = createInputNode(); + auto *alpha = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->alpha(alpha); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->alpha(), alpha); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Relu) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->features(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Relu6) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->features(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Reshape) +{ + auto *input = createInputNode(); + auto *shape = createInputNode(); + + auto *op = createNode(); + op->tensor(input); + op->shape(shape); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->shape(), shape); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, ResizeBilinear) +{ + auto *input = createInputNode(); + auto *size = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->size(size); + op->align_corners(true); + op->half_pixel_centers(true); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->size(), size); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners())); + EXPECT_THAT(kernel->params().half_pixel_centers, Eq(op->half_pixel_centers())); +} + +TEST_F(KernelBuilderTest, ResizeNearestNeighbor) +{ + auto *input = createInputNode(); + auto *size = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->size(size); + op->align_corners(true); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->size(), size); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners())); + // TODO currently half_pixel_centers are not implemented on CircleResizeNearestNeighbor + // after adding, need to be updated. +} + +TEST_F(KernelBuilderTest, ReverseV2) +{ + auto *input = createInputNode(); + auto *axes = createInputNode(); + + auto *op = createNode(); + op->tensor(input); + op->axis(axes); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->axes(), axes); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Rsqrt) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Slice) +{ + auto *input = createInputNode(); + auto *begin = createInputNode(); + auto *size = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->begin(begin); + op->size(size); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->begin(), begin); + checkTensor(kernel->size(), size); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Softmax) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->logits(input); + + op->beta(11.0f); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().beta, Eq(op->beta())); +} + +TEST_F(KernelBuilderTest, SpaceToDepth) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->input(input); + + op->block_size(11); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().block_size, op->block_size()); +} + +TEST_F(KernelBuilderTest, Split) +{ + auto *axis = createInputNode(); + auto *input = createInputNode(); + auto *op = createNode(); + auto *output1 = createNodeOut(op, 0); + auto *output2 = createNodeOut(op, 1); + + op->split_dim(axis); + op->input(input); + + op->num_split(2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->axis(), axis); + checkTensor(kernel->input(), input); + checkTensor(kernel->output(0), output1); + checkTensor(kernel->output(1), output2); +} + +TEST_F(KernelBuilderTest, SplitV) +{ + auto *input = createInputNode(); + auto *size_splits = createInputNode(); + auto *axis = createInputNode(); + auto *op = createNode(); + auto *output0 = createNodeOut(op, 0); + auto *output1 = createNodeOut(op, 1); + + op->input(input); + op->size_splits(size_splits); + op->split_dim(axis); + + op->num_split(2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->size_splits(), size_splits); + checkTensor(kernel->axis(), axis); + checkTensor(kernel->output(0), output0); + checkTensor(kernel->output(1), output1); +} + +TEST_F(KernelBuilderTest, Sqrt) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, SquaredDifference) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Squeeze) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->input(input); + + op->squeeze_dims({11, 13}); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().squeeze_dims, ElementsAreArray(op->squeeze_dims())); +} + +TEST_F(KernelBuilderTest, StridedSlice) +{ + auto *input = createInputNode(); + auto *begin = createInputNode(); + auto *end = createInputNode(); + auto *strides = createInputNode(); + + auto *op = createNode(); + op->input(input); + op->begin(begin); + op->end(end); + op->strides(strides); + + op->begin_mask(11); + op->ellipsis_mask(13); + op->end_mask(17); + op->new_axis_mask(19); + op->shrink_axis_mask(23); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->begin(), begin); + checkTensor(kernel->end(), end); + checkTensor(kernel->strides(), strides); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().begin_mask, Eq(op->begin_mask())); + EXPECT_THAT(kernel->params().ellipsis_mask, Eq(op->ellipsis_mask())); + EXPECT_THAT(kernel->params().end_mask, Eq(op->end_mask())); + EXPECT_THAT(kernel->params().new_axis_mask, Eq(op->new_axis_mask())); + EXPECT_THAT(kernel->params().shrink_axis_mask, Eq(op->shrink_axis_mask())); +} + +TEST_F(KernelBuilderTest, Sub) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode(); + op->x(input1); + op->y(input2); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + +TEST_F(KernelBuilderTest, Tanh) +{ + auto *input = createInputNode(); + + auto *op = createNode(); + op->x(input); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, Transpose) +{ + auto *input = createInputNode(); + auto *perm = createInputNode(); + + auto *op = createNode(); + op->a(input); + op->perm(perm); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->perm(), perm); + checkTensor(kernel->output(), op); +} + +TEST_F(KernelBuilderTest, TransposeConv) +{ + auto *output_shape = createInputNode(); + auto *filter = createInputNode(); + auto *input = createInputNode(); + auto *bias = createInputNode(); + + auto *op = createNode(); + op->inputSizes(output_shape); + op->filter(filter); + op->outBackprop(input); + op->bias(bias); + + op->padding(luci::Padding::SAME); + op->stride()->h(11); + op->stride()->w(13); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->output_shape(), output_shape); + checkTensor(kernel->filter(), filter); + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); + checkTensor(kernel->bias(), bias); + EXPECT_THAT(kernel->params().padding, Eq(op->padding())); + EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); + EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); +} + +TEST_F(KernelBuilderTest, Unpack) +{ + auto *input = createInputNode(); + auto *op = createNode(); + auto *output1 = createNodeOut(op, 0); + auto *output2 = createNodeOut(op, 1); + + op->value(input); + + op->num(2); + op->axis(11); + + auto kernel = buildKernel(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(0), output1); + checkTensor(kernel->output(1), output2); + EXPECT_THAT(kernel->params().axis, Eq(op->axis())); +} + +TEST_F(KernelBuilderTest, NonExisting1_NEG) +{ + auto *op = createNode(); + ASSERT_ANY_THROW(buildKernel(op)); +} + +TEST_F(KernelBuilderTest, NonExisting2_NEG) +{ + auto *op = createNode(); + ASSERT_ANY_THROW(buildKernel(op)); +} + +TEST_F(KernelBuilderTest, NonExisting3_NEG) +{ + auto *op = createNode(); + ASSERT_ANY_THROW(buildKernel(op)); +} + +} // namespace +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.cpp b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.cpp new file mode 100644 index 0000000..23c96a6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "loader/KernelBuilderHelper.h" + +#include + +namespace luci_interpreter +{ + +const Tensor *KernelBuilderHelper::getInputTensor(const loco::Node *node) const +{ + const Tensor *tensor = _node_to_tensor.at(node); + assert(tensor != nullptr); + return tensor; +} + +const Tensor *KernelBuilderHelper::getOptionalInputTensor(const loco::Node *node) const +{ + if (dynamic_cast(node)) + { + return nullptr; + } + return getInputTensor(node); +} + +Tensor *KernelBuilderHelper::getOutputTensor(const loco::Node *node) const +{ + Tensor *tensor = _node_to_tensor.at(node); + assert(tensor != nullptr); + return tensor; +} + +std::vector +KernelBuilderHelper::getOutputTensors(const std::vector &nodes) const +{ + std::vector tensors; + tensors.reserve(nodes.size()); + for (const loco::Node *node : nodes) + tensors.push_back(getOutputTensor(node)); + return tensors; +} + +RuntimeGraph *KernelBuilderHelper::getRuntimeGraph(const loco::Graph *graph) const +{ + RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph); + assert(runtime_graph != nullptr); + return runtime_graph; +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.h b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.h new file mode 100644 index 0000000..d6fb253 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/KernelBuilderHelper.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_KERNELBUILDER_HELPER_H +#define LUCI_INTERPRETER_LOADER_KERNELBUILDER_HELPER_H + +#include "core/Kernel.h" +#include "core/RuntimeGraph.h" + +#include +#include + +#include +#include + +namespace luci_interpreter +{ + +class KernelBuilderHelper +{ +public: + KernelBuilderHelper( + const std::unordered_map &graph_to_runtime_graph, + const std::unordered_map &node_to_tensor) + : _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor) + { + } + +public: + const Tensor *getInputTensor(const loco::Node *node) const; + const Tensor *getOptionalInputTensor(const loco::Node *node) const; + + Tensor *getOutputTensor(const loco::Node *node) const; + std::vector getOutputTensors(const std::vector &nodes) const; + + RuntimeGraph *getRuntimeGraph(const loco::Graph *graph) const; + +public: + const std::unordered_map &graph_to_runtime_graph() const + { + return _graph_to_runtime_graph; + } + + const std::unordered_map &node_to_tensor() const + { + return _node_to_tensor; + } + +private: + const std::unordered_map &_graph_to_runtime_graph; + const std::unordered_map &_node_to_tensor; +}; + +template +std::vector collectOutputNodes(const loco::Node *node) +{ + std::vector output_nodes; + for (const loco::Node *loco_node : loco::succs(node)) + { + output_nodes.push_back(loco::must_cast(loco_node)); + } + std::sort(output_nodes.begin(), output_nodes.end(), + [](const CircleNodeOut *node1, const CircleNodeOut *node2) { + return node1->index() < node2->index(); + }); + return {output_nodes.cbegin(), output_nodes.cend()}; +} + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_KERNELBUILDER_HELPER_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.cpp b/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.cpp new file mode 100644 index 0000000..2f278b0 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "ModuleLoader.h" + +#include "GraphLoader.h" + +namespace luci_interpreter +{ + +ModuleLoader::ModuleLoader(const luci::Module *module, RuntimeModule *runtime_module, + RuntimeToIR &runtime_to_ir, + std::unordered_map &node_to_tensor, + IMemoryManager *memory_manager) + : _module(module), _runtime_module(runtime_module), _runtime_to_ir(runtime_to_ir), + _node_to_tensor(node_to_tensor), _memory_manager(memory_manager) +{ +} + +void ModuleLoader::load() +{ + // Runtime graphs have to be created in advance, because they will be needed during the loading + // process for control flow nodes. + for (size_t i = 0; i < _module->size(); ++i) + { + _graph_to_runtime_graph.emplace(_module->graph(i), _runtime_module->addGraph(_memory_manager)); + } + for (size_t i = 0; i < _module->size(); ++i) + { + const loco::Graph *graph = _module->graph(i); + RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph); + GraphLoader loader(graph, runtime_graph, _runtime_to_ir, _graph_to_runtime_graph, + _node_to_tensor, _memory_manager); + loader.loadTensors(); + loader.initInputOutputTensors(); + loader.loadOperators(); + } +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.h b/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.h new file mode 100644 index 0000000..11326a2 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/ModuleLoader.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_MODULELOADER_H +#define LUCI_INTERPRETER_LOADER_MODULELOADER_H + +#include "core/RuntimeModule.h" +#include "loader/RuntimeToIR.h" +#include "luci_interpreter/MemoryManager.h" + +#include + +#include + +namespace luci_interpreter +{ + +class ModuleLoader +{ +public: + ModuleLoader(const luci::Module *module, RuntimeModule *runtime_module, + RuntimeToIR &runtime_to_ir, + std::unordered_map &node_to_tensor, + IMemoryManager *memory_manager); + + void load(); + +private: + IMemoryManager *_memory_manager; + const luci::Module *_module; + RuntimeModule *_runtime_module; + RuntimeToIR &_runtime_to_ir; + std::unordered_map &_node_to_tensor; + std::unordered_map _graph_to_runtime_graph; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_MODULELOADER_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/RuntimeToIR.h b/compiler/luci-micro/luci-interpreter/src/loader/RuntimeToIR.h new file mode 100644 index 0000000..9ea8b1f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/RuntimeToIR.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H +#define LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H + +#include "luci_interpreter/core/Tensor.h" + +#include + +#include + +namespace luci_interpreter +{ + +// Maps runtime entities back to IR entities. It is used to implement observing functionality. +struct RuntimeToIR +{ + std::unordered_map tensor_to_node; + std::unordered_map kernel_to_node; +}; + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Add.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Add.cpp new file mode 100644 index 0000000..501e847 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Add.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Add.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleAdd(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + AddParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input1, input2, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp new file mode 100644 index 0000000..f3ca557 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ArgMax.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleArgMax(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *axis = helper.getInputTensor(node->dimension()); + Tensor *output = helper.getOutputTensor(node); + + ArgMaxParams params{}; + params.output_type = node->output_type(); + + return std::make_unique(input, axis, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp new file mode 100644 index 0000000..a813570 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/AveragePool2D.h" +#include + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleAveragePool2D(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->value()); + Tensor *output = helper.getOutputTensor(node); + + Pool2DParams params{}; + params.padding = node->padding(); + params.filter_height = node->filter()->h(); + params.filter_width = node->filter()->w(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + params.activation = node->fusedActivationFunction(); + + // It is unknown what data will be stored in scratchpad tensor, + // using UINT8 as a most general option + auto scratchpad = std::make_unique(DataType::U8, Shape({}), AffineQuantization{}, ""); + scratchpad->set_observable(false); + scratchpad->set_data_buffer(nullptr); + // If node has execution plan then read memory offsets for scratchpad temporary tensor + // from the beginning of shared memory buffer. + // Used in Static Memory Manager. + // TODO move tensors offset initialization to one place + if (luci::has_execution_plan(node)) + { + const auto execution_plan = luci::get_execution_plan(node); + // Check whether the offset for the current CircleConv2D temporary was found. + if (execution_plan.offsets().size() > 1) + // If this is true, then we keep this offset in scratchpad. + scratchpad->set_offset(execution_plan.offsets().at(1)); + } + Tensor *tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad)); + + return std::make_unique(input, output, tmp, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp new file mode 100644 index 0000000..9da2f6d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/BatchMatMul.h" +#include + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleBatchMatMul(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *lhs = helper.getInputTensor(node->x()); + const Tensor *rhs = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + auto lhs_scratchpad = + std::make_unique(lhs->element_type(), Shape({}), AffineQuantization{}, ""); + lhs_scratchpad->set_observable(false); + lhs_scratchpad->set_data_buffer(nullptr); + auto rhs_scratchpad = + std::make_unique(rhs->element_type(), Shape({}), AffineQuantization{}, ""); + rhs_scratchpad->set_observable(false); + rhs_scratchpad->set_data_buffer(nullptr); + // If node has execution plan then read memory offsets for scratchpad temporary tensor + // from the beginning of shared memory buffer. + // Used in Static Memory Manager. + // TODO move tensors offset initialization to one place + if (luci::has_execution_plan(node)) + { + const auto execution_plan = luci::get_execution_plan(node); + // Check whether the offset for the current BatchMatMul temporary was found. + if (execution_plan.offsets().size() > 1) + { + assert(execution_plan.offsets().size() == 3); + + // If this is true, then we keep this offset in scratchpad. + lhs_scratchpad->set_offset(execution_plan.offsets().at(1)); + rhs_scratchpad->set_offset(execution_plan.offsets().at(2)); + } + } + Tensor *lhs_tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(lhs_scratchpad)); + Tensor *rhs_tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(rhs_scratchpad)); + + BatchMatMulParams params; + params.adj_x = node->adj_x(); + params.adj_y = node->adj_y(); + + return std::make_unique(lhs, rhs, output, lhs_tmp, rhs_tmp, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp new file mode 100644 index 0000000..ac6ebb3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/BatchToSpaceND.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleBatchToSpaceND(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *block_shape = helper.getInputTensor(node->block_shape()); + const Tensor *crops = helper.getInputTensor(node->crops()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, block_shape, crops, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Builders.h b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Builders.h new file mode 100644 index 0000000..eab2840 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Builders.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef LUCI_INTERPRETER_LOADER_NODES_BUILDERS_H +#define LUCI_INTERPRETER_LOADER_NODES_BUILDERS_H + +#include "loader/KernelBuilderHelper.h" + +#include "luci/IR/CircleNodes.h" + +namespace luci_interpreter +{ + +#define REGISTER_KERNEL(name) \ + std::unique_ptr build_kernel_Circle##name(const luci::CircleNode *circle_node, \ + KernelBuilderHelper &helper); + +#include "KernelsToBuild.lst" + +#undef REGISTER_KERNEL + +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_LOADER_NODES_BUILDERS_H diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Cast.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Cast.cpp new file mode 100644 index 0000000..a16354c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Cast.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Cast.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleCast(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp new file mode 100644 index 0000000..ba2564e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Concatenation.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleConcatenation(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + std::vector inputs(node->numValues()); + for (uint32_t i = 0; i < node->numValues(); ++i) + { + inputs[i] = helper.getInputTensor(node->values(i)); + } + Tensor *output = helper.getOutputTensor(node); + + ConcatenationParams params{}; + params.axis = node->axis(); + params.activation = node->fusedActivationFunction(); + + return std::make_unique(std::move(inputs), output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp new file mode 100644 index 0000000..218165e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Conv2D.h" +#include + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleConv2D(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *filter = helper.getInputTensor(node->filter()); + const Tensor *bias = helper.getOptionalInputTensor(node->bias()); + Tensor *output = helper.getOutputTensor(node); + + // It is unknown what data will be stored in scratchpad tensor, + // using UINT8 as a most general option + auto scratchpad = std::make_unique(DataType::U8, Shape({}), AffineQuantization{}, ""); + scratchpad->set_observable(false); + scratchpad->set_data_buffer(nullptr); + // If node has execution plan then read memory offsets for scratchpad temporary tensor + // from the beginning of shared memory buffer. + // Used in Static Memory Manager. + // TODO move tensors offset initialization to one place + if (luci::has_execution_plan(node)) + { + const auto execution_plan = luci::get_execution_plan(node); + // Check whether the offset for the current CircleConv2D temporary was found. + if (execution_plan.offsets().size() > 1) + // If this is true, then we keep this offset in scratchpad. + scratchpad->set_offset(execution_plan.offsets().at(1)); + } + Tensor *tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad)); + + Conv2DParams params{}; + params.padding = node->padding(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + params.dilation_height_factor = node->dilation()->h(); + params.dilation_width_factor = node->dilation()->w(); + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input, filter, bias, output, tmp, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp new file mode 100644 index 0000000..1749463 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/DepthToSpace.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleDepthToSpace(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->input()); + Tensor *output = helper.getOutputTensor(node); + + DepthToSpaceParams params{}; + params.block_size = node->block_size(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp new file mode 100644 index 0000000..8af1e3b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/DepthwiseConv2D.h" +#include + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleDepthwiseConv2D(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *filter = helper.getInputTensor(node->filter()); + const Tensor *bias = helper.getInputTensor(node->bias()); + Tensor *output = helper.getOutputTensor(node); + + DepthwiseConv2DParams params{}; + params.padding = node->padding(); + params.depth_multiplier = node->depthMultiplier(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + params.dilation_height_factor = node->dilation()->h(); + params.dilation_width_factor = node->dilation()->w(); + params.activation = node->fusedActivationFunction(); + + // It is unknown what data will be stored in scratchpad tensor, + // using UINT8 as a most general option + auto scratchpad = std::make_unique(DataType::U8, Shape({}), AffineQuantization{}, ""); + scratchpad->set_observable(false); + scratchpad->set_data_buffer(nullptr); + // If node has execution plan then read memory offsets for scratchpad temporary tensor + // from the beginning of shared memory buffer. + // Used in Static Memory Manager. + // TODO move tensors offset initialization to one place + if (luci::has_execution_plan(node)) + { + const auto execution_plan = luci::get_execution_plan(node); + // Check whether the offset for the current CircleConv2D temporary was found. + if (execution_plan.offsets().size() > 1) + // If this is true, then we keep this offset in scratchpad. + scratchpad->set_offset(execution_plan.offsets().at(1)); + } + Tensor *tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad)); + + return std::make_unique(input, filter, bias, output, tmp, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp new file mode 100644 index 0000000..787322e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Dequantize.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleDequantize(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + + const Tensor *input = helper.getInputTensor(node->input()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Div.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Div.cpp new file mode 100644 index 0000000..0611dfd --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Div.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Div.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleDiv(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + DivParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input1, input2, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Elu.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Elu.cpp new file mode 100644 index 0000000..a79985e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Elu.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Elu.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleElu(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Equal.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Equal.cpp new file mode 100644 index 0000000..5969288 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Equal.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Equal.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleEqual(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) + +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Exp.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Exp.cpp new file mode 100644 index 0000000..30d11cb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Exp.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Exp.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleExp(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp new file mode 100644 index 0000000..9840c34 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ExpandDims.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleExpandDims(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *axis = helper.getInputTensor(node->axis()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, axis, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Fill.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Fill.cpp new file mode 100644 index 0000000..3aefdf1 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Fill.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Fill.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleFill(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const auto dims = helper.getInputTensor(node->dims()); + const auto value = helper.getInputTensor(node->value()); + auto output = helper.getOutputTensor(node); + + return std::make_unique(dims, value, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Floor.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Floor.cpp new file mode 100644 index 0000000..e0a2231 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Floor.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Floor.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleFloor(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp new file mode 100644 index 0000000..a45d89e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/FloorDiv.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleFloorDiv(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp new file mode 100644 index 0000000..b7b742b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/FullyConnected.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleFullyConnected(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *weights = helper.getInputTensor(node->weights()); + const Tensor *bias = helper.getOptionalInputTensor(node->bias()); + Tensor *output = helper.getOutputTensor(node); + + FullyConnectedParams params{}; + params.activation = node->fusedActivationFunction(); + params.keep_num_dims = node->keep_num_dims(); + + return std::make_unique(input, weights, bias, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Gather.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Gather.cpp new file mode 100644 index 0000000..2ee2906 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Gather.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Gather.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleGather(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *params = helper.getInputTensor(node->params()); + const Tensor *indices = helper.getInputTensor(node->indices()); + Tensor *output = helper.getOutputTensor(node); + + GatherParams gparams{}; + gparams.axis = node->axis(); + // TODO support batch_dims + gparams.batch_dims = 0; + + return std::make_unique(params, indices, output, gparams); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Greater.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Greater.cpp new file mode 100644 index 0000000..80aa63c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Greater.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Greater.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleGreater(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp new file mode 100644 index 0000000..272f284 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/GreaterEqual.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleGreaterEqual(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/If.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/If.cpp new file mode 100644 index 0000000..3ac7d49 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/If.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/If.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleIf(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + auto output_nodes = collectOutputNodes(node); + assert(node->arity() == 1 + node->input_count()); + assert(output_nodes.size() == static_cast(node->output_count())); + + const Tensor *cond = helper.getInputTensor(node->cond()); + std::vector inputs(node->input_count()); + for (uint32_t i = 0; i < node->input_count(); ++i) + { + inputs[i] = helper.getInputTensor(node->input(i)); + } + std::vector outputs = helper.getOutputTensors(output_nodes); + + RuntimeGraph *then_graph = helper.getRuntimeGraph(node->then_graph()); + RuntimeGraph *else_graph = helper.getRuntimeGraph(node->else_graph()); + + return std::make_unique(cond, std::move(inputs), std::move(outputs), then_graph, + else_graph); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp new file mode 100644 index 0000000..06031e5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/InstanceNorm.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleInstanceNorm(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *gamma = helper.getInputTensor(node->gamma()); + const Tensor *beta = helper.getInputTensor(node->beta()); + + Tensor *output = helper.getOutputTensor(node); + + InstanceNormParams params{}; + params.epsilon = node->epsilon(); + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input, gamma, beta, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp new file mode 100644 index 0000000..6e22e6d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/L2Normalize.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleL2Normalize(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + L2NormParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp new file mode 100644 index 0000000..95b5589 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/L2Pool2D.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleL2Pool2D(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->value()); + Tensor *output = helper.getOutputTensor(node); + + Pool2DParams params{}; + params.padding = node->padding(); + params.filter_height = node->filter()->h(); + params.filter_width = node->filter()->w(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp new file mode 100644 index 0000000..bbf5067 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LeakyRelu.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLeakyRelu(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); + + LeakyReluParams params{}; + params.alpha = node->alpha(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Less.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Less.cpp new file mode 100644 index 0000000..ae914ec --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Less.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Less.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLess(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp new file mode 100644 index 0000000..f1b424b --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LessEqual.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLessEqual(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp new file mode 100644 index 0000000..962ca2d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LocalResponseNormalization.h" + +namespace luci_interpreter +{ + +std::unique_ptr +build_kernel_CircleLocalResponseNormalization(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + const Tensor *input = helper.getInputTensor(node->input()); + Tensor *output = helper.getOutputTensor(node); + + LocalResponseNormalizationParams params{}; + params.radius = node->radius(); + params.bias = node->bias(); + params.alpha = node->alpha(); + params.beta = node->beta(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp new file mode 100644 index 0000000..4322041 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LogSoftmax.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLogSoftmax(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->logits()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp new file mode 100644 index 0000000..bf3cb67 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LogicalAnd.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLogicalAnd(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp new file mode 100644 index 0000000..fefcd9a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LogicalNot.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLogicalNot(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp new file mode 100644 index 0000000..a416cb4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/LogicalOr.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLogicalOr(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Logistic.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Logistic.cpp new file mode 100644 index 0000000..4a69dee --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Logistic.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Logistic.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleLogistic(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp new file mode 100644 index 0000000..f66a206 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/MaxPool2D.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMaxPool2D(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->value()); + Tensor *output = helper.getOutputTensor(node); + + Pool2DParams params{}; + params.padding = node->padding(); + params.filter_height = node->filter()->h(); + params.filter_width = node->filter()->w(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Maximum.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Maximum.cpp new file mode 100644 index 0000000..d0bff77 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Maximum.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Maximum.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMaximum(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mean.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mean.cpp new file mode 100644 index 0000000..0dec63e --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mean.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Mean.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMean(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *axes = helper.getInputTensor(node->reduction_indices()); + Tensor *output = helper.getOutputTensor(node); + + auto temp_index_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + temp_index_unique->set_observable(false); + temp_index_unique->set_data_buffer(nullptr); + Tensor *temp_index = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(temp_index_unique)); + + auto resolved_axes_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + resolved_axes_unique->set_observable(false); + resolved_axes_unique->set_data_buffer(nullptr); + Tensor *resolved_axes = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(resolved_axes_unique)); + + auto temp_sum_unique = + std::make_unique(input->element_type(), Shape({}), AffineQuantization{}, ""); + temp_sum_unique->set_observable(false); + temp_sum_unique->set_data_buffer(nullptr); + Tensor *temp_sum = helper.getRuntimeGraph(node->graph())->addTensor(std::move(temp_sum_unique)); + + ReducerParams params{}; + params.keep_dims = node->keep_dims(); + + return std::make_unique(input, axes, output, temp_index, resolved_axes, temp_sum, + params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Minimum.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Minimum.cpp new file mode 100644 index 0000000..1a49c10 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Minimum.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Minimum.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMinimum(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp new file mode 100644 index 0000000..b221b45 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/MirrorPad.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMirrorPad(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *paddings = helper.getInputTensor(node->paddings()); + Tensor *output = helper.getOutputTensor(node); + + MirrorPadParams params{}; + params.mode = node->mode(); + + return std::make_unique(input, paddings, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mul.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mul.cpp new file mode 100644 index 0000000..f998485 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Mul.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Mul.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleMul(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + MulParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input1, input2, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Neg.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Neg.cpp new file mode 100644 index 0000000..9a9ecf9 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Neg.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Neg.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleNeg(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp new file mode 100644 index 0000000..3916a58 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/NotEqual.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleNotEqual(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(x, y, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/OneHot.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/OneHot.cpp new file mode 100644 index 0000000..a401609 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/OneHot.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/OneHot.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleOneHot(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 4); + + const Tensor *indices = helper.getInputTensor(node->indices()); + const Tensor *depth = helper.getInputTensor(node->depth()); + const Tensor *on_value = helper.getInputTensor(node->on_value()); + const Tensor *off_value = helper.getInputTensor(node->off_value()); + Tensor *output = helper.getOutputTensor(node); + + OneHotParams params{}; + params.axis = node->axis(); + + return std::make_unique(indices, depth, on_value, off_value, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/PRelu.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/PRelu.cpp new file mode 100644 index 0000000..f3d700c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/PRelu.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/PRelu.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CirclePRelu(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *alpha = helper.getInputTensor(node->alpha()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, alpha, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pack.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pack.cpp new file mode 100644 index 0000000..efc5850 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pack.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Pack.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CirclePack(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == node->values_count()); + + std::vector inputs(node->values_count()); + for (uint32_t i = 0; i < node->values_count(); ++i) + { + inputs[i] = helper.getInputTensor(node->values(i)); + } + Tensor *output = helper.getOutputTensor(node); + + PackParams params{}; + params.axis = node->axis(); + params.values_count = node->values_count(); + + return std::make_unique(std::move(inputs), output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pad.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pad.cpp new file mode 100644 index 0000000..67ce997 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pad.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Pad.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CirclePad(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *paddings = helper.getInputTensor(node->paddings()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, paddings, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/PadV2.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/PadV2.cpp new file mode 100644 index 0000000..e378a97 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/PadV2.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/PadV2.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CirclePadV2(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *paddings = helper.getInputTensor(node->paddings()); + const Tensor *constant_values = helper.getInputTensor(node->constant_values()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, paddings, constant_values, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pow.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pow.cpp new file mode 100644 index 0000000..d32fc3d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Pow.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Pow.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CirclePow(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Quantize.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Quantize.cpp new file mode 100644 index 0000000..cb36fb6 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Quantize.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Quantize.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleQuantize(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->input()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu.cpp new file mode 100644 index 0000000..1d64c1c --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Relu.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleRelu(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu6.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu6.cpp new file mode 100644 index 0000000..e50cd25 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Relu6.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Relu6.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleRelu6(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Reshape.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Reshape.cpp new file mode 100644 index 0000000..76ddd88 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Reshape.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Reshape.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleReshape(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->tensor()); + const Tensor *shape = helper.getInputTensor(node->shape()); + Tensor *output = helper.getOutputTensor(node); + + // NOTE 'newShape' attribute is ignored. + return std::make_unique(input, shape, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp new file mode 100644 index 0000000..dc2b88a --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ResizeBilinear.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleResizeBilinear(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *size = helper.getInputTensor(node->size()); + Tensor *output = helper.getOutputTensor(node); + + ResizeBilinearParams params{}; + params.align_corners = node->align_corners(); + params.half_pixel_centers = node->half_pixel_centers(); + + return std::make_unique(input, size, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp new file mode 100644 index 0000000..c7058ae --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ResizeNearestNeighbor.h" + +namespace luci_interpreter +{ + +std::unique_ptr +build_kernel_CircleResizeNearestNeighbor(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *size = helper.getInputTensor(node->size()); + Tensor *output = helper.getOutputTensor(node); + + ResizeNearestNeighborParams params{}; + params.align_corners = node->align_corners(); + // TODO update half_pixel_centers after CircleResizeNearestNeighbor updated + // Current CircleResizeNearestNeighbor don't have half_pixel_centers. + // default value on current is false. + // it need to be updated when CircleResizeNearestNeighbor updated. + params.half_pixel_centers = false; + + return std::make_unique(input, size, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp new file mode 100644 index 0000000..c1a7f53 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/ReverseV2.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleReverseV2(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->tensor()); + const Tensor *axes = helper.getInputTensor(node->axis()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, axes, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp new file mode 100644 index 0000000..0714a5d --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Rsqrt.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleRsqrt(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/SVDF.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SVDF.cpp new file mode 100644 index 0000000..d172ef4 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SVDF.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/SVDF.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSVDF(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 5); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *feature = helper.getInputTensor(node->weight_feature()); + const Tensor *time = helper.getInputTensor(node->weight_time()); + const Tensor *bias = helper.getOptionalInputTensor(node->bias()); + const Tensor *input_activation_state = helper.getInputTensor(node->input_activation_state()); + Tensor *output = helper.getOutputTensor(node); + + auto scratchpad_tensor = std::make_unique(input_activation_state->element_type(), + Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + DataType data_type = input->element_type() == DataType::S8 ? DataType::S32 : DataType::FLOAT32; + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_1 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + if (data_type == DataType::FLOAT32 && + (feature->element_type() == DataType::S8 || feature->element_type() == DataType::U8)) + { + data_type = feature->element_type(); + } + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_2 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + data_type = DataType::FLOAT32; + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_3 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_4 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_5 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + scratchpad_tensor = std::make_unique(data_type, Shape({}), AffineQuantization{}, ""); + scratchpad_tensor->set_observable(false); + scratchpad_tensor->set_data_buffer(nullptr); + Tensor *tmp_6 = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratchpad_tensor)); + + SVDFParams params{}; + params.activation = node->fusedActivationFunction(); + params.svdf_rank = node->svdf_rank(); + params.asymmetric_quantize_inputs = node->asymmetric_quantize_inputs(); + + return std::make_unique(input, feature, time, bias, input_activation_state, output, + tmp, tmp_1, tmp_2, tmp_3, tmp_4, tmp_5, tmp_6, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Shape.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Shape.cpp new file mode 100644 index 0000000..d1edbc7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Shape.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Shape.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleShape(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const auto input = helper.getInputTensor(node->input()); + auto output = helper.getOutputTensor(node); + + ShapeParams shape_params{}; + shape_params.out_type = node->out_type(); + + return std::make_unique(input, output, shape_params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Slice.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Slice.cpp new file mode 100644 index 0000000..60ac641 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Slice.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Slice.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSlice(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *begin = helper.getInputTensor(node->begin()); + const Tensor *size = helper.getInputTensor(node->size()); + + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, begin, size, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Softmax.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Softmax.cpp new file mode 100644 index 0000000..f41f63f --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Softmax.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Softmax.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSoftmax(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->logits()); + Tensor *output = helper.getOutputTensor(node); + + SoftmaxParams params{}; + params.beta = node->beta(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp new file mode 100644 index 0000000..b6e6cf5 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/SpaceToBatchND.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSpaceToBatchND(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *block_shape = helper.getInputTensor(node->block_shape()); + const Tensor *paddings = helper.getInputTensor(node->paddings()); + + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, block_shape, paddings, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp new file mode 100644 index 0000000..63fdb95 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/SpaceToDepth.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSpaceToDepth(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + const Tensor *input = helper.getInputTensor(node->input()); + + Tensor *output = helper.getOutputTensor(node); + + SpaceToDepthParams params{}; + params.block_size = node->block_size(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Split.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Split.cpp new file mode 100644 index 0000000..3f6d4a7 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Split.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Split.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSplit(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + auto output_nodes = collectOutputNodes(node); + assert(node->arity() == 2); + assert(output_nodes.size() == static_cast(node->num_split())); + + const Tensor *axis = helper.getInputTensor(node->split_dim()); + const Tensor *input = helper.getInputTensor(node->input()); + std::vector outputs = helper.getOutputTensors(output_nodes); + + // NOTE 'num_splits' attribute is ignored. + return std::make_unique(axis, input, std::move(outputs)); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/SplitV.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SplitV.cpp new file mode 100644 index 0000000..0788822 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SplitV.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/SplitV.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSplitV(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + auto output_nodes = collectOutputNodes(node); + assert(node->arity() == 3); + assert(output_nodes.size() == static_cast(node->num_split())); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *sizes_data = helper.getInputTensor(node->size_splits()); + const Tensor *axis = helper.getInputTensor(node->split_dim()); + std::vector outputs = helper.getOutputTensors(output_nodes); + + // NOTE 'num_splits' attribute is ignored. + return std::make_unique(input, sizes_data, axis, std::move(outputs)); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp new file mode 100644 index 0000000..b9843fe --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Sqrt.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSqrt(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Square.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Square.cpp new file mode 100644 index 0000000..0ad7c17 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Square.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Square.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSquare(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp new file mode 100644 index 0000000..e4c6fd8 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/SquaredDifference.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSquaredDifference(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input1, input2, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp new file mode 100644 index 0000000..6885f80 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Squeeze.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSqueeze(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->input()); + Tensor *output = helper.getOutputTensor(node); + + SqueezeParams params{}; + params.squeeze_dims = node->squeeze_dims(); + + return std::make_unique(input, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp new file mode 100644 index 0000000..359b4e3 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/StridedSlice.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleStridedSlice(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 4); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *begin = helper.getInputTensor(node->begin()); + const Tensor *end = helper.getInputTensor(node->end()); + const Tensor *strides = helper.getInputTensor(node->strides()); + + Tensor *output = helper.getOutputTensor(node); + + StridedSliceParams params{}; + params.begin_mask = node->begin_mask(); + params.ellipsis_mask = node->ellipsis_mask(); + params.end_mask = node->end_mask(); + params.new_axis_mask = node->new_axis_mask(); + params.shrink_axis_mask = node->shrink_axis_mask(); + + return std::make_unique(input, begin, end, strides, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sub.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sub.cpp new file mode 100644 index 0000000..a6252cb --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Sub.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Sub.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSub(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input1 = helper.getInputTensor(node->x()); + const Tensor *input2 = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); + + SubParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique(input1, input2, output, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Tanh.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Tanh.cpp new file mode 100644 index 0000000..a58ef60 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Tanh.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Tanh.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleTanh(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Transpose.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Transpose.cpp new file mode 100644 index 0000000..ea17d83 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Transpose.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Transpose.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleTranspose(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->a()); + const Tensor *perm = helper.getInputTensor(node->perm()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, perm, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp new file mode 100644 index 0000000..d773e30 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/TransposeConv.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleTransposeConv(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 4); + + const Tensor *input_sizes = helper.getInputTensor(node->inputSizes()); + const Tensor *filter = helper.getInputTensor(node->filter()); + const Tensor *out_backprop = helper.getInputTensor(node->outBackprop()); + const Tensor *bias = helper.getOptionalInputTensor(node->bias()); + + Tensor *output = helper.getOutputTensor(node); + + DataType scratch_data_type = + helper.getInputTensor(node)->element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + + auto scratch_tensor = + std::make_unique(scratch_data_type, Shape({}), AffineQuantization{}, ""); + scratch_tensor->set_observable(false); + scratch_tensor->set_data_buffer(nullptr); + Tensor *tmp = helper.getRuntimeGraph(node->graph())->addTensor(std::move(scratch_tensor)); + + TransposeConvParams params{}; + params.padding = node->padding(); + params.stride_height = node->stride()->h(); + params.stride_width = node->stride()->w(); + + return std::make_unique(input_sizes, filter, out_backprop, bias, output, + tmp, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/Unpack.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Unpack.cpp new file mode 100644 index 0000000..a1c0d32 --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/Unpack.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/Unpack.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleUnpack(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + auto output_nodes = collectOutputNodes(node); + assert(node->arity() == 1); + assert(output_nodes.size() == static_cast(node->num())); + + const Tensor *input = helper.getInputTensor(node->value()); + std::vector outputs = helper.getOutputTensors(output_nodes); + + UnpackParams params{}; + params.axis = node->axis(); + + // NOTE 'num' attribute is ignored. + return std::make_unique(input, std::move(outputs), params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/luci-interpreter/src/loader/nodes/While.cpp b/compiler/luci-micro/luci-interpreter/src/loader/nodes/While.cpp new file mode 100644 index 0000000..8fde6ec --- /dev/null +++ b/compiler/luci-micro/luci-interpreter/src/loader/nodes/While.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Builders.h" + +#include "kernels/While.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleWhile(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + + auto output_nodes = collectOutputNodes(node); + assert(node->arity() == node->input_count()); + assert(output_nodes.size() == static_cast(node->output_count())); + + std::vector inputs(node->input_count()); + for (uint32_t i = 0; i < node->input_count(); ++i) + { + inputs[i] = helper.getInputTensor(node->input(i)); + } + std::vector outputs = helper.getOutputTensors(output_nodes); + + RuntimeGraph *cond_graph = helper.getRuntimeGraph(node->cond_graph()); + RuntimeGraph *body_graph = helper.getRuntimeGraph(node->body_graph()); + + return std::make_unique(std::move(inputs), std::move(outputs), cond_graph, + body_graph); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-micro/standalone/CMakeLists.txt b/compiler/luci-micro/standalone/CMakeLists.txt index 7953359..d304826 100644 --- a/compiler/luci-micro/standalone/CMakeLists.txt +++ b/compiler/luci-micro/standalone/CMakeLists.txt @@ -7,6 +7,9 @@ set(BUILD_WHITELIST "dummy") add_subdirectory(${NNAS_ROOT}/infra/nncc ${CMAKE_CURRENT_BINARY_DIR}/nncc) set(ONE_COMPILER_SRC_DIR "${NNAS_PROJECT_SOURCE_DIR}/compiler") +nnas_find_package(FlatBuffersSource EXACT 2.0 QUIET) + +include_directories(${FlatBuffersSource_DIR}/include) add_subdirectory(${ONE_COMPILER_SRC_DIR}/loco ${CMAKE_CURRENT_BINARY_DIR}/loco) add_subdirectory(${ONE_COMPILER_SRC_DIR}/angkor ${CMAKE_CURRENT_BINARY_DIR}/angkor) @@ -14,7 +17,21 @@ add_subdirectory(${ONE_COMPILER_SRC_DIR}/oops ${CMAKE_CURRENT_BINARY_DIR}/oops) add_subdirectory(${ONE_COMPILER_SRC_DIR}/pepper-str ${CMAKE_CURRENT_BINARY_DIR}/pepper-str) add_subdirectory(${ONE_COMPILER_SRC_DIR}/logo ${CMAKE_CURRENT_BINARY_DIR}/logo) add_subdirectory(${ONE_COMPILER_SRC_DIR}/logo-core ${CMAKE_CURRENT_BINARY_DIR}/logo-core) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/hermes-std ${CMAKE_CURRENT_BINARY_DIR}/hermes-std) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/hermes ${CMAKE_CURRENT_BINARY_DIR}/hermes) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/pepper-strcast ${CMAKE_CURRENT_BINARY_DIR}/pepper-strcast) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/foder ${CMAKE_CURRENT_BINARY_DIR}/foder) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/mio-circle04 ${CMAKE_CURRENT_BINARY_DIR}/mio-circle04) + add_subdirectory(${ONE_COMPILER_SRC_DIR}/locomotiv ${CMAKE_CURRENT_BINARY_DIR}/locomotiv) add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/lang ${CMAKE_CURRENT_BINARY_DIR}/luci/lang) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/import ${CMAKE_CURRENT_BINARY_DIR}/luci/import) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/profile ${CMAKE_CURRENT_BINARY_DIR}/luci/profile) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/env ${CMAKE_CURRENT_BINARY_DIR}/luci/env) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/plan ${CMAKE_CURRENT_BINARY_DIR}/luci/plan) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/log ${CMAKE_CURRENT_BINARY_DIR}/luci/log) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci/logex ${CMAKE_CURRENT_BINARY_DIR}/luci/logex) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/locop ${CMAKE_CURRENT_BINARY_DIR}/locop) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/pp ${CMAKE_CURRENT_BINARY_DIR}/pp) -add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci-interpreter ${CMAKE_CURRENT_BINARY_DIR}/luci-interpreter) +add_subdirectory(${ONE_COMPILER_SRC_DIR}/luci-micro/luci-interpreter ${CMAKE_CURRENT_BINARY_DIR}/luci-interpreter) diff --git a/compiler/luci-pass-value-test/CMakeLists.txt b/compiler/luci-pass-value-test/CMakeLists.txt index 034fe52..3489f1e 100644 --- a/compiler/luci-pass-value-test/CMakeLists.txt +++ b/compiler/luci-pass-value-test/CMakeLists.txt @@ -17,6 +17,13 @@ macro(addeval RECIPE PASS_OPTION) set(PASS_CIRCLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PASS_CIRCLE_FILE}") set(DASH_PASS_OPTION "--${PASS_OPTION}") + foreach(MORE_OPTIONS ${ARGN}) + list(APPEND DASH_PASS_OPTION "--${MORE_OPTIONS}") + endforeach() + # NOTE if there are two options, 'DASH_PASS_OPTION' will be like '--option_a;--option_b' + # add_custom_command() will translate ';' to two arguments as '--optiona_a --optionb' + # do not use set(DASH_PASS_OPTION "${DASH_PASS_OPTION} --${ARG}")) + # as this will become like '"--optiona_a --optionb"' which is one string argument # Generate optimized .circle add_custom_command(OUTPUT ${PASS_CIRCLE_OUTPUT_PATH} diff --git a/compiler/luci-pass-value-test/test.lst b/compiler/luci-pass-value-test/test.lst index 67476c6..cdff159 100644 --- a/compiler/luci-pass-value-test/test.lst +++ b/compiler/luci-pass-value-test/test.lst @@ -14,6 +14,8 @@ addeval(Net_Conv_Add_Mul_002 fuse_batchnorm_with_conv) addeval(Net_Conv_Min_Max_000 transform_min_max_to_relu6) addeval(Net_Conv_Min_Relu_000 transform_min_relu_to_relu6) addeval(Net_Conv_Relu6_000 fuse_activation_function) +addeval(Net_Densify_Add_000 fold_densify) +addeval(Net_Dequantize_Add_000 fold_dequantize) addeval(Net_DwConv_BN_000 fuse_batchnorm_with_dwconv) addeval(Net_DwConv_BN_001 fuse_batchnorm_with_dwconv) addeval(Net_Reshape_Neg_000 forward_reshape_to_unaryop) @@ -25,10 +27,17 @@ addeval(Net_TConv_Add_002 fuse_add_with_tconv) addeval(Net_TConv_BN_000 fuse_batchnorm_with_tconv) addeval(Net_TConv_BN_001 fuse_batchnorm_with_tconv) addeval(Net_TConv_BN_002 fuse_batchnorm_with_tconv) +addeval(Net_TConv_BN_003 fuse_batchnorm_with_tconv) +addeval(Net_TConv_BN_004 fuse_batchnorm_with_tconv) addeval(Net_InstanceNorm_001 fuse_instnorm) addeval(Net_InstanceNorm_002 fuse_instnorm) addeval(Net_InstanceNorm_003 fuse_instnorm) addeval(Net_StridedSlice_StridedSlice_000 remove_unnecessary_strided_slice) +addeval(FullyConnected_007 replace_non_const_fc_with_batch_matmul) + +# test for limited support for FLOAT16 +addeval(Net_Dequantize_Add_000 fold_dequantize) +addeval(Net_Densify_Dequantize_Add_000 fold_dequantize fold_densify) # test SignatureDef, with any optimization #addeval(SignatureDef_MultiOut_000 fuse_instnorm) diff --git a/compiler/luci-value-test/test.lst b/compiler/luci-value-test/test.lst index f62b729..932da95 100644 --- a/compiler/luci-value-test/test.lst +++ b/compiler/luci-value-test/test.lst @@ -161,6 +161,8 @@ addeval(Squeeze_001) addeval(StridedSlice_000) addeval(StridedSlice_001) addeval(StridedSlice_002) +addeval(StridedSlice_003) +addeval(StridedSlice_004) addeval(Sub_000) addeval(Sub_U8_000) #addeval(Sum_000) diff --git a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h index 0ff21a3..7516197 100644 --- a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h +++ b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h @@ -118,6 +118,10 @@ public: return circle::CreateCosOptions(_builder).Union(); } flatbuffers::Offset visit(luci::CircleCustom *) { return _no_option; } + flatbuffers::Offset visit(luci::CircleDensify *) + { + return circle::CreateDensifyOptions(_builder).Union(); + } flatbuffers::Offset visit(luci::CircleDepthToSpace *node) { return circle::CreateDepthToSpaceOptions(_builder, node->block_size()).Union(); diff --git a/compiler/luci/export/src/CircleOps.lst b/compiler/luci/export/src/CircleOps.lst index 1b69093..8a75ef7 100644 --- a/compiler/luci/export/src/CircleOps.lst +++ b/compiler/luci/export/src/CircleOps.lst @@ -32,6 +32,7 @@ CIRCLE_NODE(CircleConcatenation, BuiltinOperator_CONCATENATION, BuiltinOptions_C CIRCLE_NODE(CircleConv2D, BuiltinOperator_CONV_2D, BuiltinOptions_Conv2DOptions) CIRCLE_NODE(CircleCos, BuiltinOperator_COS, BuiltinOptions_CosOptions) CIRCLE_NODE(CircleCustom, BuiltinOperator_CUSTOM, BuiltinOptions_NONE) +CIRCLE_NODE(CircleDensify, BuiltinOperator_DENSIFY, BuiltinOptions_DensifyOptions) CIRCLE_NODE(CircleDepthToSpace, BuiltinOperator_DEPTH_TO_SPACE, BuiltinOptions_DepthToSpaceOptions) CIRCLE_NODE(CircleDepthwiseConv2D, BuiltinOperator_DEPTHWISE_CONV_2D, BuiltinOptions_DepthwiseConv2DOptions) CIRCLE_NODE(CircleDequantize, BuiltinOperator_DEQUANTIZE, BuiltinOptions_DequantizeOptions) diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp index b3bb850..97e8107 100644 --- a/compiler/luci/export/src/CircleTensorExporter.cpp +++ b/compiler/luci/export/src/CircleTensorExporter.cpp @@ -434,6 +434,12 @@ flatbuffers::Offset encodeOpBuffer(FlatBufferBuilder &builder, l break; } + // NOTE loco::DataType::FLOAT16 is added but we do not export this type + // as backends currently don't support this type. + // currently this is supported only for "Tensor(Float16) - Dequantize" + // sequence so that after 'fold_dequantize' option this Tensor is + // converted to FLOAT32. + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); } diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt index 1b2db23..bc0a00b 100644 --- a/compiler/luci/import/CMakeLists.txt +++ b/compiler/luci/import/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(luci_import PRIVATE luci_log) target_link_libraries(luci_import PRIVATE luci_logex) target_link_libraries(luci_import PRIVATE nncc_common) target_link_libraries(luci_import PRIVATE locop) +target_link_libraries(luci_import PRIVATE foder) target_link_libraries(luci_import PRIVATE oops) target_link_libraries(luci_import PRIVATE mio_circle04_helper) install(TARGETS luci_import DESTINATION lib) diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h index 7a5045e..a4a6d7c 100644 --- a/compiler/luci/import/include/luci/Import/Nodes.h +++ b/compiler/luci/import/include/luci/Import/Nodes.h @@ -35,6 +35,7 @@ #include "Nodes/CircleConv2D.h" #include "Nodes/CircleCos.h" #include "Nodes/CircleCustom.h" +#include "Nodes/CircleDensify.h" #include "Nodes/CircleDepthToSpace.h" #include "Nodes/CircleDepthwiseConv2D.h" #include "Nodes/CircleDequantize.h" diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDensify.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDensify.h new file mode 100644 index 0000000..42bdac1 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDensify.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_IMPORT_OP_CIRCLE_DENSIFY_H__ +#define __LUCI_IMPORT_OP_CIRCLE_DENSIFY_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleDensifyGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_DENSIFY_H__ diff --git a/compiler/luci/import/include/luci/ImporterEx.h b/compiler/luci/import/include/luci/ImporterEx.h new file mode 100644 index 0000000..852d4c8 --- /dev/null +++ b/compiler/luci/import/include/luci/ImporterEx.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_IMPORTER_EX_H__ +#define __LUCI_IMPORTER_EX_H__ + +#include "luci/IR/Module.h" + +#include +#include + +namespace luci +{ + +class ImporterEx final +{ +public: + ImporterEx() = default; + +public: + std::unique_ptr importVerifyModule(const std::string &input_path) const; +}; + +} // namespace luci + +#endif // __LUCI_IMPORTER_EX_H__ diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp index fe2d830..d3b52aa 100644 --- a/compiler/luci/import/src/GraphBuilderRegistry.cpp +++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp @@ -44,6 +44,7 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(CONCATENATION, CircleConcatenationGraphBuilder); // 2 CIRCLE_NODE(CONV_2D, CircleConv2DGraphBuilder); // 3 CIRCLE_NODE(COS, CircleCosGraphBuilder); // 108 + CIRCLE_NODE(DENSIFY, CircleDensifyGraphBuilder); // 124 CIRCLE_NODE(DEPTH_TO_SPACE, CircleDepthToSpaceGraphBuilder); // 5 CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DGraphBuilder); // 4 CIRCLE_NODE(DEQUANTIZE, CircleDequantizeGraphBuilder); // 6 @@ -160,7 +161,6 @@ GraphBuilderRegistry::GraphBuilderRegistry() // BuiltinOperator_DELEGATE = 51, // BuiltinOperator_ARG_MAX = 56, // BuiltinOperator_HARD_SWISH = 117, - // BuiltinOperator_DENSIFY = 124, // Register builders for nodes which not handles in builders registered above. #define CIRCLE_NODE(CLASS) add(std::make_unique()) diff --git a/compiler/luci/import/src/ImporterEx.cpp b/compiler/luci/import/src/ImporterEx.cpp new file mode 100644 index 0000000..db585fd --- /dev/null +++ b/compiler/luci/import/src/ImporterEx.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Importer.h" +#include "luci/ImporterEx.h" + +#include + +#include +#include + +namespace luci +{ + +std::unique_ptr ImporterEx::importVerifyModule(const std::string &input_path) const +{ + foder::FileLoader file_loader{input_path}; + std::vector model_data; + + try + { + model_data = file_loader.load(); + } + catch (const std::runtime_error &err) + { + std::cerr << err.what() << std::endl; + return nullptr; + } + + flatbuffers::Verifier verifier{reinterpret_cast(model_data.data()), model_data.size()}; + if (!circle::VerifyModelBuffer(verifier)) + { + std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; + return nullptr; + } + + const circle::Model *circle_model = circle::GetModel(model_data.data()); + if (circle_model == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + return nullptr; + } + + Importer importer; + return importer.importModule(circle_model); +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp index a4f190d..88f2ae3 100644 --- a/compiler/luci/import/src/Nodes/CircleConst.cpp +++ b/compiler/luci/import/src/Nodes/CircleConst.cpp @@ -166,6 +166,10 @@ CircleNode *CircleConstNodeBuilder::build(TensorIndex tensor_index, copy_data(buffer, num_elements, const_node); break; + case loco::DataType::FLOAT16: + copy_data(buffer, num_elements, const_node); + break; + case loco::DataType::U8: copy_data(buffer, num_elements, const_node); break; diff --git a/compiler/luci/import/src/Nodes/CircleDensify.cpp b/compiler/luci/import/src/Nodes/CircleDensify.cpp new file mode 100644 index 0000000..0a4b218 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleDensify.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Import/Nodes/CircleDensify.h" + +#include + +#include + +namespace luci +{ + +bool CircleDensifyGraphBuilder::validate(const ValidateArgs &args) const +{ + return GraphBuilder::validate(args, 1); +} + +CircleNode *CircleDensifyGraphBuilder::build_node(const circle::OperatorT &, + const std::vector &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + node->input(inputs.at(0)); + + // No options for Densify + + return node; +} + +} // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h index d89ea03..901f1cb 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h @@ -32,6 +32,7 @@ #include "Nodes/CircleConv2D.h" #include "Nodes/CircleCos.h" #include "Nodes/CircleCustom.h" +#include "Nodes/CircleDensify.h" #include "Nodes/CircleDepthToSpace.h" #include "Nodes/CircleDepthwiseConv2D.h" #include "Nodes/CircleDequantize.h" diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst index 1472008..f227a03 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.lst +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst @@ -30,6 +30,7 @@ CIRCLE_NODE(CONCATENATION, CircleConcatenation) CIRCLE_NODE(CONV_2D, CircleConv2D) CIRCLE_NODE(COS, CircleCos) CIRCLE_NODE(CUSTOM, CircleCustom) +CIRCLE_NODE(DENSIFY, CircleDensify) CIRCLE_NODE(DEPTH_TO_SPACE, CircleDepthToSpace) CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2D) CIRCLE_NODE(DEQUANTIZE, CircleDequantize) diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDensify.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDensify.h new file mode 100644 index 0000000..7acad03 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDensify.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_IR_CIRCLE_DENSIFY_H__ +#define __LUCI_IR_CIRCLE_DENSIFY_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief DENSIFY in Circle + */ +class CircleDensify final : public FixedArityNode<1, CircleNodeImpl> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_DENSIFY_H__ diff --git a/compiler/luci/lang/src/Nodes/CircleConst.cpp b/compiler/luci/lang/src/Nodes/CircleConst.cpp index c2d82c8..a4854ec 100644 --- a/compiler/luci/lang/src/Nodes/CircleConst.cpp +++ b/compiler/luci/lang/src/Nodes/CircleConst.cpp @@ -77,6 +77,7 @@ INSTANTIATE(loco::DataType::S8); INSTANTIATE(loco::DataType::FLOAT32); INSTANTIATE(loco::DataType::U8); INSTANTIATE(loco::DataType::BOOL); +INSTANTIATE(loco::DataType::FLOAT16); #undef INSTANTIATE diff --git a/compiler/luci/lang/src/Nodes/CircleDensify.test.cpp b/compiler/luci/lang/src/Nodes/CircleDensify.test.cpp new file mode 100644 index 0000000..ae83784 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleDensify.test.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/IR/Nodes/CircleDensify.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include + +TEST(CircleDensifyTest, constructor) +{ + luci::CircleDensify densify_node; + + ASSERT_EQ(luci::CircleDialect::get(), densify_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::DENSIFY, densify_node.opcode()); + + ASSERT_EQ(nullptr, densify_node.input()); +} + +TEST(CircleDensifyTest, input_NEG) +{ + luci::CircleDensify densify_node; + luci::CircleDensify node; + + densify_node.input(&node); + ASSERT_NE(nullptr, densify_node.input()); + + densify_node.input(nullptr); + ASSERT_EQ(nullptr, densify_node.input()); +} + +TEST(CircleDensifyTest, arity_NEG) +{ + luci::CircleDensify densify_node; + + ASSERT_NO_THROW(densify_node.arg(0)); + ASSERT_THROW(densify_node.arg(1), std::out_of_range); +} + +TEST(CircleDensifyTest, visit_mutable_NEG) +{ + struct TestVisitor final : public luci::CircleNodeMutableVisitor + { + }; + + luci::CircleDensify densify_node; + + TestVisitor tv; + ASSERT_THROW(densify_node.accept(&tv), std::exception); +} + +TEST(CircleDensifyTest, visit_NEG) +{ + struct TestVisitor final : public luci::CircleNodeVisitor + { + }; + + luci::CircleDensify densify_node; + + TestVisitor tv; + ASSERT_THROW(densify_node.accept(&tv), std::exception); +} diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp index eff0830..8409f25 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp @@ -137,6 +137,7 @@ CircleNodeSummaryBuilder::create_builder(const luci::CircleNode *node) CIRCLE_NODE(CONV_2D, CircleConv2DSummaryBuilder) CIRCLE_NODE(COS, CircleCosSummaryBuilder) CIRCLE_NODE(CUSTOM, CircleCustomSummaryBuilder) + CIRCLE_NODE(DENSIFY, CircleDensifySummaryBuilder) CIRCLE_NODE(DEPTH_TO_SPACE, CircleDepthToSpaceSummaryBuilder) CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DSummaryBuilder) CIRCLE_NODE(DEQUANTIZE, CircleDequantizeSummaryBuilder) diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp index 6df9270..48e4579 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp @@ -374,6 +374,22 @@ void CircleConcatenationSummaryBuilder::build_attributes(const luci::CircleNode s.args().append("fused_activation_function", to_str(concat->fusedActivationFunction())); } +void CircleConstSummaryBuilder::build_attributes(const luci::CircleNode *node, + locop::NodeSummary &s) +{ + auto circonst = loco::must_cast(node); + s.args().append("dtype", to_str(circonst->dtype())); + s.args().append("rank", std::to_string(circonst->rank())); + std::string shape; + for (uint32_t r = 0; r < circonst->rank(); ++r) + { + if (!shape.empty()) + shape += " "; + shape += std::to_string(circonst->dim(r).value()); + } + s.args().append("shape", "[" + shape + "]"); +} + void CircleConstSummaryBuilder::update_status(locop::NodeSummary &s) { s.state(locop::NodeDesc::State::PartiallyKnown); diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilders.h b/compiler/luci/logex/src/CircleNodeSummaryBuilders.h index 6cd24b7..f0cac4e 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilders.h +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilders.h @@ -167,6 +167,7 @@ private: class CircleConstSummaryBuilder final : public CircleNodeSummaryBuilder { private: + void build_attributes(const luci::CircleNode *node, locop::NodeSummary &s); void update_status(locop::NodeSummary &s); }; @@ -189,6 +190,10 @@ private: void build_attributes(const luci::CircleNode *node, locop::NodeSummary &s); }; +class CircleDensifySummaryBuilder final : public CircleNodeWithINPUTSummaryBuilder +{ +}; + class CircleDepthToSpaceSummaryBuilder final : public CircleNodeWithINPUTSummaryBuilder { private: diff --git a/compiler/luci/partition/include/luci/ConnectNode.h b/compiler/luci/partition/include/luci/ConnectNode.h new file mode 100644 index 0000000..2d9d41d --- /dev/null +++ b/compiler/luci/partition/include/luci/ConnectNode.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_PARTITION_CONNECT_NODE_H__ +#define __LUCI_PARTITION_CONNECT_NODE_H__ + +#include +#include + +namespace luci +{ + +/** + * @note MapNode2Clone is used as a map from original node to cloned node + * to find input of a cloned node + * + * (Original) (Clone) + * + * [A] [A'] + * | [B] | [B'] + * | | | | + * \ / \ / + * [C] [C'] + * + * From view of [C'] we need to find [A'] and [B']. We know [C] from [C'], + * then we can get from input of [C] as [A], [B] then [A]->[A'] and [B]->[B'] + * from the map. + */ +using MapNode2Clone = std::map; + +struct CloneContext +{ + std::pair emplace(const CircleNode *org, CircleNode *clone) + { + return node2clone.emplace(org, clone); + } + MapNode2Clone::iterator find(const CircleNode *org) { return node2clone.find(org); } + MapNode2Clone::iterator end(void) { return node2clone.end(); } + + MapNode2Clone::const_iterator find(const CircleNode *org) const { return node2clone.find(org); } + MapNode2Clone::const_iterator end(void) const { return node2clone.end(); } + + MapNode2Clone node2clone; +}; + +class ConnectNode final : public luci::CircleNodeVisitor +{ +public: + ConnectNode(luci::CloneContext &clonecontext) : _clonecontext(clonecontext){}; + +public: + void visit(const luci::CircleAbs *) final; + void visit(const luci::CircleAdd *) final; + void visit(const luci::CircleAddN *) final; + void visit(const luci::CircleArgMax *) final; + void visit(const luci::CircleArgMin *) final; + void visit(const luci::CircleAveragePool2D *) final; + void visit(const luci::CircleBatchMatMul *) final; + void visit(const luci::CircleBatchToSpaceND *) final; + void visit(const luci::CircleCast *) final; + void visit(const luci::CircleCeil *) final; + void visit(const luci::CircleConcatenation *) final; + void visit(const luci::CircleConst *) final; + void visit(const luci::CircleConv2D *) final; + void visit(const luci::CircleCos *) final; + void visit(const luci::CircleCustom *) final; + void visit(const luci::CircleDensify *) final; + void visit(const luci::CircleDepthToSpace *) final; + void visit(const luci::CircleDepthwiseConv2D *) final; + void visit(const luci::CircleDequantize *) final; + void visit(const luci::CircleDiv *) final; + void visit(const luci::CircleElu *) final; + void visit(const luci::CircleEqual *) final; + void visit(const luci::CircleExp *) final; + void visit(const luci::CircleExpandDims *) final; + void visit(const luci::CircleFakeQuant *) final; + void visit(const luci::CircleFill *) final; + void visit(const luci::CircleFloor *) final; + void visit(const luci::CircleFloorDiv *) final; + void visit(const luci::CircleFloorMod *) final; + void visit(const luci::CircleFullyConnected *) final; + void visit(const luci::CircleGather *) final; + void visit(const luci::CircleGatherNd *) final; + void visit(const luci::CircleGreater *) final; + void visit(const luci::CircleGreaterEqual *) final; + void visit(const luci::CircleIf *) final; + void visit(const luci::CircleL2Normalize *) final; + void visit(const luci::CircleL2Pool2D *) final; + void visit(const luci::CircleLeakyRelu *) final; + void visit(const luci::CircleLess *) final; + void visit(const luci::CircleLessEqual *) final; + void visit(const luci::CircleLocalResponseNormalization *) final; + void visit(const luci::CircleLog *) final; + void visit(const luci::CircleLogicalAnd *) final; + void visit(const luci::CircleLogicalNot *) final; + void visit(const luci::CircleLogicalOr *) final; + void visit(const luci::CircleLogistic *) final; + void visit(const luci::CircleLogSoftmax *) final; + void visit(const luci::CircleMatrixDiag *) final; + void visit(const luci::CircleMatrixSetDiag *) final; + void visit(const luci::CircleMaximum *) final; + void visit(const luci::CircleMaxPool2D *) final; + void visit(const luci::CircleMean *) final; + void visit(const luci::CircleMinimum *) final; + void visit(const luci::CircleMirrorPad *) final; + void visit(const luci::CircleMul *) final; + void visit(const luci::CircleNeg *) final; + void visit(const luci::CircleNonMaxSuppressionV4 *) final; + void visit(const luci::CircleNonMaxSuppressionV5 *) final; + void visit(const luci::CircleNotEqual *) final; + void visit(const luci::CircleOneHot *) final; + void visit(const luci::CirclePack *) final; + void visit(const luci::CirclePad *) final; + void visit(const luci::CirclePadV2 *) final; + void visit(const luci::CirclePow *) final; + void visit(const luci::CirclePRelu *) final; + void visit(const luci::CircleQuantize *) final; + void visit(const luci::CircleRange *) final; + void visit(const luci::CircleRank *) final; + void visit(const luci::CircleReduceAny *) final; + void visit(const luci::CircleReduceMax *) final; + void visit(const luci::CircleReduceMin *) final; + void visit(const luci::CircleReduceProd *) final; + void visit(const luci::CircleRelu *) final; + void visit(const luci::CircleRelu6 *) final; + void visit(const luci::CircleReluN1To1 *) final; + void visit(const luci::CircleReshape *) final; + void visit(const luci::CircleResizeBilinear *) final; + void visit(const luci::CircleResizeNearestNeighbor *) final; + void visit(const luci::CircleReverseSequence *) final; + void visit(const luci::CircleReverseV2 *) final; + void visit(const luci::CircleRound *) final; + void visit(const luci::CircleRsqrt *) final; + void visit(const luci::CircleScatterNd *) final; + void visit(const luci::CircleSegmentSum *) final; + void visit(const luci::CircleSelect *) final; + void visit(const luci::CircleSelectV2 *) final; + void visit(const luci::CircleShape *) final; + void visit(const luci::CircleSin *) final; + void visit(const luci::CircleSlice *) final; + void visit(const luci::CircleSoftmax *) final; + void visit(const luci::CircleSpaceToBatchND *) final; + void visit(const luci::CircleSpaceToDepth *) final; + void visit(const luci::CircleSparseToDense *) final; + void visit(const luci::CircleSplit *) final; + void visit(const luci::CircleSplitV *) final; + void visit(const luci::CircleSqrt *) final; + void visit(const luci::CircleSquare *) final; + void visit(const luci::CircleSquaredDifference *) final; + void visit(const luci::CircleSqueeze *) final; + void visit(const luci::CircleStridedSlice *) final; + void visit(const luci::CircleSVDF *) final; + void visit(const luci::CircleSub *) final; + void visit(const luci::CircleSum *) final; + void visit(const luci::CircleTanh *) final; + void visit(const luci::CircleTile *) final; + void visit(const luci::CircleTopKV2 *) final; + void visit(const luci::CircleTranspose *) final; + void visit(const luci::CircleTransposeConv *) final; + void visit(const luci::CircleUnidirectionalSequenceLSTM *) final; + void visit(const luci::CircleUnique *) final; + void visit(const luci::CircleUnpack *) final; + void visit(const luci::CircleWhere *) final; + void visit(const luci::CircleWhile *) final; + void visit(const luci::CircleZerosLike *) final; + + // Circle Only + void visit(const luci::CircleBCQFullyConnected *) final; + void visit(const luci::CircleBCQGather *) final; + void visit(const luci::CircleInstanceNorm *) final; + + // NOTE CircleInput and CircleOutput are not handled here as these need + // link with graph I/O + + // Virtual + void visit(const luci::CircleCustomOut *) final; + void visit(const luci::CircleIfOut *) final; + // void visit(const luci::CircleInput *) final; + void visit(const luci::CircleNonMaxSuppressionV4Out *) final; + void visit(const luci::CircleNonMaxSuppressionV5Out *) final; + // void visit(const luci::CircleOutput *) final; + void visit(const luci::CircleOutputDummy *) final; + void visit(const luci::CircleOutputExclude *) final; + void visit(const luci::CircleSplitOut *) final; + void visit(const luci::CircleSplitVOut *) final; + void visit(const luci::CircleTopKV2Out *) final; + void visit(const luci::CircleUniqueOut *) final; + void visit(const luci::CircleUnpackOut *) final; + void visit(const luci::CircleVariable *) final; + void visit(const luci::CircleWhileOut *) final; + +public: + luci::CircleNode *find_clone(const luci::CircleNode *node); + +protected: + luci::CloneContext &_clonecontext; +}; + +/** + * @brief Connect cloned node from input node + */ +void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext); + +} // namespace luci + +#endif // __LUCI_PARTITION_CONNECT_NODE_H__ diff --git a/compiler/luci/partition/src/ConnectNode.cpp b/compiler/luci/partition/src/ConnectNode.cpp index 336be7c..3d8c211 100644 --- a/compiler/luci/partition/src/ConnectNode.cpp +++ b/compiler/luci/partition/src/ConnectNode.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include diff --git a/compiler/luci/partition/src/ConnectNode.h b/compiler/luci/partition/src/ConnectNode.h deleted file mode 100644 index e60567c..0000000 --- a/compiler/luci/partition/src/ConnectNode.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __LUCI_PARTITION_CONNECT_NODE_H__ -#define __LUCI_PARTITION_CONNECT_NODE_H__ - -#include -#include - -namespace luci -{ - -/** - * @note MapNode2Clone is used as a map from original node to cloned node - * to find input of a cloned node - * - * (Original) (Clone) - * - * [A] [A'] - * | [B] | [B'] - * | | | | - * \ / \ / - * [C] [C'] - * - * From view of [C'] we need to find [A'] and [B']. We know [C] from [C'], - * then we can get from input of [C] as [A], [B] then [A]->[A'] and [B]->[B'] - * from the map. - */ -using MapNode2Clone = std::map; - -struct CloneContext -{ - std::pair emplace(const CircleNode *org, CircleNode *clone) - { - return node2clone.emplace(org, clone); - } - MapNode2Clone::iterator find(const CircleNode *org) { return node2clone.find(org); } - MapNode2Clone::iterator end(void) { return node2clone.end(); } - - MapNode2Clone::const_iterator find(const CircleNode *org) const { return node2clone.find(org); } - MapNode2Clone::const_iterator end(void) const { return node2clone.end(); } - - MapNode2Clone node2clone; -}; - -class ConnectNode final : public luci::CircleNodeVisitor -{ -public: - ConnectNode(luci::CloneContext &clonecontext) : _clonecontext(clonecontext){}; - -public: - void visit(const luci::CircleAbs *) final; - void visit(const luci::CircleAdd *) final; - void visit(const luci::CircleAddN *) final; - void visit(const luci::CircleArgMax *) final; - void visit(const luci::CircleArgMin *) final; - void visit(const luci::CircleAveragePool2D *) final; - void visit(const luci::CircleBatchMatMul *) final; - void visit(const luci::CircleBatchToSpaceND *) final; - void visit(const luci::CircleCast *) final; - void visit(const luci::CircleCeil *) final; - void visit(const luci::CircleConcatenation *) final; - void visit(const luci::CircleConst *) final; - void visit(const luci::CircleConv2D *) final; - void visit(const luci::CircleCos *) final; - void visit(const luci::CircleCustom *) final; - void visit(const luci::CircleDepthToSpace *) final; - void visit(const luci::CircleDepthwiseConv2D *) final; - void visit(const luci::CircleDequantize *) final; - void visit(const luci::CircleDiv *) final; - void visit(const luci::CircleElu *) final; - void visit(const luci::CircleEqual *) final; - void visit(const luci::CircleExp *) final; - void visit(const luci::CircleExpandDims *) final; - void visit(const luci::CircleFakeQuant *) final; - void visit(const luci::CircleFill *) final; - void visit(const luci::CircleFloor *) final; - void visit(const luci::CircleFloorDiv *) final; - void visit(const luci::CircleFloorMod *) final; - void visit(const luci::CircleFullyConnected *) final; - void visit(const luci::CircleGather *) final; - void visit(const luci::CircleGatherNd *) final; - void visit(const luci::CircleGreater *) final; - void visit(const luci::CircleGreaterEqual *) final; - void visit(const luci::CircleIf *) final; - void visit(const luci::CircleL2Normalize *) final; - void visit(const luci::CircleL2Pool2D *) final; - void visit(const luci::CircleLeakyRelu *) final; - void visit(const luci::CircleLess *) final; - void visit(const luci::CircleLessEqual *) final; - void visit(const luci::CircleLocalResponseNormalization *) final; - void visit(const luci::CircleLog *) final; - void visit(const luci::CircleLogicalAnd *) final; - void visit(const luci::CircleLogicalNot *) final; - void visit(const luci::CircleLogicalOr *) final; - void visit(const luci::CircleLogistic *) final; - void visit(const luci::CircleLogSoftmax *) final; - void visit(const luci::CircleMatrixDiag *) final; - void visit(const luci::CircleMatrixSetDiag *) final; - void visit(const luci::CircleMaximum *) final; - void visit(const luci::CircleMaxPool2D *) final; - void visit(const luci::CircleMean *) final; - void visit(const luci::CircleMinimum *) final; - void visit(const luci::CircleMirrorPad *) final; - void visit(const luci::CircleMul *) final; - void visit(const luci::CircleNeg *) final; - void visit(const luci::CircleNonMaxSuppressionV4 *) final; - void visit(const luci::CircleNonMaxSuppressionV5 *) final; - void visit(const luci::CircleNotEqual *) final; - void visit(const luci::CircleOneHot *) final; - void visit(const luci::CirclePack *) final; - void visit(const luci::CirclePad *) final; - void visit(const luci::CirclePadV2 *) final; - void visit(const luci::CirclePow *) final; - void visit(const luci::CirclePRelu *) final; - void visit(const luci::CircleQuantize *) final; - void visit(const luci::CircleRange *) final; - void visit(const luci::CircleRank *) final; - void visit(const luci::CircleReduceAny *) final; - void visit(const luci::CircleReduceMax *) final; - void visit(const luci::CircleReduceMin *) final; - void visit(const luci::CircleReduceProd *) final; - void visit(const luci::CircleRelu *) final; - void visit(const luci::CircleRelu6 *) final; - void visit(const luci::CircleReluN1To1 *) final; - void visit(const luci::CircleReshape *) final; - void visit(const luci::CircleResizeBilinear *) final; - void visit(const luci::CircleResizeNearestNeighbor *) final; - void visit(const luci::CircleReverseSequence *) final; - void visit(const luci::CircleReverseV2 *) final; - void visit(const luci::CircleRound *) final; - void visit(const luci::CircleRsqrt *) final; - void visit(const luci::CircleScatterNd *) final; - void visit(const luci::CircleSegmentSum *) final; - void visit(const luci::CircleSelect *) final; - void visit(const luci::CircleSelectV2 *) final; - void visit(const luci::CircleShape *) final; - void visit(const luci::CircleSin *) final; - void visit(const luci::CircleSlice *) final; - void visit(const luci::CircleSoftmax *) final; - void visit(const luci::CircleSpaceToBatchND *) final; - void visit(const luci::CircleSpaceToDepth *) final; - void visit(const luci::CircleSparseToDense *) final; - void visit(const luci::CircleSplit *) final; - void visit(const luci::CircleSplitV *) final; - void visit(const luci::CircleSqrt *) final; - void visit(const luci::CircleSquare *) final; - void visit(const luci::CircleSquaredDifference *) final; - void visit(const luci::CircleSqueeze *) final; - void visit(const luci::CircleStridedSlice *) final; - void visit(const luci::CircleSVDF *) final; - void visit(const luci::CircleSub *) final; - void visit(const luci::CircleSum *) final; - void visit(const luci::CircleTanh *) final; - void visit(const luci::CircleTile *) final; - void visit(const luci::CircleTopKV2 *) final; - void visit(const luci::CircleTranspose *) final; - void visit(const luci::CircleTransposeConv *) final; - void visit(const luci::CircleUnidirectionalSequenceLSTM *) final; - void visit(const luci::CircleUnique *) final; - void visit(const luci::CircleUnpack *) final; - void visit(const luci::CircleWhere *) final; - void visit(const luci::CircleWhile *) final; - void visit(const luci::CircleZerosLike *) final; - - // Circle Only - void visit(const luci::CircleBCQFullyConnected *) final; - void visit(const luci::CircleBCQGather *) final; - void visit(const luci::CircleInstanceNorm *) final; - - // NOTE CircleInput and CircleOutput are not handled here as these need - // link with graph I/O - - // Virtual - void visit(const luci::CircleCustomOut *) final; - void visit(const luci::CircleIfOut *) final; - // void visit(const luci::CircleInput *) final; - void visit(const luci::CircleNonMaxSuppressionV4Out *) final; - void visit(const luci::CircleNonMaxSuppressionV5Out *) final; - // void visit(const luci::CircleOutput *) final; - void visit(const luci::CircleOutputDummy *) final; - void visit(const luci::CircleOutputExclude *) final; - void visit(const luci::CircleSplitOut *) final; - void visit(const luci::CircleSplitVOut *) final; - void visit(const luci::CircleTopKV2Out *) final; - void visit(const luci::CircleUniqueOut *) final; - void visit(const luci::CircleUnpackOut *) final; - void visit(const luci::CircleVariable *) final; - void visit(const luci::CircleWhileOut *) final; - -public: - luci::CircleNode *find_clone(const luci::CircleNode *node); - -protected: - luci::CloneContext &_clonecontext; -}; - -/** - * @brief Connect cloned node from input node - */ -void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext); - -} // namespace luci - -#endif // __LUCI_PARTITION_CONNECT_NODE_H__ diff --git a/compiler/luci/partition/src/ConnectNode.test.h b/compiler/luci/partition/src/ConnectNode.test.h index ac4878a..18bb52a 100644 --- a/compiler/luci/partition/src/ConnectNode.test.h +++ b/compiler/luci/partition/src/ConnectNode.test.h @@ -17,7 +17,7 @@ #ifndef __CONNECT_NODE_TEST_H__ #define __CONNECT_NODE_TEST_H__ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include #include diff --git a/compiler/luci/partition/src/Nodes/CircleAbs.cpp b/compiler/luci/partition/src/Nodes/CircleAbs.cpp index a3fde4c..a7fbc37 100644 --- a/compiler/luci/partition/src/Nodes/CircleAbs.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAbs.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleAbs.test.cpp b/compiler/luci/partition/src/Nodes/CircleAbs.test.cpp index f3e7215..ac805c1 100644 --- a/compiler/luci/partition/src/Nodes/CircleAbs.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAbs.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.cpp index d393997..0754be6 100644 --- a/compiler/luci/partition/src/Nodes/CircleAdd.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAdd.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp index e457b83..99ae52c 100644 --- a/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleAddN.cpp b/compiler/luci/partition/src/Nodes/CircleAddN.cpp index 81e5e09..90aaeee 100644 --- a/compiler/luci/partition/src/Nodes/CircleAddN.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAddN.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleAddN.test.cpp b/compiler/luci/partition/src/Nodes/CircleAddN.test.cpp index 5d0a748..37743d3 100644 --- a/compiler/luci/partition/src/Nodes/CircleAddN.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAddN.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleArgMax.cpp b/compiler/luci/partition/src/Nodes/CircleArgMax.cpp index 1409586..99b30d3 100644 --- a/compiler/luci/partition/src/Nodes/CircleArgMax.cpp +++ b/compiler/luci/partition/src/Nodes/CircleArgMax.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleArgMax.test.cpp b/compiler/luci/partition/src/Nodes/CircleArgMax.test.cpp index c816fbe..77248e0 100644 --- a/compiler/luci/partition/src/Nodes/CircleArgMax.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleArgMax.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleArgMin.cpp b/compiler/luci/partition/src/Nodes/CircleArgMin.cpp index 6151aa9..1bb3d84 100644 --- a/compiler/luci/partition/src/Nodes/CircleArgMin.cpp +++ b/compiler/luci/partition/src/Nodes/CircleArgMin.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleArgMin.test.cpp b/compiler/luci/partition/src/Nodes/CircleArgMin.test.cpp index d150be4..ed0cf03 100644 --- a/compiler/luci/partition/src/Nodes/CircleArgMin.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleArgMin.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/partition/src/Nodes/CircleAveragePool2D.cpp index 5476657..1df86c7 100644 --- a/compiler/luci/partition/src/Nodes/CircleAveragePool2D.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAveragePool2D.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleAveragePool2D.test.cpp b/compiler/luci/partition/src/Nodes/CircleAveragePool2D.test.cpp index fba2be8..266120b 100644 --- a/compiler/luci/partition/src/Nodes/CircleAveragePool2D.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleAveragePool2D.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.cpp b/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.cpp index 5b1dd85..6d50f0e 100644 --- a/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.test.cpp b/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.test.cpp index 3d64f4b..2191f5b 100644 --- a/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBCQFullyConnected.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleBCQGather.cpp b/compiler/luci/partition/src/Nodes/CircleBCQGather.cpp index 90c4d9e..a9e810a 100644 --- a/compiler/luci/partition/src/Nodes/CircleBCQGather.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBCQGather.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleBCQGather.test.cpp b/compiler/luci/partition/src/Nodes/CircleBCQGather.test.cpp index bbbd3f1..0324d85 100644 --- a/compiler/luci/partition/src/Nodes/CircleBCQGather.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBCQGather.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleBatchMatMul.cpp b/compiler/luci/partition/src/Nodes/CircleBatchMatMul.cpp index c3992a6..5a459e7 100644 --- a/compiler/luci/partition/src/Nodes/CircleBatchMatMul.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBatchMatMul.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleBatchMatMul.test.cpp b/compiler/luci/partition/src/Nodes/CircleBatchMatMul.test.cpp index 94336d3..e6d26a6 100644 --- a/compiler/luci/partition/src/Nodes/CircleBatchMatMul.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBatchMatMul.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.cpp b/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.cpp index 2a463af..40b8f70 100644 --- a/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.test.cpp b/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.test.cpp index 544f5e1..e9cb350 100644 --- a/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleBatchToSpaceND.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleCast.cpp b/compiler/luci/partition/src/Nodes/CircleCast.cpp index f7630cd..e1301aa 100644 --- a/compiler/luci/partition/src/Nodes/CircleCast.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCast.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleCast.test.cpp b/compiler/luci/partition/src/Nodes/CircleCast.test.cpp index 0051190..d7b679a 100644 --- a/compiler/luci/partition/src/Nodes/CircleCast.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCast.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleCeil.cpp b/compiler/luci/partition/src/Nodes/CircleCeil.cpp index a0c9403..e7b5f5a 100644 --- a/compiler/luci/partition/src/Nodes/CircleCeil.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCeil.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleCeil.test.cpp b/compiler/luci/partition/src/Nodes/CircleCeil.test.cpp index dbd7e53..cb03648 100644 --- a/compiler/luci/partition/src/Nodes/CircleCeil.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCeil.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleConcatenation.cpp b/compiler/luci/partition/src/Nodes/CircleConcatenation.cpp index fb24d21..d895685 100644 --- a/compiler/luci/partition/src/Nodes/CircleConcatenation.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConcatenation.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleConcatenation.test.cpp b/compiler/luci/partition/src/Nodes/CircleConcatenation.test.cpp index 4d64b85..b5c05e2 100644 --- a/compiler/luci/partition/src/Nodes/CircleConcatenation.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConcatenation.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleConst.cpp b/compiler/luci/partition/src/Nodes/CircleConst.cpp index 118cd8d..b88f5ef 100644 --- a/compiler/luci/partition/src/Nodes/CircleConst.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConst.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace luci { diff --git a/compiler/luci/partition/src/Nodes/CircleConv2D.cpp b/compiler/luci/partition/src/Nodes/CircleConv2D.cpp index 46716f0..ca9cce1 100644 --- a/compiler/luci/partition/src/Nodes/CircleConv2D.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConv2D.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleConv2D.test.cpp b/compiler/luci/partition/src/Nodes/CircleConv2D.test.cpp index 829adec..4596d96 100644 --- a/compiler/luci/partition/src/Nodes/CircleConv2D.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConv2D.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleCos.cpp b/compiler/luci/partition/src/Nodes/CircleCos.cpp index 9dcf81e..76b1baa 100644 --- a/compiler/luci/partition/src/Nodes/CircleCos.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCos.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleCos.test.cpp b/compiler/luci/partition/src/Nodes/CircleCos.test.cpp index 6c92b93..ba806a3 100644 --- a/compiler/luci/partition/src/Nodes/CircleCos.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCos.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleCustom.cpp b/compiler/luci/partition/src/Nodes/CircleCustom.cpp index ac16ebe..cc16048 100644 --- a/compiler/luci/partition/src/Nodes/CircleCustom.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCustom.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleCustom.test.cpp b/compiler/luci/partition/src/Nodes/CircleCustom.test.cpp index 9f40b52..f7fe866 100644 --- a/compiler/luci/partition/src/Nodes/CircleCustom.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCustom.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleCustomOut.cpp b/compiler/luci/partition/src/Nodes/CircleCustomOut.cpp index fee1a1a..0d83cff 100644 --- a/compiler/luci/partition/src/Nodes/CircleCustomOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCustomOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleCustomOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleCustomOut.test.cpp index 0a29397..ddd4e93 100644 --- a/compiler/luci/partition/src/Nodes/CircleCustomOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleCustomOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleDensify.cpp b/compiler/luci/partition/src/Nodes/CircleDensify.cpp new file mode 100644 index 0000000..cfb236a --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDensify.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleDensify *node) +{ + auto *cloned = loco::must_cast(cn->find_clone(node)); + + luci::CircleNode *input = loco::must_cast(node->input()); + + cloned->input(cn->find_clone(input)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleDensify *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleDensify.test.cpp b/compiler/luci/partition/src/Nodes/CircleDensify.test.cpp new file mode 100644 index 0000000..94076a8 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDensify.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/ConnectNode.h" + +#include "ConnectNode.test.h" + +#include + +#include + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT +{ +public: + NodeGraphlet() = default; +}; + +class TestNodeGraph : public TestIOGraph, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + NodeGraphlet::init(g()); + + node()->input(input()); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Densify) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(1, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); +} + +TEST(ConnectNodeTest, connect_Densify_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleDepthToSpace.cpp b/compiler/luci/partition/src/Nodes/CircleDepthToSpace.cpp index ade266e..c044b4c 100644 --- a/compiler/luci/partition/src/Nodes/CircleDepthToSpace.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDepthToSpace.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleDepthToSpace.test.cpp b/compiler/luci/partition/src/Nodes/CircleDepthToSpace.test.cpp index 997360a..1b61a35 100644 --- a/compiler/luci/partition/src/Nodes/CircleDepthToSpace.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDepthToSpace.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.cpp index 19d1d5f..2bd9ab5 100644 --- a/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.test.cpp b/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.test.cpp index 681f98b..02976a4 100644 --- a/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDepthwiseConv2D.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleDequantize.cpp b/compiler/luci/partition/src/Nodes/CircleDequantize.cpp index 3a520d4..ac2642b 100644 --- a/compiler/luci/partition/src/Nodes/CircleDequantize.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDequantize.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleDequantize.test.cpp b/compiler/luci/partition/src/Nodes/CircleDequantize.test.cpp index 7f6006c..d3a43d3 100644 --- a/compiler/luci/partition/src/Nodes/CircleDequantize.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDequantize.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.cpp index 4803385..8941a41 100644 --- a/compiler/luci/partition/src/Nodes/CircleDiv.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDiv.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp index 2269323..7900bea 100644 --- a/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleElu.cpp b/compiler/luci/partition/src/Nodes/CircleElu.cpp index d21cd4c..b772265 100644 --- a/compiler/luci/partition/src/Nodes/CircleElu.cpp +++ b/compiler/luci/partition/src/Nodes/CircleElu.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleElu.test.cpp b/compiler/luci/partition/src/Nodes/CircleElu.test.cpp index 94774cc..20b2050 100644 --- a/compiler/luci/partition/src/Nodes/CircleElu.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleElu.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleEqual.cpp b/compiler/luci/partition/src/Nodes/CircleEqual.cpp index 6a126c0..2dc0e75 100644 --- a/compiler/luci/partition/src/Nodes/CircleEqual.cpp +++ b/compiler/luci/partition/src/Nodes/CircleEqual.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleEqual.test.cpp b/compiler/luci/partition/src/Nodes/CircleEqual.test.cpp index 20b5391..c0d3bd9 100644 --- a/compiler/luci/partition/src/Nodes/CircleEqual.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleEqual.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleExp.cpp b/compiler/luci/partition/src/Nodes/CircleExp.cpp index 95fb1cd..c1da790 100644 --- a/compiler/luci/partition/src/Nodes/CircleExp.cpp +++ b/compiler/luci/partition/src/Nodes/CircleExp.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleExp.test.cpp b/compiler/luci/partition/src/Nodes/CircleExp.test.cpp index 16d7244..286f205 100644 --- a/compiler/luci/partition/src/Nodes/CircleExp.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleExp.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleExpandDims.cpp b/compiler/luci/partition/src/Nodes/CircleExpandDims.cpp index 6fccd63..a6ce649 100644 --- a/compiler/luci/partition/src/Nodes/CircleExpandDims.cpp +++ b/compiler/luci/partition/src/Nodes/CircleExpandDims.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleExpandDims.test.cpp b/compiler/luci/partition/src/Nodes/CircleExpandDims.test.cpp index 8a51565..37af10f 100644 --- a/compiler/luci/partition/src/Nodes/CircleExpandDims.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleExpandDims.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFakeQuant.cpp b/compiler/luci/partition/src/Nodes/CircleFakeQuant.cpp index 4855d80..5dfaee1 100644 --- a/compiler/luci/partition/src/Nodes/CircleFakeQuant.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFakeQuant.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFakeQuant.test.cpp b/compiler/luci/partition/src/Nodes/CircleFakeQuant.test.cpp index 3821d75..2a2ec0c 100644 --- a/compiler/luci/partition/src/Nodes/CircleFakeQuant.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFakeQuant.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFill.cpp b/compiler/luci/partition/src/Nodes/CircleFill.cpp index 06fca7b..32688cd 100644 --- a/compiler/luci/partition/src/Nodes/CircleFill.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFill.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFill.test.cpp b/compiler/luci/partition/src/Nodes/CircleFill.test.cpp index 97a5a34..4b3872a 100644 --- a/compiler/luci/partition/src/Nodes/CircleFill.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFill.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFloor.cpp b/compiler/luci/partition/src/Nodes/CircleFloor.cpp index 7ad3924..f7409a2 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloor.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFloor.test.cpp b/compiler/luci/partition/src/Nodes/CircleFloor.test.cpp index 1a964ea..883d362 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloor.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloor.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFloorDiv.cpp b/compiler/luci/partition/src/Nodes/CircleFloorDiv.cpp index 3b92b00..57e435c 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloorDiv.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloorDiv.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFloorDiv.test.cpp b/compiler/luci/partition/src/Nodes/CircleFloorDiv.test.cpp index 3d28015..1eb603c 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloorDiv.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloorDiv.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFloorMod.cpp b/compiler/luci/partition/src/Nodes/CircleFloorMod.cpp index 9f868d0..1b942d2 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloorMod.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloorMod.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFloorMod.test.cpp b/compiler/luci/partition/src/Nodes/CircleFloorMod.test.cpp index 89a0941..680bf16 100644 --- a/compiler/luci/partition/src/Nodes/CircleFloorMod.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFloorMod.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/partition/src/Nodes/CircleFullyConnected.cpp index da27303..206b47a 100644 --- a/compiler/luci/partition/src/Nodes/CircleFullyConnected.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFullyConnected.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleFullyConnected.test.cpp b/compiler/luci/partition/src/Nodes/CircleFullyConnected.test.cpp index fc88204..39eea55 100644 --- a/compiler/luci/partition/src/Nodes/CircleFullyConnected.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleFullyConnected.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleGather.cpp b/compiler/luci/partition/src/Nodes/CircleGather.cpp index 0ee4583..4f059cb 100644 --- a/compiler/luci/partition/src/Nodes/CircleGather.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGather.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleGather.test.cpp b/compiler/luci/partition/src/Nodes/CircleGather.test.cpp index 7f4e084..f427e04 100644 --- a/compiler/luci/partition/src/Nodes/CircleGather.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGather.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleGatherNd.cpp b/compiler/luci/partition/src/Nodes/CircleGatherNd.cpp index 4be05ca..6a9c3b4 100644 --- a/compiler/luci/partition/src/Nodes/CircleGatherNd.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGatherNd.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleGatherNd.test.cpp b/compiler/luci/partition/src/Nodes/CircleGatherNd.test.cpp index d673698..0207e91 100644 --- a/compiler/luci/partition/src/Nodes/CircleGatherNd.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGatherNd.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleGreater.cpp b/compiler/luci/partition/src/Nodes/CircleGreater.cpp index 7bc2a14..9f4b18f 100644 --- a/compiler/luci/partition/src/Nodes/CircleGreater.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGreater.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleGreater.test.cpp b/compiler/luci/partition/src/Nodes/CircleGreater.test.cpp index 842370d..61d1f59 100644 --- a/compiler/luci/partition/src/Nodes/CircleGreater.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGreater.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleGreaterEqual.cpp b/compiler/luci/partition/src/Nodes/CircleGreaterEqual.cpp index 536a0ae..76130a8 100644 --- a/compiler/luci/partition/src/Nodes/CircleGreaterEqual.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGreaterEqual.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleGreaterEqual.test.cpp b/compiler/luci/partition/src/Nodes/CircleGreaterEqual.test.cpp index 76dc770..7e4e1ef 100644 --- a/compiler/luci/partition/src/Nodes/CircleGreaterEqual.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGreaterEqual.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleIf.cpp b/compiler/luci/partition/src/Nodes/CircleIf.cpp index 1672a13..45e4ec4 100644 --- a/compiler/luci/partition/src/Nodes/CircleIf.cpp +++ b/compiler/luci/partition/src/Nodes/CircleIf.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleIf.test.cpp b/compiler/luci/partition/src/Nodes/CircleIf.test.cpp index dbd25c8..cbb7662 100644 --- a/compiler/luci/partition/src/Nodes/CircleIf.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleIf.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleIfOut.cpp b/compiler/luci/partition/src/Nodes/CircleIfOut.cpp index 969bdd9..2eb5dda 100644 --- a/compiler/luci/partition/src/Nodes/CircleIfOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleIfOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleIfOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleIfOut.test.cpp index 9207654..ec2dde3 100644 --- a/compiler/luci/partition/src/Nodes/CircleIfOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleIfOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleInstanceNorm.cpp b/compiler/luci/partition/src/Nodes/CircleInstanceNorm.cpp index 386652f..f64ffd8 100644 --- a/compiler/luci/partition/src/Nodes/CircleInstanceNorm.cpp +++ b/compiler/luci/partition/src/Nodes/CircleInstanceNorm.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleInstanceNorm.test.cpp b/compiler/luci/partition/src/Nodes/CircleInstanceNorm.test.cpp index b932223..4363c6c 100644 --- a/compiler/luci/partition/src/Nodes/CircleInstanceNorm.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleInstanceNorm.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleL2Normalize.cpp b/compiler/luci/partition/src/Nodes/CircleL2Normalize.cpp index 61ddba2..df26930 100644 --- a/compiler/luci/partition/src/Nodes/CircleL2Normalize.cpp +++ b/compiler/luci/partition/src/Nodes/CircleL2Normalize.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleL2Normalize.test.cpp b/compiler/luci/partition/src/Nodes/CircleL2Normalize.test.cpp index 4fc2372..b114a15 100644 --- a/compiler/luci/partition/src/Nodes/CircleL2Normalize.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleL2Normalize.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleL2Pool2D.cpp b/compiler/luci/partition/src/Nodes/CircleL2Pool2D.cpp index 24333d5..1eacddb 100644 --- a/compiler/luci/partition/src/Nodes/CircleL2Pool2D.cpp +++ b/compiler/luci/partition/src/Nodes/CircleL2Pool2D.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleL2Pool2D.test.cpp b/compiler/luci/partition/src/Nodes/CircleL2Pool2D.test.cpp index 4032848..22f99d5 100644 --- a/compiler/luci/partition/src/Nodes/CircleL2Pool2D.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleL2Pool2D.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLeakyRelu.cpp b/compiler/luci/partition/src/Nodes/CircleLeakyRelu.cpp index 3da1ba2..1702dde 100644 --- a/compiler/luci/partition/src/Nodes/CircleLeakyRelu.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLeakyRelu.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLeakyRelu.test.cpp b/compiler/luci/partition/src/Nodes/CircleLeakyRelu.test.cpp index 5a0d1dd..71dc55e 100644 --- a/compiler/luci/partition/src/Nodes/CircleLeakyRelu.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLeakyRelu.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLess.cpp b/compiler/luci/partition/src/Nodes/CircleLess.cpp index aab495f..52726f9 100644 --- a/compiler/luci/partition/src/Nodes/CircleLess.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLess.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLess.test.cpp b/compiler/luci/partition/src/Nodes/CircleLess.test.cpp index ab65e5d..c5d194e 100644 --- a/compiler/luci/partition/src/Nodes/CircleLess.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLess.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLessEqual.cpp b/compiler/luci/partition/src/Nodes/CircleLessEqual.cpp index ec129db..e9a3c41 100644 --- a/compiler/luci/partition/src/Nodes/CircleLessEqual.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLessEqual.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLessEqual.test.cpp b/compiler/luci/partition/src/Nodes/CircleLessEqual.test.cpp index 0dd8986..29f4aba 100644 --- a/compiler/luci/partition/src/Nodes/CircleLessEqual.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLessEqual.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.cpp b/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.cpp index 6b0d1cd..7a00bf9 100644 --- a/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.test.cpp b/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.test.cpp index e197338..5e57238 100644 --- a/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLocalResponseNormalization.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLog.cpp b/compiler/luci/partition/src/Nodes/CircleLog.cpp index c43570f..676d22f 100644 --- a/compiler/luci/partition/src/Nodes/CircleLog.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLog.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLog.test.cpp b/compiler/luci/partition/src/Nodes/CircleLog.test.cpp index 8a43f6f..0a2b975 100644 --- a/compiler/luci/partition/src/Nodes/CircleLog.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLog.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLogSoftmax.cpp b/compiler/luci/partition/src/Nodes/CircleLogSoftmax.cpp index de582c8..c67b08f 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogSoftmax.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogSoftmax.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLogSoftmax.test.cpp b/compiler/luci/partition/src/Nodes/CircleLogSoftmax.test.cpp index 1e60bf5..b6daeb7 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogSoftmax.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogSoftmax.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalAnd.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalAnd.cpp index 28e8f42..1498d85 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalAnd.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalAnd.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalAnd.test.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalAnd.test.cpp index a1189f0..0b95136 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalAnd.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalAnd.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalNot.cpp index e265782..f9c077e 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalNot.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalNot.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalNot.test.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalNot.test.cpp index f6b3459..88dff36 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalNot.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalNot.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalOr.cpp index 418dc02..59592e4 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalOr.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalOr.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLogicalOr.test.cpp b/compiler/luci/partition/src/Nodes/CircleLogicalOr.test.cpp index fee3f47..35f8029 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogicalOr.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogicalOr.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleLogistic.cpp b/compiler/luci/partition/src/Nodes/CircleLogistic.cpp index 7d78851..804597b 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogistic.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogistic.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleLogistic.test.cpp b/compiler/luci/partition/src/Nodes/CircleLogistic.test.cpp index c4b3f7f..241d840 100644 --- a/compiler/luci/partition/src/Nodes/CircleLogistic.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleLogistic.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMatrixDiag.cpp b/compiler/luci/partition/src/Nodes/CircleMatrixDiag.cpp index e92806a..297e9f2 100644 --- a/compiler/luci/partition/src/Nodes/CircleMatrixDiag.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMatrixDiag.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMatrixDiag.test.cpp b/compiler/luci/partition/src/Nodes/CircleMatrixDiag.test.cpp index 03e3c3c..472cab8 100644 --- a/compiler/luci/partition/src/Nodes/CircleMatrixDiag.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMatrixDiag.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.cpp b/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.cpp index 29bb7fe..b327aac 100644 --- a/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.test.cpp b/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.test.cpp index 5503ea1..4ff797c 100644 --- a/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMatrixSetDiag.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/partition/src/Nodes/CircleMaxPool2D.cpp index 75a665a..dee90e5 100644 --- a/compiler/luci/partition/src/Nodes/CircleMaxPool2D.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMaxPool2D.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMaxPool2D.test.cpp b/compiler/luci/partition/src/Nodes/CircleMaxPool2D.test.cpp index 1699649..949e0d7 100644 --- a/compiler/luci/partition/src/Nodes/CircleMaxPool2D.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMaxPool2D.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMaximum.cpp b/compiler/luci/partition/src/Nodes/CircleMaximum.cpp index 2ba6055..459917e 100644 --- a/compiler/luci/partition/src/Nodes/CircleMaximum.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMaximum.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMaximum.test.cpp b/compiler/luci/partition/src/Nodes/CircleMaximum.test.cpp index 370174c..e6a6d57 100644 --- a/compiler/luci/partition/src/Nodes/CircleMaximum.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMaximum.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMean.cpp b/compiler/luci/partition/src/Nodes/CircleMean.cpp index b634e58..c704d00 100644 --- a/compiler/luci/partition/src/Nodes/CircleMean.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMean.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMean.test.cpp b/compiler/luci/partition/src/Nodes/CircleMean.test.cpp index 53435d9..838d7ae 100644 --- a/compiler/luci/partition/src/Nodes/CircleMean.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMean.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMinimum.cpp b/compiler/luci/partition/src/Nodes/CircleMinimum.cpp index cdf7575..8958bf6 100644 --- a/compiler/luci/partition/src/Nodes/CircleMinimum.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMinimum.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMinimum.test.cpp b/compiler/luci/partition/src/Nodes/CircleMinimum.test.cpp index 2fe6b0d..a6c86a2 100644 --- a/compiler/luci/partition/src/Nodes/CircleMinimum.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMinimum.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMirrorPad.cpp b/compiler/luci/partition/src/Nodes/CircleMirrorPad.cpp index 16a24ab..91c3cb9 100644 --- a/compiler/luci/partition/src/Nodes/CircleMirrorPad.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMirrorPad.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMirrorPad.test.cpp b/compiler/luci/partition/src/Nodes/CircleMirrorPad.test.cpp index 605a126..b837e10 100644 --- a/compiler/luci/partition/src/Nodes/CircleMirrorPad.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMirrorPad.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleMul.cpp b/compiler/luci/partition/src/Nodes/CircleMul.cpp index 2cd2b40..12e1472 100644 --- a/compiler/luci/partition/src/Nodes/CircleMul.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMul.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleMul.test.cpp b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp index 99cf082..b316679 100644 --- a/compiler/luci/partition/src/Nodes/CircleMul.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNeg.cpp b/compiler/luci/partition/src/Nodes/CircleNeg.cpp index 413ad49..e9dcc45 100644 --- a/compiler/luci/partition/src/Nodes/CircleNeg.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNeg.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNeg.test.cpp b/compiler/luci/partition/src/Nodes/CircleNeg.test.cpp index bd74a36..ab13c94 100644 --- a/compiler/luci/partition/src/Nodes/CircleNeg.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNeg.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.cpp index 63ff3f0..88d72e1 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.test.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.test.cpp index 2771aef..e796a14 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.cpp index 80e4704..61caa3a 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp index 5a0a8da..eb04f26 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.cpp index c1f1177..3b0b755 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.test.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.test.cpp index 1f20fbb..c9c31b3 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.cpp index 69e3cc8..3eed260 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp index e001b0b..2c5822f 100644 --- a/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleNotEqual.cpp b/compiler/luci/partition/src/Nodes/CircleNotEqual.cpp index c40c2a2..29a6a43 100644 --- a/compiler/luci/partition/src/Nodes/CircleNotEqual.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNotEqual.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleNotEqual.test.cpp b/compiler/luci/partition/src/Nodes/CircleNotEqual.test.cpp index 360940c..2983e1b 100644 --- a/compiler/luci/partition/src/Nodes/CircleNotEqual.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleNotEqual.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleOneHot.cpp b/compiler/luci/partition/src/Nodes/CircleOneHot.cpp index d76f492..d172fb8 100644 --- a/compiler/luci/partition/src/Nodes/CircleOneHot.cpp +++ b/compiler/luci/partition/src/Nodes/CircleOneHot.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleOneHot.test.cpp b/compiler/luci/partition/src/Nodes/CircleOneHot.test.cpp index 3c555c2..59780e4 100644 --- a/compiler/luci/partition/src/Nodes/CircleOneHot.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleOneHot.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleOutputDummy.cpp b/compiler/luci/partition/src/Nodes/CircleOutputDummy.cpp index a033e80..61d7620 100644 --- a/compiler/luci/partition/src/Nodes/CircleOutputDummy.cpp +++ b/compiler/luci/partition/src/Nodes/CircleOutputDummy.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace luci { diff --git a/compiler/luci/partition/src/Nodes/CircleOutputExclude.cpp b/compiler/luci/partition/src/Nodes/CircleOutputExclude.cpp index 106eb40..36ce350 100644 --- a/compiler/luci/partition/src/Nodes/CircleOutputExclude.cpp +++ b/compiler/luci/partition/src/Nodes/CircleOutputExclude.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace luci { diff --git a/compiler/luci/partition/src/Nodes/CirclePRelu.cpp b/compiler/luci/partition/src/Nodes/CirclePRelu.cpp index b8a2341..6a23257 100644 --- a/compiler/luci/partition/src/Nodes/CirclePRelu.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePRelu.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CirclePRelu.test.cpp b/compiler/luci/partition/src/Nodes/CirclePRelu.test.cpp index e5bcedc..f2a2e2c 100644 --- a/compiler/luci/partition/src/Nodes/CirclePRelu.test.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePRelu.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CirclePack.cpp b/compiler/luci/partition/src/Nodes/CirclePack.cpp index 3268810..d4b49bf 100644 --- a/compiler/luci/partition/src/Nodes/CirclePack.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePack.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CirclePack.test.cpp b/compiler/luci/partition/src/Nodes/CirclePack.test.cpp index 68c5138..665b137 100644 --- a/compiler/luci/partition/src/Nodes/CirclePack.test.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePack.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CirclePad.cpp b/compiler/luci/partition/src/Nodes/CirclePad.cpp index eb2a89c..0a1d6f7 100644 --- a/compiler/luci/partition/src/Nodes/CirclePad.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePad.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CirclePad.test.cpp b/compiler/luci/partition/src/Nodes/CirclePad.test.cpp index 24ea83f..72f97d6 100644 --- a/compiler/luci/partition/src/Nodes/CirclePad.test.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePad.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CirclePadV2.cpp b/compiler/luci/partition/src/Nodes/CirclePadV2.cpp index 001fecb..969cc27 100644 --- a/compiler/luci/partition/src/Nodes/CirclePadV2.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePadV2.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CirclePadV2.test.cpp b/compiler/luci/partition/src/Nodes/CirclePadV2.test.cpp index aea8e0c..9829f62 100644 --- a/compiler/luci/partition/src/Nodes/CirclePadV2.test.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePadV2.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CirclePow.cpp b/compiler/luci/partition/src/Nodes/CirclePow.cpp index fb180ee..ce69e74 100644 --- a/compiler/luci/partition/src/Nodes/CirclePow.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePow.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CirclePow.test.cpp b/compiler/luci/partition/src/Nodes/CirclePow.test.cpp index 7a5be4d..f4e49c0 100644 --- a/compiler/luci/partition/src/Nodes/CirclePow.test.cpp +++ b/compiler/luci/partition/src/Nodes/CirclePow.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleQuantize.cpp b/compiler/luci/partition/src/Nodes/CircleQuantize.cpp index 340c1da..903a94e 100644 --- a/compiler/luci/partition/src/Nodes/CircleQuantize.cpp +++ b/compiler/luci/partition/src/Nodes/CircleQuantize.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleQuantize.test.cpp b/compiler/luci/partition/src/Nodes/CircleQuantize.test.cpp index 1f348b4..5ca1a6b 100644 --- a/compiler/luci/partition/src/Nodes/CircleQuantize.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleQuantize.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRange.cpp b/compiler/luci/partition/src/Nodes/CircleRange.cpp index f295338..fa1a02c 100644 --- a/compiler/luci/partition/src/Nodes/CircleRange.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRange.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRange.test.cpp b/compiler/luci/partition/src/Nodes/CircleRange.test.cpp index 59a95f1..b5b0c8a 100644 --- a/compiler/luci/partition/src/Nodes/CircleRange.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRange.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRank.cpp b/compiler/luci/partition/src/Nodes/CircleRank.cpp index f7cce76..35b4764 100644 --- a/compiler/luci/partition/src/Nodes/CircleRank.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRank.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRank.test.cpp b/compiler/luci/partition/src/Nodes/CircleRank.test.cpp index 74c520b..5a0a71a 100644 --- a/compiler/luci/partition/src/Nodes/CircleRank.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRank.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReduceAny.cpp b/compiler/luci/partition/src/Nodes/CircleReduceAny.cpp index ed762db..262e12a 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceAny.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceAny.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReduceAny.test.cpp b/compiler/luci/partition/src/Nodes/CircleReduceAny.test.cpp index 792f511..45c2920 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceAny.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceAny.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReduceMax.cpp b/compiler/luci/partition/src/Nodes/CircleReduceMax.cpp index 09586ec..d91c78e 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceMax.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceMax.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReduceMax.test.cpp b/compiler/luci/partition/src/Nodes/CircleReduceMax.test.cpp index 8fbaf65..2ad18f3 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceMax.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceMax.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReduceMin.cpp b/compiler/luci/partition/src/Nodes/CircleReduceMin.cpp index 105214d..65fca6a 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceMin.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceMin.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReduceMin.test.cpp b/compiler/luci/partition/src/Nodes/CircleReduceMin.test.cpp index c37d624..db48f54 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceMin.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceMin.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReduceProd.cpp b/compiler/luci/partition/src/Nodes/CircleReduceProd.cpp index 2fb4e3e..daac168 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceProd.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceProd.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReduceProd.test.cpp b/compiler/luci/partition/src/Nodes/CircleReduceProd.test.cpp index cc1ac83..f5f69f0 100644 --- a/compiler/luci/partition/src/Nodes/CircleReduceProd.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReduceProd.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRelu.cpp b/compiler/luci/partition/src/Nodes/CircleRelu.cpp index d3617bd..63ac31b 100644 --- a/compiler/luci/partition/src/Nodes/CircleRelu.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRelu.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRelu.test.cpp b/compiler/luci/partition/src/Nodes/CircleRelu.test.cpp index ccaf576..ec4d10f 100644 --- a/compiler/luci/partition/src/Nodes/CircleRelu.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRelu.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRelu6.cpp b/compiler/luci/partition/src/Nodes/CircleRelu6.cpp index fb9ba6f..c2956c4 100644 --- a/compiler/luci/partition/src/Nodes/CircleRelu6.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRelu6.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRelu6.test.cpp b/compiler/luci/partition/src/Nodes/CircleRelu6.test.cpp index 1341b0e..e9ecbe2 100644 --- a/compiler/luci/partition/src/Nodes/CircleRelu6.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRelu6.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReluN1To1.cpp b/compiler/luci/partition/src/Nodes/CircleReluN1To1.cpp index 476195b..1141297 100644 --- a/compiler/luci/partition/src/Nodes/CircleReluN1To1.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReluN1To1.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReluN1To1.test.cpp b/compiler/luci/partition/src/Nodes/CircleReluN1To1.test.cpp index 7dc63c6..ae60a97 100644 --- a/compiler/luci/partition/src/Nodes/CircleReluN1To1.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReluN1To1.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReshape.cpp b/compiler/luci/partition/src/Nodes/CircleReshape.cpp index e596704..49f7c64 100644 --- a/compiler/luci/partition/src/Nodes/CircleReshape.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReshape.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReshape.test.cpp b/compiler/luci/partition/src/Nodes/CircleReshape.test.cpp index 73cbbdf..198cfa1 100644 --- a/compiler/luci/partition/src/Nodes/CircleReshape.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReshape.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleResizeBilinear.cpp b/compiler/luci/partition/src/Nodes/CircleResizeBilinear.cpp index 0f50401..41fdedf 100644 --- a/compiler/luci/partition/src/Nodes/CircleResizeBilinear.cpp +++ b/compiler/luci/partition/src/Nodes/CircleResizeBilinear.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleResizeBilinear.test.cpp b/compiler/luci/partition/src/Nodes/CircleResizeBilinear.test.cpp index c2d8b71..437e448 100644 --- a/compiler/luci/partition/src/Nodes/CircleResizeBilinear.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleResizeBilinear.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.cpp b/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.cpp index c985b7f..567db49 100644 --- a/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.cpp +++ b/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.test.cpp b/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.test.cpp index 9cc2e55..5dc99a3 100644 --- a/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleResizeNearestNeighbor.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReverseSequence.cpp b/compiler/luci/partition/src/Nodes/CircleReverseSequence.cpp index 225d29e..348cdbb 100644 --- a/compiler/luci/partition/src/Nodes/CircleReverseSequence.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReverseSequence.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReverseSequence.test.cpp b/compiler/luci/partition/src/Nodes/CircleReverseSequence.test.cpp index 408fc0c..7519103 100644 --- a/compiler/luci/partition/src/Nodes/CircleReverseSequence.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReverseSequence.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleReverseV2.cpp b/compiler/luci/partition/src/Nodes/CircleReverseV2.cpp index d59a7de..4b8c4a4 100644 --- a/compiler/luci/partition/src/Nodes/CircleReverseV2.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReverseV2.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleReverseV2.test.cpp b/compiler/luci/partition/src/Nodes/CircleReverseV2.test.cpp index d41ad8e..351c6f2 100644 --- a/compiler/luci/partition/src/Nodes/CircleReverseV2.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleReverseV2.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRound.cpp b/compiler/luci/partition/src/Nodes/CircleRound.cpp index 9170bcd..97d0028 100644 --- a/compiler/luci/partition/src/Nodes/CircleRound.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRound.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRound.test.cpp b/compiler/luci/partition/src/Nodes/CircleRound.test.cpp index fad0904..02f335d 100644 --- a/compiler/luci/partition/src/Nodes/CircleRound.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRound.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp index 03e64aa..44abd5e 100644 --- a/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleRsqrt.test.cpp b/compiler/luci/partition/src/Nodes/CircleRsqrt.test.cpp index d76b96e..39ae1f8 100644 --- a/compiler/luci/partition/src/Nodes/CircleRsqrt.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleRsqrt.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSVDF.cpp b/compiler/luci/partition/src/Nodes/CircleSVDF.cpp index f661a79..e2b99c4 100644 --- a/compiler/luci/partition/src/Nodes/CircleSVDF.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSVDF.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSVDF.test.cpp b/compiler/luci/partition/src/Nodes/CircleSVDF.test.cpp index 5fae520..af8cd55 100644 --- a/compiler/luci/partition/src/Nodes/CircleSVDF.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSVDF.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleScatterNd.cpp b/compiler/luci/partition/src/Nodes/CircleScatterNd.cpp index 62912b7..88a3ecf 100644 --- a/compiler/luci/partition/src/Nodes/CircleScatterNd.cpp +++ b/compiler/luci/partition/src/Nodes/CircleScatterNd.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleScatterNd.test.cpp b/compiler/luci/partition/src/Nodes/CircleScatterNd.test.cpp index f271f88..4ce7875 100644 --- a/compiler/luci/partition/src/Nodes/CircleScatterNd.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleScatterNd.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSegmentSum.cpp b/compiler/luci/partition/src/Nodes/CircleSegmentSum.cpp index 5fc320a..6540416 100644 --- a/compiler/luci/partition/src/Nodes/CircleSegmentSum.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSegmentSum.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSegmentSum.test.cpp b/compiler/luci/partition/src/Nodes/CircleSegmentSum.test.cpp index a6bcff2..453b7cc 100644 --- a/compiler/luci/partition/src/Nodes/CircleSegmentSum.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSegmentSum.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSelect.cpp b/compiler/luci/partition/src/Nodes/CircleSelect.cpp index dbe1dd4..436e956 100644 --- a/compiler/luci/partition/src/Nodes/CircleSelect.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSelect.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSelect.test.cpp b/compiler/luci/partition/src/Nodes/CircleSelect.test.cpp index 912934b..2a38de5 100644 --- a/compiler/luci/partition/src/Nodes/CircleSelect.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSelect.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSelectV2.cpp b/compiler/luci/partition/src/Nodes/CircleSelectV2.cpp index 28072c8..a8b6ab5 100644 --- a/compiler/luci/partition/src/Nodes/CircleSelectV2.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSelectV2.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSelectV2.test.cpp b/compiler/luci/partition/src/Nodes/CircleSelectV2.test.cpp index e8d128e..c2ebdbe 100644 --- a/compiler/luci/partition/src/Nodes/CircleSelectV2.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSelectV2.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleShape.cpp b/compiler/luci/partition/src/Nodes/CircleShape.cpp index f93cf14..2fb3dcd 100644 --- a/compiler/luci/partition/src/Nodes/CircleShape.cpp +++ b/compiler/luci/partition/src/Nodes/CircleShape.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleShape.test.cpp b/compiler/luci/partition/src/Nodes/CircleShape.test.cpp index 9b4afdc..38033a3 100644 --- a/compiler/luci/partition/src/Nodes/CircleShape.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleShape.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSin.cpp b/compiler/luci/partition/src/Nodes/CircleSin.cpp index 62c776e..0ef6059 100644 --- a/compiler/luci/partition/src/Nodes/CircleSin.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSin.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSin.test.cpp b/compiler/luci/partition/src/Nodes/CircleSin.test.cpp index fbee6f6..e141b45 100644 --- a/compiler/luci/partition/src/Nodes/CircleSin.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSin.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSlice.cpp b/compiler/luci/partition/src/Nodes/CircleSlice.cpp index 7895d9e..811d81f 100644 --- a/compiler/luci/partition/src/Nodes/CircleSlice.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSlice.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSlice.test.cpp b/compiler/luci/partition/src/Nodes/CircleSlice.test.cpp index 3c666ad..0718c7f 100644 --- a/compiler/luci/partition/src/Nodes/CircleSlice.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSlice.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSoftmax.cpp b/compiler/luci/partition/src/Nodes/CircleSoftmax.cpp index 0a93787..6b08f00 100644 --- a/compiler/luci/partition/src/Nodes/CircleSoftmax.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSoftmax.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSoftmax.test.cpp b/compiler/luci/partition/src/Nodes/CircleSoftmax.test.cpp index b256298..571ad80 100644 --- a/compiler/luci/partition/src/Nodes/CircleSoftmax.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSoftmax.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.cpp b/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.cpp index b94948b..dc48b36 100644 --- a/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.test.cpp b/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.test.cpp index 279e9b2..0fcf22f 100644 --- a/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSpaceToBatchND.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.cpp b/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.cpp index bd4523c..55d562f 100644 --- a/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.test.cpp b/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.test.cpp index 207163d..771c1f3 100644 --- a/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSpaceToDepth.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSparseToDense.cpp b/compiler/luci/partition/src/Nodes/CircleSparseToDense.cpp index d1ed188..cc2f5e9 100644 --- a/compiler/luci/partition/src/Nodes/CircleSparseToDense.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSparseToDense.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSparseToDense.test.cpp b/compiler/luci/partition/src/Nodes/CircleSparseToDense.test.cpp index 2257186..06b3814 100644 --- a/compiler/luci/partition/src/Nodes/CircleSparseToDense.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSparseToDense.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSplit.cpp b/compiler/luci/partition/src/Nodes/CircleSplit.cpp index d6d62a8..5f851f0 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplit.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplit.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSplit.test.cpp b/compiler/luci/partition/src/Nodes/CircleSplit.test.cpp index d8d0953..a4242b9 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplit.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplit.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSplitOut.cpp b/compiler/luci/partition/src/Nodes/CircleSplitOut.cpp index 4021f20..1a44758 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSplitOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleSplitOut.test.cpp index 85fe268..b7cf6fc 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSplitV.cpp b/compiler/luci/partition/src/Nodes/CircleSplitV.cpp index f132057..43ebe07 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitV.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitV.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSplitV.test.cpp b/compiler/luci/partition/src/Nodes/CircleSplitV.test.cpp index 3ac1d6c..877a447 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitV.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitV.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSplitVOut.cpp b/compiler/luci/partition/src/Nodes/CircleSplitVOut.cpp index 2034805..4bac6c5 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitVOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitVOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSplitVOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleSplitVOut.test.cpp index 434dfb0..b3cf4d9 100644 --- a/compiler/luci/partition/src/Nodes/CircleSplitVOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSplitVOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSqrt.cpp b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp index f737aac..fd6d0ec 100644 --- a/compiler/luci/partition/src/Nodes/CircleSqrt.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSqrt.test.cpp b/compiler/luci/partition/src/Nodes/CircleSqrt.test.cpp index fa7f7fe..be29883 100644 --- a/compiler/luci/partition/src/Nodes/CircleSqrt.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSqrt.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSquare.cpp b/compiler/luci/partition/src/Nodes/CircleSquare.cpp index 1476a86..56dd544 100644 --- a/compiler/luci/partition/src/Nodes/CircleSquare.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSquare.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSquare.test.cpp b/compiler/luci/partition/src/Nodes/CircleSquare.test.cpp index bb6a7c3..a509b31 100644 --- a/compiler/luci/partition/src/Nodes/CircleSquare.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSquare.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp index 40dd317..e47be2c 100644 --- a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.test.cpp b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.test.cpp index 9cfe9ee..a900f1d 100644 --- a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSqueeze.cpp b/compiler/luci/partition/src/Nodes/CircleSqueeze.cpp index bc9fda2..ffe3c91 100644 --- a/compiler/luci/partition/src/Nodes/CircleSqueeze.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSqueeze.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSqueeze.test.cpp b/compiler/luci/partition/src/Nodes/CircleSqueeze.test.cpp index 1f09710..7a6e2bf 100644 --- a/compiler/luci/partition/src/Nodes/CircleSqueeze.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSqueeze.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleStridedSlice.cpp b/compiler/luci/partition/src/Nodes/CircleStridedSlice.cpp index 3bdca8a..953b451 100644 --- a/compiler/luci/partition/src/Nodes/CircleStridedSlice.cpp +++ b/compiler/luci/partition/src/Nodes/CircleStridedSlice.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleStridedSlice.test.cpp b/compiler/luci/partition/src/Nodes/CircleStridedSlice.test.cpp index 130ff91..3e950fd 100644 --- a/compiler/luci/partition/src/Nodes/CircleStridedSlice.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleStridedSlice.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSub.cpp b/compiler/luci/partition/src/Nodes/CircleSub.cpp index 8ac294b..c5bea08 100644 --- a/compiler/luci/partition/src/Nodes/CircleSub.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSub.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSub.test.cpp b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp index 7c0d837..ca51865 100644 --- a/compiler/luci/partition/src/Nodes/CircleSub.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleSum.cpp b/compiler/luci/partition/src/Nodes/CircleSum.cpp index bef1d46..e929fd0 100644 --- a/compiler/luci/partition/src/Nodes/CircleSum.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSum.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleSum.test.cpp b/compiler/luci/partition/src/Nodes/CircleSum.test.cpp index 1ed65c0..21f6bbb 100644 --- a/compiler/luci/partition/src/Nodes/CircleSum.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleSum.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTanh.cpp b/compiler/luci/partition/src/Nodes/CircleTanh.cpp index e6c56eb..ef5c2c9 100644 --- a/compiler/luci/partition/src/Nodes/CircleTanh.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTanh.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTanh.test.cpp b/compiler/luci/partition/src/Nodes/CircleTanh.test.cpp index 17cd487..1e2d062 100644 --- a/compiler/luci/partition/src/Nodes/CircleTanh.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTanh.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTile.cpp b/compiler/luci/partition/src/Nodes/CircleTile.cpp index 0381b4d..0c21743 100644 --- a/compiler/luci/partition/src/Nodes/CircleTile.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTile.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTile.test.cpp b/compiler/luci/partition/src/Nodes/CircleTile.test.cpp index 79d1ba1..9449c1f 100644 --- a/compiler/luci/partition/src/Nodes/CircleTile.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTile.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTopKV2.cpp b/compiler/luci/partition/src/Nodes/CircleTopKV2.cpp index ce8a6f5..41dfa9c 100644 --- a/compiler/luci/partition/src/Nodes/CircleTopKV2.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTopKV2.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTopKV2.test.cpp b/compiler/luci/partition/src/Nodes/CircleTopKV2.test.cpp index f08f3f3..e0c4a3a 100644 --- a/compiler/luci/partition/src/Nodes/CircleTopKV2.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTopKV2.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTopKV2Out.cpp b/compiler/luci/partition/src/Nodes/CircleTopKV2Out.cpp index 6ca6e3d..19f0fa7 100644 --- a/compiler/luci/partition/src/Nodes/CircleTopKV2Out.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTopKV2Out.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTopKV2Out.test.cpp b/compiler/luci/partition/src/Nodes/CircleTopKV2Out.test.cpp index a5c1c43..ba085f6 100644 --- a/compiler/luci/partition/src/Nodes/CircleTopKV2Out.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTopKV2Out.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTranspose.cpp b/compiler/luci/partition/src/Nodes/CircleTranspose.cpp index 1cbb546..cbbdb00 100644 --- a/compiler/luci/partition/src/Nodes/CircleTranspose.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTranspose.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTranspose.test.cpp b/compiler/luci/partition/src/Nodes/CircleTranspose.test.cpp index b3b1630..8476838 100644 --- a/compiler/luci/partition/src/Nodes/CircleTranspose.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTranspose.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/partition/src/Nodes/CircleTransposeConv.cpp index 469cc9a..6b6819d 100644 --- a/compiler/luci/partition/src/Nodes/CircleTransposeConv.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTransposeConv.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp index ee9fb0e..68adaad 100644 --- a/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp b/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp index 3f0374a..3323014 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp b/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp index aeefef0..2630461 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleUnique.cpp b/compiler/luci/partition/src/Nodes/CircleUnique.cpp index 79ca594..c035b7e 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnique.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnique.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleUnique.test.cpp b/compiler/luci/partition/src/Nodes/CircleUnique.test.cpp index 23f2998..910087a 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnique.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnique.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleUniqueOut.cpp b/compiler/luci/partition/src/Nodes/CircleUniqueOut.cpp index f244dd6..23b1aba 100644 --- a/compiler/luci/partition/src/Nodes/CircleUniqueOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUniqueOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleUniqueOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleUniqueOut.test.cpp index 8876407..9549574 100644 --- a/compiler/luci/partition/src/Nodes/CircleUniqueOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUniqueOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleUnpack.cpp b/compiler/luci/partition/src/Nodes/CircleUnpack.cpp index f83c5d8..43ebcb4 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnpack.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnpack.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleUnpack.test.cpp b/compiler/luci/partition/src/Nodes/CircleUnpack.test.cpp index b164cc3..444b043 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnpack.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnpack.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleUnpackOut.cpp b/compiler/luci/partition/src/Nodes/CircleUnpackOut.cpp index b8982ff..ee1de15 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnpackOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnpackOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleUnpackOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleUnpackOut.test.cpp index 9ed4409..2aaef8d 100644 --- a/compiler/luci/partition/src/Nodes/CircleUnpackOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleUnpackOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleVariable.cpp b/compiler/luci/partition/src/Nodes/CircleVariable.cpp index f7f6f21..e7a794a 100644 --- a/compiler/luci/partition/src/Nodes/CircleVariable.cpp +++ b/compiler/luci/partition/src/Nodes/CircleVariable.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace luci { diff --git a/compiler/luci/partition/src/Nodes/CircleWhere.cpp b/compiler/luci/partition/src/Nodes/CircleWhere.cpp index 8ef2742..d0fc846 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhere.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhere.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleWhere.test.cpp b/compiler/luci/partition/src/Nodes/CircleWhere.test.cpp index 942f804..f17131c 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhere.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhere.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleWhile.cpp b/compiler/luci/partition/src/Nodes/CircleWhile.cpp index 7820aca..95b77f7 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhile.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhile.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleWhile.test.cpp b/compiler/luci/partition/src/Nodes/CircleWhile.test.cpp index bffb786..6ee7aba 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhile.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhile.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleWhileOut.cpp b/compiler/luci/partition/src/Nodes/CircleWhileOut.cpp index 1cb4419..5cd6835 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhileOut.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhileOut.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleWhileOut.test.cpp b/compiler/luci/partition/src/Nodes/CircleWhileOut.test.cpp index 901f31b..f58eba0 100644 --- a/compiler/luci/partition/src/Nodes/CircleWhileOut.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleWhileOut.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/Nodes/CircleZerosLike.cpp b/compiler/luci/partition/src/Nodes/CircleZerosLike.cpp index 715042d..795d88d 100644 --- a/compiler/luci/partition/src/Nodes/CircleZerosLike.cpp +++ b/compiler/luci/partition/src/Nodes/CircleZerosLike.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" namespace { diff --git a/compiler/luci/partition/src/Nodes/CircleZerosLike.test.cpp b/compiler/luci/partition/src/Nodes/CircleZerosLike.test.cpp index 74c873c..f887bc3 100644 --- a/compiler/luci/partition/src/Nodes/CircleZerosLike.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleZerosLike.test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "ConnectNode.test.h" diff --git a/compiler/luci/partition/src/PartitionIR.cpp b/compiler/luci/partition/src/PartitionIR.cpp index 60dc74f..969fa70 100644 --- a/compiler/luci/partition/src/PartitionIR.cpp +++ b/compiler/luci/partition/src/PartitionIR.cpp @@ -64,7 +64,7 @@ std::unique_ptr PGroups::make_copy(void) const // note: d_pgroup is now nullptr as it's moved } - return std::move(d_pgroups); + return d_pgroups; } GroupKey PGroups::group_of(luci::CircleNode *node) const diff --git a/compiler/luci/partition/src/PartitionMerge.cpp b/compiler/luci/partition/src/PartitionMerge.cpp index 4c3971b..aa8a827 100644 --- a/compiler/luci/partition/src/PartitionMerge.cpp +++ b/compiler/luci/partition/src/PartitionMerge.cpp @@ -255,7 +255,7 @@ std::unique_ptr merge_pgroups(const luci::PGroups *s_pgroups) } } while (changed); - return std::move(d_pgroups); + return d_pgroups; } } // namespace luci diff --git a/compiler/luci/partition/src/PartitionPGroups.cpp b/compiler/luci/partition/src/PartitionPGroups.cpp index eaeacf9..2e95f08 100644 --- a/compiler/luci/partition/src/PartitionPGroups.cpp +++ b/compiler/luci/partition/src/PartitionPGroups.cpp @@ -257,7 +257,7 @@ std::unique_ptr produce_pgroups(const luci::Module *source, } } - return std::move(pgroups); + return pgroups; } } // namespace luci diff --git a/compiler/luci/partition/src/PartitionPModules.cpp b/compiler/luci/partition/src/PartitionPModules.cpp index beaaf60..251dbea 100644 --- a/compiler/luci/partition/src/PartitionPModules.cpp +++ b/compiler/luci/partition/src/PartitionPModules.cpp @@ -15,7 +15,7 @@ */ #include "PartitionPModules.h" -#include "ConnectNode.h" +#include "luci/ConnectNode.h" #include "luci/Service/CircleNodeClone.h" #include "luci/Log.h" @@ -156,7 +156,7 @@ std::unique_ptr clone_graph(loco::Graph *graph_org, luci::CloneCont add_graph_output(graph_clone, output_clone); } - return std::move(graph); + return graph; } void clone_recursive_subgraphs(luci::PartedModule &pm, loco::Graph *graph, diff --git a/compiler/luci/pass/CMakeLists.txt b/compiler/luci/pass/CMakeLists.txt index 5237c6d..d9d004d 100644 --- a/compiler/luci/pass/CMakeLists.txt +++ b/compiler/luci/pass/CMakeLists.txt @@ -1,9 +1,16 @@ nnas_find_package(FlatBuffers EXACT 2.0 QUIET) +nnas_find_package(Fp16Source QUIET) + if(NOT FlatBuffers_FOUND) message(STATUS "FlatBuffers NOT FOUND") return() endif(NOT FlatBuffers_FOUND) +if(NOT Fp16Source_FOUND) + message(STATUS "Fp16Source NOT FOUND") + return() +endif(NOT Fp16Source_FOUND) + file(GLOB_RECURSE SOURCES "src/*.cpp") file(GLOB_RECURSE TESTS "src/*.test.cpp") list(REMOVE_ITEM SOURCES ${TESTS}) @@ -14,6 +21,7 @@ endif(NOT LUCI_LIBRARY_TYPE) add_library(luci_pass ${LUCI_LIBRARY_TYPE} ${SOURCES}) target_include_directories(luci_pass PRIVATE src) +target_include_directories(luci_pass PRIVATE ${Fp16Source_DIR}/include) target_include_directories(luci_pass PUBLIC include) target_link_libraries(luci_pass PUBLIC loco) target_link_libraries(luci_pass PUBLIC logo_core) diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h index c803898..b94822c 100644 --- a/compiler/luci/pass/include/luci/CircleOptimizer.h +++ b/compiler/luci/pass/include/luci/CircleOptimizer.h @@ -47,8 +47,10 @@ public: ResolveCustomOpBatchMatMul, ResolveCustomOpMatMul, ResolveCustomOpMaxPoolWithArgmax, + ResolveCustomOpSplitV, FoldAddV2, FoldCast, + FoldDensify, FoldDepthwiseConv2D, FoldDequantize, FoldGather, @@ -61,6 +63,7 @@ public: ShuffleWeightTo16x1Float32, RemoveRedundantTranspose, ReplaceMulAddWithDepthwiseConv, + ReplaceNonConstFCWithBatchMatMul, ReplaceSubWithAdd, SubstitutePackToReshape, SubstitutePadV2ToPad, diff --git a/compiler/luci/pass/include/luci/Pass/FoldDensifyPass.h b/compiler/luci/pass/include/luci/Pass/FoldDensifyPass.h new file mode 100644 index 0000000..8ec81b1 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FoldDensifyPass.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_FOLD_DENSIFY_PASS_H__ +#define __LUCI_FOLD_DENSIFY_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to Fold Densify if input is Sparse Constant + * + */ +struct FoldDensifyPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FoldDensifyPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FOLD_DENSIFY_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveRedundantDequantizePass.h b/compiler/luci/pass/include/luci/Pass/RemoveRedundantDequantizePass.h new file mode 100644 index 0000000..2deb752 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveRedundantDequantizePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_REMOVE_REDUNDANT_DEQUANTIZE_PASS_H__ +#define __LUCI_REMOVE_REDUNDANT_DEQUANTIZE_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to remove redundant dequantize operations + */ +struct RemoveRedundantDequantizePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveRedundantDequantizePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_REDUNDANT_DEQUANTIZE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapeNetPass.h b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapeNetPass.h new file mode 100644 index 0000000..19948a3 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapeNetPass.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_REMOVE_UNNECESSARY_RESHAPE_NET_PASS_H__ +#define __LUCI_REMOVE_UNNECESSARY_RESHAPE_NET_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to remove unnecessary Reshape nodes. + * @details This class will remove unnecessary pre/post-Reshape nodes. + * See https://github.com/Samsung/ONE/issues/9600 for more details. + */ +struct RemoveUnnecessaryReshapeNetPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveUnnecessaryReshapeNetPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_UNNECESSARY_RESHAPE_NET_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h b/compiler/luci/pass/include/luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h new file mode 100644 index 0000000..24e16ec --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__ +#define __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to replace "FC with non-const weight" with Batched MatMul + */ +struct ReplaceNonConstFCWithBatchMatMulPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::ReplaceNonConstFCWithBatchMatMulPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/ResolveCustomOpSplitVPass.h b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpSplitVPass.h new file mode 100644 index 0000000..d4f0147 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpSplitVPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __LUCI_RESOLVE_CUSTOM_OP_SPLIT_V_PASS_H__ +#define __LUCI_RESOLVE_CUSTOM_OP_SPLIT_V_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to resolve certain custom op of subgraph into splitv op in circle schema. + */ +struct ResolveCustomOpSplitVPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::ResolveCustomOpSplitVPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_RESOLVE_CUSTOM_OP_SPLIT_V_PASS_H__ diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp index 6dbb22d..74c569d 100644 --- a/compiler/luci/pass/src/CircleOptimizer.cpp +++ b/compiler/luci/pass/src/CircleOptimizer.cpp @@ -20,6 +20,7 @@ #include "luci/Pass/ExpandBroadcastConstPass.h" #include "luci/Pass/FoldAddV2Pass.h" #include "luci/Pass/FoldCastPass.h" +#include "luci/Pass/FoldDensifyPass.h" #include "luci/Pass/FoldDepthwiseConv2DPass.h" #include "luci/Pass/FoldDequantizePass.h" #include "luci/Pass/FoldGatherPass.h" @@ -43,15 +44,18 @@ #include "luci/Pass/RemoveRedundantTransposePass.h" #include "luci/Pass/RemoveRedundantQuantizePass.h" #include "luci/Pass/RemoveUnnecessaryReshapePass.h" +#include "luci/Pass/RemoveUnnecessaryReshapeNetPass.h" #include "luci/Pass/RemoveUnnecessarySlicePass.h" #include "luci/Pass/RemoveUnnecessaryStridedSlicePass.h" #include "luci/Pass/RemoveUnnecessarySplitPass.h" +#include "luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h" #include "luci/Pass/ReplaceMulAddWithDepthwiseConvPass.h" #include "luci/Pass/ReplaceSubWithAddPass.h" #include "luci/Pass/ResolveCustomOpAddPass.h" #include "luci/Pass/ResolveCustomOpBatchMatMulPass.h" #include "luci/Pass/ResolveCustomOpMatMulPass.h" #include "luci/Pass/ResolveCustomOpMaxPoolWithArgmaxPass.h" +#include "luci/Pass/ResolveCustomOpSplitVPass.h" #include "luci/Pass/SparsifyTensorPass.h" #include "luci/Pass/ShuffleWeightTo16x1Float32Pass.h" #include "luci/Pass/SubstitutePackToReshapePass.h" @@ -127,7 +131,8 @@ bool OptimizeOptionsImpl::query(Algorithm algo) return true; } -void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_output) +// TODO Make a struct for args +void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_output, bool fuse_fc) { logo::Phase phase; @@ -135,6 +140,21 @@ void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_out phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); + // Resolve custom Ops + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + + // Fuse FullyConnected with Add + // Why we perform FuseAddWithFullyConnectedPass before ConvertNCHWToNHWCPass? + // FullyConnected Op's layout is not changed in ConvertNCHWToNHWCPass, while + // Add Op's layer is changed from NCHW to NHWC. + // This disables fusion of Add and FullyConnected after ConvertNCHWToNHWC. + if (fuse_fc) + phase.emplace_back(std::make_unique()); + phase.emplace_back( std::make_unique(preserve_input, preserve_output)); @@ -190,7 +210,9 @@ void CircleOptimizer::optimize(loco::Graph *g) const bool preserve_output = _options->param(Options::AlgorithmParameters::NCHW_to_NHWC_output_shape) != "true"; - convert_nchw_to_nhwc(g, preserve_input, preserve_output); + bool fuse_fc = _options->query(Options::Algorithm::FuseAddWithFullyConnected); + + convert_nchw_to_nhwc(g, preserve_input, preserve_output, fuse_fc); } /* TRANSFORM DECLARATION BEGIN */ @@ -220,6 +242,10 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } + if (_options->query(Options::Algorithm::ResolveCustomOpSplitV)) + { + phase.emplace_back(std::make_unique()); + } if (_options->query(Options::Algorithm::FuseInstanceNorm)) { phase.emplace_back(std::make_unique()); @@ -260,6 +286,10 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } + if (_options->query(Options::Algorithm::FoldDensify)) + { + phase.emplace_back(std::make_unique()); + } if (_options->query(Options::Algorithm::FoldDepthwiseConv2D)) { phase.emplace_back(std::make_unique()); @@ -307,6 +337,7 @@ void CircleOptimizer::optimize(loco::Graph *g) const if (_options->query(Options::Algorithm::RemoveUnnecessaryReshape)) { phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); } if (_options->query(Options::Algorithm::RemoveUnnecessarySlice)) { @@ -332,6 +363,10 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } + if (_options->query(Options::Algorithm::ReplaceNonConstFCWithBatchMatMul)) + { + phase.emplace_back(std::make_unique()); + } if (_options->query(Options::Algorithm::ReplaceMulAddWithDepthwiseConv)) { phase.emplace_back(std::make_unique()); diff --git a/compiler/luci/pass/src/CircleQuantizer.cpp b/compiler/luci/pass/src/CircleQuantizer.cpp index ce38a90..9a6550b 100644 --- a/compiler/luci/pass/src/CircleQuantizer.cpp +++ b/compiler/luci/pass/src/CircleQuantizer.cpp @@ -22,6 +22,7 @@ #include "luci/Pass/RequantizePass.h" #include "luci/Pass/ConvertToFakeQuantizedModelPass.h" #include "luci/Pass/FoldDequantizePass.h" +#include "luci/Pass/RemoveRedundantDequantizePass.h" #include "luci/Pass/QuantizePreCheckerPass.h" #include "luci/Pass/QuantizeWithMinMaxPass.h" #include "luci/Pass/QuantizeDequantizeWeightsPass.h" @@ -252,8 +253,8 @@ void CircleQuantizer::quantize(loco::Graph *g) const static const std::vector qwmm_supported_input_model_dtype{"float32"}; static const std::vector qwmm_supported_output_model_dtype{"uint8", "int16"}; static const std::vector qwmm_supported_granularity{"layer", "channel"}; - static const std::vector qwmm_supported_input_type{"uint8", "int16"}; - static const std::vector qwmm_supported_output_type{"uint8", "int16"}; + static const std::vector qwmm_supported_input_type{"uint8", "int16", "float32"}; + static const std::vector qwmm_supported_output_type{"uint8", "int16", "float32"}; auto input_model_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_model_dtype); @@ -434,6 +435,8 @@ void CircleQuantizer::quantize(loco::Graph *g) const phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); + // Remove redundant Dequantize Ops generated during fake quantization + phase.emplace_back(std::make_unique()); // Fold Dequantize Ops generated during fake quantization phase.emplace_back(std::make_unique()); diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp index ce4f540..55a29d1 100644 --- a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp @@ -28,6 +28,69 @@ namespace { +// Return true if from can be broadcasted to to +// to's shape is [N, C, H, W] +bool broadcastable(const luci::CircleConst *from, const luci::CircleNode *to) +{ + assert(to->rank() == 4); // FIX_CALLER_UNLESS + + const auto from_rank = from->rank(); + if (from_rank > 4) + return false; + + // Scalar is always broadcastable + if (from_rank == 0) + return true; + + for (uint32_t i = 1; i <= from_rank; i++) + { + auto to_index = 4 - i; + auto from_index = from_rank - i; + + if (from->dim(from_index).value() != to->dim(to_index).value() and + from->dim(from_index).value() != 1) + return false; + } + + return true; +} + +// Expand node to rank 4 +// node should have rank less than or equal to 4 +void expand_to_rank_4(luci::CircleConst *node) +{ + auto original_rank = node->rank(); + + assert(original_rank <= 4); // FIX_CALLER_UNLESS + + if (original_rank == 4) + return; + + std::vector original_shape; + for (uint32_t i = 0; i < original_rank; i++) + { + original_shape.emplace_back(node->dim(i).value()); + } + + node->rank(4); + for (uint32_t i = 0; i < (4 - original_rank); i++) + node->dim(i) = 1; + + for (uint32_t i = 0; i < original_rank; i++) + node->dim(i + (4 - original_rank)) = original_shape.at(i); +} + +bool is_output(const loco::Node *node) +{ + auto cnode = loco::must_cast(node); + auto opcode = cnode->opcode(); + if (opcode == luci::CircleOpcode::CIRCLEOUTPUT || + opcode == luci::CircleOpcode::CIRCLEOUTPUTEXCLUDE) + return true; + + return false; +} + bool is_same_shape(const luci::CircleNode *node, const std::vector &shape) { if (not node) @@ -484,7 +547,7 @@ bool is_NCHW_with_s_const(const T *node, luci::CircleNode *&pred_node, // // Find MUL with an NCHW pattern described below // - Input (non-constant) shape : [N, C, H, W] -// - Input (constant) shape : [1, C, 1, 1], [N, C, H, W] or a scalar (1) +// - Input (constant) shape : broadcastable to [N, C, H, W] // - Output shape : [N, C, H, W] bool is_NCHW_with_const(const luci::CircleMul *node, luci::CircleNode *&pred_node, luci::CircleConst *&multiplier) @@ -511,32 +574,12 @@ bool is_NCHW_with_const(const luci::CircleMul *node, luci::CircleNode *&pred_nod if (pred_node->rank() != 4) return false; - const auto const_rank = multiplier->rank(); - // Support Rank 4 or scalar (rank 0 or 1) - if (const_rank != 4 && const_rank != 0 && const_rank != 1) + if (not broadcastable(multiplier, node)) return false; - const auto input_cdim = pred_node->dim(1); - const auto output_cdim = node->dim(1); - - if (const_rank == 4) - { - bool supported_shape = false; - - // Check multiplier is (1, C, 1, 1) - if (is_same_shape(multiplier, {1, node->dim(1), 1, 1})) - supported_shape = true; - - // Check multiplier is (N, C, H, W) - if (is_same_shape(multiplier, {node->dim(0), node->dim(1), node->dim(2), node->dim(3)})) - supported_shape = true; + expand_to_rank_4(multiplier); - return supported_shape; - } - if (input_cdim == output_cdim) - return true; - else - return false; + return true; } // We assume ADD with const input is NCHW if, @@ -569,32 +612,12 @@ bool is_NCHW_with_const(const luci::CircleAdd *node, luci::CircleNode *&pred_nod if (pred_node->rank() != 4) return false; - const auto const_rank = beta->rank(); - // Support Rank 4 or scalar (rank 0 or 1) - if (const_rank != 4 && const_rank != 0 && const_rank != 1) + if (not broadcastable(beta, node)) return false; - const auto input_cdim = pred_node->dim(1); - const auto output_cdim = node->dim(1); - - if (const_rank == 4) - { - bool supported_shape = false; - - // Check beta is (1, C, 1, 1) - if (is_same_shape(beta, {1, node->dim(1), 1, 1})) - supported_shape = true; - - // Check beta is (N, C, H, W) - if (is_same_shape(beta, {node->dim(0), node->dim(1), node->dim(2), node->dim(3)})) - supported_shape = true; + expand_to_rank_4(beta); - return supported_shape; - } - if (input_cdim == output_cdim) - return true; - else - return false; + return true; } // We assume SUB with const input is NCHW if, @@ -675,6 +698,24 @@ template bool convert_unary_x(T *node) return true; } +template bool convert_unary_logits(T *node) +{ + const auto pred_node = loco::must_cast(node->logits()); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->logits(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; +} + class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor { // Default @@ -742,17 +783,14 @@ class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor if (is_NCHW_with_const(node, pred_node, beta)) { + assert(beta->rank() == 4); // FIX is_NCHW_with_const unless + auto nhwc_const = create_NHWC_from_NCHW(beta); + if (nhwc_const == nullptr) + return false; + node->y(nhwc_const); + auto pre_trans = create_pre_transpose(node); pre_trans->a(pred_node); - - if (beta->rank() == 4) - { - auto nhwc_const = create_NHWC_from_NCHW(beta); - if (nhwc_const == nullptr) - return false; - node->y(nhwc_const); - } - node->x(pre_trans); } else if (beta == nullptr) @@ -816,6 +854,11 @@ class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor bool visit(luci::CircleLogistic *node) { return convert_unary_x(node); } + bool visit(luci::CircleLogSoftmax *node) + { + return convert_unary_logits(node); + } + bool visit(luci::CircleMaximum *node) { luci::CircleNode *pred_node = nullptr; @@ -954,15 +997,15 @@ class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor if (is_NCHW_with_const(node, pred_node, multiplier)) { + assert(multiplier->rank() == 4); // FIX is_NCHW_with_const unless + auto nhwc_const = create_NHWC_from_NCHW(multiplier); + if (nhwc_const == nullptr) + return false; + node->y(nhwc_const); + auto pre_trans = create_pre_transpose(node); pre_trans->a(pred_node); node->x(pre_trans); - - if (multiplier->rank() == 4) - { - auto nhwc_const = create_NHWC_from_NCHW(multiplier); - node->y(nhwc_const); - } } else if (multiplier == nullptr) { @@ -1049,12 +1092,127 @@ class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor return true; } + // TODO Reduce duplicate code with CircleMean + bool visit(luci::CircleReduceMax *node) + { + auto input = loco::must_cast(node->input()); + if (input->rank() != 4) + return false; + + auto rindices = dynamic_cast(node->reduction_indices()); + if (not rindices) + return false; + + auto nhwc_rindices = create_NHWC_rindices(rindices); + if (not nhwc_rindices) + return false; + + auto pre_trans = create_pre_transpose(node); + pre_trans->a(input); + node->input(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + node->reduction_indices(nhwc_rindices); + + if (node->keep_dims()) + { + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; + } + + // The below codes handle the cases where node->keep_dims() == false + // 1D output never needs a transpose + if (node->rank() <= 1) + return true; + + std::vector reduced_dims_nhwc(4, false); + uint32_t num_reduced_indices = nhwc_rindices->size(); + + for (uint32_t ri = 0; ri < num_reduced_indices; ++ri) + { + reduced_dims_nhwc[nhwc_rindices->at(ri)] = true; + } + + // if channel dimension has been reduced, we don't need a transpose + if (reduced_dims_nhwc[3]) + return true; + + // likewise, if both space dimensions are reduced, no transpose is needed + if (reduced_dims_nhwc[1] && reduced_dims_nhwc[2]) + return true; + + std::vector post_trans_ind; + // case 1: only N is reduced + if (num_reduced_indices == 1 && reduced_dims_nhwc[0]) + post_trans_ind = {2, 0, 1}; + + // case 2: only H or W is reduced + if (num_reduced_indices == 1 && (reduced_dims_nhwc[1] || reduced_dims_nhwc[2])) + post_trans_ind = {0, 2, 1}; + + // case 3: N and either H or W are reduced + if (num_reduced_indices == 2) + post_trans_ind = {1, 0}; + + auto post_trans = create_Nd_transpose(node, post_trans_ind); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; + } + bool visit(luci::CircleRelu *node) { return convert_unary_features(node); } bool visit(luci::CircleRelu6 *node) { return convert_unary_features(node); } bool visit(luci::CircleRsqrt *node) { return convert_unary_x(node); } + bool visit(luci::CircleSoftmax *node) { return convert_unary_logits(node); } + + bool visit(luci::CircleSplitV *node) + { + // Change split dimension + auto axis = dynamic_cast(node->split_dim()); + if (not axis) + return false; + + if (axis->dtype() != loco::DataType::S32) + return false; + + if (axis->size() != 1) + return false; + + axis->at(0) = nchw_axis_to_nhwc(axis->at(0)); + + // Insert pre-transpose + const auto pred_node = loco::must_cast(node->input()); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->input(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + // Insert post-transposes + for (auto succ : loco::succs(node)) + { + auto svo = loco::must_cast(succ); + + auto post_trans = create_post_transpose(svo); + loco::replace(svo).with(post_trans); + post_trans->a(svo); + } + + return true; + } + bool visit(luci::CircleSquaredDifference *node) { // TODO support CircleConst input @@ -1195,6 +1353,8 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) // pre-Transpose --- [intermediate Ops] --- post-Transpose // | // +--[intermediate Ops] --- post-Transpose + // + // NOTE Intermediate Ops SHOULD NOT contain pre-Transpose/Reshape for (auto node : loco::postorder_traversal(loco::output_nodes(g))) { if (has_data_format(node)) @@ -1202,25 +1362,51 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) if (is_pre_transpose(node) || is_pre_reshape(node)) { + std::set intermediate; + + // Variable to check intermediate Ops contain pre-Transpose/Reshape + bool has_pre = false; + + // Variable to check the pattern is closed with post-Transpose/Reshape + bool is_closed = true; + // For recursive call of lambda - std::function set_data_format_to_succs; - set_data_format_to_succs = [&](loco::Node *n) { + std::function collect_intermediate; + collect_intermediate = [&](loco::Node *n) { for (auto succ : loco::succs(n)) { // Exit condition if (is_post_transpose(succ) || is_post_reshape(succ)) continue; - if (not has_data_format(succ)) + if (is_pre_transpose(succ) || is_pre_reshape(succ)) + { + has_pre = true; + break; + } + + if (is_output(succ)) { - set_data_format(succ, DataFormat::NHWC); + is_closed = false; + break; } - set_data_format_to_succs(succ); + intermediate.emplace(succ); + + collect_intermediate(succ); } }; - set_data_format_to_succs(node); + collect_intermediate(node); + + if (has_pre or not is_closed) + continue; + + for (auto inter : intermediate) + { + if (not has_data_format(inter)) + set_data_format(inter, DataFormat::NHWC); + } } } @@ -1248,6 +1434,7 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) case luci::CircleOpcode::ELU: case luci::CircleOpcode::LEAKY_RELU: case luci::CircleOpcode::LOGISTIC: + case luci::CircleOpcode::LOG_SOFTMAX: case luci::CircleOpcode::MAXIMUM: case luci::CircleOpcode::MEAN: case luci::CircleOpcode::MINIMUM: @@ -1255,9 +1442,12 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) case luci::CircleOpcode::NEG: case luci::CircleOpcode::PAD: case luci::CircleOpcode::PADV2: + case luci::CircleOpcode::REDUCE_MAX: case luci::CircleOpcode::RELU: case luci::CircleOpcode::RELU6: case luci::CircleOpcode::RSQRT: + case luci::CircleOpcode::SOFTMAX: + case luci::CircleOpcode::SPLIT_V: case luci::CircleOpcode::SQUARED_DIFFERENCE: case luci::CircleOpcode::SUB: if (!has_data_format(node)) @@ -1296,7 +1486,8 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) if (circle_node->rank() != 4) { // TODO replace the check above with the input rank check, and remove the condition below - if (not dynamic_cast(node)) + if (not dynamic_cast(node) and + not dynamic_cast(node)) continue; } diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp index dd81d13..6bb3d32 100644 --- a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp @@ -16,6 +16,8 @@ #include +#include + #include "luci/Pass/ConvertNCHWToNHWCPass.h" #include "luci/Pass/CircleShapeInferencePass.h" @@ -23,6 +25,8 @@ #include +using namespace luci::test; + namespace { @@ -202,6 +206,173 @@ public: luci::CircleConst *post_shape = nullptr; }; +/** + * Graph with pre-Reshape but no post-Transpose/Reshape. + * + * BEFORE + * [Input] + * | + * [Pre-Reshape] + * | + * [Relu] + * | + * [Output] + * + * AFTER + * [Input] + * | + * [Pre-Reshape] + * | + * [Pre-Transpose] + * | + * [Relu] + * | + * [Post-Transpose] + * | + * [Output] + */ +class NoPostReshapeGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + relu = g.nodes()->create(); + pre_reshape = g.nodes()->create(); + pre_shape = g.nodes()->create(); + + pre_shape->dtype(loco::DataType::S32); + + uint32_t channel_size = 16; + auto in = loco::must_cast(input); + in->shape({1, channel_size, 4, 4}); + pre_shape->shape({4}); + + pre_shape->size(4); + pre_shape->at(0) = 1; + pre_shape->at(1) = 4; + pre_shape->at(2) = 4; + pre_shape->at(3) = channel_size; + + pre_reshape->tensor(input); + pre_reshape->shape(pre_shape); + relu->features(pre_reshape); + + relu->name("Relu"); + pre_reshape->name("pre-reshape"); + + return relu; + } + +public: + luci::CircleRelu *relu = nullptr; + luci::CircleReshape *pre_reshape = nullptr; + luci::CircleConst *pre_shape = nullptr; +}; + +/** + * Graph with two pre-Reshapes + * + * BEFORE + * [Input] + * | + * [Pre-Reshape] + * | + * [Relu] + * | + * [Pre-Reshape] + * | + * [Post-Reshape] + * | + * [Output] + * + * AFTER + * [Input] + * | + * [Pre-Reshape] + * | + * [Pre-Transpose] + * | + * [Relu] + * | + * [Post-Transpose] + * | + * [Pre-Reshape] + * | + * [Post-Reshape] + * | + * [Output] + */ +class ReluNotClosedGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + relu = g.nodes()->create(); + pre_reshape = g.nodes()->create(); + pre_reshape_2 = g.nodes()->create(); + post_reshape = g.nodes()->create(); + pre_shape = g.nodes()->create(); + pre_shape_2 = g.nodes()->create(); + post_shape = g.nodes()->create(); + + pre_shape->dtype(loco::DataType::S32); + pre_shape_2->dtype(loco::DataType::S32); + post_shape->dtype(loco::DataType::S32); + + uint32_t channel_size = 16; + auto in = loco::must_cast(input); + in->shape({1, channel_size, 4, 4}); + pre_shape->shape({4}); + pre_shape_2->shape({4}); + post_shape->shape({4}); + + pre_shape->size(4); + pre_shape->at(0) = 1; + pre_shape->at(1) = 4; + pre_shape->at(2) = 4; + pre_shape->at(3) = channel_size; + + pre_shape_2->size(4); + pre_shape_2->at(0) = 1; + pre_shape_2->at(1) = 4; + pre_shape_2->at(2) = channel_size; + pre_shape_2->at(3) = 4; + + post_shape->size(4); + post_shape->at(0) = 1; + post_shape->at(1) = 4; + post_shape->at(2) = 4; + post_shape->at(3) = channel_size; + + pre_reshape->tensor(input); + pre_reshape->shape(pre_shape); + + relu->features(pre_reshape); + + pre_reshape_2->tensor(relu); + pre_reshape_2->shape(pre_shape_2); + + post_reshape->tensor(pre_reshape_2); + post_reshape->shape(post_shape); + + relu->name("Relu"); + pre_reshape->name("pre-reshape"); + pre_reshape->name("pre-reshape-2"); + post_reshape->name("post-reshape"); + + return post_reshape; + } + +public: + luci::CircleRelu *relu = nullptr; + luci::CircleReshape *pre_reshape = nullptr; + luci::CircleReshape *pre_reshape_2 = nullptr; + luci::CircleReshape *post_reshape = nullptr; + luci::CircleConst *pre_shape = nullptr; + luci::CircleConst *pre_shape_2 = nullptr; + luci::CircleConst *post_shape = nullptr; +}; + class AddScalarGraph final : public SimpleGraph { protected: @@ -312,6 +483,22 @@ public: luci::CircleLogistic *logistic = nullptr; }; +class LogSoftmaxGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + log_softmax = g.nodes()->create(); + log_softmax->logits(input); + log_softmax->name("log_softmax"); + + return log_softmax; + } + +public: + luci::CircleLogSoftmax *log_softmax = nullptr; +}; + class MaximumGraph final : public SimpleGraph { protected: @@ -642,6 +829,51 @@ public: luci::CircleConst *const_value = nullptr; }; +class ReduceMaxGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + rm = g.nodes()->create(); + rindices = g.nodes()->create(); + + rm->dtype(loco::DataType::FLOAT32); + rindices->dtype(loco::DataType::S32); + + rm->shape(_shape); + rindices->shape({static_cast(_axes.size())}); + + rindices->size(_axes.size()); + for (uint32_t i = 0; i < _axes.size(); ++i) + { + rindices->at(i) = _axes[i]; + } + + rm->input(input); + rm->reduction_indices(rindices); + rm->keep_dims(_keep_dims); + + rm->name("reduce_max"); + rindices->name("rindices"); + + return rm; + } + +public: + void keep_dims(bool val) { _keep_dims = val; } + void axes(std::vector val) { _axes = val; } + void shape(std::initializer_list val) { _shape = val; } + +public: + luci::CircleReduceMax *rm = nullptr; + luci::CircleConst *rindices = nullptr; + +private: + bool _keep_dims = true; + std::vector _axes = {2, 3}; + std::initializer_list _shape = {1, 16, 1, 1}; +}; + class ReluGraph final : public SimpleGraph { protected: @@ -690,6 +922,111 @@ public: luci::CircleRsqrt *rsqrt = nullptr; }; +class SoftmaxGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + softmax = g.nodes()->create(); + softmax->logits(input); + softmax->name("softmax"); + + return softmax; + } + +public: + luci::CircleSoftmax *softmax = nullptr; +}; + +class SplitVGraphlet +{ +public: + SplitVGraphlet() = default; + +public: + void init(loco::Graph *g) + { + // CircleCustom(SplitV) + _splitv = g->nodes()->create(); + _splitv->shape({1, 2, 2, 192}); + _splitv->dtype(loco::DataType::FLOAT32); + _splitv->name("splitv"); + + // CircleConst + auto size_splits = g->nodes()->create(); + size_splits->dtype(loco::DataType::S32); + size_splits->shape({3}); + size_splits->size(3); + size_splits->at(0) = 32; + size_splits->at(1) = 32; + size_splits->at(2) = 128; + + // CircleConst + auto split_dim = g->nodes()->create(); + split_dim->dtype(loco::DataType::S32); + split_dim->rank(0); + split_dim->size(1); + split_dim->scalar() = 3; + + _splitv->size_splits(size_splits); + _splitv->split_dim(split_dim); + _splitv->num_split(3); + + // CircleSplitVOut + _splitv_out1 = g->nodes()->create(); + _splitv_out1->shape({1, 2, 2, 32}); + _splitv_out1->dtype(loco::DataType::FLOAT32); + _splitv_out1->index(0); + _splitv_out1->input(_splitv); + _splitv_out1->name("splitv_out1"); + + // CircleSplitVOut + _splitv_out2 = g->nodes()->create(); + _splitv_out2->shape({1, 2, 2, 32}); + _splitv_out2->dtype(loco::DataType::FLOAT32); + _splitv_out2->index(1); + _splitv_out2->input(_splitv); + _splitv_out2->name("splitv_out2"); + + // CircleSplitVOut + _splitv_out3 = g->nodes()->create(); + _splitv_out3->shape({1, 2, 2, 128}); + _splitv_out3->dtype(loco::DataType::FLOAT32); + _splitv_out3->index(2); + _splitv_out3->input(_splitv); + _splitv_out3->name("splitv_out3"); + } + +public: + luci::CircleSplitV *splitv() { return _splitv; } + +protected: + luci::CircleSplitV *_splitv = nullptr; + luci::CircleSplitVOut *_splitv_out1 = nullptr; + luci::CircleSplitVOut *_splitv_out2 = nullptr; + luci::CircleSplitVOut *_splitv_out3 = nullptr; +}; + +class SplitVGraph : public TestIGraphlet, public TestOsGraphlet<3>, public SplitVGraphlet +{ +public: + SplitVGraph() = default; + + void init(void) + { + TestIGraphlet::init(g(), {1, 2, 2, 192}); + TestOsGraphlet<3>::init(g(), {{1, 2, 2, 32}, {1, 2, 2, 32}, {1, 2, 2, 128}}); + SplitVGraphlet::init(g()); + + // connect graph + _splitv->input(input()); + + output(0)->from(_splitv_out1); + output(1)->from(_splitv_out2); + output(2)->from(_splitv_out3); + } +}; + class SquaredDifferenceGraph final : public SimpleGraph { protected: @@ -929,8 +1266,11 @@ TEST(ConvertNCHWToNHWC, AddScalar) auto new_beta = dynamic_cast(g.add->y()); EXPECT_NE(nullptr, new_beta); - EXPECT_EQ(1, new_beta->rank()); + EXPECT_EQ(4, new_beta->rank()); EXPECT_EQ(1, new_beta->dim(0).value()); + EXPECT_EQ(1, new_beta->dim(1).value()); + EXPECT_EQ(1, new_beta->dim(2).value()); + EXPECT_EQ(1, new_beta->dim(3).value()); check_pre_trans(g.output->from()); } @@ -1017,6 +1357,26 @@ TEST(ConvertNCHWToNHWC, Logistic) EXPECT_EQ(16, g.logistic->dim(3).value()); } +TEST(ConvertNCHWToNHWC, LogSoftmax) +{ + LogSoftmaxGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.log_softmax->logits()); + + auto log_softmax_succs = loco::succs(g.log_softmax); + EXPECT_EQ(1, log_softmax_succs.size()); + check_post_trans(*log_softmax_succs.begin()); + + // Check log_softmax shape + EXPECT_EQ(1, g.log_softmax->dim(0).value()); + EXPECT_EQ(4, g.log_softmax->dim(1).value()); + EXPECT_EQ(4, g.log_softmax->dim(2).value()); + EXPECT_EQ(16, g.log_softmax->dim(3).value()); +} + TEST(ConvertNCHWToNHWC, Maximum) { MaximumGraph g; @@ -1265,8 +1625,11 @@ TEST(ConvertNCHWToNHWC, MulScalar) auto new_multiplier = dynamic_cast(g.mul->y()); EXPECT_NE(nullptr, new_multiplier); - EXPECT_EQ(1, new_multiplier->rank()); + EXPECT_EQ(4, new_multiplier->rank()); EXPECT_EQ(1, new_multiplier->dim(0).value()); + EXPECT_EQ(1, new_multiplier->dim(1).value()); + EXPECT_EQ(1, new_multiplier->dim(2).value()); + EXPECT_EQ(1, new_multiplier->dim(3).value()); check_pre_trans(g.output->from()); } @@ -1451,6 +1814,85 @@ TEST(ConvertNCHWToNHWC, Preserve_Input_Output) } } +TEST(ConvertNCHWToNHWC, ReduceMax) +{ + ReduceMaxGraph g; + g.init(); + + run_phase(&g.g, false, false); + + check_pre_trans(g.rm->input()); + + auto rm_succs = loco::succs(g.rm); + EXPECT_EQ(1, rm_succs.size()); + check_post_trans(*rm_succs.begin()); + + auto new_rindices = dynamic_cast(g.rm->reduction_indices()); + EXPECT_NE(nullptr, new_rindices); + EXPECT_EQ(1, new_rindices->rank()); + EXPECT_EQ(2, new_rindices->dim(0).value()); + EXPECT_EQ(2, new_rindices->size()); + EXPECT_EQ(1, new_rindices->at(0)); + EXPECT_EQ(2, new_rindices->at(1)); +} + +TEST(ConvertNCHWToNHWC, ReduceMax_keep_dims_false) +{ + struct TC + { + std::vector nchw_ind; + std::vector nhwc_ind; + std::initializer_list shape; + bool needs_transpose = false; + }; + + uint32_t n = 1; + uint32_t c = 16; + uint32_t h = 4; + uint32_t w = 4; + + std::vector test_cases{{{0}, {0}, {c, h, w}, true}, {{1}, {3}, {n, h, w}, false}, + {{2}, {1}, {n, c, w}, true}, {{3}, {2}, {n, c, h}, true}, + {{0, 1}, {0, 3}, {h, w}, false}, {{0, 2}, {0, 1}, {c, w}, true}, + {{0, 3}, {0, 2}, {c, h}, true}, {{1, 2}, {3, 1}, {n, w}, false}, + {{1, 3}, {3, 2}, {n, h}, false}, {{2, 3}, {1, 2}, {n, c}, false}, + {{0, 1, 2}, {0, 3, 1}, {w}, false}}; + + for (auto &tc : test_cases) + { + ReduceMaxGraph g; + g.keep_dims(false); + g.axes(tc.nchw_ind); + g.shape(tc.shape); + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.rm->input()); + + auto rm_succs = loco::succs(g.rm); + EXPECT_EQ(1, rm_succs.size()); + if (tc.needs_transpose) + { + EXPECT_NE(nullptr, dynamic_cast(*rm_succs.begin())); + } + else + { + EXPECT_NE(nullptr, dynamic_cast(*rm_succs.begin())); + } + + auto new_rindices = dynamic_cast(g.rm->reduction_indices()); + EXPECT_NE(nullptr, new_rindices); + EXPECT_EQ(1, new_rindices->rank()); + EXPECT_EQ(tc.nhwc_ind.size(), new_rindices->dim(0).value()); + EXPECT_EQ(tc.nhwc_ind.size(), new_rindices->size()); + for (uint32_t i = 0; i < tc.nhwc_ind.size(); ++i) + { + EXPECT_EQ(tc.nhwc_ind[i], new_rindices->at(i)); + } + } +} + TEST(ConvertNCHWToNHWC, Relu) { ReluGraph g; @@ -1511,6 +1953,57 @@ TEST(ConvertNCHWToNHWC, Rsqrt) EXPECT_EQ(16, g.rsqrt->dim(3).value()); } +TEST(ConvertNCHWToNHWC, Softmax) +{ + SoftmaxGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.softmax->logits()); + + auto softmax_succs = loco::succs(g.softmax); + EXPECT_EQ(1, softmax_succs.size()); + check_post_trans(*softmax_succs.begin()); + + // Check softmax shape + EXPECT_EQ(1, g.softmax->dim(0).value()); + EXPECT_EQ(4, g.softmax->dim(1).value()); + EXPECT_EQ(4, g.softmax->dim(2).value()); + EXPECT_EQ(16, g.softmax->dim(3).value()); +} + +TEST(ConvertNCHWToNHWC, SplitV) +{ + SplitVGraph g; + g.init(); + + run_phase(g.g(), true, true); + + check_pre_trans(g.splitv()->input()); + + auto splitv_succs = loco::succs(g.splitv()); + for (auto svo : loco::succs(g.splitv())) + { + for (auto succ : loco::succs(svo)) + { + check_post_trans(succ); + } + } + + // Check splitv() shape + EXPECT_EQ(1, g.splitv()->dim(0).value()); + EXPECT_EQ(2, g.splitv()->dim(1).value()); + EXPECT_EQ(192, g.splitv()->dim(2).value()); + EXPECT_EQ(2, g.splitv()->dim(3).value()); + + // Check axis + auto axis = dynamic_cast(g.splitv()->split_dim()); + EXPECT_NE(nullptr, axis); + EXPECT_EQ(1, axis->size()); + EXPECT_EQ(2, axis->at(0)); +} + TEST(ConvertNCHWToNHWC, SquaredDifference) { SquaredDifferenceGraph g; @@ -1602,3 +2095,31 @@ TEST(ConvertNCHWToNHWC, SubScalar) check_pre_trans(g.output->from()); } + +TEST(ConvertNCHWToNHWC, Not_Closed_Case1_NEG) +{ + NoPostReshapeGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.relu->features()); + + auto relu_succs = loco::succs(g.relu); + EXPECT_EQ(1, relu_succs.size()); + check_post_trans(*relu_succs.begin()); +} + +TEST(ConvertNCHWToNHWC, Not_Closed_Case2_NEG) +{ + ReluNotClosedGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.relu->features()); + + auto relu_succs = loco::succs(g.relu); + EXPECT_EQ(1, relu_succs.size()); + check_post_trans(*relu_succs.begin()); +} diff --git a/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp b/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp index 11970ff..72f5901 100644 --- a/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp +++ b/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp @@ -184,8 +184,63 @@ struct FakeQuantize final : public luci::CircleNodeMutableVisitor // For non-const activation, insert Quantize-Dequantize Ops // and dequantize the node - void visit(luci::CircleConv2D *node) { fq_activation(node); } void visit(luci::CircleAdd *node) { fq_activation(node); } + void visit(luci::CircleAveragePool2D *node) { fq_activation(node); } + void visit(luci::CircleBatchMatMul *node) { fq_activation(node); } + void visit(luci::CircleConv2D *node) { fq_activation(node); } + void visit(luci::CircleDepthwiseConv2D *node) { fq_activation(node); } + void visit(luci::CircleDiv *node) { fq_activation(node); } + void visit(luci::CircleFullyConnected *node) { fq_activation(node); } + void visit(luci::CircleInstanceNorm *node) { fq_activation(node); } + void visit(luci::CircleLeakyRelu *node) { fq_activation(node); } + void visit(luci::CircleLogistic *node) { fq_activation(node); } + void visit(luci::CircleLogSoftmax *node) { fq_activation(node); } + void visit(luci::CircleMaxPool2D *node) { fq_activation(node); } + void visit(luci::CircleMul *node) { fq_activation(node); } + void visit(luci::CircleNeg *node) { fq_activation(node); } + void visit(luci::CirclePad *node) { fq_activation(node); } + void visit(luci::CirclePRelu *node) { fq_activation(node); } + void visit(luci::CircleMean *node) { fq_activation(node); } + void visit(luci::CircleReduceMax *node) { fq_activation(node); } + void visit(luci::CircleRelu *node) { fq_activation(node); } + void visit(luci::CircleRelu6 *node) { fq_activation(node); } + void visit(luci::CircleResizeBilinear *node) { fq_activation(node); } + void visit(luci::CircleResizeNearestNeighbor *node) { fq_activation(node); } + void visit(luci::CircleRsqrt *node) { fq_activation(node); } + void visit(luci::CircleSoftmax *node) { fq_activation(node); } + void visit(luci::CircleSqrt *node) { fq_activation(node); } + void visit(luci::CircleTanh *node) { fq_activation(node); } + void visit(luci::CircleTransposeConv *node) { fq_activation(node); } + + // For Ops that do not change the value of input, do nothing + // (dtype will be automatically updated by type inference) + void visit(luci::CircleCast *) {} + void visit(luci::CircleConcatenation *) {} + void visit(luci::CircleGather *) {} + void visit(luci::CircleSlice *) {} + void visit(luci::CircleStridedSlice *) {} + void visit(luci::CircleReshape *) {} + void visit(luci::CircleSplit *) {} + void visit(luci::CircleSplitOut *) {} + void visit(luci::CircleSplitV *) {} + void visit(luci::CircleSplitVOut *) {} + void visit(luci::CircleTranspose *) {} + + // For Ops that return index, fake quantization is unnecessary + void visit(luci::CircleArgMax *) {} + + // Virtual node + void visit(luci::CircleOutputExclude *) {} + + void visit(luci::CircleQuantize *node) + { + RETURN_UNLESS(is_quant_act(node)); + + insert_dequantize(node); + } + + // Dequantize Op does nothing in fp32 model + void visit(luci::CircleDequantize *) {} }; #undef RETURN_UNLESS diff --git a/compiler/luci/pass/src/FoldDensifyPass.cpp b/compiler/luci/pass/src/FoldDensifyPass.cpp new file mode 100644 index 0000000..5ddc743 --- /dev/null +++ b/compiler/luci/pass/src/FoldDensifyPass.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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 "luci/Pass/FoldDensifyPass.h" +#include "helpers/SparsityFormatConverter.h" + +#include +#include + +#include +#include + +namespace +{ + +bool is_foldable_const(luci::CircleConst *node) +{ + if (node->sparsityparam() == nullptr) + return false; + + if (node->dtype() == loco::DataType::FLOAT32) + return true; + if (node->dtype() == loco::DataType::FLOAT16) + return true; + + return false; +} + +luci::CircleConst *densified_const_node(luci::CircleConst *const_node) +{ + assert(const_node->sparsityparam()); + + auto name = const_node->name(); + assert(name.length() > 0); + auto g = const_node->graph(); + auto new_const_node = g->nodes()->create(); + + new_const_node->dtype(const_node->dtype()); + new_const_node->rank(const_node->rank()); + + uint32_t dim_size = 1; + std::vector dense_shape; + for (uint32_t i = 0; i < new_const_node->rank(); ++i) + { + assert(const_node->dim(i).known()); + new_const_node->dim(i) = const_node->dim(i); + + uint32_t value = const_node->dim(i).value(); + dim_size *= value; + dense_shape.emplace_back(static_cast(value)); + } + + if (const_node->dtype() == loco::DataType::FLOAT32) + new_const_node->size(dim_size); + else + { + assert(const_node->dtype() == loco::DataType::FLOAT16); + new_const_node->size(dim_size); + } + + new_const_node->shape_status(luci::ShapeStatus::VALID); + new_const_node->name(name + "_DS"); + + if (const_node->dtype() == loco::DataType::FLOAT32) + { + auto const_items = const_node->size(); + auto f_data = std::make_unique(const_items); + for (size_t i = 0; i < const_items; ++i) + f_data[i] = const_node->at(i); + + sparsity::TfLiteSparsity sp = to_tflite_sparsity(const_node->sparsityparam()); + sparsity::FormatConverter converter(dense_shape, sp); + converter.SparseToDense(f_data.get()); + const auto &data_dense = converter.GetData(); + assert(data_dense.size() == dim_size); + + for (uint32_t i = 0; i < dim_size; ++i) + new_const_node->at(i) = data_dense[i]; + + luci::freeTfLiteSparsity(sp); + } + else + { + assert(const_node->dtype() == loco::DataType::FLOAT16); + + auto const_items = const_node->size(); + auto f_data = std::make_unique(const_items); + for (size_t i = 0; i < const_items; ++i) + f_data[i] = const_node->at(i); + + // Primitive type for FLOAT16 is UINT16 + sparsity::TfLiteSparsity sp = to_tflite_sparsity(const_node->sparsityparam()); + sparsity::FormatConverter converter(dense_shape, sp); + converter.SparseToDense(f_data.get()); + const auto &data_dense = converter.GetData(); + assert(data_dense.size() == dim_size); + for (uint32_t i = 0; i < dim_size; ++i) + new_const_node->at(i) = data_dense[i]; + + luci::freeTfLiteSparsity(sp); + } + + return new_const_node; +} + +/** + * @brief Fold Densify if input is Sparse Constant + */ +bool fold_densify(luci::CircleDensify *densify) +{ + auto const_input = dynamic_cast(densify->input()); + if (not const_input) + return false; + + if (not is_foldable_const(const_input)) + return false; + + auto dense_const = densified_const_node(const_input); + assert(dense_const); + + loco::replace(densify).with(dense_const); + luci::add_origin(dense_const, luci::composite_origin( + {luci::get_origin(densify), luci::get_origin(const_input)})); + + return true; +} + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * + * [CircleConst](sparse) + * | + * [CircleDensify] + * | + * [CircleNode] + * | + * + * AFTER + * + * [CircleConst](dense) [CircleConst](sparse) + * | | + * [CircleNode] [CircleDensify] + * | + */ +bool FoldDensifyPass::run(loco::Graph *g) +{ + bool changed = false; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto densify = dynamic_cast(node)) + { + if (fold_densify(densify)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FoldDensifyPass.test.cpp b/compiler/luci/pass/src/FoldDensifyPass.test.cpp new file mode 100644 index 0000000..2f9736f --- /dev/null +++ b/compiler/luci/pass/src/FoldDensifyPass.test.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/FoldDensifyPass.h" +#include "PassTestGraphs.h" + +#include + +#include + +namespace +{ + +class FoldDensifyPassGraph : public luci::ConstantFoldingAddTestGraph +{ +public: + FoldDensifyPassGraph(std::initializer_list shape) + : luci::ConstantFoldingAddTestGraph(shape, loco::DataType::FLOAT32) + { + _densify = _g.nodes()->create(); + _x = _g.nodes()->create(); + + _densify->dtype(loco::DataType::FLOAT32); + _x->dtype(loco::DataType::FLOAT32); + + _densify->shape(shape); + _x->shape(shape); + + _densify->input(_x); + + _densify->name("densify"); + _x->name("x"); + } + + loco::Node *createFoldedPattern() override { return _densify; } + +public: + void fill_const_dense(void) + { + uint32_t num_elems = 1; + for (uint32_t r = 0; r < _x->rank(); ++r) + num_elems *= _x->dim(r).value(); + + _x->size(num_elems); + for (uint32_t i = 0; i < num_elems; i++) + _x->at(i) = static_cast(i + 1); + } + + void fill_const_sparse(void) + { + // fill 4x4 of + // [[1 0 0 0] + // [0 2 0 0] + // [0 0 3 0] + // [0 0 0 4]] + + // values of 1.0, 2.0, 3.0, 4.0 + uint32_t udata[] = {0x3f800000, 0x40000000, 0x40400000, 0x40800000}; + float *fdata = reinterpret_cast(udata); + + _x->size(4); + for (uint32_t i = 0; i < 4; i++) + _x->at(i) = fdata[i]; + + auto sparsityparam = std::make_unique(); + sparsityparam->traversal_order = std::vector({0, 1}); + sparsityparam->block_map = std::vector({}); + + auto dm0 = luci::DimMetaData(luci::DimensionType::DENSE, 4); + + std::vector as_vec = {0, 1, 2, 3, 4}; + std::vector ai_vec = {0, 1, 2, 3}; + auto as = luci::SparseIndexVector(luci::SparseIndexVectorType::I32, as_vec); + auto ai = luci::SparseIndexVector(luci::SparseIndexVectorType::I32, ai_vec); + auto dm1 = luci::DimMetaData(luci::DimensionType::SPARSE_CSR, 0, as, ai); + sparsityparam->dim_metadata.emplace_back(dm0); + sparsityparam->dim_metadata.emplace_back(dm1); + + _x->sparsityparam(std::move(sparsityparam)); + } + +protected: + luci::CircleDensify *_densify = nullptr; + luci::CircleConst *_x = nullptr; +}; + +class FoldDensifyPassGraphTest : public FoldDensifyPassGraph, public ::testing::Test +{ +public: + FoldDensifyPassGraphTest() : FoldDensifyPassGraph({4, 4}) {} + + virtual void SetUp() { init(); } +}; + +} // namespace + +TEST(FoldDensifyPassGraph, name) +{ + luci::FoldDensifyPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(FoldDensifyPassGraphTest, no_sparsity_param_NEG) +{ + fill_const_dense(); + + luci::FoldDensifyPass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(FoldDensifyPassGraphTest, sparsity_param) +{ + fill_const_sparse(); + + luci::FoldDensifyPass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + EXPECT_EQ(2, folded_const->rank()); + EXPECT_EQ(4, folded_const->dim(0).value()); + EXPECT_EQ(4, folded_const->dim(1).value()); + EXPECT_EQ(16, folded_const->size()); + for (int y = 0; y < 4; ++y) + { + for (int x = 0; x < 4; ++x) + { + float ovalue = folded_const->at(y * 4 + x); + float fvalue = 0.0; + if (x == y) + { + // diagonal position + fvalue = static_cast(y + 1); + } + EXPECT_EQ(fvalue, ovalue); + } + } +} diff --git a/compiler/luci/pass/src/FoldDequantizePass.cpp b/compiler/luci/pass/src/FoldDequantizePass.cpp index 3dd4f8c..b6526de 100644 --- a/compiler/luci/pass/src/FoldDequantizePass.cpp +++ b/compiler/luci/pass/src/FoldDequantizePass.cpp @@ -19,6 +19,8 @@ #include #include +#include + namespace { @@ -32,6 +34,9 @@ bool is_hybrid_kernel_supported(loco::Node *node) bool is_foldable_const(luci::CircleConst *node) { + if (node->dtype() == loco::DataType::FLOAT16) + return true; + if (node->quantparam() == nullptr) return false; @@ -39,17 +44,18 @@ bool is_foldable_const(luci::CircleConst *node) return true; if (node->dtype() == loco::DataType::U8) return true; + if (node->dtype() == loco::DataType::S16) + return true; + if (node->dtype() == loco::DataType::S32) + return true; + if (node->dtype() == loco::DataType::S64) + return true; return false; } luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) { - if (const_node->quantparam() == nullptr) - { - throw std::runtime_error("Given constant node has no quantization parameter"); - } - auto name = const_node->name(); assert(name.length() > 0); auto g = const_node->graph(); @@ -67,38 +73,70 @@ luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) new_const_node->shape_status(luci::ShapeStatus::VALID); new_const_node->name(name + "_DQ"); + if (const_node->dtype() == loco::DataType::FLOAT16) + { + for (uint32_t i = 0; i < new_const_node->size(); ++i) + { + auto raw = const_node->at(i); + new_const_node->at(i) = fp16_ieee_to_fp32_value(raw); + } + return new_const_node; + } + + if (const_node->quantparam() == nullptr) + { + throw std::runtime_error("Given constant node has no quantization parameter"); + } + const int32_t q_dim = const_node->quantparam()->quantized_dimension; - const int32_t q_dim_value = const_node->dim(q_dim).value(); + // For scalar, q_dim_value is 1 + // For non-scalar, q_dim_value is the size of quantized dimension + const int32_t q_dim_value = const_node->rank() == 0 ? 1 : const_node->dim(q_dim).value(); int32_t right_count = q_dim_value; for (uint32_t i = q_dim + 1; i < const_node->rank(); ++i) right_count *= const_node->dim(i).value(); - if (const_node->dtype() == loco::DataType::S8) + for (uint32_t i = 0; i < new_const_node->size(); ++i) { - for (uint32_t i = 0; i < const_node->size(); ++i) - { - uint32_t qd = (i % right_count) / (right_count / q_dim_value); - if (qd >= const_node->quantparam()->zerop.size()) - qd = 0; + uint32_t qd = (i % right_count) / (right_count / q_dim_value); + if (qd >= const_node->quantparam()->zerop.size()) + qd = 0; - new_const_node->at(i) = - (float)(const_node->at(i) - const_node->quantparam()->zerop.at(qd)) * - const_node->quantparam()->scale.at(qd); - } - } - else - { - for (uint32_t i = 0; i < const_node->size(); ++i) + switch (const_node->dtype()) { - uint32_t qd = (i % right_count) / (right_count / q_dim_value); - if (qd >= const_node->quantparam()->zerop.size()) - qd = 0; - - new_const_node->at(i) = - (float)((int)const_node->at(i) - - const_node->quantparam()->zerop.at(qd)) * - const_node->quantparam()->scale.at(qd); + case loco::DataType::S8: + new_const_node->at(i) = + static_cast(const_node->at(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); + break; + case loco::DataType::S16: + new_const_node->at(i) = + static_cast(const_node->at(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); + break; + case loco::DataType::S32: + new_const_node->at(i) = + static_cast(const_node->at(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); + break; + case loco::DataType::S64: + new_const_node->at(i) = + static_cast(const_node->at(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); + break; + case loco::DataType::U8: + new_const_node->at(i) = + static_cast(const_node->at(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); + break; + default: + throw std::runtime_error("Not supported dtype for FoldDequantizePass"); } } @@ -160,7 +198,7 @@ bool FoldDequantizePass::run(loco::Graph *g) { bool changed = false; - for (auto node : loco::all_nodes(g)) + for (auto node : loco::active_nodes(loco::output_nodes(g))) { if (auto circle_dequant = dynamic_cast(node)) { diff --git a/compiler/luci/pass/src/FoldDequantizePass.test.cpp b/compiler/luci/pass/src/FoldDequantizePass.test.cpp index d82a7bc..fb5b6ad 100644 --- a/compiler/luci/pass/src/FoldDequantizePass.test.cpp +++ b/compiler/luci/pass/src/FoldDequantizePass.test.cpp @@ -15,12 +15,389 @@ */ #include "luci/Pass/FoldDequantizePass.h" +#include "PassTestGraphs.h" #include +namespace +{ + +template +class FoldDequantizeTest : public luci::ConstantFoldingAddTestGraph, public ::testing::Test +{ +public: + FoldDequantizeTest() : luci::ConstantFoldingAddTestGraph({2, 2, 2}, DT) {} + + virtual void SetUp() { init(); } + + loco::Node *createFoldedPattern() override + { + _dequantize = _g.nodes()->create(); + _input = _g.nodes()->create(); + + _dequantize->dtype(loco::DataType::FLOAT32); + _input->dtype(DT); + + _input->shape({2, 2, 2}); + + _input->size
(8); + _input->at
(0) = 0; + _input->at
(1) = 1; + _input->at
(2) = 2; + _input->at
(3) = 3; + _input->at
(4) = 4; + _input->at
(5) = 5; + _input->at
(6) = 6; + _input->at
(7) = 7; + + auto qparam = std::make_unique(); + qparam->quantized_dimension = 1; + qparam->scale.push_back(5.0); + qparam->scale.push_back(10.0); + qparam->zerop.push_back(1); + qparam->zerop.push_back(2); + _input->quantparam(std::move(qparam)); + + _dequantize->input(_input); + + _dequantize->name("dequantize"); + _input->name("input"); + + return _dequantize; + } + + void createScalarPattern() + { + _input->rank(0); + _input->size
(1); + _input->at
(0) = 1; + + auto qparam = std::make_unique(); + qparam->quantized_dimension = 0; + qparam->scale.push_back(1.0); + qparam->zerop.push_back(0); + _input->quantparam(std::move(qparam)); + } + + void createNotFoldablePattern() { _input->quantparam(nullptr); } + +protected: + luci::CircleDequantize *_dequantize = nullptr; + luci::CircleConst *_input = nullptr; +}; + +class S8FoldDequantizeTest : public FoldDequantizeTest +{ +}; + +class S16FoldDequantizeTest : public FoldDequantizeTest +{ +}; + +class S32FoldDequantizeTest : public FoldDequantizeTest +{ +}; + +class S64FoldDequantizeTest : public FoldDequantizeTest +{ +}; + +class U8FoldDequantizeTest : public FoldDequantizeTest +{ +}; + +class F16FoldDequantizeTest : public luci::ConstantFoldingTestGraph, public ::testing::Test +{ +public: + F16FoldDequantizeTest() : ConstantFoldingTestGraph({2, 2}, loco::DataType::FLOAT16) {} + + virtual void SetUp() { init(); } + + loco::Node *createFoldedPattern() override + { + const auto DT = loco::DataType::FLOAT16; + _dequantize = _g.nodes()->create(); + _f16const = _g.nodes()->create(); + + _dequantize->dtype(loco::DataType::FLOAT32); + _f16const->dtype(DT); + + _f16const->shape({2, 2}); + + _f16const->size(4); + _f16const->at
(0) = 49408; // -2.5f + _f16const->at
(1) = 47104; // -0.5f + _f16const->at
(2) = 0; // 0.0f + _f16const->at
(3) = 15872; // 1.5f + // NOTE how to get uint16_t value of float16 ? + // Use compiler/souschef/src/Gaussian.cpp GaussianFloat16DataChef::generate() + // uint16_t value = fp16_ieee_from_fp32_value(-2.5); + // printf("-2.5 = %u\r\n", value); + + _dequantize->input(_f16const); + + _dequantize->name("dequantize"); + _f16const->name("input"); + + _output->from(_dequantize); + + return _dequantize; + } + + void createNotFoldablePattern() { _dequantize->input(_input); } + +protected: + luci::CircleConst *getFoldedPattern() override + { + return dynamic_cast(_output->from()); + } + + void init() override { createFoldedPattern(); } + +protected: + luci::CircleDequantize *_dequantize = nullptr; + luci::CircleConst *_f16const = nullptr; +}; + +} // namespace + TEST(FoldDequantizePassTest, name) { luci::FoldDequantizePass pass; auto const name = pass.name(); ASSERT_NE(nullptr, name); } + +TEST_F(U8FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(3, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(2, folded_const->dim(2).value()); + EXPECT_EQ(-5.0, folded_const->at(0)); + EXPECT_EQ(0.0, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(10.0, folded_const->at(3)); + EXPECT_EQ(15.0, folded_const->at(4)); + EXPECT_EQ(20.0, folded_const->at(5)); + EXPECT_EQ(40.0, folded_const->at(6)); + EXPECT_EQ(50.0, folded_const->at(7)); +} + +TEST_F(U8FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(S8FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(3, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(2, folded_const->dim(2).value()); + EXPECT_EQ(-5.0, folded_const->at(0)); + EXPECT_EQ(0.0, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(10.0, folded_const->at(3)); + EXPECT_EQ(15.0, folded_const->at(4)); + EXPECT_EQ(20.0, folded_const->at(5)); + EXPECT_EQ(40.0, folded_const->at(6)); + EXPECT_EQ(50.0, folded_const->at(7)); +} + +TEST_F(S8FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(S16FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(3, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(2, folded_const->dim(2).value()); + EXPECT_EQ(-5.0, folded_const->at(0)); + EXPECT_EQ(0.0, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(10.0, folded_const->at(3)); + EXPECT_EQ(15.0, folded_const->at(4)); + EXPECT_EQ(20.0, folded_const->at(5)); + EXPECT_EQ(40.0, folded_const->at(6)); + EXPECT_EQ(50.0, folded_const->at(7)); +} + +TEST_F(S16FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(S32FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(3, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(2, folded_const->dim(2).value()); + EXPECT_EQ(-5.0, folded_const->at(0)); + EXPECT_EQ(0.0, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(10.0, folded_const->at(3)); + EXPECT_EQ(15.0, folded_const->at(4)); + EXPECT_EQ(20.0, folded_const->at(5)); + EXPECT_EQ(40.0, folded_const->at(6)); + EXPECT_EQ(50.0, folded_const->at(7)); +} + +TEST_F(S32FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(S64FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(3, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(2, folded_const->dim(2).value()); + EXPECT_EQ(-5.0, folded_const->at(0)); + EXPECT_EQ(0.0, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(10.0, folded_const->at(3)); + EXPECT_EQ(15.0, folded_const->at(4)); + EXPECT_EQ(20.0, folded_const->at(5)); + EXPECT_EQ(40.0, folded_const->at(6)); + EXPECT_EQ(50.0, folded_const->at(7)); +} + +TEST_F(S64FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} + +TEST_F(U8FoldDequantizeTest, fold_dequant_scalar) +{ + createScalarPattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Check type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(0, folded_const->rank()); + EXPECT_EQ(1.0, folded_const->at(0)); +} + +TEST_F(F16FoldDequantizeTest, fold_dequant_basic) +{ + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::FLOAT32, folded_const->dtype()); + EXPECT_EQ(2, folded_const->rank()); + EXPECT_EQ(2, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->dim(1).value()); + EXPECT_EQ(-2.5, folded_const->at(0)); + EXPECT_EQ(-0.5, folded_const->at(1)); + EXPECT_EQ(0.0, folded_const->at(2)); + EXPECT_EQ(1.5, folded_const->at(3)); +} + +TEST_F(F16FoldDequantizeTest, fold_dequant_basic_NEG) +{ + createNotFoldablePattern(); + + luci::FoldDequantizePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_EQ(nullptr, folded_const); +} diff --git a/compiler/luci/pass/src/FoldSparseToDensePass.cpp b/compiler/luci/pass/src/FoldSparseToDensePass.cpp index 0c6fc43..ed60d88 100644 --- a/compiler/luci/pass/src/FoldSparseToDensePass.cpp +++ b/compiler/luci/pass/src/FoldSparseToDensePass.cpp @@ -19,6 +19,8 @@ #include +#include + namespace { diff --git a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp index 2c990f0..bc09abe 100644 --- a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp +++ b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace { @@ -55,6 +56,26 @@ void copy_shape(luci::CircleReshape *reshape, luci::CircleReshape *new_reshape) new_reshape->newShape()->dim(r) = reshape->newShape()->dim(r); } +luci::CircleReshape *create_cloned_reshape(luci::CircleReshape *reshape) +{ + assert(reshape != nullptr); // FIX_CALLER_UNLESS + + luci::CircleConst *cloned_shape = clone_shape(reshape); + if (cloned_shape == nullptr) + return nullptr; + + auto cloned_node = luci::clone_node(reshape, reshape->graph()); + if (cloned_node == nullptr) + return nullptr; + + auto new_reshape = loco::must_cast(cloned_node); + new_reshape->shape(cloned_shape); + new_reshape->name(reshape->name() + "_C"); + luci::add_origin(new_reshape, luci::get_origin(reshape)); + + return new_reshape; +} + bool forward_reshape(luci::CircleReshape *reshape, luci::CircleNeg *neg) { assert(reshape != nullptr); @@ -85,6 +106,26 @@ bool forward_reshape(luci::CircleReshape *reshape, luci::CircleNeg *neg) return true; } +bool forward_reshape(luci::CircleReshape *reshape, luci::CircleLogistic *logit) +{ + assert(reshape != nullptr); // FIX_CALLER_UNLESS + assert(logit != nullptr); // FIX_CALLER_UNLESS + + auto new_reshape = create_cloned_reshape(reshape); + if (not new_reshape) + return false; + + // reconnect network + loco::replace(logit).with(new_reshape); + logit->x(reshape->tensor()); + new_reshape->tensor(logit); + + // Do shape inference for this node again. + logit->shape_status(luci::ShapeStatus::UNDEFINED); + + return true; +} + class ForwardReshape final : public luci::CircleNodeMutableVisitor { protected: @@ -103,6 +144,14 @@ protected: return forward_reshape(reshape, node); } + bool visit(luci::CircleLogistic *node) + { + auto reshape = as_reshape(node->x()); + if (reshape == nullptr) + return false; + + return forward_reshape(reshape, node); + } // TODO add more unary operators }; diff --git a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp index 2593a01..3735132 100644 --- a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp +++ b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp @@ -65,6 +65,42 @@ protected: luci::CircleConst *_reshape_shape = nullptr; }; +// TODO Reduce duplicate code with ReshapeNegGraphlet +class ReshapeLogisticGraphlet +{ +public: + ReshapeLogisticGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 shape_in, const ShapeU32 shape_out) + { + std::vector shape_out_v = shape_out; + + _reshape_shape = g->nodes()->create(); + _reshape = g->nodes()->create(); + _logistic = g->nodes()->create(); + + _reshape_shape->dtype(loco::DataType::S32); + _reshape_shape->rank(1); + _reshape_shape->dim(0).set(shape_out_v.size()); + _reshape_shape->shape_status(luci::ShapeStatus::VALID); + // values + const auto size = shape_out_v.size(); + _reshape_shape->size(size); + for (uint32_t i = 0; i < size; i++) + _reshape_shape->at(i) = shape_out_v[i]; + + _reshape_shape->name("reshape_shape"); + _reshape->name("reshape"); + _logistic->name("logistic"); + } + +protected: + luci::CircleReshape *_reshape = nullptr; + luci::CircleLogistic *_logistic = nullptr; + luci::CircleConst *_reshape_shape = nullptr; +}; + class ForwardReshapeToNegGraph : public TestIOGraph, public ReshapeNegGraphlet { public: @@ -85,6 +121,26 @@ public: } }; +class ForwardReshapeToLogisticGraph : public TestIOGraph, public ReshapeLogisticGraphlet +{ +public: + ForwardReshapeToLogisticGraph() = default; + +public: + void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIOGraph::init(shape_in, shape_out); + ReshapeLogisticGraphlet::init(g(), shape_in, shape_out); + + // connect network + _reshape->tensor(input()); + _reshape->shape(_reshape_shape); + _logistic->x(_reshape); + + output()->from(_logistic); + } +}; + class ForwardReshapeToNegGraphTest : public ::testing::Test { public: @@ -101,6 +157,22 @@ protected: luci::ForwardReshapeToUnaryOpPass _pass; }; +class ForwardReshapeToLogisticGraphTest : public ::testing::Test +{ +public: + ForwardReshapeToLogisticGraphTest() = default; + + void run_pass(void) + { + while (_pass.run(_graph.g())) + ; + } + +protected: + ForwardReshapeToLogisticGraph _graph; + luci::ForwardReshapeToUnaryOpPass _pass; +}; + } // namespace TEST(ForwardReshapeToUnaryOpPassTest, name) @@ -123,3 +195,17 @@ TEST_F(ForwardReshapeToNegGraphTest, simple_forward) neg = dynamic_cast(reshape->tensor()); ASSERT_NE(nullptr, neg); } + +TEST_F(ForwardReshapeToLogisticGraphTest, forward) +{ + _graph.init({2, 2, 2}, {2, 4}); + + run_pass(); + + auto reshape = dynamic_cast(_graph.output()->from()); + auto log = dynamic_cast(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, log); + log = dynamic_cast(reshape->tensor()); + ASSERT_NE(nullptr, log); +} diff --git a/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.cpp b/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.cpp index 97a962c..3cf31ed 100644 --- a/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.cpp +++ b/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.cpp @@ -99,6 +99,12 @@ bool fuse_add_with_fc(luci::CircleFullyConnected *fc) fused_bias->at(i) += const_bias->at(i); } + // At this point, it is guarateed that fused_bias's shape is [1, 1, ..., N] or [N] + // where N is weights->dim(0). + // The shape is normalized to [N] to become the bias of FC + fused_bias->rank(1); + fused_bias->dim(0) = weights->dim(0); + fc->bias(fused_bias); fc->fusedActivationFunction(add->fusedActivationFunction()); diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp index 2bca570..852bc8b 100644 --- a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp +++ b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp @@ -37,10 +37,10 @@ namespace * \ | * [CircleTransposeConv] [CircleAdd] * | - * ([CircleRelu6]) + * ([CircleRelu/Relu6]) * | * - * Note: CircleRelu6 is inserted if Add activation is ReLU6 + * Note: CircleRelu/Relu6 is inserted if Add activation is ReLU6 */ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) { @@ -65,7 +65,8 @@ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) if (add->dtype() != loco::DataType::FLOAT32) return false; if (add->fusedActivationFunction() != luci::FusedActFunc::NONE && - add->fusedActivationFunction() != luci::FusedActFunc::RELU6) + add->fusedActivationFunction() != luci::FusedActFunc::RELU6 && + add->fusedActivationFunction() != luci::FusedActFunc::RELU) return false; // get addition @@ -102,6 +103,19 @@ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) // remove add node replace(add).with(relu); } + else if (add->fusedActivationFunction() == luci::FusedActFunc::RELU) + { + auto name = addition->name(); + assert(name.length() > 0); + // separate relu op from add op + auto relu = add->graph()->nodes()->create(); + relu->features(tconv); + relu->name(name + "/Relu"); + luci::add_origin(relu, luci::get_origin(add)); + + // remove add node + replace(add).with(relu); + } else { replace(add).with(tconv); diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp index 3379549..e6b54df 100644 --- a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp +++ b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp @@ -29,7 +29,7 @@ namespace * NOTE TF's BatchNormalization is converted to Mul and Add. * * BEFORE - * | [CircleOutputExclude] + * | [CircleConst]/[CircleOutputExclude] * | / [CircleConst] * | / / * [CircleTransposeConv] [CircleConst] @@ -40,7 +40,7 @@ namespace * | * * AFTER - * | [CircleOutputExclude] + * | [CircleConst]/[CircleOutputExclude] * +-------------------------------------+ / [CircleConst] * | | / / * | [CircleTransposeConv] [CircleConst] @@ -69,9 +69,10 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) return false; // check scale and shift constant attributes - if (scale->rank() != 1) + // TODO maybe rank check is not needed + if (scale->rank() != 1 && scale->rank() != 4) return false; - if (shift->rank() != 1) + if (shift->rank() != 1 && shift->rank() != 4) return false; // check mul, add attributes if (mul->dtype() != loco::DataType::FLOAT32) @@ -82,9 +83,8 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) add->fusedActivationFunction() != luci::FusedActFunc::RELU6) return false; - // tconv bias should be not set - if (not dynamic_cast(tconv->bias())) - return false; + // tconv bias is optional + auto bias = dynamic_cast(tconv->bias()); // get weight of tconv auto filter = dynamic_cast(tconv->filter()); @@ -96,10 +96,36 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) return false; auto filter_out_chn = filter->dim(0).value(); - if (filter_out_chn != scale->dim(0).value()) + // allow scale/shift and bias shape of [N], [1,1,1,N]; BN works for "channel-wise" + auto srank = scale->rank() - 1; + if (filter_out_chn != scale->dim(srank).value()) return false; - if (filter_out_chn != shift->dim(0).value()) + for (uint32_t d = 0; d < srank; ++d) + { + if (1 != scale->dim(d).value()) + return false; + } + srank = shift->rank() - 1; + if (filter_out_chn != shift->dim(srank).value()) return false; + for (uint32_t d = 0; d < srank; ++d) + { + if (1 != shift->dim(d).value()) + return false; + } + if (bias) + { + if (bias->dtype() != loco::DataType::FLOAT32) + return false; + srank = bias->rank() - 1; + if (filter_out_chn != bias->dim(srank).value()) + return false; + for (uint32_t d = 0; d < srank; ++d) + { + if (1 != bias->dim(d).value()) + return false; + } + } auto name = add->name(); assert(name.length() > 0); @@ -151,6 +177,11 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) for (uint32_t c = 0; c < filter_out_chn; ++c) { fused_bias->at(c) = shift->at(c); + if (bias != nullptr) + { + fused_bias->at(c) += + bias->at(c) * scale->at(c); + } } fused_bias->name(name + "/TransposeConv/bias"); @@ -166,6 +197,10 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) luci::add_origin(fused_tconv, luci::composite_origin( {luci::get_origin(add), luci::get_origin(mul), luci::get_origin(tconv)})); + if (bias != nullptr) + { + luci::add_origin(fused_tconv, luci::get_origin(bias)); + } if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6) { diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.cpp index f3ec6cd..10a651e 100644 --- a/compiler/luci/pass/src/FuseInstanceNormPass.cpp +++ b/compiler/luci/pass/src/FuseInstanceNormPass.cpp @@ -325,6 +325,10 @@ public: } private: + bool condition_common_1_5(uint32_t ifm_channel_depth); + bool condition_common_3_4(); + +private: template bool match(); public: @@ -368,21 +372,8 @@ private: if (not(condition)) \ return false; -template <> bool InstanceNormPattern::match() +bool InstanceNormPattern::condition_common_1_5(uint32_t ifm_channel_depth) { - CHECK_OR_FALSE(luci::fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal)); - CHECK_OR_FALSE(luci::fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm)); - - auto ifm_circle = loco::must_cast(ifm); - CHECK_OR_FALSE(ifm_circle->shape_status() == luci::ShapeStatus::VALID); - CHECK_OR_FALSE(ifm_circle->rank() == 4); - CHECK_OR_FALSE(ifm_circle->dim(3).known()); - uint32_t ifm_channel_depth = ifm_circle->dim(3).value(); - - CHECK_OR_FALSE(luci::fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma)); - - CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth)); - add_as_variance = dynamic_cast(rsqrt->x()); CHECK_OR_FALSE(add_as_variance); @@ -408,6 +399,70 @@ template <> bool InstanceNormPattern::matchx(); + CHECK_OR_FALSE(ifm); + + luci::CircleNode *ifm_node = loco::must_cast(ifm); + CHECK_OR_FALSE(ifm_node->rank() == 4); + CHECK_OR_FALSE(ifm_node->dim(3).known()); + + mean_of_ifm = dynamic_cast(sub->y()); + CHECK_OR_FALSE(mean_of_ifm); + CHECK_OR_FALSE(ifm == mean_of_ifm->input()); + + // continue search from add_as_variance + CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance)); + CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); + // TODO Support regarding broadcast + CHECK_OR_FALSE(const_as_epsilon->size() == 1); + + mean_as_variance = dynamic_cast(sqrt->x()); + CHECK_OR_FALSE(mean_as_variance); + + square = dynamic_cast(mean_as_variance->input()); + CHECK_OR_FALSE(square); + + sub_2 = dynamic_cast(square->x()); + CHECK_OR_FALSE(sub_2); + CHECK_OR_FALSE(ifm == sub_2->x()); + + mean_of_ifm_2 = dynamic_cast(sub_2->y()); + CHECK_OR_FALSE(mean_of_ifm_2); + CHECK_OR_FALSE(ifm == mean_of_ifm_2->input()); + + loco::Node *ifm_should_be = nullptr; + luci::CircleMean *mean_of_ifm_2_should_be = nullptr; + CHECK_OR_FALSE( + luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2)); + CHECK_OR_FALSE(ifm == ifm_should_be); + CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be); + + return true; +} + +template <> bool InstanceNormPattern::match() +{ + CHECK_OR_FALSE(luci::fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal)); + CHECK_OR_FALSE(luci::fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm)); + + auto ifm_circle = loco::must_cast(ifm); + CHECK_OR_FALSE(ifm_circle->shape_status() == luci::ShapeStatus::VALID); + CHECK_OR_FALSE(ifm_circle->rank() == 4); + CHECK_OR_FALSE(ifm_circle->dim(3).known()); + uint32_t ifm_channel_depth = ifm_circle->dim(3).value(); + + CHECK_OR_FALSE(luci::fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma)); + + CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth)); + + CHECK_OR_FALSE(condition_common_1_5(ifm_channel_depth)); + luci::CircleMul *mul_gamma_should_be = nullptr; luci::CircleMean *mean_of_ifm_should_be = nullptr; @@ -488,44 +543,7 @@ template <> bool InstanceNormPattern::matchx(); - CHECK_OR_FALSE(ifm); - - luci::CircleNode *ifm_node = loco::must_cast(ifm); - CHECK_OR_FALSE(ifm_node->rank() == 4); - CHECK_OR_FALSE(ifm_node->dim(3).known()); - - mean_of_ifm = dynamic_cast(sub->y()); - CHECK_OR_FALSE(mean_of_ifm); - CHECK_OR_FALSE(ifm == mean_of_ifm->input()); - - // continue search from add_as_variance - CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance)); - CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); - // TODO Support regarding broadcast - CHECK_OR_FALSE(const_as_epsilon->size() == 1); - - mean_as_variance = dynamic_cast(sqrt->x()); - CHECK_OR_FALSE(mean_as_variance); - - square = dynamic_cast(mean_as_variance->input()); - CHECK_OR_FALSE(square); - - sub_2 = dynamic_cast(square->x()); - CHECK_OR_FALSE(sub_2); - CHECK_OR_FALSE(ifm == sub_2->x()); - - mean_of_ifm_2 = dynamic_cast(sub_2->y()); - CHECK_OR_FALSE(mean_of_ifm_2); - CHECK_OR_FALSE(ifm == mean_of_ifm_2->input()); - - loco::Node *ifm_should_be = nullptr; - luci::CircleMean *mean_of_ifm_2_should_be = nullptr; - CHECK_OR_FALSE( - luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2)); - CHECK_OR_FALSE(ifm == ifm_should_be); - CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be); + CHECK_OR_FALSE(condition_common_3_4()); _matched = true; return true; @@ -546,44 +564,7 @@ template <> bool InstanceNormPattern::matchx(); - CHECK_OR_FALSE(ifm); - - luci::CircleNode *ifm_node = loco::must_cast(ifm); - CHECK_OR_FALSE(ifm_node->rank() == 4); - CHECK_OR_FALSE(ifm_node->dim(3).known()); - - mean_of_ifm = dynamic_cast(sub->y()); - CHECK_OR_FALSE(mean_of_ifm); - CHECK_OR_FALSE(ifm == mean_of_ifm->input()); - - // continue search from add_as_variance - CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance)); - CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); - // TODO Support regarding broadcast - CHECK_OR_FALSE(const_as_epsilon->size() == 1); - - mean_as_variance = dynamic_cast(sqrt->x()); - CHECK_OR_FALSE(mean_as_variance); - - square = dynamic_cast(mean_as_variance->input()); - CHECK_OR_FALSE(square); - - sub_2 = dynamic_cast(square->x()); - CHECK_OR_FALSE(sub_2); - CHECK_OR_FALSE(ifm == sub_2->x()); - - mean_of_ifm_2 = dynamic_cast(sub_2->y()); - CHECK_OR_FALSE(mean_of_ifm_2); - CHECK_OR_FALSE(ifm == mean_of_ifm_2->input()); - - loco::Node *ifm_should_be = nullptr; - luci::CircleMean *mean_of_ifm_2_should_be = nullptr; - CHECK_OR_FALSE( - luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2)); - CHECK_OR_FALSE(ifm == ifm_should_be); - CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be); + CHECK_OR_FALSE(condition_common_3_4()); assert(const_as_gamma == nullptr); assert(const_as_beta == nullptr); @@ -612,30 +593,7 @@ template <> bool InstanceNormPattern::matchdim(3).known()); uint32_t ifm_channel_depth = ifm_circle->dim(3).value(); - add_as_variance = dynamic_cast(rsqrt->x()); - CHECK_OR_FALSE(add_as_variance); - - CHECK_OR_FALSE( - luci::fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); - - CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); - // TODO Support regarding broadcast - CHECK_OR_FALSE(const_as_epsilon->size() == 1); - - CHECK_OR_FALSE(is_instance_mean_v1(mean_as_variance)); - - sqdiff = dynamic_cast(mean_as_variance->input()); - CHECK_OR_FALSE(sqdiff); - - loco::Node *ifm_should_be = nullptr; - CHECK_OR_FALSE(luci::fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff)); - CHECK_OR_FALSE(ifm == ifm_should_be); - CHECK_OR_FALSE(is_instance_mean_v1(mean_of_ifm)); - CHECK_OR_FALSE(ifm == mean_of_ifm->input()); - - const_as_beta = dynamic_cast(sub->x()); - CHECK_OR_FALSE(const_as_beta); - CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth)); + CHECK_OR_FALSE(condition_common_1_5(ifm_channel_depth)); luci::CircleRsqrt *rsqrt_should_be = nullptr; luci::CircleMean *mean_of_ifm_should_be = nullptr; diff --git a/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp b/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp index b497548..e8fa2a4 100644 --- a/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp +++ b/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp @@ -23,6 +23,7 @@ #include #include +#include namespace { diff --git a/compiler/luci/pass/src/PropagateQParamForwardPass.cpp b/compiler/luci/pass/src/PropagateQParamForwardPass.cpp index 003e4c2..aaadb28 100644 --- a/compiler/luci/pass/src/PropagateQParamForwardPass.cpp +++ b/compiler/luci/pass/src/PropagateQParamForwardPass.cpp @@ -138,13 +138,18 @@ struct PropagateQParamForward final : public luci::CircleNodeMutableVisitorquantparam(luci::make_predefined_qparam(input_node->opcode(), node->dtype())); + case luci::ActivationQType::PreDefinedLogistic: + case luci::ActivationQType::PreDefinedTanh: + case luci::ActivationQType::PreDefinedSoftmax: + node->quantparam(luci::make_predefined_qparam(qtype, node->dtype())); break; case luci::ActivationQType::IntScale: luci::set_int_scale(node); break; default: + // This assert ensures this switch-satement handles all ActivationQTypes + // TODO Find a better design to remove coupling with ActivationQType + assert(qtype == luci::ActivationQType::MinMax); break; } diff --git a/compiler/luci/pass/src/QuantizationUtils.cpp b/compiler/luci/pass/src/QuantizationUtils.cpp index ad86ced..06a4ae9 100644 --- a/compiler/luci/pass/src/QuantizationUtils.cpp +++ b/compiler/luci/pass/src/QuantizationUtils.cpp @@ -20,6 +20,7 @@ #include #include +#include namespace luci { @@ -276,31 +277,70 @@ uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices) indices[2] * dimension.dim(3).value() + indices[3]; } +// Activation (ofm) qtype is determined in different ways. +// 1. Pre-defined values: Some Ops have pre-defined qparams (ex: LOGISTIC, TANH) +// 2. Integer scale: Output of some Ops should be integers (ex: FLOOR, CEIL) +// 3. Activation qtype of input: Some Ops propagate qparam from input to output (ex: QUANTIZE, +// TRANSPOSE, etc. See PropagateQParamForwardPass.cpp for more details). ActivationQType activation_qtype(const CircleNode *node) { auto fused_act_node = dynamic_cast *>(node); if (fused_act_node && fused_act_node->fusedActivationFunction() == FusedActFunc::TANH) - return ActivationQType::PreDefinedValue; + return ActivationQType::PreDefinedTanh; + +#define RETURN_INPUT_ACTIVATION_QTYPE(CLASS, INPUT) \ + { \ + auto n = loco::must_cast(node); \ + auto input = loco::must_cast(n->INPUT()); \ + return activation_qtype(input); \ + } switch (node->opcode()) { case CircleOpcode::LOGISTIC: + return ActivationQType::PreDefinedLogistic; case CircleOpcode::TANH: + return ActivationQType::PreDefinedTanh; case CircleOpcode::SOFTMAX: - return ActivationQType::PreDefinedValue; + return ActivationQType::PreDefinedSoftmax; case CircleOpcode::FLOOR: case CircleOpcode::FLOOR_DIV: case CircleOpcode::FLOOR_MOD: case CircleOpcode::CEIL: return ActivationQType::IntScale; + case CircleOpcode::GATHER: + RETURN_INPUT_ACTIVATION_QTYPE(CircleGather, params); + case CircleOpcode::RESHAPE: + RETURN_INPUT_ACTIVATION_QTYPE(CircleReshape, tensor); + case CircleOpcode::TRANSPOSE: + RETURN_INPUT_ACTIVATION_QTYPE(CircleTranspose, a); + case CircleOpcode::STRIDED_SLICE: + RETURN_INPUT_ACTIVATION_QTYPE(CircleStridedSlice, input); + case CircleOpcode::SPLIT: + RETURN_INPUT_ACTIVATION_QTYPE(CircleSplit, input); + case CircleOpcode::CIRCLESPLITOUT: + RETURN_INPUT_ACTIVATION_QTYPE(CircleSplitOut, input); + case CircleOpcode::SPLIT_V: + RETURN_INPUT_ACTIVATION_QTYPE(CircleSplitV, input); + case CircleOpcode::CIRCLESPLITVOUT: + RETURN_INPUT_ACTIVATION_QTYPE(CircleSplitVOut, input); + case CircleOpcode::UNPACK: + RETURN_INPUT_ACTIVATION_QTYPE(CircleUnpack, value); + case CircleOpcode::CIRCLEUNPACKOUT: + RETURN_INPUT_ACTIVATION_QTYPE(CircleUnpackOut, input); + case CircleOpcode::QUANTIZE: + RETURN_INPUT_ACTIVATION_QTYPE(CircleQuantize, input); default: break; } +#undef RETURN_INPUT_ACTIVATION_QTYPE + return ActivationQType::MinMax; } -std::unique_ptr make_predefined_qparam(CircleOpcode opcode, loco::DataType dtype) +std::unique_ptr make_predefined_qparam(ActivationQType qtype, + loco::DataType dtype) { auto qparam = std::make_unique(); @@ -309,9 +349,9 @@ std::unique_ptr make_predefined_qparam(CircleOpcode opcode, lo qparam->zerop.emplace_back(zp); }; - switch (opcode) + switch (qtype) { - case CircleOpcode::LOGISTIC: + case ActivationQType::PreDefinedLogistic: if (dtype == loco::DataType::U8) set_qparam(1.0f / 256.0f, 0); else @@ -320,7 +360,7 @@ std::unique_ptr make_predefined_qparam(CircleOpcode opcode, lo set_qparam(1.0f / 32768.0f, 0); } break; - case CircleOpcode::TANH: + case ActivationQType::PreDefinedTanh: if (dtype == loco::DataType::U8) set_qparam(2.0f / 256.0f, 128); else @@ -329,7 +369,7 @@ std::unique_ptr make_predefined_qparam(CircleOpcode opcode, lo set_qparam(1.0f / 32768.0f, 0); } break; - case CircleOpcode::SOFTMAX: + case ActivationQType::PreDefinedSoftmax: if (dtype == loco::DataType::U8) set_qparam(1.0f / 255.0f, 0); else @@ -341,7 +381,7 @@ std::unique_ptr make_predefined_qparam(CircleOpcode opcode, lo default: throw std::runtime_error("Unsupported opcode with pre-defined qparam"); } - return std::move(qparam); + return qparam; } // For nodes with integer output, we use integer scale @@ -395,4 +435,74 @@ void quant_const(luci::CircleConst *node, loco::DataType quant_type) node->quantparam(std::move(quantparam)); } +namespace +{ + +// TODO move this to a more global helper file +int nbits(loco::DataType dt) noexcept +{ + switch (dt) + { + case loco::DataType::S8: + case loco::DataType::U8: + return 8; + case loco::DataType::S16: + case loco::DataType::U16: + case loco::DataType::FLOAT16: + return 16; + case loco::DataType::S32: + case loco::DataType::U32: + case loco::DataType::FLOAT32: + return 32; + case loco::DataType::S64: + return 64; + default: + return 64; // a safe large default + } +} + +// TODO Check if the metric is valid +// Returns true if [min,max] is poorly representable +bool range_check(float min, float max, loco::DataType dtype) +{ + float thresh = 1.5f; + return log2f(max) - log2f(min) > nbits(dtype) * thresh; +} + +bool warn_scale_zp(float scale, int64_t zp, luci::CircleNode *n) +{ + float min, max; + // estimate min/max + switch (n->dtype()) + { + case loco::DataType::U8: + min = scale * (0 - zp); + max = scale * (255 - zp); + break; + case loco::DataType::S16: + min = scale * (-32767); + max = scale * (32767); + break; + default: + return false; + } + return range_check(min, max, n->dtype()); +} + +} // namespace + +void warn_accuracy_with_range(luci::CircleNode *n) +{ + LOGGER(l); + auto qp = n->quantparam(); + auto k = qp->zerop.size(); + for (uint32_t i = 0; i < k; i++) + { + if (warn_scale_zp(qp->scale[i], qp->zerop[i], n)) + WARN(l) << "Quantization of " << i << "-th channel of " << n->name() + << "'s quantization may cause accuracy issues" << std::endl; + ; + } +} + } // namespace luci diff --git a/compiler/luci/pass/src/QuantizationUtils.h b/compiler/luci/pass/src/QuantizationUtils.h index cd8cec9..4d5316c 100644 --- a/compiler/luci/pass/src/QuantizationUtils.h +++ b/compiler/luci/pass/src/QuantizationUtils.h @@ -62,15 +62,19 @@ bool is_quantized(const CircleNode *node); enum ActivationQType { - MinMax, // Quantize using recorded min/max - PreDefinedValue, // Quantize using pre-defined values - IntScale, // Round scale to a positive integer + MinMax, // Quantize using recorded min/max + PreDefinedLogistic, // Quantize using pre-defined values + PreDefinedTanh, // Quantize using pre-defined values + PreDefinedSoftmax, // Quantize using pre-defined values + IntScale, // Round scale to a positive integer }; ActivationQType activation_qtype(const CircleNode *node); // Create qparam with pre-defined values for speical operators -std::unique_ptr make_predefined_qparam(CircleOpcode opcode, loco::DataType dtype); +std::unique_ptr make_predefined_qparam(CircleNode *node, loco::DataType dtype); +std::unique_ptr make_predefined_qparam(ActivationQType qtype, + loco::DataType dtype); // Update node's scale to a positive integer (for special Ops e.g., Floor, Ceil) void set_int_scale(luci::CircleNode *node); @@ -78,6 +82,10 @@ void set_int_scale(luci::CircleNode *node); // Quantize const tensor using its min/max values void quant_const(luci::CircleConst *node, loco::DataType quant_type); +// Check that a node is quantized without significant loss of precision; +// Emits warnings to log with WARN +void warn_accuracy_with_range(luci::CircleNode *n); + } // namespace luci #endif // __LUCI_QUANTIZATION_UTILS_H__ diff --git a/compiler/luci/pass/src/QuantizeActivation.cpp b/compiler/luci/pass/src/QuantizeActivation.cpp index 1493318..95251a8 100644 --- a/compiler/luci/pass/src/QuantizeActivation.cpp +++ b/compiler/luci/pass/src/QuantizeActivation.cpp @@ -114,29 +114,26 @@ void QuantizeSpecialActivation::visit(luci::CircleNode *node) auto fused_act_node = dynamic_cast *>(node); if (fused_act_node != nullptr && fused_act_node->fusedActivationFunction() == FusedActFunc::TANH) { - auto qparam = make_predefined_qparam(luci::CircleOpcode::TANH, output_type); + auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type); node->quantparam(std::move(qparam)); } } void QuantizeSpecialActivation::visit(luci::CircleLogistic *node) { - assert(activation_qtype(node) == luci::ActivationQType::PreDefinedValue); - auto qparam = make_predefined_qparam(luci::CircleOpcode::LOGISTIC, output_type); + auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedLogistic, output_type); node->quantparam(std::move(qparam)); } void QuantizeSpecialActivation::visit(luci::CircleTanh *node) { - assert(activation_qtype(node) == luci::ActivationQType::PreDefinedValue); - auto qparam = make_predefined_qparam(luci::CircleOpcode::TANH, output_type); + auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type); node->quantparam(std::move(qparam)); } void QuantizeSpecialActivation::visit(luci::CircleSoftmax *node) { - assert(activation_qtype(node) == luci::ActivationQType::PreDefinedValue); - auto qparam = make_predefined_qparam(luci::CircleOpcode::SOFTMAX, output_type); + auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedSoftmax, output_type); node->quantparam(std::move(qparam)); } diff --git a/compiler/luci/pass/src/QuantizeBias.cpp b/compiler/luci/pass/src/QuantizeBias.cpp index aa49623..de97a14 100644 --- a/compiler/luci/pass/src/QuantizeBias.cpp +++ b/compiler/luci/pass/src/QuantizeBias.cpp @@ -22,6 +22,7 @@ #include #include +#include using namespace luci; @@ -201,6 +202,18 @@ CircleConst *QuantizeBias::quantized_bias(CircleNode *input, const CircleNode *w std::vector scaling_factor(size); std::vector zp(size); + if (const_bias->rank() == 0) + { + // TODO Support quantization of scalar bias + throw std::runtime_error("Quantization of scalar bias is not yet supported (" + + const_bias->name() + ")"); + } + if (size != const_bias->dim(const_bias->rank() - 1).value()) + { + throw std::runtime_error(const_bias->name() + + " (bias) should have the shape of [1, 1, .. 1, channel]"); + } + if (output_type == loco::DataType::U8) { new_bias = quant_bias_per_channel(const_bias, input_scale, weight_scale, scaling_factor, zp); @@ -218,6 +231,7 @@ CircleConst *QuantizeBias::quantized_bias(CircleNode *input, const CircleNode *w auto quantparam = std::make_unique(); quantparam->scale = scaling_factor; quantparam->zerop = zp; + quantparam->quantized_dimension = const_bias->rank() - 1; assert(new_bias->quantparam() == nullptr); // bias should not be quantized before new_bias->quantparam(std::move(quantparam)); diff --git a/compiler/luci/pass/src/QuantizeBias.test.cpp b/compiler/luci/pass/src/QuantizeBias.test.cpp new file mode 100644 index 0000000..0104a19 --- /dev/null +++ b/compiler/luci/pass/src/QuantizeBias.test.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "QuantizeBias.h" + +#include +#include +#include + +#include + +using namespace luci; + +namespace +{ + +using namespace luci::test; + +// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp +template +luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, + const std::vector &shape, T value) +{ + auto node = g->nodes()->create(); + node->dtype(dtype); + node->rank(shape.size()); + + uint32_t size = 1; + for (uint32_t i = 0; i < shape.size(); ++i) + { + node->dim(i) = shape.at(i); + size *= shape.at(i); + } + node->shape_status(luci::ShapeStatus::VALID); + +#define INIT_VALUES(DT) \ + { \ + node->size
(size); \ + for (uint32_t i = 0; i < size; ++i) \ + node->at
(i) = value; \ + } + + switch (dtype) + { + case loco::DataType::U8: + INIT_VALUES(loco::DataType::U8); + break; + case loco::DataType::S16: + INIT_VALUES(loco::DataType::S16); + break; + case loco::DataType::S32: + INIT_VALUES(loco::DataType::S32); + break; + case loco::DataType::FLOAT32: + INIT_VALUES(loco::DataType::FLOAT32) + break; + default: + INTERNAL_EXN("create_const_node called with unsupported type"); + break; + } + return node; +} + +/** + * Simple graph for test + * + * BEFORE + * + * [IFM] [WEIGHTS] [BIAS(FP32)] + * \ | / + * [FC] + * | + * [OFM] + * + * AFTER + * + * [IFM] [WEIGHTS] [BIAS(Quantized)] + * \ | / + * [FC] + * | + * [OFM] + */ +struct Q8FCGraphlet +{ +public: + Q8FCGraphlet() = default; + virtual ~Q8FCGraphlet() = default; + + void init(loco::Graph *g, const ShapeU32 out_shape, const ShapeU32 w_shape, + const ShapeU32 bias_shape, const float bv) + { + _fc = g->nodes()->create(); + _fc->input(_x); + _x->dtype(loco::DataType::U8); + { + auto quantparam = std::make_unique(); + quantparam->scale.push_back(1.0); + quantparam->zerop.push_back(0); + quantparam->quantized_dimension = 0; + _x->quantparam(std::move(quantparam)); + } + + auto weights = create_const_node(g, loco::DataType::U8, w_shape, 1.0); + auto w_qparam = std::make_unique(); + std::vector w_scale(weights->dim(0).value(), 1.0); + std::vector w_zp(weights->dim(0).value(), 0); + w_qparam->scale = w_scale; + w_qparam->zerop = w_zp; + w_qparam->quantized_dimension = 0; + weights->quantparam(std::move(w_qparam)); + _fc->weights(weights); + _fc->fusedActivationFunction(luci::FusedActFunc::NONE); + _fc->dtype(loco::DataType::U8); + _fc->shape(out_shape); + auto l = _fc->dim(_fc->rank() - 1).value(); + _fc->bias(create_const_node(g, loco::DataType::FLOAT32, bias_shape, bv)); + _fc->name("fc"); + { + auto quantparam = std::make_unique(); + quantparam->scale.push_back(1.0); + quantparam->zerop.push_back(0); + quantparam->quantized_dimension = 0; + _fc->quantparam(std::move(quantparam)); + } + } + +public: + luci::CircleFullyConnected *fc() { return _fc; } + +protected: + luci::CircleFullyConnected *_fc = nullptr; + luci::CircleInput *_x = nullptr; +}; + +struct Q8FCGraph final : public TestIGraphlet, public TestOGraphlet, public Q8FCGraphlet +{ + void init(const ShapeU32 in_shape, const ShapeU32 w_shape, const ShapeU32 out_shape, + const ShapeU32 bias_shape, const float bv) + { + TestIGraphlet::init(g(), in_shape); + TestOGraphlet::init(g(), out_shape); + _x = input(); + Q8FCGraphlet::init(g(), out_shape, w_shape, bias_shape, bv); + output()->from(_fc); + } +}; + +class CQ8QuantizeBiasFCTest : public ::testing::Test +{ +public: + Q8FCGraph g; + luci::QuantizeBias qb{loco::DataType::FLOAT32, loco::DataType::U8, + luci::QuantizationGranularity::ChannelWise}; +}; + +} // namespace + +TEST_F(CQ8QuantizeBiasFCTest, fully_connected) +{ + g.init({1, 18, 80}, {256, 80}, {18, 256}, {1, 256}, 1); + g.fc()->accept(&qb); + + auto bias = loco::must_cast(g.fc()->bias()); + auto qparam = bias->quantparam(); + + EXPECT_NE(nullptr, qparam); + EXPECT_EQ(256, qparam->scale.size()); + EXPECT_EQ(256, qparam->zerop.size()); + EXPECT_EQ(1, qparam->quantized_dimension); +} + +TEST_F(CQ8QuantizeBiasFCTest, wrong_bias_shape_NEG) +{ + g.init({1, 18, 80}, {256, 80}, {18, 256}, {1, 2, 128}, 1); + EXPECT_ANY_THROW(g.fc()->accept(&qb)); // Wrong bias shape +} diff --git a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp index c9b35e0..ef047d3 100644 --- a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp +++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace { @@ -352,15 +353,15 @@ private: private: // Check if // 1. node is const - // 2. node was not quantized + // 2. node's dtype is float32 bool is_quantizable(loco::Node *node) { auto const_node = dynamic_cast(node); if (not const_node) return false; - // Skip if this is already quantized - if (is_quantized(const_node)) + // Skip if this is not float32 + if (const_node->dtype() != loco::DataType::FLOAT32) return false; return true; diff --git a/compiler/luci/pass/src/QuantizeWeights.cpp b/compiler/luci/pass/src/QuantizeWeights.cpp index 11322ab..500ae12 100644 --- a/compiler/luci/pass/src/QuantizeWeights.cpp +++ b/compiler/luci/pass/src/QuantizeWeights.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace luci; diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp index d9a9d4d..0051445 100644 --- a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp +++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp @@ -41,10 +41,28 @@ namespace { using namespace luci; + +bool use_predefined_values(ActivationQType qtype) +{ + switch (qtype) + { + case ActivationQType::PreDefinedLogistic: + case ActivationQType::PreDefinedTanh: + case ActivationQType::PreDefinedSoftmax: + return true; + default: + // This ensures this switch-statement handles all ActivationQTypes + assert(qtype == ActivationQType::IntScale or qtype == ActivationQType::MinMax); + break; + } + + return false; +} + // Create a Quantize Op whose // dtype is out_type // shape is the same with node -// qparam is computed using node's min/max +// qparam is computed according to node's qtype luci::CircleQuantize *create_quantize_op(luci::CircleNode *node, loco::DataType out_type) { auto quantize = node->graph()->nodes()->create(); @@ -60,9 +78,9 @@ luci::CircleQuantize *create_quantize_op(luci::CircleNode *node, loco::DataType assert(qparam); // FIX_CALLER_UNLESS auto qtype = luci::activation_qtype(node); - if (qtype == ActivationQType::PreDefinedValue) + if (use_predefined_values(qtype)) { - quantize->quantparam(luci::make_predefined_qparam(node->opcode(), out_type)); + quantize->quantparam(luci::make_predefined_qparam(qtype, out_type)); return quantize; } @@ -105,6 +123,23 @@ luci::CircleQuantize *create_quantize_op(luci::CircleNode *node, loco::DataType return quantize; } +// Create Dequantize Op whose shape is the same with node +luci::CircleDequantize *create_dequantize(luci::CircleNode *node) +{ + auto dequantize = node->graph()->nodes()->create(); + dequantize->name(node->name() + "_Dequantize"); + dequantize->dtype(loco::DataType::FLOAT32); + dequantize->rank(node->rank()); + for (uint32_t i = 0; i < node->rank(); i++) + dequantize->dim(i).set(node->dim(i).value()); + + dequantize->shape_status(luci::ShapeStatus::VALID); + + luci::add_origin(dequantize, luci::get_origin(node)); + + return dequantize; +} + } // namespace namespace luci @@ -229,11 +264,13 @@ private: INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleFullyConnected, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleGather, params) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleInstanceNorm, input) + INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleLeakyRelu, features) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleLocalResponseNormalization, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleLogistic, x) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleMaxPool2D, value) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleMean, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleMirrorPad, input) + INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleNeg, x) INSERT_QUANTIZE_TO_UNARY_OP(luci::CirclePad, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CirclePadV2, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CirclePRelu, input) @@ -241,6 +278,7 @@ private: INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleReduceMax, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleReduceMin, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleRelu, features) + INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleRelu6, features) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleReshape, tensor) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleResizeBilinear, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleResizeNearestNeighbor, input) @@ -250,6 +288,7 @@ private: INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSoftmax, logits) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSpaceToBatchND, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSpaceToDepth, input) + INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSqueeze, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSqrt, x) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleStridedSlice, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleSum, input) @@ -353,7 +392,9 @@ void QuantizeWithMinMaxPass::set_input_type(loco::Graph *g) const luci::add_origin(quant_op, luci::get_origin(succ)); } - // Requantize input + // Update qparam of input + // This step is skipped if input_type is float32 + if (_ctx->input_type != loco::DataType::FLOAT32) { auto quantparam = input->quantparam(); assert(quantparam); @@ -376,11 +417,13 @@ void QuantizeWithMinMaxPass::set_input_type(loco::Graph *g) const assert(_ctx->input_type == loco::DataType::S16); compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); } - input->dtype(_ctx->input_type); input->quantparam()->scale[0] = scaling_factor; input->quantparam()->zerop[0] = zp; } + // Update dtype of input + input->dtype(_ctx->input_type); + auto graph_input = inputs->at(input->index()); graph_input->dtype(_ctx->input_type); } @@ -405,13 +448,26 @@ void QuantizeWithMinMaxPass::set_output_type(loco::Graph *g) const if (not from->quantparam()) continue; - // Insert Quantize Op - auto quant_op = create_quantize_op(from, _ctx->output_type); - loco::replace(from).with(quant_op); - quant_op->input(from); + // Insert Dequantize Op for float32 output_type + if (_ctx->output_type == loco::DataType::FLOAT32) + { + auto dequant_op = create_dequantize(from); + loco::replace(from).with(dequant_op); + dequant_op->input(from); + } + else + { + // Insert Quantize Op for non-float32 output_type + auto quant_op = create_quantize_op(from, _ctx->output_type); + loco::replace(from).with(quant_op); + quant_op->input(from); - // TODO Set a proper origin (Quantize should have its own Origin) - luci::add_origin(quant_op, luci::get_origin(from)); + // TODO Set a proper origin (Quantize should have its own Origin) + luci::add_origin(quant_op, luci::get_origin(from)); + } + + // Update dtype of output + output->dtype(_ctx->output_type); auto graph_output = outputs->at(output->index()); graph_output->dtype(_ctx->output_type); @@ -594,12 +650,25 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g) // Set output type set_output_type(g); + // Remove redundant Quantize Op + { + logo::Phase phase; + + phase.emplace_back(std::make_unique()); + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); + } + // Remove min/max values for (auto node : loco::active_nodes(loco::output_nodes(g))) { auto circle_node = loco::must_cast(node); if (auto qparam = circle_node->quantparam()) { + warn_accuracy_with_range(circle_node); qparam->min.clear(); qparam->max.clear(); } diff --git a/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp index cebafd3..21b4fe1 100644 --- a/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp +++ b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp @@ -1088,6 +1088,31 @@ private: luci::CircleConst *_const = nullptr; }; +class ReduceMaxTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({4, 3, 2}, {2}); + + _axis = create_const(g(), {4}, {1, 0, -3, -3}); + _reduce_max = g()->nodes()->create(); + { + _reduce_max->input(input()); + _reduce_max->reduction_indices(_axis); + _reduce_max->name("test"); + _reduce_max->keep_dims(false); + } + output()->from(_reduce_max); + + set_minmax_to_non_const(g(), -1, 1); + } + +private: + luci::CircleReduceMax *_reduce_max = nullptr; + luci::CircleConst *_axis = nullptr; +}; + class ResizeBilinearTestGraph final : public SimpleTestGraph { public: @@ -2345,6 +2370,34 @@ TEST(QuantizedModelVerifierTest, Pow_wrong_granularity_NEG) SUCCEED(); } +TEST(QuantizedModelVerifierTest, ReduceMax) +{ + TEST_WITH_GRAPH(ReduceMaxTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ReduceMaxTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ReduceMaxTestGraph, Type::S16, Granularity::ChannelWise); + + TEST_WITH_LAYER_INFO(ReduceMaxTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_LAYER_INFO(ReduceMaxTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_LAYER_INFO(ReduceMaxTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ReduceMax_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ReduceMaxTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(ReduceMaxTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(ReduceMaxTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ReduceMax_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(ReduceMaxTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(ReduceMaxTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(ReduceMaxTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + TEST(QuantizedModelVerifierTest, ResizeBilinear) { TEST_WITH_GRAPH(ResizeBilinearTestGraph, Type::U8, Granularity::LayerWise); diff --git a/compiler/luci/pass/src/RemoveRedundantDequantizePass.cpp b/compiler/luci/pass/src/RemoveRedundantDequantizePass.cpp new file mode 100644 index 0000000..66cd9d7 --- /dev/null +++ b/compiler/luci/pass/src/RemoveRedundantDequantizePass.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/RemoveRedundantDequantizePass.h" + +#include + +namespace +{ + +bool remove_redundant_dequant(luci::CircleDequantize *dequant) +{ + assert(dequant != nullptr); + + auto prev = loco::must_cast(dequant->input()); + if (prev->dtype() != loco::DataType::FLOAT32) + return false; + + replace(dequant).with(prev); + + return true; +} + +} // namespace + +namespace luci +{ +/** + * Dequantize Op does the below things on the ifm. + * 1. Element-wise update of quantized values (u8/s16) to fp32 values + * 2. Update dtype to fp32 + * If the previous node is not quantized, dequantize Op is redundant. + * + * BEFORE + * + * [CircleNode (A)] + * | + * [CircleNode (B)] (fp32) + * | + * [CircleDequantize] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode (A)] + * | + * [CircleNode (B)] (fp32) + * | + * [CircleNode] + */ +bool RemoveRedundantDequantizePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto target_node = dynamic_cast(node); + if (target_node != nullptr) + { + if (remove_redundant_dequant(target_node)) + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveRedundantDequantizePass.test.cpp b/compiler/luci/pass/src/RemoveRedundantDequantizePass.test.cpp new file mode 100644 index 0000000..adb2f14 --- /dev/null +++ b/compiler/luci/pass/src/RemoveRedundantDequantizePass.test.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/RemoveRedundantDequantizePass.h" + +#include + +#include + +#include + +namespace +{ + +using namespace luci::test; + +class DequantizeGraphlet +{ +public: + DequantizeGraphlet() = default; + +public: + void init(loco::Graph *g) + { + _dequantize = g->nodes()->create(); + _dequantize->dtype(loco::DataType::FLOAT32); + _dequantize->name("dequantize"); + } + +protected: + luci::CircleDequantize *_dequantize = nullptr; +}; + +class RedundantDequantizeGraph : public TestIOGraph, public DequantizeGraphlet +{ +public: + RedundantDequantizeGraph() = default; + +public: + void init(void) + { + TestIOGraph::init({1}, {1}); + DequantizeGraphlet::init(g()); + + _dequantize->input(input()); + + output()->from(_dequantize); + } + + void init_u8_input(void) + { + TestIOGraph::init({1}, {1}); + DequantizeGraphlet::init(g()); + + // Use u8 input (dequantize is not redundant anymore) + input()->dtype(loco::DataType::U8); + { + auto qparam = std::make_unique(); + qparam->scale = {1}; + qparam->zerop = {1}; + input()->quantparam(std::move(qparam)); + } + + _dequantize->input(input()); + + output()->from(_dequantize); + } +}; + +} // namespace + +TEST(RemoveRedundantDequantizePass, single_redundant_dequantize) +{ + RedundantDequantizeGraph g; + luci::RemoveRedundantDequantizePass pass; + + g.init(); + + EXPECT_TRUE(pass.run(g.g())); + + int count = 0; + for (auto node : loco::active_nodes(loco::output_nodes(g.g()))) + { + if (dynamic_cast(node)) + { + count++; + } + } + + ASSERT_EQ(0, count); +} + +TEST(RemoveRedundantDequantizePass, wrong_dtype_NEG) +{ + RedundantDequantizeGraph g; + luci::RemoveRedundantDequantizePass pass; + + g.init_u8_input(); + + EXPECT_FALSE(pass.run(g.g())); +} diff --git a/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.cpp b/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.cpp new file mode 100644 index 0000000..476ec68 --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/RemoveUnnecessaryReshapeNetPass.h" + +#include + +namespace +{ + +bool acceptable_intermediate_op(const loco::Node *node) +{ + if (not node) + return false; + + const auto opcode = loco::must_cast(node)->opcode(); + + switch (opcode) + { + case luci::CircleOpcode::ADD: + case luci::CircleOpcode::MUL: + case luci::CircleOpcode::TANH: + case luci::CircleOpcode::LOGISTIC: + break; + + default: + return false; + } + + return true; +} + +bool same_shape(const loco::Node *a, const loco::Node *b) +{ + auto a_cnode = loco::must_cast(a); + auto b_cnode = loco::must_cast(b); + + if (a_cnode->rank() != b_cnode->rank()) + return false; + + for (uint32_t i = 0; i < a_cnode->rank(); i++) + { + if (not(a_cnode->dim(i) == b_cnode->dim(i))) + return false; + } + return true; +} + +class PreReshapeFinder +{ +public: + PreReshapeFinder(const luci::CircleReshape *post_reshape) : _post_reshape(post_reshape) + { + assert(post_reshape != nullptr); // FIX_CALLER_UNLESS + } + +public: + // Return true if pre_reshapes are found + bool collect_pre_reshapes(loco::Node *node) + { + // TODO Support diamond case + if (loco::succs(node).size() != 1) + return false; + + if (auto pre_reshape = dynamic_cast(node)) + { + // Check ifm of pre-reshape and ofm of post_reshape + if (not same_shape(pre_reshape->tensor(), _post_reshape)) + return false; + + // Check ofm of pre-reshape and ifm of post_reshape + if (not same_shape(pre_reshape, _post_reshape->tensor())) + return false; + + _pre_reshapes.emplace_back(pre_reshape); + return true; + } + + if (not acceptable_intermediate_op(node)) + return false; + + for (uint32_t i = 0; i < node->arity(); i++) + { + if (not collect_pre_reshapes(node->arg(i))) + return false; + } + + return true; + } + +public: + std::vector pre_reshapes(void) const { return _pre_reshapes; } + +private: + const luci::CircleReshape *_post_reshape = nullptr; + std::vector _pre_reshapes; +}; + +bool remove_unnecessary_reshape_net(luci::CircleReshape *reshape) +{ + PreReshapeFinder finder(reshape); + if (not finder.collect_pre_reshapes(reshape->tensor())) + return false; + + // Remove pre_reshapes + for (auto pre_reshape : finder.pre_reshapes()) + { + loco::replace(pre_reshape).with(pre_reshape->tensor()); + } + + // Remove post_reshape + loco::replace(reshape).with(reshape->tensor()); + + return true; +} + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * + * [CircleNode] + * | + * [CircleReshape_1] (shape: A -> B) + * | + * [CircleNode] (ex: Add/Mul/Tanh/Logistic ..) + * | + * [CircleReshape_2] (shape: B -> A) + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * | \ + * | [CircleReshape_1] + * [CircleNode] + * | \ + * | [CircleReshape_2] + * [CircleNode] + **/ +bool RemoveUnnecessaryReshapeNetPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto reshape_node = dynamic_cast(node)) + { + if (remove_unnecessary_reshape_net(reshape_node)) + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.test.cpp b/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.test.cpp new file mode 100644 index 0000000..4ad707b --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryReshapeNetPass.test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/RemoveUnnecessaryReshapeNetPass.h" + +#include + +#include + +namespace +{ + +class RemoveUnnecessaryReshapeNet : public ::testing::Test +{ +public: + RemoveUnnecessaryReshapeNet() {} + + void createReshapeConst(luci::CircleReshape *target, const std::vector shape) + { + auto shape_const = g.nodes()->create(); + shape_const->dtype(loco::DataType::S32); + shape_const->size(shape.size()); + shape_const->shape_status(luci::ShapeStatus::VALID); + shape_const->rank(1); + shape_const->dim(0).set(shape.size()); + for (int32_t i = 0; i < shape.size(); i++) + { + shape_const->at(i) = static_cast(shape.at(i)); + } + shape_const->name("shape_const"); + target->shape(shape_const); + target->rank(shape.size()); + for (uint32_t i = 0; i < shape.size(); i++) + { + target->dim(i) = shape[i]; + } + target->shape_status(luci::ShapeStatus::VALID); + } + + void buildGraph(const std::initializer_list base_shape, + const std::initializer_list first_shape, + const std::initializer_list second_shape) + { + // Input Create. + input = g.nodes()->create(); + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + input->shape_status(luci::ShapeStatus::VALID); + input->shape(base_shape); + input->name("input"); + + // Create first reshape. + first_reshape = g.nodes()->create(); + first_reshape->tensor(input); + first_reshape->name("Reshape"); + createReshapeConst(first_reshape, first_shape); + + // Create logistic. + logistic = g.nodes()->create(); + logistic->x(first_reshape); + logistic->name("logistic"); + logistic->shape(first_shape); + logistic->shape_status(luci::ShapeStatus::VALID); + + // Create second reshape. + second_reshape = g.nodes()->create(); + second_reshape->tensor(logistic); + second_reshape->name("second_reshape"); + createReshapeConst(second_reshape, second_shape); + + // Output Connect. + output = g.nodes()->create(); + output->from(second_reshape); + output->name("output"); + auto graph_output = g.outputs()->create(); + output->index(graph_output->index()); + } + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleReshape *first_reshape = nullptr; + luci::CircleLogistic *logistic = nullptr; + luci::CircleReshape *second_reshape = nullptr; + luci::CircleOutput *output = nullptr; +}; + +} // namespace + +TEST_F(RemoveUnnecessaryReshapeNet, simple_case) +{ + buildGraph({1, 1, 1, 32}, {1, 1, 32, 1}, {1, 1, 1, 32}); + luci::RemoveUnnecessaryReshapeNetPass pass; + + ASSERT_TRUE(pass.run(&g)); + + int count = 0; + for (auto node : loco::active_nodes(loco::output_nodes(&g))) + { + if (auto reshape = dynamic_cast(node)) + count++; + } + ASSERT_EQ(0, count); +} + +TEST_F(RemoveUnnecessaryReshapeNet, shape_mismatch_NEG) +{ + buildGraph({1, 1, 1, 32}, {1, 1, 32, 1}, {1, 1, 2, 16}); + luci::RemoveUnnecessaryReshapeNetPass pass; + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp new file mode 100644 index 0000000..741b709 --- /dev/null +++ b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 +#include +#include + +namespace +{ + +// TODO move to global helper list if needed +/** + * @brief Create a node with `inp` as input from fused activation fucntion `act` + */ +luci::CircleNode *fromActivation(luci::CircleNode *inp, luci::FusedActFunc act) +{ + switch (act) + { + case luci::FusedActFunc::NONE: + return inp; + case luci::FusedActFunc::RELU: + { + auto n = inp->graph()->nodes()->create(); + n->features(inp); + return n; + } + case luci::FusedActFunc::RELU6: + { + auto n = inp->graph()->nodes()->create(); + n->features(inp); + return n; + } + case luci::FusedActFunc::RELU_N1_TO_1: + { + auto n = inp->graph()->nodes()->create(); + n->features(inp); + return n; + } + case luci::FusedActFunc::TANH: + { + auto n = inp->graph()->nodes()->create(); + n->x(inp); + return n; + } + case luci::FusedActFunc::SIGN_BIT: + { + throw std::invalid_argument("no matching node to create from fused activation"); + } + default: + throw std::invalid_argument("invalid fused activation"); + } +} + +/** + * Replace Fully Connected with Batched MatMul + * + * BEFORE + * + * [Node1] [Node2] + * | | + * [transpose]? [transpose]? + * \ / + * [FullyConnected] + * + * AFTER + * + * [Node1] [Node2] + * \ / + * [BatchMatMul] [BiasValue]? + * \ / + * [Add]? + * | + * [Activation]? + * + * Nodes with "?" denote optional elements + */ +bool replace_fc_with_matmul(luci::CircleFullyConnected *fc) +{ + luci::CircleNode *x = nullptr; + luci::CircleNode *y = nullptr; + luci::CircleNode *b = nullptr; + luci::CircleTranspose *ty = nullptr; + luci::CircleTranspose *tx = nullptr; + bool adj_x = false; + bool adj_y = true; + + if (dynamic_cast(fc->weights())) + return false; // NonConst + + if ((ty = dynamic_cast(fc->weights()))) // is y a transpose? + { + adj_y = false; + if (dynamic_cast(ty->a())) + return false; + else + y = loco::must_cast(ty->a()); + } + else + { // y is not transpose and not const + y = loco::must_cast(fc->weights()); + } + if ((tx = dynamic_cast(fc->input()))) + { + adj_x = true; + x = loco::must_cast(tx->a()); + } + else + { + x = loco::must_cast(fc->input()); + } + + b = loco::must_cast(fc->bias()); + + if (x->dtype() != loco::DataType::FLOAT32 || y->dtype() != loco::DataType::FLOAT32 || + b->dtype() != loco::DataType::FLOAT32) + return false; + + auto name = fc->name(); + assert(name.length() > 0); + + auto matmul = fc->graph()->nodes()->create(); + matmul->x(x); + matmul->y(y); + matmul->adj_x(adj_x); + matmul->adj_y(adj_y); + matmul->name(name); + matmul->dtype(fc->dtype()); + + luci::add_origin(matmul, luci::get_origin(fc)); + + auto all_zero = [](const luci::CircleConst *c) { + bool ac = true; + for (uint32_t i = 0; i < c->size() && ac; i++) + { + ac &= c->at(i) == 0.0f; + } + return ac; + }; + + auto bc = dynamic_cast(b); + if ((nullptr != bc) && !all_zero(bc)) + { + auto bias_add = fc->graph()->nodes()->create(); + bias_add->x(matmul); + bias_add->y(b); + bias_add->name(fc->name() + "/bias_add"); + bias_add->dtype(fc->dtype()); + add_origin(bias_add, get_origin(fc)); + bias_add->fusedActivationFunction(fc->fusedActivationFunction()); + loco::replace(fc).with(bias_add); + } + else + { + auto n = fromActivation(matmul, fc->fusedActivationFunction()); + add_origin(n, luci::get_origin(fc)); + n->name(fc->name() + "fusedActivation"); + n->dtype(fc->dtype()); + loco::replace(fc).with(n); + } + + return true; +} +} // namespace + +namespace luci +{ + +bool ReplaceNonConstFCWithBatchMatMulPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto fc = dynamic_cast(node)) + { + if (replace_fc_with_matmul(fc)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp new file mode 100644 index 0000000..7606a61 --- /dev/null +++ b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h" + +#include +#include + +#include + +namespace +{ + +using namespace luci::test; + +// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp +template +luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, + const std::vector &shape, + const std::vector &values) +{ + auto node = g->nodes()->create(); + node->dtype(dtype); + node->rank(shape.size()); + + uint32_t size = 1; + for (uint32_t i = 0; i < shape.size(); ++i) + { + node->dim(i) = shape.at(i); + size *= shape.at(i); + } + node->shape_status(luci::ShapeStatus::VALID); + +#define INIT_VALUES(DT) \ + { \ + node->size
(size); \ + for (uint32_t i = 0; i < values.size(); ++i) \ + node->at
(i) = values[i]; \ + } + + switch (dtype) + { + case loco::DataType::U8: + INIT_VALUES(loco::DataType::U8); + break; + case loco::DataType::S16: + INIT_VALUES(loco::DataType::S16); + break; + case loco::DataType::S32: + INIT_VALUES(loco::DataType::S32); + break; + case loco::DataType::FLOAT32: + INIT_VALUES(loco::DataType::FLOAT32) + break; + default: + INTERNAL_EXN("create_const_node called with unsupported type"); + break; + } + return node; +} + +/** + * Simple graph for test + * + * BEFORE + * + * [IFM1] [IFM2] [BIAS] + * \ | / + * [FC] + * | + * [Res] + * + * AFTER + * [IFM1] [IFM2] + * \ | + * [BatchMatMul] [BIAS] + * \ / + * [Add] + * | + * [Res] + * + */ +struct FCGraphlet +{ +public: + FCGraphlet() = default; + virtual ~FCGraphlet() = default; + + void init(loco::Graph *g, const ShapeU32 r_shape, const float bv) + { + _tr_y = g->nodes()->create(); + _tr_y->a(_y); + std::vector tr_val = {1, 0}; + _tr_y->perm(create_const_node(g, loco::DataType::S32, {2}, tr_val)); + + _fc = g->nodes()->create(); + _fc->input(_x); + _fc->weights(_tr_y); + _fc->fusedActivationFunction(luci::FusedActFunc::NONE); + _fc->dtype(loco::DataType::FLOAT32); + _fc->shape(r_shape); + auto l = _fc->dim(_fc->rank() - 1).value(); + std::vector bias_val(l, bv); + _fc->bias(create_const_node(g, loco::DataType::FLOAT32, {l}, bias_val)); + _fc->name("fc"); + } + +public: + luci::CircleFullyConnected *fc() { return _fc; } + +protected: + luci::CircleFullyConnected *_fc = nullptr; + luci::CircleTranspose *_tr_y = nullptr; + luci::CircleInput *_x = nullptr; + luci::CircleInput *_y = nullptr; +}; + +struct FCGraph : public TestIsGraphlet<2>, public TestOGraphlet, public FCGraphlet +{ + FCGraph() = default; + virtual ~FCGraph() = default; + void init(const ShapeU32 x_shape, const ShapeU32 y_shape, const ShapeU32 r_shape, const float bv) + { + TestIsGraphlet<2>::init(g(), {x_shape, y_shape}); + TestOGraphlet::init(g(), r_shape); + _x = input(0); + _y = input(1); + FCGraphlet::init(g(), r_shape, bv); + output()->from(_fc); + } +}; + +class ReplaceNonConstFCWithBatchMatMulPassTest : public ::testing::Test +{ +public: + FCGraph g; + luci::ReplaceNonConstFCWithBatchMatMulPass pass; +}; + +} // namespace + +TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, simple_test) +{ + g.init({2, 3}, {2, 3}, {2, 2}, 0.0f); + + auto ret = pass.run(g.g()); + EXPECT_EQ(true, ret); + + auto mm = dynamic_cast(g.output()->from()); + EXPECT_NE(nullptr, mm); +} + +TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, nonzero_bias_test) +{ + g.init({2, 3}, {2, 3}, {2, 2}, 1.0f); + + auto ret = pass.run(g.g()); + EXPECT_EQ(true, ret); + + auto mm = dynamic_cast(g.output()->from()); + EXPECT_NE(nullptr, mm); +} + +TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, wrong_op_NEG) +{ + loco::Graph g; + + auto inp = g.nodes()->create(); + auto relu = g.nodes()->create(); + relu->features(inp); + + luci::ReplaceNonConstFCWithBatchMatMulPass pass; + auto changed = pass.run(&g); + + EXPECT_EQ(false, changed); +} diff --git a/compiler/luci/pass/src/ResolveCustomOpSplitVPass.cpp b/compiler/luci/pass/src/ResolveCustomOpSplitVPass.cpp new file mode 100644 index 0000000..a650658 --- /dev/null +++ b/compiler/luci/pass/src/ResolveCustomOpSplitVPass.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/ResolveCustomOpSplitVPass.h" + +#include +#include +#include + +namespace +{ + +// Input node is const S64 +// Return s32 version of node +// Return nullptr if s64 value is out of range of s32 +luci::CircleConst *s64_to_s32(luci::CircleConst *node) +{ + assert(node); + assert(node->dtype() == loco::DataType::S64); + + auto cloned = luci::clone(node); + luci::add_origin(cloned, luci::get_origin(node)); + + const auto num_elems = node->size(); + + cloned->dtype(loco::DataType::S32); + cloned->size(num_elems); + + for (uint32_t i = 0; i < num_elems; i++) + { + int64_t val = node->at(i); + if (val < std::numeric_limits::min() or val > std::numeric_limits::max()) + return nullptr; + + cloned->at(i) = static_cast(val); + } + + return cloned; +} + +/** BEFORE + * + * [CircleNode] + * \ + * \ [size_splits] [split_dim] + * \ | / + * [CircleCustom(SplitV))] + * | + * [CircleCustomOut] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * | \ + * | \ [size_splits] [split_dim] + * | \ | / + * | \ | / + * | \ | / + * [CircleCustom(SplitV)] [CircleSplitV] + * | | + * [CircleCustomOut] [CircleSplitVOut] + * | + * [CircleNode] + */ +bool resolve_splitv(luci::CircleCustom *node) +{ + const std::string custom_code = node->custom_code(); + const std::vector custom_options = node->custom_options(); + + if (custom_code != "SplitV") + return false; + + if (node->numInputs() != 3) + return false; + + auto size_splits = dynamic_cast(node->inputs(1)); + if (not size_splits) + return false; + + // Convert size_splits to S32, because luci-interpeter does not support + // S64 size_splits yet + // TODO Support S64 size_splits + if (size_splits->dtype() == loco::DataType::S64) + { + size_splits = s64_to_s32(size_splits); + if (not size_splits) + return false; + } + if (size_splits->dtype() != loco::DataType::S32) + return false; + + auto split_dim = dynamic_cast(node->inputs(2)); + if (not split_dim) + return false; + + if (split_dim->dtype() == loco::DataType::S64) + { + split_dim = s64_to_s32(split_dim); + if (not split_dim) + return false; + } + if (split_dim->dtype() != loco::DataType::S32) + return false; + + if (size_splits->rank() != 1) + return false; + + const auto num_split = size_splits->dim(0).value(); + + auto split_v = node->graph()->nodes()->create(); + split_v->input(node->inputs(0)); + split_v->size_splits(size_splits); + split_v->split_dim(split_dim); + split_v->num_split(num_split); + split_v->name(node->name()); + luci::add_origin(split_v, luci::get_origin(node)); + + int32_t i = 0; + const auto succs = loco::succs(node); + for (auto succ : succs) + { + auto custom_out = loco::must_cast(succ); // FIX_CALLER_UNLESS + + auto split_v_out = node->graph()->nodes()->create(); + split_v_out->input(split_v); + split_v_out->name(node->name() + "_out_" + std::to_string(i)); + split_v_out->index(i++); + luci::add_origin(split_v_out, luci::get_origin(node)); + loco::replace(custom_out).with(split_v_out); + } + + return true; +} + +} // namespace + +namespace luci +{ + +bool ResolveCustomOpSplitVPass::run(loco::Graph *g) +{ + bool changed = false; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto cop = dynamic_cast(node); + if (not cop) + continue; + + if (resolve_splitv(cop)) + changed = true; + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ResolveCustomOpSplitVPass.test.cpp b/compiler/luci/pass/src/ResolveCustomOpSplitVPass.test.cpp new file mode 100644 index 0000000..e7738aa --- /dev/null +++ b/compiler/luci/pass/src/ResolveCustomOpSplitVPass.test.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Pass/ResolveCustomOpSplitVPass.h" + +#include + +#include +#include + +using namespace luci::test; + +namespace +{ + +/** + * graph having Custom operator SplitV + * + * [Input] [Const] [Const] + * \ | / + * [Custom(SplitV)] + * / | \ + * [CustomOut] [CustomOut] [CustomOut] + * | | | + * [Output] [Output] [Output] + */ +class SplitVGraphlet +{ +public: + SplitVGraphlet() = default; + +public: + void init(loco::Graph *g) + { + // CircleCustom(SplitV) + _splitv = g->nodes()->create(3, 3); + _splitv->custom_code("SplitV"); + _splitv->shape({1, 2, 2, 192}); + _splitv->dtype(loco::DataType::FLOAT32); + _splitv->name("splitv"); + + // CircleConst + auto size_splits = g->nodes()->create(); + size_splits->dtype(loco::DataType::S64); + size_splits->shape({3}); + size_splits->size(3); + size_splits->at(0) = 32; + size_splits->at(1) = 32; + size_splits->at(2) = 128; + + // CircleConst + auto split_dim = g->nodes()->create(); + split_dim->dtype(loco::DataType::S32); + split_dim->rank(0); + split_dim->size(1); + split_dim->scalar() = 3; + + _splitv->inputs(1, size_splits); + _splitv->inputs(2, split_dim); + + // CircleCustomOut + _splitv_out1 = g->nodes()->create(); + _splitv_out1->shape({1, 2, 2, 32}); + _splitv_out1->dtype(loco::DataType::FLOAT32); + _splitv_out1->index(0); + _splitv_out1->input(_splitv); + + // CircleCustomOut + _splitv_out2 = g->nodes()->create(); + _splitv_out2->shape({1, 2, 2, 32}); + _splitv_out2->dtype(loco::DataType::FLOAT32); + _splitv_out2->index(1); + _splitv_out2->input(_splitv); + + // CircleCustomOut + _splitv_out3 = g->nodes()->create(); + _splitv_out3->shape({1, 2, 2, 128}); + _splitv_out3->dtype(loco::DataType::FLOAT32); + _splitv_out3->index(2); + _splitv_out3->input(_splitv); + } + +public: + luci::CircleCustom *splitv() { return _splitv; } + +protected: + luci::CircleCustom *_splitv = nullptr; + luci::CircleCustomOut *_splitv_out1 = nullptr; + luci::CircleCustomOut *_splitv_out2 = nullptr; + luci::CircleCustomOut *_splitv_out3 = nullptr; +}; + +class SplitVGraph : public TestIGraphlet, public TestOsGraphlet<3>, public SplitVGraphlet +{ +public: + SplitVGraph() = default; + + void init(void) + { + TestIGraphlet::init(g(), {1, 2, 2, 192}); + TestOsGraphlet<3>::init(g(), {{1, 2, 2, 32}, {1, 2, 2, 32}, {1, 2, 2, 128}}); + SplitVGraphlet::init(g()); + + // connect graph + _splitv->inputs(0, input()); + + output(0)->from(_splitv_out1); + output(1)->from(_splitv_out2); + output(2)->from(_splitv_out3); + } +}; + +class SplitVGraphTest : public ::testing::Test +{ +public: + SplitVGraph g; + luci::ResolveCustomOpSplitVPass pass; +}; + +} // namespace + +TEST_F(SplitVGraphTest, simple_test) +{ + g.init(); + + auto ret = pass.run(g.g()); + EXPECT_EQ(true, ret); + + auto svo_1 = dynamic_cast(g.output(0)->from()); + EXPECT_NE(nullptr, svo_1); + auto svo_2 = dynamic_cast(g.output(1)->from()); + EXPECT_NE(nullptr, svo_2); + auto svo_3 = dynamic_cast(g.output(2)->from()); + EXPECT_NE(nullptr, svo_3); + + auto sv = dynamic_cast(svo_1->input()); + EXPECT_NE(nullptr, sv); + sv = dynamic_cast(svo_2->input()); + EXPECT_NE(nullptr, sv); + sv = dynamic_cast(svo_3->input()); + EXPECT_NE(nullptr, sv); + + auto size_splits = loco::must_cast(sv->size_splits()); + EXPECT_EQ(loco::DataType::S32, size_splits->dtype()); + EXPECT_EQ(32, size_splits->at(0)); + EXPECT_EQ(32, size_splits->at(1)); + EXPECT_EQ(128, size_splits->at(2)); + + auto split_dim = loco::must_cast(sv->split_dim()); + EXPECT_EQ(loco::DataType::S32, split_dim->dtype()); + EXPECT_EQ(3, split_dim->scalar()); +} + +TEST_F(SplitVGraphTest, wrong_op_NEG) +{ + g.init(); + + g.splitv()->custom_code("AddV2"); + + auto ret = pass.run(g.g()); + EXPECT_EQ(false, ret); +} diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h b/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h index 442183c..408e6b8 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h +++ b/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h @@ -197,6 +197,13 @@ private: return true; } + bool visit(const luci::CircleReduceMax *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + bool visit(const luci::CircleRelu *node) { RETURN_FALSE_UNLESS(is_lwq(node)); diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp b/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp index 4e1c062..cf86aca 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp +++ b/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp @@ -302,6 +302,15 @@ bool VerifyQuantizedNodeTypeBase::visit(const luci::CirclePow *nod } template +bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleReduceMax *node) +{ + RETURN_FALSE_UNLESS(has_type(node, Qtype)) + RETURN_FALSE_UNLESS(has_type(node->input(), Qtype)) + RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), loco::DataType::S32)) + return true; +} + +template bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleRelu *node) { return group_has_type(node, Qtype); diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeType.h b/compiler/luci/pass/src/VerifyQuantizedNodeType.h index ff1acbd..789d3c7 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeType.h +++ b/compiler/luci/pass/src/VerifyQuantizedNodeType.h @@ -104,6 +104,7 @@ private: bool visit(const luci::CirclePadV2 *node); bool visit(const luci::CirclePRelu *node); bool visit(const luci::CirclePow *node); + bool visit(const luci::CircleReduceMax *node); bool visit(const luci::CircleRelu *node); bool visit(const luci::CircleReshape *node); bool visit(const luci::CircleResizeBilinear *node); diff --git a/compiler/luci/pass/src/helpers/SparsityFormatConverter.cpp b/compiler/luci/pass/src/helpers/SparsityFormatConverter.cpp new file mode 100644 index 0000000..72b7d60 --- /dev/null +++ b/compiler/luci/pass/src/helpers/SparsityFormatConverter.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +// codes under namespace sparsity referenced from +// https://github.com/tensorflow/tensorflow/blob/3f878cff5b698b82eea85db2b60d65a2e320850e/ +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.h +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.cc + +#include "SparsityFormatConverter.h" + +#include + +#include + +namespace sparsity +{ + +namespace +{ + +uint64_t GetFlattenedIndex(const std::vector &indices, const std::vector &shape) +{ + uint64_t index = 0; + int sub_elements = 1; + for (int i = shape.size() - 1; i >= 0; i--) + { + index += indices[i] * sub_elements; + sub_elements *= shape[i]; + } + return index; +} + +std::vector TfLiteIntArrayToVector(const TfLiteIntArray *int_array) +{ + std::vector values; + if (!int_array) + { + return values; + } + + values.resize(int_array->size); + for (int i = 0; i < int_array->size; i++) + { + values[i] = int_array->data[i]; + } + + return values; +} + +} // namespace + +template +FormatConverter::FormatConverter(const std::vector &shape, const TfLiteSparsity &sparsity) +{ + auto traversal_order = TfLiteIntArrayToVector(sparsity.traversal_order); + auto block_map = TfLiteIntArrayToVector(sparsity.block_map); + + std::vector format(sparsity.dim_metadata_size); + std::vector dense_size(sparsity.dim_metadata_size); + std::vector> segments(sparsity.dim_metadata_size); + std::vector> indices(sparsity.dim_metadata_size); + for (int i = 0; i < sparsity.dim_metadata_size; i++) + { + format[i] = sparsity.dim_metadata[i].format; + dense_size[i] = sparsity.dim_metadata[i].dense_size; + segments[i] = TfLiteIntArrayToVector(sparsity.dim_metadata[i].array_segments); + indices[i] = TfLiteIntArrayToVector(sparsity.dim_metadata[i].array_indices); + } + + InitSparseToDenseConverter(shape, std::move(traversal_order), std::move(format), + std::move(dense_size), std::move(segments), std::move(indices), + std::move(block_map)); +} + +template +void FormatConverter::InitSparseToDenseConverter( + std::vector shape, std::vector traversal_order, std::vector format, + std::vector dense_size, std::vector> segments, + std::vector> indices, std::vector block_map) +{ + dense_shape_ = std::move(shape); + traversal_order_ = std::move(traversal_order); + block_map_ = std::move(block_map); + format_ = std::move(format); + + dense_size_ = 1; + for (size_t i = 0; i < dense_shape_.size(); i++) + { + dense_size_ *= dense_shape_[i]; + } + + dim_metadata_.resize(2 * format_.size()); + for (size_t i = 0; i < format_.size(); i++) + { + if (format_[i] == kTfLiteDimDense) + { + dim_metadata_[2 * i] = {dense_size[i]}; + } + else + { + dim_metadata_[2 * i] = std::move(segments[i]); + dim_metadata_[2 * i + 1] = std::move(indices[i]); + } + } + + int original_rank = dense_shape_.size(); + int block_dim = 0; + + blocked_shape_.resize(original_rank); + block_size_.resize(block_map_.size()); + for (int i = 0; i < original_rank; i++) + { + if (block_dim < (int)block_map_.size() && block_map_[block_dim] == i) + { + if (original_rank + block_dim < (int)traversal_order_.size()) + { + int orig_dim = traversal_order_[original_rank + block_dim]; + block_size_[block_dim] = dense_size[orig_dim]; + blocked_shape_[i] = dense_shape_[i] / dense_size[orig_dim]; + block_dim++; + } + } + else + { + blocked_shape_[i] = dense_shape_[i]; + } + } +} + +template +void FormatConverter::Populate(const T *src_data, std::vector indices, int level, + int prev_idx, int *src_data_ptr, T *dest_data) +{ + if (static_cast(level) == indices.size()) + { + int orig_rank = dense_shape_.size(); + std::vector orig_idx; + orig_idx.resize(orig_rank); + int i = 0; + for (; static_cast(i) < orig_idx.size(); i++) + { + int orig_dim = traversal_order_[i]; + orig_idx[orig_dim] = indices[i]; + } + + for (; static_cast(i) < indices.size(); i++) + { + const int block_idx = traversal_order_[i] - orig_rank; + const int orig_dim = block_map_[block_idx]; + orig_idx[orig_dim] = orig_idx[orig_dim] * block_size_[block_idx] + indices[i]; + } + + dest_data[GetFlattenedIndex(orig_idx, dense_shape_)] = src_data[*src_data_ptr]; + + *src_data_ptr = *src_data_ptr + 1; + return; + } + + const int metadata_idx = 2 * level; + const int shape_of_level = dim_metadata_[metadata_idx][0]; + if (format_[level] == kTfLiteDimDense) + { + for (int i = 0; i < shape_of_level; i++) + { + indices[level] = i; + Populate(src_data, indices, level + 1, prev_idx * shape_of_level + i, src_data_ptr, + dest_data); + } + } + else if (static_cast(prev_idx + 1) < dim_metadata_[metadata_idx].size()) + { + const auto &array_segments = dim_metadata_[metadata_idx]; + const auto &array_indices = dim_metadata_[metadata_idx + 1]; + for (int i = array_segments[prev_idx]; i < array_segments[prev_idx + 1]; i++) + { + if (static_cast(i) < array_indices.size() && + static_cast(level) < indices.size()) + { + indices[level] = array_indices[i]; + Populate(src_data, indices, level + 1, i, src_data_ptr, dest_data); + } + } + } +} + +template bool FormatConverter::SparseToDense(const T *src_data) +{ + data_.resize(dense_size_); + std::fill(data_.begin(), data_.end(), T(0)); + + int total_rank = traversal_order_.size(); + int src_data_ptr = 0; + std::vector indices(total_rank); + Populate(src_data, indices, 0, 0, &src_data_ptr, data_.data()); + + return true; +} + +template class FormatConverter; +template class FormatConverter; + +} // namespace sparsity + +#include + +namespace luci +{ + +sparsity::TfLiteDimensionType to_tflite_sparsity(luci::DimensionType dt) +{ + switch (dt) + { + case luci::DimensionType::DENSE: + return sparsity::TfLiteDimensionType::kTfLiteDimDense; + case luci::DimensionType::SPARSE_CSR: + return sparsity::TfLiteDimensionType::kTfLiteDimSparseCSR; + } + return sparsity::TfLiteDimensionType::kTfLiteDimDense; +} + +sparsity::TfLiteIntArray *to_tflite_sparsity(const luci::SparseIndexVector &data) +{ + auto type = data.type(); + switch (type) + { + case luci::SparseIndexVectorType::NONE: + { + std::vector empty; + return makeTfLiteArray(empty); + } + case luci::SparseIndexVectorType::I32: + return makeTfLiteArray(*data.as_int32_vector()); + case luci::SparseIndexVectorType::U16: + return makeTfLiteArray(*data.as_uint16_vector()); + case luci::SparseIndexVectorType::U8: + return makeTfLiteArray(*data.as_uint8_vector()); + default: + INTERNAL_EXN_V("unsupported SparseIndexVectorType", oops::to_uint32(type)); + } +} + +sparsity::TfLiteSparsity to_tflite_sparsity(const luci::SparsityParam *sp) +{ + sparsity::TfLiteSparsity tflsp; + tflsp.traversal_order = makeTfLiteArray(sp->traversal_order); + tflsp.block_map = makeTfLiteArray(sp->block_map); + tflsp.dim_metadata = makeTfLiteDimensionMetadata(sp->dim_metadata); + tflsp.dim_metadata_size = sp->dim_metadata.size(); + return tflsp; +} + +template sparsity::TfLiteIntArray *makeTfLiteArray(const std::vector &data) +{ + size_t cn = data.size(); + size_t sz = 1 + data.size(); + sparsity::TfLiteIntArray *sp = (sparsity::TfLiteIntArray *)(new int[sz]); + sp->size = cn; + for (size_t i = 0; i < cn; ++i) + { + sp->data[i] = data[i]; + } + return sp; +} + +sparsity::TfLiteDimensionMetadata * +makeTfLiteDimensionMetadata(const std::vector &data) +{ + size_t cn = data.size(); + sparsity::TfLiteDimensionMetadata *tfldm = new sparsity::TfLiteDimensionMetadata[cn]; + + for (size_t i = 0; i < cn; ++i) + { + tfldm[i].format = to_tflite_sparsity(data[i].format()); + tfldm[i].dense_size = data[i].dense_size(); + tfldm[i].array_segments = to_tflite_sparsity(data[i].array_segments()); + tfldm[i].array_indices = to_tflite_sparsity(data[i].array_indices()); + } + + return tfldm; +} + +void freeTfLiteSparsity(sparsity::TfLiteSparsity &tflsp) +{ + assert(tflsp.traversal_order); + assert(tflsp.block_map); + delete[] tflsp.traversal_order; + delete[] tflsp.block_map; + + for (int i = 0; i < tflsp.dim_metadata_size; ++i) + { + assert(tflsp.dim_metadata[i].array_segments); + assert(tflsp.dim_metadata[i].array_indices); + delete[] tflsp.dim_metadata[i].array_segments; + delete[] tflsp.dim_metadata[i].array_indices; + } +} + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/SparsityFormatConverter.h b/compiler/luci/pass/src/helpers/SparsityFormatConverter.h new file mode 100644 index 0000000..fcd9bbc --- /dev/null +++ b/compiler/luci/pass/src/helpers/SparsityFormatConverter.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef __LUCI_PASS_HELPERS_SPARSITY_FORMAT_CONVERTER_H__ +#define __LUCI_PASS_HELPERS_SPARSITY_FORMAT_CONVERTER_H__ + +#include +#include + +// codes under namespace sparsity referenced from +// https://github.com/tensorflow/tensorflow/blob/3f878cff5b698b82eea85db2b60d65a2e320850e/ +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.h +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.cc + +namespace sparsity +{ + +// Storage format of each dimension in a sparse tensor. +typedef enum TfLiteDimensionType +{ + kTfLiteDimDense = 0, + kTfLiteDimSparseCSR, +} TfLiteDimensionType; + +// Fixed size list of integers. Used for dimensions and inputs/outputs tensor +// indices +typedef struct TfLiteIntArray +{ + int size; + int data[]; +} TfLiteIntArray; + +// Metadata to encode each dimension in a sparse tensor. +typedef struct TfLiteDimensionMetadata +{ + TfLiteDimensionType format; + int dense_size; + TfLiteIntArray *array_segments; + TfLiteIntArray *array_indices; +} TfLiteDimensionMetadata; + +// Parameters used to encode a sparse tensor. For detailed explanation of each +// field please refer to lite/schema/schema.fbs. +typedef struct TfLiteSparsity +{ + TfLiteIntArray *traversal_order; + TfLiteIntArray *block_map; + TfLiteDimensionMetadata *dim_metadata; + int dim_metadata_size; +} TfLiteSparsity; + +// A converter that keeps an internal representation of sparse tensor parameters +// and converts tensors between dense and sparse formats. +template class FormatConverter +{ +public: + /* Creates a sparse to dense converter. + * @param shape Shape of the target dense tensor. + * @param sparsity Sparsity parameter of the sparse TfLiteTensor. + */ + FormatConverter(const std::vector &shape, const TfLiteSparsity &sparsity); + + const std::vector &GetData() { return data_; } + const std::vector> &GetDimMetadata() { return dim_metadata_; } + + bool SparseToDense(const T *src_data); + +private: + // Helper function for initializing this converter for sparse to dense + // conversion. + void InitSparseToDenseConverter(std::vector shape, std::vector traversal_order, + std::vector format, + std::vector dense_size, + std::vector> segments, + std::vector> indices, + std::vector block_map); + + void Populate(const T *src_data, std::vector indices, int level, int prev_idx, + int *src_data_ptr, T *dest_data); + +private: + std::vector dense_shape_; + std::vector blocked_shape_; + size_t dense_size_; + std::vector traversal_order_; + std::vector format_; + std::vector block_size_; + std::vector block_map_; + std::vector> dim_metadata_; + std::vector data_; +}; + +extern template class FormatConverter; +extern template class FormatConverter; + +} // namespace sparsity + +#include + +namespace luci +{ + +sparsity::TfLiteDimensionType to_tflite_sparsity(luci::DimensionType dt); +sparsity::TfLiteIntArray *to_tflite_sparsity(const luci::SparseIndexVector &data); +sparsity::TfLiteSparsity to_tflite_sparsity(const luci::SparsityParam *sp); + +template sparsity::TfLiteIntArray *makeTfLiteArray(const std::vector &data); +sparsity::TfLiteDimensionMetadata * +makeTfLiteDimensionMetadata(const std::vector &data); + +void freeTfLiteSparsity(sparsity::TfLiteSparsity &tflsp); + +} // namespace luci + +#endif // __LUCI_PASS_HELPERS_SPARSITY_FORMAT_CONVERTER_H__ diff --git a/compiler/luci/requires.cmake b/compiler/luci/requires.cmake index e896188..0a5e6a5 100644 --- a/compiler/luci/requires.cmake +++ b/compiler/luci/requires.cmake @@ -10,4 +10,5 @@ require("oops") require("hermes") require("hermes-std") require("tflchef") +require("circlechef") require("tflite2circle") diff --git a/compiler/luci/service/src/CircleCloneNode.h b/compiler/luci/service/src/CircleCloneNode.h index 99e4561..95f06db 100644 --- a/compiler/luci/service/src/CircleCloneNode.h +++ b/compiler/luci/service/src/CircleCloneNode.h @@ -72,6 +72,7 @@ public: CloneNodeLet(loco::Graph *graph) : _graph(graph){}; public: + luci::CircleNode *visit(const luci::CircleDensify *) final; luci::CircleNode *visit(const luci::CircleDepthToSpace *) final; luci::CircleNode *visit(const luci::CircleDepthwiseConv2D *) final; luci::CircleNode *visit(const luci::CircleDequantize *) final; diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp index 9d156f3..a368fae 100644 --- a/compiler/luci/service/src/CircleShapeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp @@ -204,6 +204,7 @@ template loco::NodeShape broadcast_xy(const CIRCLENODE *node) return loco::NodeShape{inputs_shape}; \ } +DECLARE_USE_SINGLE(input); DECLARE_USE_SINGLE(inputs); DECLARE_USE_SINGLE(x); DECLARE_USE_SINGLE(logits); @@ -258,10 +259,10 @@ loco::NodeShape infer_add_n(const luci::CircleAddN *node) return loco::NodeShape{shape}; } -loco::NodeShape infer_arg_max(const luci::CircleArgMax *node) +template loco::NodeShape infer_arg_maxmin(const CIRCLENODE *node) { - auto input_shape = luci::shape_get(node->input()).as(); - auto dimension_shape = luci::shape_get(node->dimension()).as(); + auto input_shape = luci::shape_get(node->input()).template as(); + auto dimension_shape = luci::shape_get(node->dimension()).template as(); int64_t select_axis = 0; { @@ -271,55 +272,19 @@ loco::NodeShape infer_arg_max(const luci::CircleArgMax *node) // Support S32 for now. auto const_shape_node = loco::must_cast(node->dimension()); LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32, - "Only support int32 CircleConst for CircleArgMax"); + "Only support int32 CircleConst for CircleArgMax/CircleArgMin"); if (const_shape_node->rank() > 1) INTERNAL_EXN_V("Only support rank 0/1 CircleConst", oops::to_uint32(const_shape_node->rank())); - select_axis = const_shape_node->scalar(); - } - assert(select_axis < input_shape.rank()); - assert(select_axis >= 0); // TODO support minus of this breaks - - // NOTE select_axis is removed - loco::TensorShape shape_output; - uint32_t rank = input_shape.rank(); - uint32_t shrink = static_cast(select_axis); - assert(rank > 0); - shape_output.rank(rank - 1); - for (uint32_t r = 0, d = 0; r < rank; ++r) - { - if (r == shrink) - continue; - shape_output.dim(d++) = input_shape.dim(r); + select_axis = const_shape_node->template scalar(); } - return loco::NodeShape{shape_output}; -} - -loco::NodeShape infer_arg_min(const luci::CircleArgMin *node) -{ - auto input_shape = luci::shape_get(node->input()).as(); - auto dimension_shape = luci::shape_get(node->dimension()).as(); - - int64_t select_axis = 0; - { - LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr"); - - // Only support node's shape() is CircleConst with S32/S64 - // Support S32 for now. - auto const_shape_node = loco::must_cast(node->dimension()); - LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32, - "Only support int32 CircleConst for CircleArgMin"); - - if (const_shape_node->rank() > 1) - INTERNAL_EXN_V("Only support rank 0/1 CircleConst", - oops::to_uint32(const_shape_node->rank())); - select_axis = const_shape_node->scalar(); - } assert(select_axis < input_shape.rank()); - assert(select_axis >= 0); // TODO support minus of this breaks + + if (select_axis < 0) + select_axis += input_shape.rank(); // NOTE select_axis is removed loco::TensorShape shape_output; @@ -1180,45 +1145,17 @@ loco::NodeShape infer_reshape(const luci::CircleReshape *node) return loco::NodeShape{output_shape}; } -loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node) +template loco::NodeShape infer_resize_type(const CIRCLENODE *node) { - auto input_shape = luci::shape_get(node->input()).as(); - - if (input_shape.rank() != 4) - INTERNAL_EXN("Expected ResizeBilinear input to have rank 4"); - - auto *const_node = loco::must_cast(node->size()); - - if (const_node->dtype() != loco::DataType::S32) - INTERNAL_EXN("Only S32 datatype is supported for ResizeBilinear size"); - - if (const_node->rank() != 1) - INTERNAL_EXN("Expected size tensor of rank 1"); - - if (const_node->dim(0).value() != 2) - INTERNAL_EXN("Expected size tensor with shape [2]"); - - loco::TensorShape output_shape; - output_shape.rank(4); - output_shape.dim(0) = input_shape.dim(0); - output_shape.dim(1) = const_node->at(0); - output_shape.dim(2) = const_node->at(1); - output_shape.dim(3) = input_shape.dim(3); - - return loco::NodeShape{output_shape}; -} - -loco::NodeShape infer_resize_nearest_neighbor(const luci::CircleResizeNearestNeighbor *node) -{ - auto input_shape = luci::shape_get(node->input()).as(); + auto input_shape = luci::shape_get(node->input()).template as(); if (input_shape.rank() != 4) - INTERNAL_EXN("Expected ResizeNearesNeighbor input to have rank 4"); + INTERNAL_EXN("Expected input to have rank 4"); auto *const_node = loco::must_cast(node->size()); if (const_node->dtype() != loco::DataType::S32) - INTERNAL_EXN("Only S32 datatype is supported for ResizeNearesNeighbor size"); + INTERNAL_EXN("Only S32 datatype is supported for size"); if (const_node->rank() != 1) INTERNAL_EXN("Expected size tensor of rank 1"); @@ -1229,8 +1166,8 @@ loco::NodeShape infer_resize_nearest_neighbor(const luci::CircleResizeNearestNei loco::TensorShape output_shape; output_shape.rank(4); output_shape.dim(0) = input_shape.dim(0); - output_shape.dim(1) = const_node->at(0); - output_shape.dim(2) = const_node->at(1); + output_shape.dim(1) = const_node->template at(0); + output_shape.dim(2) = const_node->template at(1); output_shape.dim(3) = input_shape.dim(3); return loco::NodeShape{output_shape}; @@ -2080,9 +2017,9 @@ public: loco::NodeShape visit(const luci::CircleAddN *node) final { return infer_add_n(node); } - loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_max(node); } + loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_maxmin(node); } - loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_min(node); } + loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_maxmin(node); } loco::NodeShape visit(const luci::CircleAveragePool2D *node) final { @@ -2119,6 +2056,8 @@ public: loco::NodeShape visit(const luci::CircleCustom *node) final { return use_own(node); } + loco::NodeShape visit(const luci::CircleDensify *node) final { return use_input(node); } + loco::NodeShape visit(const luci::CircleDepthToSpace *node) final { return infer_depth_to_space(node); @@ -2348,12 +2287,12 @@ public: loco::NodeShape visit(const luci::CircleResizeBilinear *node) final { - return infer_resize_bilinear(node); + return infer_resize_type(node); } loco::NodeShape visit(const luci::CircleResizeNearestNeighbor *node) final { - return infer_resize_nearest_neighbor(node); + return infer_resize_type(node); } loco::NodeShape visit(const luci::CircleReverseSequence *node) final diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp index 438c4a3..7616390 100644 --- a/compiler/luci/service/src/CircleTypeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp @@ -102,6 +102,11 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitordtype(); } + loco::DataType visit(const luci::CircleDensify *node) final + { + return luci::dtype_get(node->input()); + } + loco::DataType visit(const luci::CircleDepthToSpace *node) final { return luci::dtype_get(node->input()); diff --git a/compiler/luci/service/src/Nodes/CircleDensify.cpp b/compiler/luci/service/src/Nodes/CircleDensify.cpp new file mode 100644 index 0000000..a0d15b6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDensify.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNodeLet::visit(const luci::CircleDensify *) +{ + return _graph->nodes()->create(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleDensify.test.cpp b/compiler/luci/service/src/Nodes/CircleDensify.test.cpp new file mode 100644 index 0000000..d0f32c1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDensify.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "luci/Service/CircleNodeClone.h" + +#include + +TEST(CloneNodeTest, clone_Densify) +{ + auto g = loco::make_graph(); + auto node_densify = g->nodes()->create(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_densify, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_densify = dynamic_cast(cloned); + ASSERT_NE(nullptr, cloned_densify); +} diff --git a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp index c5864f9..77135cc 100644 --- a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp +++ b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp @@ -24,16 +24,22 @@ #include #include +#include #include #include #include +// code referenced from +// https://github.com/tensorflow/tensorflow/blob/3f878cff5b698b82eea85db2b60d65a2e320850e/ +// tensorflow/lite/kernels/strided_slice.cc +// tensorflow/lite/kernels/internal/strided_slice_logic.h + namespace { -// This Op only supports 1-4D cases and since we use the reference 4D +// This Op only supports 1-5D cases and since we use the reference 4D // implementation, the 1-3D tensors are mapped to 4D. -const int kMaxDim = 4; +const int kMaxDim = 5; const loco::DataType S32 = loco::DataType::S32; @@ -42,18 +48,47 @@ using int16 = int16_t; struct StridedSliceParams { - int8 start_indices_count; + int8 start_indices_count = 0; int16 start_indices[kMaxDim]; - int8 stop_indices_count; + int8 stop_indices_count = 0; int16 stop_indices[kMaxDim]; - int8 strides_count; + int8 strides_count = 0; int16 strides[kMaxDim]; - int16 begin_mask; - int16 ellipsis_mask; - int16 end_mask; - int16 new_axis_mask; - int16 shrink_axis_mask; + int16 begin_mask = 0; + int16 ellipsis_mask = 0; + int16 end_mask = 0; + int16 new_axis_mask = 0; + int16 shrink_axis_mask = 0; +}; + +struct StridedSliceContext +{ + StridedSliceContext(const luci::CircleStridedSlice *node) + { + params.begin_mask = node->begin_mask(); + params.ellipsis_mask = node->ellipsis_mask(); + params.end_mask = node->end_mask(); + params.new_axis_mask = node->new_axis_mask(); + params.shrink_axis_mask = node->shrink_axis_mask(); + + input = loco::must_cast(node->input()); + begin = loco::must_cast(node->begin()); + end = loco::must_cast(node->end()); + strides = loco::must_cast(node->strides()); + + loco::TensorShape input_shape = luci::shape_get(input).as(); + input_dims = input_shape.rank(); + } + StridedSliceParams params; + luci::CircleNode *input = nullptr; + luci::CircleConst *begin = nullptr; + luci::CircleConst *end = nullptr; + luci::CircleConst *strides = nullptr; + + // Equivalent input shape after adding axis according to new_axis_mask. + loco::TensorShape effective_input_shape; + uint32_t input_dims = 0; }; // Use until std::clamp() is available from C++17. @@ -70,8 +105,8 @@ inline int Clamp(const int32_t v, const int32_t lo, const int32_t hi) // Return the index for the first element along that axis. This index will be a // positive integer between [0, axis_size - 1] that can be used to index // directly into the data. -inline int StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, - uint32_t axis) +inline int32_t StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, + uint32_t axis) { const auto begin_mask = params.begin_mask; const auto *start_indices = params.start_indices; @@ -108,7 +143,16 @@ inline int StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShap } // Clamping - start = Clamp(start, 0, axis_size - 1); + if (strides[axis] > 0) + { + // Forward iteration + start = Clamp(start, 0, axis_size); + } + else + { + // Backward iteration + start = Clamp(start, -1, axis_size - 1); + } return start; } @@ -118,14 +162,14 @@ inline int StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShap // element. ie. So if you were iterating through all elements of a 1D array of // size 4, this function would return 4 as the stop, because it is one past the // "real" indices of 0, 1, 2 & 3. -inline int StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, - int axis, int start_for_axis) +inline int32_t StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, + int32_t axis, int32_t start_for_axis) { const auto end_mask = params.end_mask; const auto shrink_axis_mask = params.shrink_axis_mask; const auto *stop_indices = params.stop_indices; const auto *strides = params.strides; - const int axis_size = static_cast(input_shape.dim(axis).value()); + const int32_t axis_size = static_cast(input_shape.dim(axis).value()); if (axis_size == 0) { return 0; @@ -141,7 +185,7 @@ inline int StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape // already been adjusted for negative indices. if (shrink_axis) { - stop = start_for_axis + 1; + return start_for_axis + 1; } // end_mask override @@ -183,37 +227,125 @@ inline int StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape return stop; } -StridedSliceParams BuildStridedSliceParams(const luci::CircleStridedSlice *node) +StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context) { StridedSliceParams op_params; - if (kMaxDim < node->rank()) + // The ellipsis_mask and new_axis_mask in op_params are not used. Those masks + // are processed here to update begin_mask, end_mask and the index range. + op_params.begin_mask = 0; + op_params.ellipsis_mask = 0; + op_params.end_mask = 0; + op_params.new_axis_mask = 0; + op_params.shrink_axis_mask = 0; + + // Count indexes where the new_axis_mask is set but the ellipsis_mask is not. + loco::TensorShape begin_shape = luci::shape_get(op_context->begin).as(); + const uint32_t begin_count = begin_shape.dim(0).value(); + uint32_t num_add_axis = 0; + for (uint32_t i = 0; i < begin_count; ++i) { - INTERNAL_EXN_V("Cannot support StridedSlice rank > ", kMaxDim); + if (!((1 << i) & op_context->params.ellipsis_mask) && + ((1 << i) & op_context->params.new_axis_mask)) + { + num_add_axis++; + } } - auto begin_node = loco::must_cast(node->begin()); - auto end_node = loco::must_cast(node->end()); - auto strides_node = loco::must_cast(node->strides()); + // Calculate the dims of input after adding new axises. + const uint32_t effective_dims = op_context->input_dims + num_add_axis; + + // If begin, end and strides are not fully provided, it means Ellipsis should + // be expanded to multiple dimensions (Ex: for spec [Ellipsis, 2] on a 3D + // input, the Ellipsis should be applied for the first 2 dimensions). Besides, + // If the new_axis_mask and the ellipsis_mask are set at the same index, the + // new_axis_mask will have no effect. + int32_t effective_ellipsis_mask = 0, effective_new_axis_mask = 0; + uint32_t ellipsis_start_idx = effective_dims, expanded_ellipsis = 0; + for (uint32_t i = 0; i < effective_dims;) + { + if ((1 << i) & op_context->params.ellipsis_mask) + { + ellipsis_start_idx = i; + uint32_t ellipsis_end_idx = + std::max(i + 1, std::min(i + 1 + num_add_axis + op_context->input_dims - begin_count, + effective_dims)); + expanded_ellipsis = ellipsis_end_idx - ellipsis_start_idx - 1; + + // Set bit for effective_ellipsis_mask. + for (; i < ellipsis_end_idx; ++i) + { + effective_ellipsis_mask |= (1 << i); + } + continue; + } - uint32_t dims_count = begin_node->size(); + if ((1 << (i - expanded_ellipsis)) & op_context->params.new_axis_mask) + { + effective_new_axis_mask |= (1 << i); + } + ++i; + } - op_params.start_indices_count = dims_count; - op_params.stop_indices_count = dims_count; - op_params.strides_count = dims_count; + // Calculate effective_input_shape and its corresponding begin, end, strides. + loco::TensorShape input_shape = luci::shape_get(op_context->input).as(); + uint32_t added_ellipsis = 0, added_axises = 0; + op_context->effective_input_shape.rank(effective_dims); - for (uint32_t i = 0; i < dims_count; ++i) + for (uint32_t i = 0; i < effective_dims; ++i) { - op_params.start_indices[i] = begin_node->at(i); - op_params.stop_indices[i] = end_node->at(i); - op_params.strides[i] = strides_node->at(i); + if ((1 << i) & effective_ellipsis_mask) + { + // If ellipsis_mask, set the begin_mask and end_mask at that index. + added_ellipsis = std::max(0u, i - ellipsis_start_idx); + op_params.begin_mask |= (1 << i); + op_params.end_mask |= (1 << i); + op_params.strides[i] = 1; + op_context->effective_input_shape.dim(i) = input_shape.dim(i - added_axises); + } + else if ((1 << i) & effective_new_axis_mask) + { + // If new_axis_mask is set, it is equivalent to adding a new dim of 1 to + // input tensor. Store added shape to effective_input_shape. + op_params.start_indices[i] = 0; + op_params.stop_indices[i] = 1; + op_params.strides[i] = 1; + op_context->effective_input_shape.dim(i) = loco::Dimension(1); + added_axises++; + } + else if (i >= begin_count + expanded_ellipsis) + { + op_params.start_indices[i] = 0; + op_params.stop_indices[i] = 0; + op_params.strides[i] = 1; + op_params.begin_mask |= (1 << i); + op_params.end_mask |= (1 << i); + op_context->effective_input_shape.dim(i) = input_shape.dim(i - added_axises); + } + else + { + const uint32_t orig_idx = i - added_ellipsis; + op_params.start_indices[i] = op_context->begin->at(orig_idx); + op_params.stop_indices[i] = op_context->end->at(orig_idx); + op_params.strides[i] = op_context->strides->at(orig_idx); + if (op_context->params.begin_mask & (1 << orig_idx)) + { + op_params.begin_mask |= (1 << i); + } + if (op_context->params.end_mask & (1 << orig_idx)) + { + op_params.end_mask |= (1 << i); + } + if (op_context->params.shrink_axis_mask & (1 << orig_idx)) + { + op_params.shrink_axis_mask |= (1 << i); + } + op_context->effective_input_shape.dim(i) = input_shape.dim(i - added_axises); + } } - - op_params.begin_mask = node->begin_mask(); - op_params.ellipsis_mask = 0; - op_params.end_mask = node->end_mask(); - op_params.new_axis_mask = 0; - op_params.shrink_axis_mask = node->shrink_axis_mask(); + op_params.start_indices_count = effective_dims; + op_params.stop_indices_count = effective_dims; + op_params.strides_count = effective_dims; return op_params; } @@ -241,55 +373,54 @@ loco::TensorShape infer_output_shape(const CircleStridedSlice *node) LUCI_ASSERT(end_node->dtype() == S32, "Only support S32 for end_node"); LUCI_ASSERT(strides_node->dtype() == S32, "Only support S32 for strides_node"); - assert(node->ellipsis_mask() == 0); - assert(node->new_axis_mask() == 0); + LUCI_ASSERT(begin_node->rank() == 1, "Only support rank 1 for begin_node"); + LUCI_ASSERT(end_node->rank() == 1, "Only support rank 1 for end_node"); + LUCI_ASSERT(strides_node->rank() == 1, "Only support rank 1 for strides_node"); - auto op_params = BuildStridedSliceParams(node); loco::TensorShape input_shape = luci::shape_get(input_node).as(); - uint32_t num_input_axes = input_shape.rank(); - assert(begin_node->size() <= num_input_axes); - assert(end_node->size() <= num_input_axes); - assert(strides_node->size() <= num_input_axes); - for (uint32_t i = 0; i < strides_node->size(); i++) - { - LUCI_ASSERT(strides_node->at(i) != 0, "Stride value has to be non-zero"); - } + assert(begin_node->size() <= input_shape.rank()); + assert(end_node->size() <= input_shape.rank()); + assert(strides_node->size() <= input_shape.rank()); - uint32_t shape_size = 0; - std::array output_shape_data; + StridedSliceContext op_context(node); + auto op_params = BuildStridedSliceParams(&op_context); + auto effective_input_shape = op_context.effective_input_shape; + std::vector output_shape_vector; - for (uint32_t idx = 0; idx < num_input_axes; ++idx) + for (int32_t idx = effective_input_shape.rank() - 1; idx >= 0; --idx) { - int32_t begin = StartForAxis(op_params, input_shape, idx); - int32_t end = StopForAxis(op_params, input_shape, idx, begin); - if (end < 0) - end = input_shape.dim(idx).value() + end + 1; + int32_t stride = op_params.strides[idx]; + LUCI_ASSERT(stride != 0, "stride value has to be non-zero"); - // This is valid for both positive and negative strides - int32_t stride = strides_node->at(idx); - int32_t dim_shape = std::ceil(static_cast(end - begin) / stride); - assert(dim_shape > 0); + int32_t begin = StartForAxis(op_params, effective_input_shape, idx); + int32_t end = StopForAxis(op_params, effective_input_shape, idx, begin); // When shrinking an axis, the end position does not matter (and can be // incorrect when negative indexing is used, see Issue #19260). Always use // begin + 1 to generate a length 1 slice, since begin has - // already been adjusted for negative indices by StartForAxis. - const bool shrink_axis = node->shrink_axis_mask() & (1 << idx); + // already been adjusted for negative indices by GetBeginValueAtIndex. + const bool shrink_axis = op_params.shrink_axis_mask & (1 << idx); if (shrink_axis) { - assert(dim_shape == 1); + end = begin + 1; } - else + + // This is valid for both positive and negative strides + int32_t dim_shape = std::ceil((end - begin) / static_cast(stride)); + dim_shape = dim_shape < 0 ? 0 : dim_shape; + if (!shrink_axis) { - output_shape_data[shape_size++] = dim_shape; + output_shape_vector.push_back(dim_shape); } } + auto shape_size = output_shape_vector.size(); output_shape.rank(shape_size); for (uint32_t idx = 0; idx < shape_size; ++idx) { - output_shape.dim(idx) = output_shape_data[idx]; + // reverse copy + output_shape.dim(idx) = output_shape_vector.at(shape_size - 1u - idx); } return output_shape; diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst index 94e723f..09a25ff 100644 --- a/compiler/luci/tests/test.lst +++ b/compiler/luci/tests/test.lst @@ -39,6 +39,7 @@ addread(Conv2D_003) addread(Conv2D_U8_000) addread(Conv2D_U8_001) addread(Cos_000) +addread(Densify_000) addread(DepthToSpace_000) addread(DepthwiseConv2D_000) addread(DepthwiseConv2D_U8_000) @@ -265,6 +266,7 @@ addwrite(Conv2D_003) addwrite(Conv2D_U8_000) addwrite(Conv2D_U8_001) addwrite(Cos_000) +addwrite(Densify_000) addwrite(DepthToSpace_000) addwrite(DepthwiseConv2D_000) addwrite(DepthwiseConv2D_U8_000) diff --git a/compiler/mio-circle04/include/mio_circle/Helper.h b/compiler/mio-circle04/include/mio_circle/Helper.h index d3ffc23..7a1ba2b 100644 --- a/compiler/mio-circle04/include/mio_circle/Helper.h +++ b/compiler/mio-circle04/include/mio_circle/Helper.h @@ -19,6 +19,8 @@ #include +#include + namespace mio { namespace circle @@ -31,6 +33,21 @@ std::string opcode_name(const ::circle::OperatorCode *opcode); const char *tensor_type(const ::circle::Tensor *tensor); const char *tensor_name(const ::circle::Tensor *tensor); +template std::vector as_index_vector(const flatbuffers::Vector *flat_array) +{ + if (flat_array == nullptr) + { + throw std::runtime_error("flat array is nullptr"); + } + + std::vector ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + } // namespace circle } // namespace mio diff --git a/compiler/mio-circle04/include/mio_circle/Reader.h b/compiler/mio-circle04/include/mio_circle/Reader.h new file mode 100644 index 0000000..6306467 --- /dev/null +++ b/compiler/mio-circle04/include/mio_circle/Reader.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __MIO_CIRCLE04_READER_H__ +#define __MIO_CIRCLE04_READER_H__ + +#include + +#include +#include +#include + +// NOTE Reader class originated from circledump and for circle-tensordump +// where this class has more work to be done for stability +// as the tools are for developers not customores. + +namespace mio +{ +namespace circle +{ + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class Reader +{ +private: + using CircleSubGraphs_t = flatbuffers::Vector>; + using CircleBuffers_t = flatbuffers::Vector>; + using CircleTensors_t = flatbuffers::Vector>; + using CircleOperators_t = flatbuffers::Vector>; + using CircleMetadata_t = flatbuffers::Vector>; + using CircleSignatureDef_t = flatbuffers::Vector>; + +public: + Reader(const ::circle::Model *model); + + Reader() = delete; + +public: + uint32_t version() const { return _version; } + + const std::vector &opcodes() { return _op_codes; } + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector &inputs() const { return _inputs; } + const std::vector &outputs() const { return _outputs; } + const ::circle::DataFormat &data_format() const { return _data_format; } + const CircleMetadata_t *metadata() const { return _metadata; } + const CircleSignatureDef_t *signature_defs() const { return _signature_defs; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + ::circle::BuiltinOperator builtin_code(const ::circle::Operator *op) const; + std::string opcode_name(const ::circle::Operator *op) const; + std::vector outputs(const ::circle::Operator *op) const; + std::string tensor_name(const ::circle::Tensor *tensor) const; + std::string tensor_dtype(const ::circle::Tensor *tensor) const; + +public: + bool select_subgraph(uint32_t subgraph); + const std::string &subgraph_name(void) const { return _subgraph_name; } + uint32_t subgraph_index(void) const { return _subgraph_index; } + +private: + uint32_t _version; + + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + const CircleMetadata_t *_metadata{nullptr}; + const CircleSignatureDef_t *_signature_defs{nullptr}; + + uint32_t _subgraph_index = 0; + std::string _subgraph_name; + std::vector _op_codes; + std::vector _inputs; + std::vector _outputs; + ::circle::DataFormat _data_format = ::circle::DataFormat::DataFormat_CHANNELS_FIRST; +}; + +} // namespace circle +} // namespace mio + +#endif // __MIO_CIRCLE04_READER_H__ diff --git a/compiler/mio-circle04/src/Reader.cpp b/compiler/mio-circle04/src/Reader.cpp new file mode 100644 index 0000000..880ffae --- /dev/null +++ b/compiler/mio-circle04/src/Reader.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "mio_circle/Reader.h" +#include "mio_circle/Helper.h" + +#include +#include + +namespace mio +{ +namespace circle +{ + +Reader::Reader(const ::circle::Model *model) +{ + if (model == nullptr) + { + throw std::runtime_error("Invalid model"); + } + + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + _metadata = model->metadata(); + _signature_defs = model->signature_defs(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +{ + if (buff_data != nullptr) + { + *buff_data = nullptr; + } + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + if (buff_data != nullptr) + { + *buff_data = reinterpret_cast(array->data()); + } + return size; + } + } + } + + return 0; +} + +::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + return mio::circle::builtin_code_neutral(opcode); +} + +std::string Reader::opcode_name(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + if (!mio::circle::is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return mio::circle::opcode_name(opcode); +} + +std::vector Reader::outputs(const ::circle::Operator *op) const +{ + return as_index_vector(op->outputs()); +} + +std::string Reader::tensor_name(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_name(tensor); +} + +std::string Reader::tensor_dtype(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_type(tensor); +} + +bool Reader::select_subgraph(uint32_t sgindex) +{ + _subgraph_index = sgindex; + _tensors = nullptr; + _operators = nullptr; + + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const ::circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + auto name = subgraph->name(); + _subgraph_name = name ? name->c_str() : "(noname)"; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + _data_format = subgraph->data_format(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +} // namespace circle +} // namespace mio diff --git a/compiler/mio-circle04/src/Reader.test.cpp b/compiler/mio-circle04/src/Reader.test.cpp new file mode 100644 index 0000000..104454a --- /dev/null +++ b/compiler/mio-circle04/src/Reader.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "mio_circle/Reader.h" + +#include +#include + +class mio_circle04_reader_test : public ::testing::Test +{ +protected: + void initialization_emty(void) + { + _model = circle::CreateModelDirect(_fbb, 0, &_opcodes_vec); + circle::FinishModelBuffer(_fbb, _model); + } + + const circle::Model *circleModel(void) + { + auto ptr = _fbb.GetBufferPointer(); + return circle::GetModel(ptr); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + flatbuffers::Offset _model; + std::vector> _opcodes_vec; +}; + +TEST_F(mio_circle04_reader_test, null_Model_NEG) +{ + EXPECT_THROW(mio::circle::Reader reader(nullptr), std::runtime_error); +} + +TEST_F(mio_circle04_reader_test, empty_Model) +{ + initialization_emty(); + + const circle::Model *model = circleModel(); + EXPECT_NE(nullptr, model); + + mio::circle::Reader reader(model); + + SUCCEED(); +} + +// TODO add more tests diff --git a/compiler/mio-tflite/README.md b/compiler/mio-tflite/README.md index 187b1a5..c717ab8 100644 --- a/compiler/mio-tflite/README.md +++ b/compiler/mio-tflite/README.md @@ -1,3 +1,5 @@ # mio-tflite _mio-tflite_ provides a library to access TensorFlow lite model files + +NOTE: _mio-tflite_ is currently obsolete diff --git a/compiler/mio-tflite260/README.md b/compiler/mio-tflite260/README.md index 970569b..86d2998 100644 --- a/compiler/mio-tflite260/README.md +++ b/compiler/mio-tflite260/README.md @@ -1,3 +1,5 @@ # mio-tflite260 _mio-tflite260_ provides a library to access TensorFlow lite model files with V2.6.0. + +NOTE: _mio-tflite260_ is currently obsolete diff --git a/compiler/mir/include/mir/Graph.h b/compiler/mir/include/mir/Graph.h index bf94cfb..37bfdb3 100644 --- a/compiler/mir/include/mir/Graph.h +++ b/compiler/mir/include/mir/Graph.h @@ -103,6 +103,10 @@ private: /** * @brief Returns nodes of the graph sorted topologically. + * @note Sorting order priority + * 1) Graph input node (input index order) + * 2) Constant node (unordered - cannot predict order) + * 3) Ready node (unordered - cannot predict order) */ std::vector getSortedNodes(Graph *graph); diff --git a/compiler/mir/src/Graph.cpp b/compiler/mir/src/Graph.cpp index 04b005d..05d6dc9 100644 --- a/compiler/mir/src/Graph.cpp +++ b/compiler/mir/src/Graph.cpp @@ -44,9 +44,16 @@ std::vector getSortedNodes(Graph *graph) std::deque ready_nodes; std::unordered_map num_visited_input_edges; + // Use input vector first to maintain correct input order + for (Operation *op : graph->getInputs()) + { + ready_nodes.push_back(op); + } + for (Operation *op : graph->getNodes()) { - if (op->getNumInputs() == 0) + // Skip already pushed input node + if ((op->getNumInputs() == 0) && (op->getType() != Operation::Type::input)) { ready_nodes.push_back(op); } diff --git a/compiler/mir2loco/src/mir2loco.test.cpp b/compiler/mir2loco/src/mir2loco.test.cpp index 92ab994..244c92a 100644 --- a/compiler/mir2loco/src/mir2loco.test.cpp +++ b/compiler/mir2loco/src/mir2loco.test.cpp @@ -383,28 +383,49 @@ TEST_F(TestTransformer_mir2loco, Conv2D_Test) auto loco_graph = transformer.transform(&mir_graph); loco::Pull *pull_node = dynamic_cast(loco_graph->nodes()->at(0)); - loco::ConstGen *const_node = dynamic_cast(loco_graph->nodes()->at(1)); - loco::FeatureEncode *encode_node = - dynamic_cast(loco_graph->nodes()->at(2)); - loco::FilterEncode *filter_node = dynamic_cast(loco_graph->nodes()->at(3)); - loco::Conv2D *conv_node = dynamic_cast(loco_graph->nodes()->at(4)); - loco::FeatureDecode *decode_node = - dynamic_cast(loco_graph->nodes()->at(5)); - loco::Push *push_node = dynamic_cast(loco_graph->nodes()->at(6)); - ASSERT_NE(pull_node, nullptr); + + // ConstGen: Only one ConstGen node + // We can convince that this node is input of FilterEncode because this is only ConstGen node + loco::ConstGen *const_node = dynamic_cast(loco_graph->nodes()->at(1)); ASSERT_NE(const_node, nullptr); - ASSERT_NE(filter_node, nullptr); + + // FeatureEncode + auto pull_uses = loco::succs(pull_node); + ASSERT_EQ(pull_uses.size(), 1); + loco::FeatureEncode *encode_node = dynamic_cast(*pull_uses.begin()); ASSERT_NE(encode_node, nullptr); - ASSERT_NE(conv_node, nullptr); - ASSERT_NE(decode_node, nullptr); - ASSERT_NE(push_node, nullptr); ASSERT_EQ(encode_node->input(), pull_node); - ASSERT_EQ(filter_node->input(), const_node); + + // Conv2D + auto encode_uses = loco::succs(encode_node); + ASSERT_EQ(encode_uses.size(), 1); + loco::Conv2D *conv_node = dynamic_cast(*encode_uses.begin()); + ASSERT_NE(conv_node, nullptr); ASSERT_EQ(conv_node->ifm(), encode_node); + + // FilterEncode + auto const_uses = loco::succs(const_node); + ASSERT_EQ(const_uses.size(), 1); + loco::FilterEncode *filter_node = dynamic_cast(*const_uses.begin()); + ASSERT_NE(filter_node, nullptr); + ASSERT_EQ(filter_node->input(), const_node); ASSERT_EQ(conv_node->ker(), filter_node); + + // FeatureDecode + auto conv_uses = loco::succs(conv_node); + ASSERT_EQ(conv_uses.size(), 1); + loco::FeatureDecode *decode_node = dynamic_cast(*conv_uses.begin()); + ASSERT_NE(decode_node, nullptr); ASSERT_EQ(decode_node->input(), conv_node); + + // Push + auto decode_uses = loco::succs(decode_node); + ASSERT_EQ(decode_uses.size(), 1); + loco::Push *push_node = dynamic_cast(*decode_uses.begin()); + ASSERT_NE(push_node, nullptr); ASSERT_EQ(push_node->from(), decode_node); + // Check params ASSERT_EQ(conv_node->pad()->top(), 5); ASSERT_EQ(conv_node->pad()->left(), 9); diff --git a/compiler/moco/import/src/Importer.cpp b/compiler/moco/import/src/Importer.cpp index 333f0f6..0659fd1 100644 --- a/compiler/moco/import/src/Importer.cpp +++ b/compiler/moco/import/src/Importer.cpp @@ -190,7 +190,7 @@ std::unique_ptr Importer::import(const ModelSignature &signature, convert_graph(*source_ptr, signature, tf_graph_def, graph.get()); - return std::move(graph); + return graph; } } // namespace moco diff --git a/compiler/moco/lang/src/IR/TFNode.cpp b/compiler/moco/lang/src/IR/TFNode.cpp index 55c0e0c..b59a505 100644 --- a/compiler/moco/lang/src/IR/TFNode.cpp +++ b/compiler/moco/lang/src/IR/TFNode.cpp @@ -17,6 +17,7 @@ #include "moco/IR/TFNode.h" #include "moco/IR/TFDialect.h" +#include #include #include diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt index 8732340..90e989a 100644 --- a/compiler/one-cmds/CMakeLists.txt +++ b/compiler/one-cmds/CMakeLists.txt @@ -8,7 +8,9 @@ set(ONE_COMMAND_FILES one-optimize one-quantize one-pack + one-partition one-profile + one-infer one-codegen one-prepare-venv onecc @@ -74,7 +76,11 @@ endforeach(ONE_UTILITY) # make python directory set(ONE_PYTHON_FILES constant.py - make_cmd.py) + make_cmd.py + CfgRunner.py + OptionBuilder.py + TopologicalSortHelper.py + WorkflowRunner.py) foreach(ONE_PYTHON_FILE IN ITEMS ${ONE_PYTHON_FILES}) diff --git a/compiler/one-cmds/dummy-driver/CMakeLists.txt b/compiler/one-cmds/dummy-driver/CMakeLists.txt index 690a607..2552a02 100644 --- a/compiler/one-cmds/dummy-driver/CMakeLists.txt +++ b/compiler/one-cmds/dummy-driver/CMakeLists.txt @@ -1,16 +1,25 @@ # dummy driver for interface test set(DUMMY_DRIVER_SRC src/dummy-compile.cpp) set(HELP_DRIVER_SRC src/help-compile.cpp) +set(DUMMY_INFER_SRC src/dummy-infer.cpp) +set(DUMMY_INFER_V2_SRC src/dummy-inferV2.cpp) +set(HELP_INFER_SRC src/help-infer.cpp) set(DUMMY_PROFILE_SRC src/dummy-profile.cpp) set(HELP_PROFILE_SRC src/help-profile.cpp) add_executable(dummy-compile ${DUMMY_DRIVER_SRC}) add_executable(help-compile ${HELP_DRIVER_SRC}) +add_executable(dummy-infer ${DUMMY_INFER_SRC}) +add_executable(dummy-inferV2 ${DUMMY_INFER_V2_SRC}) +add_executable(help-infer ${HELP_INFER_SRC}) add_executable(dummy-profile ${DUMMY_PROFILE_SRC}) add_executable(help-profile ${HELP_PROFILE_SRC}) set(DUMMY_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/dummy-compile") set(HELP_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/help-compile") +set(DUMMY_INFER "${CMAKE_CURRENT_BINARY_DIR}/dummy-infer") +set(DUMMY_INFER_V2 "${CMAKE_CURRENT_BINARY_DIR}/dummy-inferV2") +set(HELP_INFER "${CMAKE_CURRENT_BINARY_DIR}/help-infer") set(DUMMY_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/dummy-profile") set(HELP_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/help-profile") @@ -26,6 +35,24 @@ install(FILES ${HELP_DRIVER} WORLD_READ WORLD_EXECUTE DESTINATION test) +install(FILES ${DUMMY_INFER} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + +install(FILES ${DUMMY_INFER_V2} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + +install(FILES ${HELP_INFER} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + install(FILES ${DUMMY_PROFILE} PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE diff --git a/compiler/one-cmds/dummy-driver/src/dummy-infer.cpp b/compiler/one-cmds/dummy-driver/src/dummy-infer.cpp new file mode 100644 index 0000000..60f5fae --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummy-infer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +/** + * dummy-infer only tests its interface rather than its functionality. + * + * ./dummy-infer ${INPUT_NAME} + * dummy-infer dummy output!!! + */ + +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::cout << "dummy-infer dummy output!!!" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/dummy-inferV2.cpp b/compiler/one-cmds/dummy-driver/src/dummy-inferV2.cpp new file mode 100644 index 0000000..4b93c70 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummy-inferV2.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +/** + * dummy-infer only tests its interface rather than its functionality. + * + * ./dummy-infer ${INPUT_NAME} + * Do inference of ${INPUT_NAME} + */ + +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::cout << "Do inference of " + std::string(argv[1]) << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/help-infer.cpp b/compiler/one-cmds/dummy-driver/src/help-infer.cpp new file mode 100644 index 0000000..821d368 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/help-infer.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +/** + * help-infer prints dummy help message. + * + * $ ./help-infer -h + * HELP MESSAGE!! + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::string opt_h{"-h"}; + std::string argv_1{argv[1]}; + + if (opt_h != argv_1) + return EXIT_FAILURE; + + std::cout << "HELP MESSAGE!!" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/how-to-use-one-commands.txt b/compiler/one-cmds/how-to-use-one-commands.txt index ebc1651..2352bbd 100644 --- a/compiler/one-cmds/how-to-use-one-commands.txt +++ b/compiler/one-cmds/how-to-use-one-commands.txt @@ -153,6 +153,7 @@ Current transformation options are - expand_broadcast_const : This will expand broadcastable constant node inputs - fold_add_v2 : This removes AddV2 operation which can be folded - fold_cast : This removes Cast operation which can be folded +- fold_densify: This removes Densify operator which can be folded - fold_dequantize : This removes Dequantize operation which can be folded - fold_dwconv : This folds Depthwise Convolution operation which can be folded - fold_gather : This removes Gather operation which can be folded @@ -205,10 +206,6 @@ Current transformation options are - transform_min_max_to_relu6: This will transform Minimum-Maximum pattern to Relu6 operator. - transform_min_relu_to_relu6: This will transform Minimum(6)-Relu pattern to Relu6 operator. -There are options to enable multiple options at once for convenience. -- O1: fuse_bcq, fuse_instnorm, resolve_customop_add, resolve_customop_batchmatmul, - resolve_customop_matmul, remove_redundant_transpose, substitute_pack_to_reshape - one-quantize ------------ diff --git a/compiler/one-cmds/one-build b/compiler/one-cmds/one-build index 5c313b4..4b1f980 100644 --- a/compiler/one-cmds/one-build +++ b/compiler/one-cmds/one-build @@ -22,7 +22,6 @@ import argparse import configparser import os -import subprocess import sys import utils as _utils @@ -83,6 +82,7 @@ def _get_driver_name(driver_name): 'one-import-onnx': 'one-import-onnx', 'one-optimize': 'one-optimize', 'one-quantize': 'one-quantize', + 'one-partition': 'one-partition', 'one-pack': 'one-pack', 'one-codegen': 'one-codegen' }[driver_name] @@ -157,7 +157,8 @@ def main(): bin_dir = os.path.dirname(os.path.realpath(__file__)) import_drivers_dict = _utils._detect_one_import_drivers(bin_dir) transform_drivers = [ - 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile' + 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile', + 'one-partition' ] _verify_cfg(import_drivers_dict, config) diff --git a/compiler/one-cmds/one-build.template.cfg b/compiler/one-cmds/one-build.template.cfg index e147896..4296081 100644 --- a/compiler/one-cmds/one-build.template.cfg +++ b/compiler/one-cmds/one-build.template.cfg @@ -5,6 +5,7 @@ one-import-bcq=False one-import-onnx=False one-optimize=True one-quantize=False +one-parition=False one-pack=True one-codegen=False diff --git a/compiler/one-cmds/one-codegen b/compiler/one-cmds/one-codegen index 726538d..86e1632 100644 --- a/compiler/one-cmds/one-codegen +++ b/compiler/one-cmds/one-codegen @@ -25,9 +25,7 @@ import glob import itertools import ntpath import os -import subprocess import sys -import tempfile import shutil import utils as _utils diff --git a/compiler/one-cmds/one-import-bcq b/compiler/one-cmds/one-import-bcq index ef89a92..c3ef0b2 100644 --- a/compiler/one-cmds/one-import-bcq +++ b/compiler/one-cmds/one-import-bcq @@ -21,7 +21,6 @@ import argparse import os -import subprocess import sys import tempfile @@ -160,9 +159,9 @@ def _convert(args): tmpdir, os.path.splitext( os.path.basename(generate_bcq_metadata_output_path))[0]) + '.tflite' - tf2tfliteV2_cmd = _make_cmd.make_tf2tfliteV2_cmd(args, tf2tfliteV2_path, - generate_bcq_metadata_output_path, - tf2tfliteV2_output_path) + tf2tfliteV2_cmd = _make_cmd.make_tf2tfliteV2_cmd( + args, tf2tfliteV2_path, generate_bcq_metadata_output_path, + tf2tfliteV2_output_path) try: output_arrays_idx = tf2tfliteV2_cmd.index('--output_arrays') tf2tfliteV2_cmd[output_arrays_idx + 1] = ','.join(bcq_output_arrays) @@ -177,8 +176,8 @@ def _convert(args): # make a command to convert from tflite to circle tflite2circle_path = os.path.join(dir_path, 'tflite2circle') tflite2circle_cmd = _make_cmd.make_tflite2circle_cmd(tflite2circle_path, - tf2tfliteV2_output_path, - getattr(args, 'output_path')) + tf2tfliteV2_output_path, + getattr(args, 'output_path')) f.write((' '.join(tflite2circle_cmd) + '\n').encode()) diff --git a/compiler/one-cmds/one-import-onnx b/compiler/one-cmds/one-import-onnx index eaa1361..ad19c2f 100644 --- a/compiler/one-cmds/one-import-onnx +++ b/compiler/one-cmds/one-import-onnx @@ -21,7 +21,6 @@ import argparse import os -import subprocess import sys import tempfile import onnx @@ -80,6 +79,12 @@ def _get_parser(): parser.add_argument('--unroll_rnn', action='store_true', help='Unroll RNN operators') parser.add_argument( '--unroll_lstm', action='store_true', help='Unroll LSTM operators') + parser.add_argument( + '--keep_io_order', + action='store_true', + help= + 'Ensure generated circle model preserves the I/O order of the original onnx model.' + ) # save intermediate file(s) parser.add_argument( @@ -87,6 +92,12 @@ def _get_parser(): action='store_true', help='Save intermediate files to output folder') + # experimental options + parser.add_argument( + '--experimental_disable_batchmatmul_unfold', + action='store_true', + help='Experimental disable BatchMatMul unfold') + return parser @@ -124,6 +135,65 @@ def _apply_verbosity(verbosity): os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' +# The index of input/output is added in front of the name. For example, +# Original input names: 'a', 'c', 'b' +# Renamed: '0001_a', '0002_c', '0003_b' +# This will preserve I/O order after import. +def _remap_io_names(onnx_model): + # gather existing name of I/O and generate new name of I/O in sort order + input_nodes = [] + output_nodes = [] + remap_inputs = [] + remap_outputs = [] + initializers = [] + # some models may have initializers as inputs. ignore them. + for initializer in onnx_model.graph.initializer: + initializers.append(initializer.name) + for idx in range(0, len(onnx_model.graph.input)): + name = onnx_model.graph.input[idx].name + if not name in initializers: + input_nodes.append(name) + remap_inputs.append(format(idx + 1, '04d') + '_' + name) + for idx in range(0, len(onnx_model.graph.output)): + name = onnx_model.graph.output[idx].name + output_nodes.append(name) + remap_outputs.append(format(idx + 1, '04d') + '_' + name) + # change names for graph input + for i in range(len(onnx_model.graph.input)): + if onnx_model.graph.input[i].name in input_nodes: + to_rename = onnx_model.graph.input[i].name + idx = input_nodes.index(to_rename) + onnx_model.graph.input[i].name = remap_inputs[idx] + # change names of all nodes in the graph + for i in range(len(onnx_model.graph.node)): + # check node.input is to change to remap_inputs or remap_outputs + for j in range(len(onnx_model.graph.node[i].input)): + if onnx_model.graph.node[i].input[j] in input_nodes: + to_rename = onnx_model.graph.node[i].input[j] + idx = input_nodes.index(to_rename) + onnx_model.graph.node[i].input[j] = remap_inputs[idx] + if onnx_model.graph.node[i].input[j] in output_nodes: + to_rename = onnx_model.graph.node[i].input[j] + idx = output_nodes.index(to_rename) + onnx_model.graph.node[i].input[j] = remap_outputs[idx] + # check node.output is to change to remap_inputs or remap_outputs + for j in range(len(onnx_model.graph.node[i].output)): + if onnx_model.graph.node[i].output[j] in output_nodes: + to_rename = onnx_model.graph.node[i].output[j] + idx = output_nodes.index(to_rename) + onnx_model.graph.node[i].output[j] = remap_outputs[idx] + if onnx_model.graph.node[i].output[j] in input_nodes: + to_rename = onnx_model.graph.node[i].output[j] + idx = input_nodes.index(to_rename) + onnx_model.graph.node[i].output[j] = remap_inputs[idx] + # change names for graph output + for i in range(len(onnx_model.graph.output)): + if onnx_model.graph.output[i].name in output_nodes: + to_rename = onnx_model.graph.output[i].name + idx = output_nodes.index(to_rename) + onnx_model.graph.output[i].name = remap_outputs[idx] + + def _convert(args): _apply_verbosity(args.verbose) @@ -142,6 +212,13 @@ def _convert(args): options.unroll_rnn = _utils._is_valid_attr(args, 'unroll_rnn') options.unroll_lstm = _utils._is_valid_attr(args, 'unroll_lstm') onnx_legalizer.legalize(onnx_model, options) + if _utils._is_valid_attr(args, 'keep_io_order'): + _remap_io_names(onnx_model) + if _utils._is_valid_attr(args, 'save_intermediate'): + basename = os.path.basename(getattr(args, 'input_path')) + fixed_path = os.path.join(tmpdir, + os.path.splitext(basename)[0] + '~.onnx') + onnx.save(onnx_model, fixed_path) tf_savedmodel = onnx_tf.backend.prepare(onnx_model) savedmodel_name = os.path.splitext(os.path.basename( @@ -166,8 +243,8 @@ def _convert(args): # make a command to convert from tflite to circle tflite2circle_path = os.path.join(dir_path, 'tflite2circle') tflite2circle_cmd = _make_cmd.make_tflite2circle_cmd(tflite2circle_path, - tf2tfliteV2_output_path, - getattr(args, 'output_path')) + tf2tfliteV2_output_path, + getattr(args, 'output_path')) f.write((' '.join(tflite2circle_cmd) + '\n').encode()) diff --git a/compiler/one-cmds/one-import-pytorch b/compiler/one-cmds/one-import-pytorch index dbf1ba6..7f39e61 100644 --- a/compiler/one-cmds/one-import-pytorch +++ b/compiler/one-cmds/one-import-pytorch @@ -80,7 +80,8 @@ def _get_parser(): tf2tflite_group.add_argument('--converter_version', default='v2') parser.add_argument('--unroll_rnn', action='store_true', help='Unroll RNN operators') - parser.add_argument('--unroll_lstm', action='store_true', help='Unroll LSTM operators') + parser.add_argument( + '--unroll_lstm', action='store_true', help='Unroll LSTM operators') # save intermediate file(s) parser.add_argument( @@ -338,8 +339,8 @@ def _convert(args): # make a command to convert from tflite to circle tflite2circle_path = os.path.join(dir_path, 'tflite2circle') tflite2circle_cmd = _make_cmd.make_tflite2circle_cmd(tflite2circle_path, - tf2tfliteV2_output_path, - getattr(args, 'output_path')) + tf2tfliteV2_output_path, + getattr(args, 'output_path')) f.write((' '.join(tflite2circle_cmd) + '\n').encode()) diff --git a/compiler/one-cmds/one-import-tf b/compiler/one-cmds/one-import-tf index 999255a..6623fa6 100644 --- a/compiler/one-cmds/one-import-tf +++ b/compiler/one-cmds/one-import-tf @@ -21,8 +21,6 @@ import argparse import os -import subprocess -import sys import tempfile import onelib.make_cmd as _make_cmd @@ -152,8 +150,8 @@ def _convert(args): tmpdir, os.path.splitext(os.path.basename(args.output_path))[0]) + '.tflite' tf2tfliteV2_cmd = _make_cmd.make_tf2tfliteV2_cmd(args, tf2tfliteV2_path, - getattr(args, 'input_path'), - tf2tfliteV2_output_path) + getattr(args, 'input_path'), + tf2tfliteV2_output_path) f.write((' '.join(tf2tfliteV2_cmd) + '\n').encode()) @@ -163,8 +161,8 @@ def _convert(args): # make a command to convert from tflite to circle tflite2circle_path = os.path.join(dir_path, 'tflite2circle') tflite2circle_cmd = _make_cmd.make_tflite2circle_cmd(tflite2circle_path, - tf2tfliteV2_output_path, - getattr(args, 'output_path')) + tf2tfliteV2_output_path, + getattr(args, 'output_path')) f.write((' '.join(tflite2circle_cmd) + '\n').encode()) diff --git a/compiler/one-cmds/one-import-tflite b/compiler/one-cmds/one-import-tflite index 2d756bf..3d96b11 100644 --- a/compiler/one-cmds/one-import-tflite +++ b/compiler/one-cmds/one-import-tflite @@ -21,7 +21,6 @@ import argparse import os -import subprocess import sys import onelib.make_cmd as _make_cmd @@ -83,8 +82,8 @@ def _convert(args): # make a command to convert from tflite to circle tflite2circle_path = os.path.join(dir_path, 'tflite2circle') tflite2circle_cmd = _make_cmd.make_tflite2circle_cmd(tflite2circle_path, - getattr(args, 'input_path'), - getattr(args, 'output_path')) + getattr(args, 'input_path'), + getattr(args, 'output_path')) f.write((' '.join(tflite2circle_cmd) + '\n').encode()) diff --git a/compiler/one-cmds/one-infer b/compiler/one-cmds/one-infer new file mode 100644 index 0000000..c7fcd8a --- /dev/null +++ b/compiler/one-cmds/one-infer @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # ''' +''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # ''' +''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # ''' +''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # ''' +''''exit 255 # ''' + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +import argparse +import copy +import glob +import itertools +import ntpath +import os +import sys + +import utils as _utils + +# TODO Find better way to suppress trackback on error +sys.tracebacklimit = 0 + + +def _get_backends_list(): + """ + [one hierarchy] + one + ├── backends + ├── bin + ├── doc + ├── include + ├── lib + ├── optimization + └── test + + The list where `one-infer` finds its backends + - `bin` folder where `one-infer` exists + - `backends` folder + + NOTE If there are backends of the same name in different places, + the closer to the top in the list, the higher the priority. + """ + dir_path = os.path.dirname(os.path.realpath(__file__)) + backend_set = set() + + # bin folder + files = [f for f in glob.glob(dir_path + '/*-infer')] + # backends folder + files += [f for f in glob.glob(dir_path + '/../backends/**/*-infer', recursive=True)] + # TODO find backends in `$PATH` + + backends_list = [] + for cand in files: + base = ntpath.basename(cand) + if (not base in backend_set) and os.path.isfile(cand) and os.access( + cand, os.X_OK): + backend_set.add(base) + backends_list.append(cand) + + return backends_list + + +def _search_backend_driver(driver): + """ + [one hierarchy] + one + ├── backends + ├── bin + ├── doc + ├── include + ├── lib + ├── optimization + └── test + + The list where `one-infer` finds its backend driver + - `bin` folder where `one-infer` exists + - `backends/**/bin/` folder + + NOTE If there are drivers of the same name in different places, + the closer to the top in the list, the higher the priority. + """ + dir_path = os.path.dirname(os.path.realpath(__file__)) + + # CASE 1: one/bin/{driver} is found + driver_path = dir_path + '/' + driver + if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): + return driver_path + + # CASE 2: one/backends/**/bin/{driver} is found + for driver_path in glob.glob( + dir_path + '/../backends/**/bin/' + driver, recursive=True): + if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): + return driver_path + + # CASE 3: {driver} is found in nowhere + return None + + +def _get_parser(backends_list): + infer_usage = 'one-infer [-h] [-v] [-C CONFIG] [-d DRIVER | -b BACKEND] [--post-process POST_PROCESS] [--] [COMMANDS FOR BACKEND DRIVER]' + parser = argparse.ArgumentParser( + description='command line tool to infer model', usage=infer_usage) + + _utils._add_default_arg(parser) + + # TODO: add tflite/onnx-infer driver to helper message when it is implemented + driver_help_message = 'backend inference driver name to execute' + parser.add_argument('-d', '--driver', type=str, help=driver_help_message) + + # get backend list in the directory + backends_name = [ntpath.basename(f) for f in backends_list] + if not backends_name: + backends_name_message = '(There is no available backend drivers)' + else: + backends_name_message = '(available backend drivers: ' + ', '.join( + backends_name) + ')' + backend_help_message = 'backend name to use ' + backends_name_message + parser.add_argument('-b', '--backend', type=str, help=backend_help_message) + + post_process_help_message = 'post processing script to convert I/O data to standard format' + parser.add_argument('--post-process', type=str, help=post_process_help_message) + + return parser + + +def _verify_arg(parser, args): + """verify given arguments""" + # `-d/--driver` and `-b/--backend` are mutually exclusive arguments. + if _utils._is_valid_attr(args, 'driver') and _utils._is_valid_attr(args, 'backend'): + parser.error( + '-d and -b options are mutually exclusive. Please use only one of them') + + missing = [] + if not _utils._is_valid_attr(args, 'driver') and not _utils._is_valid_attr( + args, 'backend'): + missing.append('{-d/--driver | -b/--backend}') + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) + + +def _parse_arg(parser): + infer_args = [] + backend_args = [] + argv = copy.deepcopy(sys.argv) + # delete file name + del argv[0] + # split by '--' + args = [list(y) for x, y in itertools.groupby(argv, lambda z: z == '--') if not x] + + # one-infer [-h] [-v] [-C CONFIG] [-d DRIVER] [-b BACKEND] [--post-process POST_PROCESS] -- [COMMANDS FOR BACKEND DRIVER] + if len(args): + infer_args = args[0] + infer_args = parser.parse_args(infer_args) + backend_args = backend_args if len(args) < 2 else args[1] + # print version + if len(args) and infer_args.version: + _utils._print_version_and_exit(__file__) + + return infer_args, backend_args + + +def _get_executable(args, backends_list): + driver = _utils._is_valid_attr(args, 'driver') + if driver: + executable = _search_backend_driver(driver) + if executable: + return executable + else: + raise FileNotFoundError(driver + ' not found') + + if _utils._is_valid_attr(args, 'backend'): + backend_base = getattr(args, 'backend') + '-infer' + for cand in backends_list: + if ntpath.basename(cand) == backend_base: + return cand + raise FileNotFoundError(backend_base + ' not found') + + +def main(): + # get backend list + backends_list = _get_backends_list() + + # parse arguments + parser = _get_parser(backends_list) + args, backend_args = _parse_arg(parser) + + # parse configuration file + _utils._parse_cfg(args, 'one-infer') + + # verify arguments + _verify_arg(parser, args) + + # make a command to run given backend driver + driver_path = _get_executable(args, backends_list) + infer_cmd = [driver_path] + backend_args + if _utils._is_valid_attr(args, 'command'): + infer_cmd += getattr(args, 'command').split() + + # run backend driver + _utils._run(infer_cmd, err_prefix=ntpath.basename(driver_path)) + + # run post process script if it's given + if _utils._is_valid_attr(args, 'post_process'): + # NOTE: the given python script will be executed by venv of ONE + python_path = sys.executable + post_process_command = [python_path] + getattr(args, + 'post_process').strip().split(' ') + _utils._run(post_process_command, err_prefix='one-infer') + + +if __name__ == '__main__': + _utils._safemain(main, __file__) diff --git a/compiler/one-cmds/one-init b/compiler/one-cmds/one-init new file mode 100644 index 0000000..04c4534 --- /dev/null +++ b/compiler/one-cmds/one-init @@ -0,0 +1,280 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # ''' +''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # ''' +''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # ''' +''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # ''' +''''exit 255 # ''' + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +import argparse +import copy +import glob +import itertools +import ntpath +import os +import sys + +import configparser +import utils as _utils + +# TODO Find better way to suppress trackback on error +sys.tracebacklimit = 0 + + +class CommentableConfigParser(configparser.ConfigParser): + """ + ConfigParser where comment can be stored + In Python ConfigParser, comment in ini file ( starting with ';') is considered a key of which + value is None. + Ref: https://stackoverflow.com/questions/6620637/writing-comments-to-files-with-configparser + """ + + def __init__(self): + # allow_no_value=True to add comment + # ref: https://stackoverflow.com/a/19432072 + configparser.ConfigParser.__init__(self, allow_no_value=True) + self.optionxform = str + + def add_comment(self, section, comment): + comment_sign = ';' + self[section][f'{comment_sign} {comment}'] = None + + +def _get_backends_list(): + """ + [one hierarchy] + one + ├── backends + ├── bin + ├── doc + ├── include + ├── lib + ├── optimization + └── test + + The list where `one-init` finds its backends + - `bin` folder where `one-init` exists + - `backends` folder + + NOTE If there are backends of the same name in different places, + the closer to the top in the list, the higher the priority. + """ + dir_path = os.path.dirname(os.path.realpath(__file__)) + backend_set = set() + + # bin folder + files = [f for f in glob.glob(dir_path + '/*-init')] + # backends folder + files += [f for f in glob.glob(dir_path + '/../backends/**/*-init', recursive=True)] + # TODO find backends in `$PATH` + + backends_list = [] + for cand in files: + base = ntpath.basename(cand) + if (not base in backend_set) and os.path.isfile(cand) and os.access( + cand, os.X_OK): + backend_set.add(base) + backends_list.append(cand) + + return backends_list + + +# TODO Add support for TF graphdef and bcq +def _get_parser(backends_list): + init_usage = ( + 'one-init [-h] [-v] [-V] ' + '[-i INPUT_PATH] ' + '[-o OUTPUT_PATH] ' + '[-m MODEL_TYPE] ' + '[-b BACKEND] ' + # args for onnx model + '[--convert_nchw_to_nhwc] ' + '[--nchw_to_nhwc_input_shape] ' + '[--nchw_to_nhwc_output_shape] ' + # args for backend driver + '[--] [COMMANDS FOR BACKEND DRIVER]') + """ + NOTE + layout options for onnx model could be difficult to users. + In one-init, we could consider easier args for the the above three: + For example, we could have another option, e.g., --input_img_layout LAYOUT + - When LAYOUT is NHWC, apply 'nchw_to_nhwc_input_shape=True' into cfg + - When LAYOUT is NCHW, apply 'nchw_to_nhwc_input_shape=False' into cfg + """ + + parser = argparse.ArgumentParser( + description='Command line tool to generate initial cfg file. ' + 'Currently tflite and onnx models are supported', + usage=init_usage) + + _utils._add_default_arg_no_CS(parser) + + parser.add_argument( + '-i', '--input_path', type=str, help='full filepath of the input model file') + parser.add_argument( + '-o', '--output_path', type=str, help='full filepath of the output cfg file') + parser.add_argument( + '-m', + '--model_type', + type=str, + help=('type of input model: "onnx", "tflite". ' + 'If the file extension passed to --input_path is ' + '".tflite" or ".onnx", this arg can be omitted.')) + + onnx_group = parser.add_argument_group('arguments when model type is onnx') + onnx_group.add_argument( + '--convert_nchw_to_nhwc', + action='store_true', + help= + 'Convert NCHW operators to NHWC under the assumption that input model is NCHW.') + onnx_group.add_argument( + '--nchw_to_nhwc_input_shape', + action='store_true', + help='Convert the input shape of the model (argument for convert_nchw_to_nhwc)') + onnx_group.add_argument( + '--nchw_to_nhwc_output_shape', + action='store_true', + help='Convert the output shape of the model (argument for convert_nchw_to_nhwc)') + + # get backend list in the directory + backends_name = [ntpath.basename(f) for f in backends_list] + if not backends_name: + backends_name_message = '(There is no available backend drivers)' + else: + backends_name_message = '(available backend drivers: ' + ', '.join( + backends_name) + ')' + backend_help_message = 'backend name to use ' + backends_name_message + parser.add_argument('-b', '--backend', type=str, help=backend_help_message) + + return parser + + +def _verify_arg(parser, args): + # check if required arguments is given + missing = [] + if not _utils._is_valid_attr(args, 'input_path'): + missing.append('-i/--input_path') + if not _utils._is_valid_attr(args, 'output_path'): + missing.append('-o/--output_path') + if not _utils._is_valid_attr(args, 'backend'): + missing.append('-b/--backend') + + if _utils._is_valid_attr(args, 'model_type'): + # TODO Support model types other than onnx and tflite (e.g., TF) + if getattr(args, 'model_type') not in ['onnx', 'tflite']: + parser.error('Allowed value for --model_type: "onnx" or "tflite"') + + if _utils._is_valid_attr(args, 'nchw_to_nhwc_input_shape'): + if not _utils._is_valid_attr(args, 'convert_nchw_to_nhwc'): + missing.append('--convert_nchw_to_nhwc') + if _utils._is_valid_attr(args, 'nchw_to_nhwc_output_shape'): + if not _utils._is_valid_attr(args, 'convert_nchw_to_nhwc'): + missing.append('--convert_nchw_to_nhwc') + + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) + + +def _parse_arg(parser): + init_args = [] + backend_args = [] + argv = copy.deepcopy(sys.argv) + # delete file name + del argv[0] + # split by '--' + args = [list(y) for x, y in itertools.groupby(argv, lambda z: z == '--') if not x] + + # one-init [-h] [-v] ... + if len(args): + init_args = args[0] + init_args = parser.parse_args(init_args) + backend_args = backend_args if len(args) < 2 else args[1] + # print version + if len(args) and init_args.version: + _utils._print_version_and_exit(__file__) + + return init_args, backend_args + + +def _get_executable(args, backends_list): + if _utils._is_valid_attr(args, 'backend'): + backend_base = getattr(args, 'backend') + '-init' + for cand in backends_list: + if ntpath.basename(cand) == backend_base: + return cand + raise FileNotFoundError(backend_base + ' not found') + + +# TODO Support workflow format (https://github.com/Samsung/ONE/pull/9354) +def _generate(): + # generate cfg file + config = CommentableConfigParser() + + def _add_onecc_sections(): + pass # NYI + + def _gen_import(): + pass # NYI + + def _gen_optimize(): + pass # NYI + + def _gen_quantize(): + pass # NYI + + def _gen_codegen(): + pass # NYI + + # + # NYI: one-profile, one-partition, one-pack, one-infer + # + + _add_onecc_sections() + + _gen_import() + _gen_optimize() + _gen_quantize() + _gen_codegen() + + with open(args.output_path, 'w') as f: + config.write(f) + + +def main(): + # get backend list + backends_list = _get_backends_list() + + # parse arguments + parser = _get_parser(backends_list) + args, backend_args = _parse_arg(parser) + + # verify arguments + _verify_arg(parser, args) + + # make a command to run given backend driver + driver_path = _get_executable(args, backends_list) + init_cmd = [driver_path] + backend_args + + # run backend driver + _utils._run(init_cmd, err_prefix=ntpath.basename(driver_path)) + + #TODO generate cfg file + + raise NotImplementedError("NYI") + + +if __name__ == '__main__': + _utils._safemain(main, __file__) diff --git a/compiler/one-cmds/one-optimize b/compiler/one-cmds/one-optimize index 8b1f3f7..481fc84 100644 --- a/compiler/one-cmds/one-optimize +++ b/compiler/one-cmds/one-optimize @@ -21,7 +21,6 @@ import argparse import os -import subprocess import sys import onelib.constant as _constant @@ -83,6 +82,14 @@ def _verify_arg(parser, args): if len(missing): parser.error('the following arguments are required: ' + ' '.join(missing)) + # default has pre-defined optimization options + default = _get_parser().parse_args() + + # check if unrecognized arguments are given + diff = set(dir(args)) - set(dir(default)) + if len(diff): + parser.error('the following arguments are unrecognized: ' + ' '.join(diff)) + def _parse_arg(parser): args = parser.parse_args() @@ -102,8 +109,8 @@ def _optimize(args): # make a command to optimize circle model circle2circle_path = os.path.join(dir_path, 'circle2circle') circle2circle_cmd = _make_cmd.make_circle2circle_cmd(args, circle2circle_path, - getattr(args, 'input_path'), - getattr(args, 'output_path')) + getattr(args, 'input_path'), + getattr(args, 'output_path')) # verbose if _utils._is_valid_attr(args, 'verbose'): diff --git a/compiler/one-cmds/one-pack b/compiler/one-cmds/one-pack index 133207d..5cab7c7 100644 --- a/compiler/one-cmds/one-pack +++ b/compiler/one-cmds/one-pack @@ -21,9 +21,7 @@ import argparse import os -import subprocess import sys -import tempfile import utils as _utils diff --git a/compiler/one-cmds/one-partition b/compiler/one-cmds/one-partition new file mode 100644 index 0000000..c0d71e5 --- /dev/null +++ b/compiler/one-cmds/one-partition @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # ''' +''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # ''' +''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # ''' +''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # ''' +''''exit 255 # ''' + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +import argparse +import configparser +import os +import sys + +import utils as _utils + +# TODO Find better way to suppress trackback on error +sys.tracebacklimit = 0 + + +def _get_parser(): + parser = argparse.ArgumentParser( + description='command line tool to partition circle model by multiple backends') + + _utils._add_default_arg(parser) + + parser.add_argument( + '--backends', type=str, help='backends in CSV to use for partitioning') + parser.add_argument('--default', type=str, help='default backend to assign') + + parser.add_argument( + '--part_file', type=str, help='partition file which provides backend to assign') + parser.add_argument('--input_file', type=str, help='input circle model filename') + parser.add_argument( + '--work_path', + type=str, + help='work path of partition, input files exist and output files are produced') + + return parser + + +def _parse_arg(parser): + args = parser.parse_args() + # print version + if args.version: + _utils._print_version_and_exit(__file__) + + return args + + +def _verify_arg(parser, args): + """verify given arguments""" + # check if required arguments is given + missing = [] + if not _utils._is_valid_attr(args, 'part_file'): + missing.append('part_file') + if not _utils._is_valid_attr(args, 'input_file'): + missing.append('input_file') + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) + return + + +def _partition(args): + # get file path to log + bin_path = os.path.dirname(os.path.realpath(__file__)) + cur_path = os.getcwd() + partition_path = os.path.join(cur_path, args.part_file) + logfile_path = partition_path + '.log' + + with open(logfile_path, 'wb', buffering=0) as f: + # make a command to package circle model and metadata into nnpackage + circle_partitioner_path = os.path.join(bin_path, 'circle-partitioner') + + cmd = [os.path.expanduser(circle_partitioner_path)] + + if _utils._is_valid_attr(args, 'backends'): + cmd.append('--backends') + cmd.append(getattr(args, 'backends')) + if _utils._is_valid_attr(args, 'default'): + cmd.append('--default') + cmd.append(getattr(args, 'default')) + if _utils._is_valid_attr(args, 'work_path'): + cmd.append('--work_path') + cmd.append(getattr(args, 'work_path')) + + cmd.append('--part_file') + cmd.append(args.part_file) + cmd.append('--input_file') + cmd.append(args.input_file) + + f.write((' '.join(cmd) + '\n').encode()) + + # run circle-partitoner + _utils._run(cmd, err_prefix='circle-partitioner', logfile=f) + + +def main(): + # parse arguments + parser = _get_parser() + args = _parse_arg(parser) + + # parse configuration file + _utils._parse_cfg(args, 'one-partition') + + if _utils._is_valid_attr(args, 'config'): + config_path = getattr(args, 'config') + _utils._parse_cfg_and_overwrite(config_path, 'one-partition', args) + + # verify arguments + _verify_arg(parser, args) + + # do partition + _partition(args) + + +if __name__ == '__main__': + _utils._safemain(main, __file__) diff --git a/compiler/one-cmds/one-prepare-venv b/compiler/one-cmds/one-prepare-venv index 0f75166..b435671 100644 --- a/compiler/one-cmds/one-prepare-venv +++ b/compiler/one-cmds/one-prepare-venv @@ -41,6 +41,7 @@ VER_ONNX_TF=1.10.0 # Install tensorflow PIP_TRUSTED_HOST="--trusted-host pypi.org " +PIP_TRUSTED_HOST+="--trusted-host pypi.python.org " PIP_TRUSTED_HOST+="--trusted-host files.pythonhost.org " PIP_TRUSTED_HOST+="--trusted-host download.pytorch.org " @@ -62,7 +63,8 @@ else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow-cpu==${VER_TENSORFLOW} fi ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install Pillow -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability +# TODO remove version fix, https://github.com/Samsung/ONE/issues/9240 +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability==0.16.0 # Install PyTorch and ONNX related # NOTE set ONE_PREPVENV_TORCH_STABLE to override 'torch_stable.html' URL. @@ -72,6 +74,8 @@ TORCH_STABLE_URL="https://download.pytorch.org/whl/torch_stable.html" if [[ ! -z "$ONE_PREPVENV_TORCH_STABLE" ]]; then TORCH_STABLE_URL="${ONE_PREPVENV_TORCH_STABLE}" fi +# TODO remove torch message +echo "Torch from '${ONE_PREPVENV_TORCH_STABLE}' -> '${TORCH_STABLE_URL}'" ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install torch==1.11.0+cpu -f ${TORCH_STABLE_URL} ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx==${VER_ONNX} @@ -84,3 +88,7 @@ if [ -n "${EXT_ONNX_TF_WHL}" ]; then else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx-tf==${VER_ONNX_TF} fi + +# NOTE refer https://github.com/protocolbuffers/protobuf/issues/10051 +# TODO remove this when issue is resolved +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==3.20.1 diff --git a/compiler/one-cmds/one-profile b/compiler/one-cmds/one-profile index ed6d8bd..b19c215 100644 --- a/compiler/one-cmds/one-profile +++ b/compiler/one-cmds/one-profile @@ -25,9 +25,7 @@ import glob import itertools import ntpath import os -import subprocess import sys -import tempfile import utils as _utils diff --git a/compiler/one-cmds/one-quantize b/compiler/one-cmds/one-quantize index f2eff24..9282007 100644 --- a/compiler/one-cmds/one-quantize +++ b/compiler/one-cmds/one-quantize @@ -21,11 +21,12 @@ import argparse import os -import subprocess import sys import tempfile +import json import utils as _utils +from utils import Command # TODO Find better way to suppress trackback on error sys.tracebacklimit = 0 @@ -67,6 +68,12 @@ def _get_parser(): action='store_true', help='generate profiling data') + # save intermediate file(s) + parser.add_argument( + '--save_intermediate', + action='store_true', + help='Save intermediate files to output folder') + ## arguments for quantization quantization_group = parser.add_argument_group('arguments for quantization') @@ -93,13 +100,13 @@ def _get_parser(): '--input_type', type=str, help= - 'data type of inputs of quantized model (supported: uint8, int16, default=quantized_dtype). QUANTIZE Op will be inserted at the beginning of the quantized model if input_type is different from quantized_dtype.' + 'data type of inputs of quantized model (supported: uint8, int16, float32, default=quantized_dtype). QUANTIZE Op will be inserted at the beginning of the quantized model if input_type is different from quantized_dtype.' ) quantization_group.add_argument( '--output_type', type=str, help= - 'data type of outputs of quantized model (supported: uint8, int16, default=quantized_dtype). QUANTIZE Op will be inserted at the end of the quantized model if output_type is different from quantized_dtype.' + 'data type of outputs of quantized model (supported: uint8, int16, float32, default=quantized_dtype). QUANTIZE Op will be inserted at the end of the quantized model if output_type is different from quantized_dtype.' ) quantization_group.add_argument( '--min_percentile', @@ -126,10 +133,50 @@ def _get_parser(): "Force MaxPool Op to have the same input/output quantparams. NOTE: This option can degrade accuracy of some models.)" ) quantization_group.add_argument( - '--quant_config', - type=str, + '--quant_config', type=str, help="Path to the quantization configuration file.") + quantization_group.add_argument( + '--evaluate_result', + action='store_true', + help= + "Evaluate accuracy of quantized model. Run inference for both fp32 model and the quantized model, and compare the inference results." + ) + quantization_group.add_argument( + '--test_data', type=str, help="Path to the test data used for evaluation.") + quantization_group.add_argument( + '--print_mae', + action='store_true', + help= + "Print MAE (Mean Absolute Error) of inference results between quantized model and fp32 model." + ) + quantization_group.add_argument( + '--print_mape', + action='store_true', + help= + "Print MAPE (Mean Absolute Percentage Error) of inference results between quantized model and fp32 model." + ) + quantization_group.add_argument( + '--print_mpeir', + action='store_true', + help= + "Print MPEIR (Mean Peak Error to Interval Ratio) of inference results between quantized model and fp32 model." + ) + quantization_group.add_argument( + '--print_top1_match', + action='store_true', + help= + "Print Top-1 match ratio of inference results between quantized model and fp32 model." + ) + quantization_group.add_argument( + '--print_top5_match', + action='store_true', + help= + "Print Top-5 match ratio of inference results between quantized model and fp32 model." + ) + quantization_group.add_argument( + '--print_mse', + action='store_true', help= - "Path to the quantization configuration file." + "Print MSE (Mean Squared Error) of inference results between quantized model and fp32 model." ) # arguments for force_quantparam option @@ -162,6 +209,14 @@ def _get_parser(): copy_quantparam_group.add_argument( '--dst_tensor_name', type=str, action='append', help='tensor name (string)') + # arguments for fake_quant option + fake_quant_group = parser.add_argument_group('arguments for fake_quantize option') + + fake_quant_group.add_argument( + '--fake_quantize', + action='store_true', + help='convert quantized model to fake-quantized fp32 model.') + return parser @@ -171,8 +226,29 @@ def _set_default_values(args): setattr(args, 'input_model_dtype', 'float32') if not _utils._is_valid_attr(args, 'quantized_dtype'): setattr(args, 'quantized_dtype', 'uint8') + if _utils._is_valid_attr(args, 'quant_config'): + # Get quantized_dtype from qconfig file + try: + with open(getattr(args, 'quant_config')) as f: + qconf = json.load(f) + if 'default_quantization_dtype' in qconf: + setattr(args, 'quantized_dtype', + qconf['default_quantization_dtype']) + except json.decoder.JSONDecodeError: + print('Failed to decode ' + getattr(args, 'quant_config') + + '. Please check it is a json file.') if not _utils._is_valid_attr(args, 'granularity'): setattr(args, 'granularity', 'layer') + if _utils._is_valid_attr(args, 'quant_config'): + # Get granularity from qconfig file + try: + with open(getattr(args, 'quant_config')) as f: + qconf = json.load(f) + if 'default_granularity' in qconf: + setattr(args, 'granularity', qconf['default_granularity']) + except json.decoder.JSONDecodeError: + print('Failed to decode ' + getattr(args, 'quant_config') + + '. Please check it is a json file.') if not _utils._is_valid_attr(args, 'mode'): setattr(args, 'mode', 'percentile') if not _utils._is_valid_attr(args, 'min_percentile'): @@ -238,11 +314,18 @@ def _quantize(args): _copy_qparam(args) return + if _utils._is_valid_attr(args, 'fake_quantize'): + # fake-quantize model + _fake_quantize(args) + return + # get file path to log dir_path = os.path.dirname(os.path.realpath(__file__)) logfile_path = os.path.realpath(args.output_path) + '.log' with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir: + if _utils._is_valid_attr(args, 'save_intermediate'): + tmpdir = os.path.dirname(logfile_path) # get driver path circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer') record_minmax_path = os.path.join(dir_path, 'record-minmax') @@ -263,13 +346,19 @@ def _quantize(args): circle_quantizer_cmd.append(getattr(args, 'quantized_dtype')) if _utils._is_valid_attr(args, 'granularity'): circle_quantizer_cmd.append(getattr(args, 'granularity')) + if _utils._is_valid_attr(args, 'quant_config'): + # NOTE --config conflicts with --config option in onecc, so + # we use quant_config for one-quantize + circle_quantizer_cmd.append('--config') + circle_quantizer_cmd.append(getattr(args, 'quant_config')) # input and output path if _utils._is_valid_attr(args, 'input_path'): circle_quantizer_cmd.append(getattr(args, 'input_path')) - tmp_output_path_1 = os.path.join( + tmp_weights_fake_quant_path = os.path.join( tmpdir, - os.path.splitext(os.path.basename(args.input_path))[0]) + '1.circle' - circle_quantizer_cmd.append(tmp_output_path_1) + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.weights_fake_quant.circle' + circle_quantizer_cmd.append(tmp_weights_fake_quant_path) # profiling if _utils._is_valid_attr(args, 'generate_profile_data'): circle_quantizer_cmd.append('--generate_profile_data') @@ -279,45 +368,23 @@ def _quantize(args): # run circle-quantizer _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f) - ## make a command to record min-max value of each tensor while running the representative dataset - circle_record_minmax_cmd = [record_minmax_path] - # verbose - if _utils._is_valid_attr(args, 'verbose'): - circle_record_minmax_cmd.append('--verbose') - # input and output path - circle_record_minmax_cmd.append('--input_model') - circle_record_minmax_cmd.append(tmp_output_path_1) - tmp_output_path_2 = os.path.join( + tmp_minmax_recorded_path = os.path.join( tmpdir, - os.path.splitext(os.path.basename(args.input_path))[0]) + '2.circle' - circle_record_minmax_cmd.append('--output_model') - circle_record_minmax_cmd.append(tmp_output_path_2) - # input data - if _utils._is_valid_attr(args, 'input_data'): - circle_record_minmax_cmd.append('--input_data') - circle_record_minmax_cmd.append(getattr(args, 'input_data')) - if _utils._is_valid_attr(args, 'input_data_format'): - circle_record_minmax_cmd.append('--input_data_format') - circle_record_minmax_cmd.append(getattr(args, 'input_data_format')) - # min and max percentile - if _utils._is_valid_attr(args, 'min_percentile'): - circle_record_minmax_cmd.append('--min_percentile') - circle_record_minmax_cmd.append(getattr(args, 'min_percentile')) - if _utils._is_valid_attr(args, 'max_percentile'): - circle_record_minmax_cmd.append('--max_percentile') - circle_record_minmax_cmd.append(getattr(args, 'max_percentile')) - # mode - if _utils._is_valid_attr(args, 'mode'): - circle_record_minmax_cmd.append('--mode') - circle_record_minmax_cmd.append(getattr(args, 'mode')) - # profiling - if _utils._is_valid_attr(args, 'generate_profile_data'): - circle_record_minmax_cmd.append('--generate_profile_data') - - f.write((' '.join(circle_record_minmax_cmd) + '\n').encode()) + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.minmax_recorded.circle' - # run record-minmax - _utils._run(circle_record_minmax_cmd, err_prefix="record_minmax", logfile=f) + ## make a command to record min-max value of each tensor while running the representative dataset + record_minmax_cmd = Command(record_minmax_path, args, f) + record_minmax_cmd.add_noarg_option_if_valid_arg('--verbose', 'verbose') \ + .add_option_with_values('--input_model', [tmp_weights_fake_quant_path]) \ + .add_option_with_values('--output_model', [tmp_minmax_recorded_path]) \ + .add_option_with_valid_args('--input_data', ['input_data']) \ + .add_option_with_valid_args('--input_data_format', ['input_data_format']) \ + .add_option_with_valid_args('--min_percentile', ['min_percentile']) \ + .add_option_with_valid_args('--max_percentile', ['max_percentile']) \ + .add_option_with_valid_args('--mode', ['mode']) \ + .add_noarg_option_if_valid_arg('--generate_profile_data', 'generate_profile_data') \ + .run() ## make a second command to quantize the model using the embedded information circle_quantizer_cmd = [circle_quantizer_path] @@ -349,7 +416,7 @@ def _quantize(args): circle_quantizer_cmd.append('--config') circle_quantizer_cmd.append(getattr(args, 'quant_config')) # input and output path - circle_quantizer_cmd.append(tmp_output_path_2) + circle_quantizer_cmd.append(tmp_minmax_recorded_path) if _utils._is_valid_attr(args, 'output_path'): circle_quantizer_cmd.append(getattr(args, 'output_path')) # profiling @@ -361,6 +428,38 @@ def _quantize(args): # run circle-quantizer _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f) + # evaluate + if _utils._is_valid_attr(args, 'evaluate_result'): + circle_eval_diff_path = os.path.join(dir_path, 'circle-eval-diff') + quant_model = "" + if _utils._is_valid_attr(args, 'output_path'): + quant_model = getattr(args, 'output_path') + tmp_fake_quant_model = os.path.join( + tmpdir, + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.fake_quant.circle' + + # do fake quantization + fake_quantize_cmd = Command(circle_quantizer_path, args, f) + fake_quantize_cmd.add_noarg_option_if_valid_arg('--verbose', 'verbose') \ + .add_option_with_values('--fake_quantize', [quant_model, tmp_fake_quant_model]) \ + .run() + + # compare fake-quant model and fp32 model + circle_eval_diff_cmd = Command(circle_eval_diff_path, args, f) + circle_eval_diff_cmd.add_option_with_valid_args('--first_model', ['input_path']) \ + .add_option_with_values('--second_model', [tmp_fake_quant_model]) \ + .add_option_with_valid_args('--first_input_data', ['test_data']) \ + .add_option_with_valid_args('--second_input_data', ['test_data']) \ + .add_option_with_valid_args('--input_data_format', ['input_data_format']) \ + .add_noarg_option_if_valid_arg('--print_mae', 'print_mae') \ + .add_noarg_option_if_valid_arg('--print_mape', 'print_mape') \ + .add_noarg_option_if_valid_arg('--print_mpeir', 'print_mpeir') \ + .add_noarg_option_if_valid_arg('--print_top1_match', 'print_top1_match') \ + .add_noarg_option_if_valid_arg('--print_top5_match', 'print_top5_match') \ + .add_noarg_option_if_valid_arg('--print_mse', 'print_mse') \ + .run() + def _write_qparam(args): # get file path to log @@ -433,6 +532,24 @@ def _copy_qparam(args): _utils._run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f) +def _fake_quantize(args): + # get file path to log + dir_path = os.path.dirname(os.path.realpath(__file__)) + logfile_path = os.path.realpath(args.output_path) + '.log' + + with open(logfile_path, 'wb') as f: + # get driver path + circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer') + q_model = getattr(args, 'input_path') + fq_model = getattr(args, 'output_path') + + # do fake quantization + fake_quantize_cmd = Command(circle_quantizer_path, args, f) + fake_quantize_cmd.add_noarg_option_if_valid_arg('--verbose', 'verbose') \ + .add_option_with_values('--fake_quantize', [q_model, fq_model]) \ + .run() + + def main(): # parse arguments parser = _get_parser() diff --git a/compiler/one-cmds/onecc b/compiler/one-cmds/onecc index 25682ff..a5ba636 100644 --- a/compiler/one-cmds/onecc +++ b/compiler/one-cmds/onecc @@ -25,6 +25,8 @@ import os import subprocess import sys +from onelib.CfgRunner import CfgRunner +from onelib.WorkflowRunner import WorkflowRunner import utils as _utils # TODO Find better way to suppress trackback on error @@ -42,6 +44,7 @@ subtool_list = { 'backend': { 'codegen': 'Code generation tool', 'profile': 'Profile backend model file', + 'infer': 'Infer backend model file' }, } @@ -64,12 +67,25 @@ def _check_subtool_exists(): def _get_parser(): - onecc_usage = 'onecc [-h] [-v] [-C CONFIG] [COMMAND ]' + onecc_usage = 'onecc [-h] [-v] [-C CONFIG] [-W WORKFLOW] [-O OPTIMIZATION] [COMMAND ]' onecc_desc = 'Run ONE driver via several commands or configuration file' parser = argparse.ArgumentParser(description=onecc_desc, usage=onecc_usage) _utils._add_default_arg(parser) + opt_name_list = _utils._get_optimization_list(get_name=True) + opt_name_list = ['-' + s for s in opt_name_list] + if not opt_name_list: + opt_help_message = '(No available optimization options)' + else: + opt_help_message = '(Available optimization options: ' + ', '.join( + opt_name_list) + ')' + opt_help_message = 'optimization name to use ' + opt_help_message + parser.add_argument('-O', type=str, metavar='OPTIMIZATION', help=opt_help_message) + + parser.add_argument( + '-W', '--workflow', type=str, metavar='WORKFLOW', help='run with workflow file') + # just for help message compile_group = parser.add_argument_group('compile to circle model') for tool, desc in subtool_list['compile'].items(): @@ -98,45 +114,17 @@ def _parse_arg(parser): def _verify_arg(parser, args): """verify given arguments""" # check if required arguments is given - if not _utils._is_valid_attr(args, 'config'): - parser.error('-C/--config argument is required') - - -def _get_driver_name(driver_name): - return { - 'one-optimize': 'one-optimize', - 'one-quantize': 'one-quantize', - 'one-pack': 'one-pack', - 'one-codegen': 'one-codegen', - 'one-profile': 'one-profile' - }[driver_name] - - -def _parse_cfg(args): - config = configparser.ConfigParser() - config.optionxform = str - parsed = config.read(os.path.expanduser(getattr(args, 'config'))) - if not parsed: - raise FileNotFoundError('Not found given configuration file') - return config - - -def _is_available_driver(config, driver_name): - return config.has_option('onecc', driver_name) and config.getboolean( - 'onecc', driver_name) - - -def _verify_cfg(import_driver_list, config): - if not config.has_section('onecc'): - raise ImportError('[onecc] section is required in configuration file') - - import_driver_cnt = 0 - for d in import_driver_list: - if _is_available_driver(config, d): - import_driver_cnt += 1 - - if import_driver_cnt > 1: - raise AssertionError('Only one import-* driver can be executed') + if not _utils._is_valid_attr(args, 'config') and not _utils._is_valid_attr( + args, 'workflow'): + parser.error('-C/--config or -W/--workflow argument is required') + # check if given optimization option exists + opt_name_list = _utils._get_optimization_list(get_name=True) + opt_name_list = [_utils._remove_prefix(s, 'O') for s in opt_name_list] + if _utils._is_valid_attr(args, 'O'): + if ' ' in getattr(args, 'O'): + parser.error('Not allowed to have space in the optimization name') + if not getattr(args, 'O') in opt_name_list: + parser.error('Invalid optimization option') def main(): @@ -158,35 +146,16 @@ def main(): # verify arguments _verify_arg(parser, args) - # parse configuration file - config = _parse_cfg(args) - - # verify configuration file bin_dir = os.path.dirname(os.path.realpath(__file__)) - import_drivers_dict = _utils._detect_one_import_drivers(bin_dir) - transform_drivers = [ - 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile' - ] - _verify_cfg(import_drivers_dict, config) - - # get sections to run - section_to_run = [] - for d in list(import_drivers_dict) + transform_drivers: - if _is_available_driver(config, d): - section_to_run.append(d) - - # run - dir_path = os.path.dirname(os.path.realpath(__file__)) - for section in section_to_run: - if section in import_drivers_dict: - # we already has driver name in dict - driver_name = import_drivers_dict[section] - else: - driver_name = _get_driver_name(section) - options = ['--config', getattr(args, 'config'), '--section', section] - if _utils._is_valid_attr(args, 'verbose'): - options.append('--verbose') - _call_driver(driver_name, options) + if _utils._is_valid_attr(args, 'config'): + runner = CfgRunner(args.config) + runner.detect_import_drivers(bin_dir) + if _utils._is_valid_attr(args, 'O'): + runner.add_opt(getattr(args, 'O')) + runner.run(bin_dir) + elif _utils._is_valid_attr(args, 'workflow'): + runner = WorkflowRunner(args.workflow) + runner.run(bin_dir) if __name__ == '__main__': diff --git a/compiler/one-cmds/onecc.template.cfg b/compiler/one-cmds/onecc.template.cfg index a23d1ce..6f6a4e2 100644 --- a/compiler/one-cmds/onecc.template.cfg +++ b/compiler/one-cmds/onecc.template.cfg @@ -1,28 +1,144 @@ +; To activate a step (or task), +; set True for the step in [onecc] section and fill options in the corresponding section [onecc] -one-import-tf=True +; neural network model to circle +one-import-tf=False one-import-tflite=False one-import-bcq=False one-import-onnx=False -one-optimize=True +; circle to circle with optimization +one-optimize=False +; circle to circle with quantization one-quantize=False -one-pack=True +; partition circle +one-partition=False +; package circle and metadata into nnpackage +one-pack=False +; generate code for backend one-codegen=False +; profile one-profile=False +; infer +one-infer=False [one-import-tf] -input_path=/path/to/inception_v3.pb -output_path=inception_v3.circle -input_arrays=input -input_shapes=1,299,299,3 -output_arrays=InceptionV3/Predictions/Reshape_1 -converter_version=v1 +# mandatory +; pb file +input_path= +; circle file +output_path= +# optional +; v1 or v2 +converter_version=v2 +; graph_def(default), saved_model or keras_model model_format=graph_def +# optional but mandatory for model_format=graph_def +; input tensor names of the input arrays, comma-separated +input_arrays= +; output tensor names of the input arrays, comma-separated +output_arrays= +; input shapes corresponding to --input_arrays, colon-separated.(ex:1,4,4,3:1,20,20,3) +input_shapes= + +[one-import-tflite] +# mandatory +; tflite file +input_path= +; circle file +output_path= + +[one-import-bcq] +# mandatory +; bcq file +input_path= +; circle file +output_path= +# optional +; v1 or v2 +converter_version=v2 +; graph_def(default), saved_model or keras_model +model_format=graph_def +# optional but mandatory for model_format=graph_def +; input tensor names of the input arrays, comma-separated +input_arrays= +; output tensor names of the input arrays, comma-separated +output_arrays= +; input shapes corresponding to --input_arrays, colon-separated.(ex:1,4,4,3:1,20,20,3) +input_shapes= + +[one-import-onnx] +# mandatory +; onnx file +input_path= +; circle file +output_path= +# optional +; True or False +unroll_rnn= +; True or False +unroll_lstm= [one-optimize] -input_path=inception_v3.circle -output_path=inception_v3.opt.circle -generate_profile_data=False +# mandatory +; circle file +input_path= +; circle file +output_path= +# //TODO: Add available options + +[one-quantize] +# mandatory +; circle file +input_path= +; circle file +output_path= +# optional arguments for quantization +; input data file (if not given, random data will be used for calibration) +input_data= +; h5/hdf5(default), list/filelist, or dir/directory +input_data_format= +; dtype of quantized model (uint8(default), int16) +quantized_dtype= +; granularity of quantization (layer(default), channel) +granularity= +; dtype of model's input (uint8, int16, float32). Same with quantized_dtype by default. +input_type= +; dtype of model's output (uint8, int16, float32). Same with quantized_dtype by default. +output_type= + +[one-partition] +# mandatory +; partition file which provides backend to assign +part_file= +; circle file +input_file= +# //TODO: Add available options [one-pack] -input_path=inception_v3.opt.circle -output_path=inception_v3_pack +# mandatory +; input path +input_path= +; output path +output_path= +# //TODO: Add available options + +[one-codegen] +# mandatory +; backend name +backend= +; commands for each backend +command= + +[one-profile] +# mandatory +; backend name +backend= +# //TODO: Add available options + +[one-infer] +# mandatory (mutually exclusive) +; backend name +backend= +; driver name +driver= +# //TODO: Add available options diff --git a/compiler/one-cmds/onelib/CfgRunner.py b/compiler/one-cmds/onelib/CfgRunner.py new file mode 100644 index 0000000..c66e5b4 --- /dev/null +++ b/compiler/one-cmds/onelib/CfgRunner.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +import configparser +import os +import warnings + +import utils as oneutils + + +def _simple_warning(message, category, filename, lineno, file=None, line=None): + return f'{category.__name__}: {message}\n' + + +class CfgRunner: + driver_sequence = [ + 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile', + 'one-partition', 'one-infer' + ] + + def __init__(self, path): + self.path = path + self.optparser = None + self.cfgparser = configparser.ConfigParser() + # make option names case sensitive + self.cfgparser.optionxform = str + parsed = self.cfgparser.read(os.path.expanduser(path)) + if not parsed: + raise FileNotFoundError('Not found given configuration file') + + self._verify_cfg(self.cfgparser) + # default import drivers + self.import_drivers = [ + 'one-import-bcq', 'one-import-onnx', 'one-import-tf', 'one-import-tflite' + ] + + def _verify_cfg(self, cfgparser): + if not cfgparser.has_section('onecc'): + if cfgparser.has_section('one-build'): + warnings.formatwarning = _simple_warning + warnings.warn( + "[one-build] section will be deprecated. Please use [onecc] section.") + else: + raise ImportError('[onecc] section is required in configuration file') + + def _is_available(self, driver): + # if there's no `onecc` section, it will find `one-build` section because of backward compatibility + return (self.cfgparser.has_option('onecc', driver) and self.cfgparser.getboolean( + 'onecc', driver)) or (self.cfgparser.has_option('one-build', driver) + and self.cfgparser.getboolean('one-build', driver)) + + def add_opt(self, opt): + self.optparser = configparser.ConfigParser() + # make option names case sensitive + self.optparser.optionxform = str + opt_book = dict( + zip(oneutils._get_optimization_list(get_name=True), + oneutils._get_optimization_list())) + parsed = self.optparser.read(opt_book['O' + opt]) + if not parsed: + raise FileNotFoundError('Not found given optimization configuration file') + if len(self.optparser.sections()) != 1 or self.optparser.sections( + )[0] != 'one-optimize': + raise AssertionError( + 'Optimization configuration file only allowed to have a \'one-optimize\' section' + ) + self.opt = opt + + def detect_import_drivers(self, dir): + self.import_drivers = list(oneutils._detect_one_import_drivers(dir).keys()) + + def run(self, working_dir, verbose=False): + section_to_run = [] + for d in self.import_drivers + self.driver_sequence: + if self._is_available(d): + section_to_run.append(d) + + for section in section_to_run: + options = ['--config', self.path, '--section', section] + if section == 'one-optimize' and self.optparser: + options += ['-O', self.opt] + if verbose: + options.append('--verbose') + driver_path = os.path.join(working_dir, section) + cmd = [driver_path] + options + oneutils._run(cmd) diff --git a/compiler/one-cmds/onelib/OptionBuilder.py b/compiler/one-cmds/onelib/OptionBuilder.py new file mode 100644 index 0000000..6a75783 --- /dev/null +++ b/compiler/one-cmds/onelib/OptionBuilder.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +from onelib.constant import CONSTANT + + +class OptionBuilder: + def __init__(self, one_cmd_type): + self.type = one_cmd_type + + def _build_default(self, commands): + options = [] + for k, v in commands.items(): + options.extend(['--' + k, v]) + return options + + def _build_with_unknown_command(self, commands): + COMMAND_K = 'command' + options = [] + for k, v in commands.items(): + if k == COMMAND_K: + continue + options.extend(['--' + k, v]) + options.extend(['--']) + options.extend(commands[COMMAND_K].split()) + return options + + def _build_import(self, commands): + options = [] + arg_0 = ['save_intermediate'] + for k, v in commands.items(): + if k in arg_0 and v == "True": + options.extend(['--' + k]) + continue + options.extend(['--' + k, v]) + return options + + def _build_optimize(self, commands): + options = [] + arg_0 = ['generate_profile_data'] + arg_1 = ['input_path', 'output_path', 'change_outputs'] + for k, v in commands.items(): + if k in arg_1: + options.extend(['--' + k, v]) + continue + if k in arg_0 and v == 'True': + options.extend(['--' + k]) + continue + for opt in CONSTANT.OPTIMIZATION_OPTS: + if k == opt[0] and v == "True": + options.extend(['--' + k]) + break + return options + + def _build_quantize(self, commands): + options = [] + arg_0 = [ + 'generate_profile_data', 'save_intermediate', 'TF-style_maxpool', + 'evaluate_result', 'print_mae', 'print_mape', 'print_mpeir', + 'print_top1_match', 'print_top5_match', 'force_quantparam', 'copy_quantparam' + ] + for k, v in commands.items(): + if k in arg_0 and v == "True": + options.extend(['--' + k]) + continue + options.extend(['--' + k, v]) + return options + + def build(self, commands): + cmd_book = dict.fromkeys( + ['one-import-bcq', 'one-import-tflite', 'one-pack', 'one-partition'], + self._build_default) + cmd_book['one-codegen'] = self._build_with_unknown_command + cmd_book['one-import-onnx'] = self._build_import + cmd_book['one-import-pytorch'] = self._build_import + cmd_book['one-import-tf'] = self._build_import + cmd_book['one-infer'] = self._build_with_unknown_command + cmd_book['one-optimize'] = self._build_optimize + cmd_book['one-profile'] = self._build_with_unknown_command + cmd_book['one-quantize'] = self._build_quantize + + return cmd_book[self.type](commands) diff --git a/compiler/one-cmds/onelib/TopologicalSortHelper.py b/compiler/one-cmds/onelib/TopologicalSortHelper.py new file mode 100644 index 0000000..d05adea --- /dev/null +++ b/compiler/one-cmds/onelib/TopologicalSortHelper.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +from collections import defaultdict + + +class TopologicalSortHelper: + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + def add_edge(self, u, v): + self.graph[u].append(v) + + def sort_util(self, v, visited, stack): + visited[v] = True + + for i in self.graph[v]: + if visited[i] == False: + self.sort_util(i, visited, stack) + + stack.insert(0, v) + + def sort(self): + visited = dict.fromkeys(self.vertices, False) + stack = [] + + for v in self.vertices: + if visited[v] == False: + self.sort_util(v, visited, stack) + + return stack diff --git a/compiler/one-cmds/onelib/WorkflowRunner.py b/compiler/one-cmds/onelib/WorkflowRunner.py new file mode 100644 index 0000000..0482dd9 --- /dev/null +++ b/compiler/one-cmds/onelib/WorkflowRunner.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +import json +import os + +from onelib.OptionBuilder import OptionBuilder +from onelib.TopologicalSortHelper import TopologicalSortHelper +from onelib.CfgRunner import CfgRunner +import utils as oneutils + + +class WorkflowRunner: + WORKFLOWS_K = 'workflows' + DEPENDENCIES_K = 'run-after' + CFG_REFERENCE_K = 'cfg-reference' + WORKFLOW_STEPS_K = 'steps' + ONE_CMD_TOOL_K = 'one-cmd' + COMMANDS_K = 'commands' + + def __init__(self, path): + try: + with open(path) as f: + self.json_contents = json.load(f) + except FileNotFoundError: + raise FileNotFoundError("Not found given workflow file") + except json.decoder.JSONDecodeError: + raise ImportError("Invalid workflow file") + + self._verify_workflow(self.json_contents) + + workflows = self.json_contents[self.WORKFLOWS_K] + self.adj = dict.fromkeys(workflows, []) + # decide the order according to the dependencies of each workflow. + helper = TopologicalSortHelper(workflows) + for workflow_k in workflows: + workflow = self.json_contents[workflow_k] + if self.DEPENDENCIES_K in workflow: + for previous_workflow in workflow[self.DEPENDENCIES_K]: + helper.add_edge(previous_workflow, workflow_k) + self.adj[previous_workflow].append(workflow_k) + self.workflow_sequence = helper.sort() + + self._check_cycle() + + def _check_cycle(self): + pos = dict() + index = 0 + workflow_num = len(self.workflow_sequence) + # number the order + for seq_idx in range(workflow_num): + pos[self.workflow_sequence[seq_idx]] = index + index += 1 + + for seq_idx in range(workflow_num): + first_wf = self.workflow_sequence[seq_idx] + for adj_wf in self.adj[first_wf]: + first_pos = 0 if first_wf not in pos else pos[first_wf] + second_pos = 0 if adj_wf not in pos else pos[adj_wf] + if (first_pos > second_pos): + raise RuntimeError("Workflows should not have a cycle") + + def _verify_workflow(self, json_contents): + # workflow file should have WORKFLOWS_K + if not self.WORKFLOWS_K in json_contents: + raise ValueError("Not found \"" + self.WORKFLOWS_K + + "\" key in workflow file") + + workflows = json_contents[self.WORKFLOWS_K] + # workflow file should have keys listed in WORKFLOWS_K + for workflow_k in workflows: + if not workflow_k in json_contents: + raise ValueError("Not found " + workflow_k + " key listed in \"" + + self.WORKFLOWS_K + "\"") + + # each workflow should have either WORKFLOW_STEPS_K or CFG_REFERENCE_K + for workflow_k in workflows: + if not self.WORKFLOW_STEPS_K in json_contents[workflow_k] and not self.CFG_REFERENCE_K in json_contents[workflow_k]: + raise ValueError("Each workflow should have either \"" + + self.WORKFLOW_STEPS_K + "\" or \"" + + self.CFG_REFERENCE_K + "\"") + for workflow_k in workflows: + if self.WORKFLOW_STEPS_K in json_contents[workflow_k] and self.CFG_REFERENCE_K in json_contents[workflow_k]: + raise ValueError("\"" + self.WORKFLOW_STEPS_K + "\" and \"" + + self.CFG_REFERENCE_K + "\" are exclusive key") + + # each step should have ONE_CMD_TOOL_K and COMMANDS_K + for workflow_k in workflows: + workflow = json_contents[workflow_k] + if self.WORKFLOW_STEPS_K in workflow: + step_keys = workflow[self.WORKFLOW_STEPS_K] + for step_k in step_keys: + step = workflow[step_k] + if not self.ONE_CMD_TOOL_K in step or not self.COMMANDS_K in step: + raise ValueError("Each step should have \"" + + self.ONE_CMD_TOOL_K + "\"" + " and \"" + + self.COMMANDS_K + "\"") + + def run(self, working_dir, verbose=False): + # run workflows in sequence + for workflow_k in self.workflow_sequence: + workflow = self.json_contents[workflow_k] + if self.WORKFLOW_STEPS_K in workflow: + steps = workflow[self.WORKFLOW_STEPS_K] + for step_k in steps: + step = workflow[step_k] + commands = step[self.COMMANDS_K] + driver_name = step[self.ONE_CMD_TOOL_K] + option_builder = OptionBuilder(driver_name) + options = option_builder.build(commands) + # get the absolute path of the caller + driver_path = os.path.join(working_dir, driver_name) + cmd = [driver_path] + options + oneutils._run(cmd) + elif self.CFG_REFERENCE_K in workflow: + cfg_path = workflow[self.CFG_REFERENCE_K]['path'] + runner = CfgRunner(cfg_path) + runner.run(working_dir, verbose) diff --git a/compiler/one-cmds/onelib/constant.py b/compiler/one-cmds/onelib/constant.py index 7ddd738..7dd79b6 100644 --- a/compiler/one-cmds/onelib/constant.py +++ b/compiler/one-cmds/onelib/constant.py @@ -14,11 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. + class CONSTANT: __slots__ = () # This prevents access via __dict__. OPTIMIZATION_OPTS = ( # (OPTION_NAME, HELP_MESSAGE) - ('O1', 'enable O1 optimization pass'), ('convert_nchw_to_nhwc', 'Experimental: This will convert NCHW operators to NHWC under the assumption that input model is NCHW.' ), @@ -29,6 +29,7 @@ class CONSTANT: 'convert the output shape of the model (argument for convert_nchw_to_nhwc)'), ('fold_add_v2', 'fold AddV2 op with constant inputs'), ('fold_cast', 'fold Cast op with constant input'), + ('fold_densify', 'fold Densify op with sparse constant input'), ('fold_dequantize', 'fold Dequantize op'), ('fold_dwconv', 'fold Depthwise Convolution op with constant inputs'), ('fold_gather', 'fold Gather op'), @@ -62,12 +63,16 @@ class CONSTANT: ('remove_unnecessary_slice', 'remove unnecessary slice ops'), ('remove_unnecessary_strided_slice', 'remove unnecessary strided slice ops'), ('remove_unnecessary_split', 'remove unnecessary split ops'), + ('replace_non_const_fc_with_batch_matmul', + 'replace FullyConnected op with non-const weights to BatchMatMul op'), + ('replace_sub_with_add', 'replace Sub op with Add op'), ('resolve_customop_add', 'convert Custom(Add) op to Add op'), ('resolve_customop_batchmatmul', 'convert Custom(BatchMatmul) op to BatchMatmul op'), ('resolve_customop_matmul', 'convert Custom(Matmul) op to Matmul op'), ('resolve_customop_max_pool_with_argmax', 'convert Custom(MaxPoolWithArgmax) to net of builtin operators'), + ('resolve_customop_splitv', 'convert Custom(SplitV) op to SplitV op'), ('shuffle_weight_to_16x1float32', 'convert weight format of FullyConnected op to SHUFFLED16x1FLOAT32.' ' Note that it only converts weights whose row is a multiple of 16'), diff --git a/compiler/one-cmds/onelib/make_cmd.py b/compiler/one-cmds/onelib/make_cmd.py index d8380f2..0015e83 100644 --- a/compiler/one-cmds/onelib/make_cmd.py +++ b/compiler/one-cmds/onelib/make_cmd.py @@ -19,6 +19,7 @@ import sys import onelib.constant as _constant + def _is_valid_attr(args, attr): return hasattr(args, attr) and getattr(args, attr) @@ -64,6 +65,10 @@ def make_tf2tfliteV2_cmd(args, driver_path, input_path, output_path): cmd.append('--output_arrays') cmd.append(getattr(args, 'output_arrays')) + # experimental options + if _is_valid_attr(args, 'experimental_disable_batchmatmul_unfold'): + cmd.append('--experimental_disable_batchmatmul_unfold') + return cmd diff --git a/compiler/one-cmds/onnx_legalizer.py b/compiler/one-cmds/onnx_legalizer.py index 26c2b75..0141514 100755 --- a/compiler/one-cmds/onnx_legalizer.py +++ b/compiler/one-cmds/onnx_legalizer.py @@ -341,7 +341,8 @@ def _dtype_to_np(dtype): raise NotImplementedError('unsupported data type') -def _generate_one_direction_RNN(transformer, X, W, R, B, initial_h, clip, activation_name): +def _generate_one_direction_RNN(transformer, X, W, R, B, initial_h, clip, + activation_name): """Generate subgraph of one direction of unrolled RNN layer Args: @@ -395,7 +396,7 @@ def _generate_one_direction_RNN(transformer, X, W, R, B, initial_h, clip, activa def _transform_unidirectional_RNN(transformer, original_node, x, tensor_infos, activation, - clip, direction, hidden_size, layout): + clip, direction, hidden_size, layout): """Generate Simple (forward or reverse) unrolled RNN Args: @@ -432,7 +433,7 @@ def _transform_unidirectional_RNN(transformer, original_node, x, tensor_infos, a else: initial_h = None state_tensors = _generate_one_direction_RNN(transformer, x, w, r, b, initial_h, clip, - activation) + activation) y_direction_dim = layout + 1 y_h_direction_dim = layout state_layout_tensors = [] @@ -447,12 +448,11 @@ def _transform_unidirectional_RNN(transformer, original_node, x, tensor_infos, a transformer.make_node( 'Unsqueeze', [state_tensors[-1]], [Y_h], axes=[y_h_direction_dim]) Y = outputs[0] - transformer.make_node( - 'Concat', state_layout_tensors, [Y], axis=seq_length_dim) + transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim) def _transform_bidirectional_RNN(transformer, original_node, x, tensor_infos, activations, - clip, hidden_size, layout): + clip, hidden_size, layout): """Generate Bidirectional unrolled RNN Args: @@ -503,10 +503,10 @@ def _transform_bidirectional_RNN(transformer, original_node, x, tensor_infos, ac initial_h[d] = transformer.make_squeeze(initial_h[d], axes=[direction_dim]) state_f_tensors = _generate_one_direction_RNN(transformer, x, w[0], r[0], b[0], - initial_h[0], clip, activations[0]) + initial_h[0], clip, activations[0]) x.reverse() state_b_tensors = _generate_one_direction_RNN(transformer, x, w[1], r[1], b[1], - initial_h[1], clip, activations[1]) + initial_h[1], clip, activations[1]) state_b_tensors.reverse() y_direction_dim = layout + 1 @@ -538,8 +538,7 @@ def _transform_bidirectional_RNN(transformer, original_node, x, tensor_infos, ac axis=y_h_direction_dim) Y = outputs[0] - transformer.make_node( - 'Concat', state_layout_tensors, [Y], axis=seq_length_dim) + transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim) def _legalize_RNN(transformer, tensor_infos, node): @@ -600,10 +599,10 @@ def _legalize_RNN(transformer, tensor_infos, node): if direction in ['forward', 'reverse']: _transform_unidirectional_RNN(transformer, node, x, tensor_infos, activations[0], - clip, direction, hidden_size, layout) + clip, direction, hidden_size, layout) elif direction == 'bidirectional': - _transform_bidirectional_RNN(transformer, node, x, tensor_infos, activations, clip, - hidden_size, layout) + _transform_bidirectional_RNN(transformer, node, x, tensor_infos, activations, + clip, hidden_size, layout) else: raise RuntimeError('Unknown RNN type') @@ -611,7 +610,7 @@ def _legalize_RNN(transformer, tensor_infos, node): def _generate_one_direction_LSTM(transformer, X, W, R, B, initial_h, initial_c, P, clip, - act, dtype, hidden_size, batch_size): + act, dtype, hidden_size, batch_size): """Generate subgraph for one direction of unrolled LSTM layer Args: @@ -754,7 +753,7 @@ def _generate_one_direction_LSTM(transformer, X, W, R, B, initial_h, initial_c, def _transform_unidirectional_LSTM(transformer, original_node, x, tensor_infos, - activations, clip, direction, hidden_size, layout): + activations, clip, direction, hidden_size, layout): """Generate Simple (forward or reverse) unrolled LSTM Args: @@ -818,17 +817,15 @@ def _transform_unidirectional_LSTM(transformer, original_node, x, tensor_infos, transformer.make_node( 'Unsqueeze', [state_h_tensors[-1]], [Y_h], axes=[y_h_direction_dim]) Y_c = outputs[2] - transformer.make_node( - 'Unsqueeze', [state_c_tensor], [Y_c], axes=[y_h_direction_dim]) + transformer.make_node('Unsqueeze', [state_c_tensor], [Y_c], axes=[y_h_direction_dim]) if direction == 'reverse': state_layout_tensors.reverse() Y = outputs[0] - transformer.make_node( - 'Concat', state_layout_tensors, [Y], axis=seq_length_dim) + transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim) -def _transform_bidirectional_LSTM(transformer, original_node, x, tensor_infos, activations, - clip, hidden_size, layout): +def _transform_bidirectional_LSTM(transformer, original_node, x, tensor_infos, + activations, clip, hidden_size, layout): """Generate Bidirectional unrolled LSTM Args: @@ -929,12 +926,10 @@ def _transform_bidirectional_LSTM(transformer, original_node, x, tensor_infos, a Y_f_c = transformer.make_unsqueeze(state_f_c_tensor, axes=[y_c_direction_dim]) Y_b_c = transformer.make_unsqueeze(state_b_c_tensor, axes=[y_c_direction_dim]) Y_c = outputs[2] - transformer.make_node( - 'Concat', [Y_f_c, Y_b_c], [Y_c], axis=y_c_direction_dim) + transformer.make_node('Concat', [Y_f_c, Y_b_c], [Y_c], axis=y_c_direction_dim) Y = outputs[0] - transformer.make_node( - 'Concat', state_layout_tensors, [Y], axis=seq_length_dim) + transformer.make_node('Concat', state_layout_tensors, [Y], axis=seq_length_dim) def _legalize_LSTM(transformer, tensor_infos, node): @@ -1001,10 +996,10 @@ def _legalize_LSTM(transformer, tensor_infos, node): if direction in ['forward', 'reverse']: _transform_unidirectional_LSTM(transformer, node, x, tensor_infos, activations, - clip, direction, hidden_size, layout) + clip, direction, hidden_size, layout) elif direction == 'bidirectional': _transform_bidirectional_LSTM(transformer, node, x, tensor_infos, activations, - clip, hidden_size, layout) + clip, hidden_size, layout) else: raise RuntimeError('Unknown LSTM type') @@ -1052,10 +1047,12 @@ def legalize(model, options): if __name__ == '__main__': if len(sys.argv) < 3: - print('usage: ./legalize_onnx.py \n' - '\n' - ' In stand-alone utility mode this tool provides basic funtionality\n' - ' If you want to have more control over applied transformations, use this legalizer as a library') + print( + 'usage: ./legalize_onnx.py \n' + '\n' + ' In stand-alone utility mode this tool provides basic funtionality\n' + ' If you want to have more control over applied transformations, use this legalizer as a library' + ) exit(1) options = LegalizeOptions() options.unroll_lstm = True diff --git a/compiler/one-cmds/requires.cmake b/compiler/one-cmds/requires.cmake index b1aabdb..c279209 100644 --- a/compiler/one-cmds/requires.cmake +++ b/compiler/one-cmds/requires.cmake @@ -1,6 +1,7 @@ require("tf2tfliteV2") require("tflite2circle") require("circle2circle") +require("circle-eval-diff") require("circle-quantizer") require("record-minmax") require("vconone") diff --git a/compiler/one-cmds/tests/CMakeLists.txt b/compiler/one-cmds/tests/CMakeLists.txt index caea756..17f55ec 100644 --- a/compiler/one-cmds/tests/CMakeLists.txt +++ b/compiler/one-cmds/tests/CMakeLists.txt @@ -4,6 +4,8 @@ file(GLOB TESTITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.test") file(GLOB CONFIGITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.cfg") file(GLOB QCONFIGITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.qconf.json") +file(GLOB PYSCRIPTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.py") +file(GLOB WORKFLOWITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.workflow.json") # Create a script to run the tests at installation folder set(DRIVER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runtestall.sh") @@ -45,6 +47,16 @@ foreach(QCONFIGITEM IN ITEMS ${QCONFIGITEMS}) install(FILES ${QCONFIGITEM} DESTINATION test) endforeach(QCONFIGITEM) +foreach(PYSCRIPT IN ITEMS ${PYSCRIPTS}) + get_filename_component(ITEM_PREFIX ${PYSCRIPT} NAME_WE) + install(FILES ${PYSCRIPT} DESTINATION test) +endforeach(PYSCRIPT) + +foreach(WORKFLOWITEM IN ITEMS ${WORKFLOWITEMS}) + get_filename_component(ITEM_PREFIX ${WORKFLOWITEM} NAME_WE) + install(FILES ${WORKFLOWITEM} DESTINATION test) +endforeach(WORKFLOWITEM) + file(APPEND "${DRIVER_SCRIPT}" "popd > /dev/null\n\n") file(APPEND "${DRIVER_SCRIPT}" diff --git a/compiler/one-cmds/tests/OONECC_024.cfg b/compiler/one-cmds/tests/OONECC_024.cfg new file mode 100644 index 0000000..a39aae0 --- /dev/null +++ b/compiler/one-cmds/tests/OONECC_024.cfg @@ -0,0 +1,2 @@ +[one-optimize] +make_batchnorm_gamma_positive=True diff --git a/compiler/one-cmds/tests/one-build_008.cfg b/compiler/one-cmds/tests/one-build_008.cfg index 615047c..8c777f6 100644 --- a/compiler/one-cmds/tests/one-build_008.cfg +++ b/compiler/one-cmds/tests/one-build_008.cfg @@ -15,7 +15,6 @@ output_path=test_onnx_model.circle [one-optimize] input_path=test_onnx_model.circle output_path=test_onnx_model.opt.circle -all=True remove_redundant_transpose=True [one-codegen] diff --git a/compiler/one-cmds/tests/one-build_009.cfg b/compiler/one-cmds/tests/one-build_009.cfg index 66bca25..b5a35dd 100644 --- a/compiler/one-cmds/tests/one-build_009.cfg +++ b/compiler/one-cmds/tests/one-build_009.cfg @@ -15,7 +15,6 @@ output_path=onnx_conv2d_conv2d.circle [one-optimize] input_path=onnx_conv2d_conv2d.circle output_path=onnx_conv2d_conv2d.opt.circle -all=True remove_redundant_transpose=True convert_nchw_to_nhwc=True diff --git a/compiler/one-cmds/tests/one-import-onnx_002.test b/compiler/one-cmds/tests/one-import-onnx_002.test new file mode 100644 index 0000000..a6a38ee --- /dev/null +++ b/compiler/one-cmds/tests/one-import-onnx_002.test @@ -0,0 +1,71 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# test for experimental_disable_batchmatmul_unfold option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./reshape_matmul.onnx" +outputfile="./reshape_matmul.circle" + +rm -rf ${outputfile} +rm -rf ${outputfile}.log + +# run test without option that should drop FULLY_CONNECTED +one-import-onnx \ +--input_path ${inputfile} \ +--output_path ${outputfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +circle-operator --code reshape_matmul.circle > ${outputfile}.log 2>&1 + +if ! grep -q "FULLY_CONNECTED" "${outputfile}.log"; then + trap_err_onexit +fi + +rm -rf ${outputfile} +rm -rf ${outputfile}.log + +# run test with option that should drop BATCH_MATMUL +one-import-onnx \ +--experimental_disable_batchmatmul_unfold \ +--input_path ${inputfile} \ +--output_path ${outputfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +circle-operator --code reshape_matmul.circle > ${outputfile}.log 2>&1 + +if ! grep -q "BATCH_MATMUL" "${outputfile}.log"; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" +exit 0 diff --git a/compiler/one-cmds/tests/one-infer-test-post-process.py b/compiler/one-cmds/tests/one-infer-test-post-process.py new file mode 100644 index 0000000..0f0e0d7 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer-test-post-process.py @@ -0,0 +1,16 @@ +# This script gets one argument and print it + +import sys +from pathlib import Path + + +def main(): + if len(sys.argv) < 2: + filepath = Path(sys.argv[0]) + sys.exit("Usage: " + filepath.name + " [Word to print]") + word = sys.argv[1] + print(word) + + +if __name__ == '__main__': + main() diff --git a/compiler/one-cmds/tests/one-infer_001.test b/compiler/one-cmds/tests/one-infer_001.test new file mode 100644 index 0000000..e7b5695 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_001.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/help-infer + exit 255 +} + +trap trap_err_onexit ERR + +# copy help-infer to bin folder +cp help-infer ../bin/help-infer + +# run test +one-infer -b help -- -h > ${filename}.log + +rm -rf ../bin/help-infer + +if grep -q "HELP MESSAGE!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_002.test b/compiler/one-cmds/tests/one-infer_002.test new file mode 100644 index 0000000..22070de --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_002.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-infer + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="sample.tvn" + +if [[ ! -s "${inputfile}" ]]; then + touch ${inputfile} +fi + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +one-infer -d dummy-infer -- ${inputfile} > ${filename}.log + +rm -rf ../bin/dummy-infer + +if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_003.test b/compiler/one-cmds/tests/one-infer_003.test new file mode 100644 index 0000000..e2aa459 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_003.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-infer + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="sample.tvn" + +if [[ ! -s "${inputfile}" ]]; then + touch ${inputfile} +fi + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +one-infer -b dummy -- ${inputfile} > ${filename}.log + +rm -rf ../bin/dummy-infer + +if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_004.test b/compiler/one-cmds/tests/one-infer_004.test new file mode 100644 index 0000000..a4cb76c --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_004.test @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# print one-infer's help message + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-infer -h > ${filename}.log + +if grep -q "command line tool to infer model" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_005.cfg b/compiler/one-cmds/tests/one-infer_005.cfg new file mode 100644 index 0000000..aca6878 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_005.cfg @@ -0,0 +1,3 @@ +[one-infer] +backend=dummy +command=sample.tvn diff --git a/compiler/one-cmds/tests/one-infer_005.test b/compiler/one-cmds/tests/one-infer_005.test new file mode 100644 index 0000000..a44dd0e --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_005.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# one-infer with configuration input + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-infer + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-infer_005.cfg" +inputfile="sample.tvn" + +if [[ ! -s "${inputfile}" ]]; then + touch ${inputfile} +fi + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +one-infer -C ${configfile} > ${filename}.log + +rm -rf ../bin/dummy-infer + +if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_006.test b/compiler/one-cmds/tests/one-infer_006.test new file mode 100644 index 0000000..2612133 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_006.test @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# one-infer with post process script + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-infer + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="sample.tvn" + +if [[ ! -s "${inputfile}" ]]; then + touch ${inputfile} +fi + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +one-infer -b dummy --post-process "./one-infer-test-post-process.py TOKEN" -- ${inputfile} > ${filename}.log 2>&1 +return_code=$? + +rm -rf ../bin/dummy-infer + +if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + if [ "$return_code" -eq "0" ]; then + echo "${filename_ext} SUCCESS" + exit 0 + fi +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-infer_neg_001.test b/compiler/one-cmds/tests/one-infer_neg_001.test new file mode 100644 index 0000000..62e7211 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_neg_001.test @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# negative usage with no input + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required: {-d/--driver | -b/--backend}" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-infer > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-infer_neg_002.test b/compiler/one-cmds/tests/one-infer_neg_002.test new file mode 100644 index 0000000..fa88876 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_neg_002.test @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# passed driver is not found + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +driver_name="neg-infer" + +trap_err_onexit() +{ + if grep -q "FileNotFoundError: ${driver_name} not found" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-infer -d ${driver_name} -- -h> ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-infer_neg_003.test b/compiler/one-cmds/tests/one-infer_neg_003.test new file mode 100644 index 0000000..a000552 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_neg_003.test @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# passed backend is not found + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +backend_name="neg" + +trap_err_onexit() +{ + if grep -q "FileNotFoundError: ${backend_name}-infer not found" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-infer -b ${backend_name} -- -h> ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-infer_neg_004.test b/compiler/one-cmds/tests/one-infer_neg_004.test new file mode 100644 index 0000000..b9130d0 --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_neg_004.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# both -b and -d option drivers are given as argument + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +backend_name="neg" +driver_name="neg2" + +trap_err_onexit() +{ + if grep -q "\-d and -b options are mutually exclusive. Please use only one of them" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-infer -d ${driver_name} -b ${backend_name} -- -h> ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-infer_neg_005.test b/compiler/one-cmds/tests/one-infer_neg_005.test new file mode 100644 index 0000000..9074deb --- /dev/null +++ b/compiler/one-cmds/tests/one-infer_neg_005.test @@ -0,0 +1,54 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# one-infer with invalid post process script + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + return_code=$? + if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + # Case of succeed of inference driver but error after it + if [ "$return_code" -ne "0" ]; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + fi + + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-infer + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="sample.tvn" + +if [[ ! -s "${inputfile}" ]]; then + touch ${inputfile} +fi + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +one-infer -b dummy --post-process "./one-infer-test-post-process.py" -- ${inputfile} > ${filename}.log 2>&1 + +rm -rf ../bin/dummy-infer +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-optimize_001.test b/compiler/one-cmds/tests/one-optimize_001.test index 8eb58f4..4152fa3 100644 --- a/compiler/one-cmds/tests/one-optimize_001.test +++ b/compiler/one-cmds/tests/one-optimize_001.test @@ -40,7 +40,7 @@ if [[ ! -s ${inputfile} ]]; then fi # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --input_path ${inputfile} \ --output_path ${outputfile} > /dev/null 2>&1 diff --git a/compiler/one-cmds/tests/one-optimize_002.test b/compiler/one-cmds/tests/one-optimize_002.test index bd64494..58f792b 100644 --- a/compiler/one-cmds/tests/one-optimize_002.test +++ b/compiler/one-cmds/tests/one-optimize_002.test @@ -40,7 +40,7 @@ if [[ ! -s ${inputfile} ]]; then fi # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --change_outputs InceptionV3/Logits/SpatialSqueeze1 \ --input_path ${inputfile} \ --output_path ${outputfile} > /dev/null 2>&1 diff --git a/compiler/one-cmds/tests/one-optimize_neg_001.test b/compiler/one-cmds/tests/one-optimize_neg_001.test index f0b5563..c67e3d4 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_001.test +++ b/compiler/one-cmds/tests/one-optimize_neg_001.test @@ -39,7 +39,7 @@ rm -rf ${outputfile} rm -rf ${outputfile}.log # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log 2>&1 diff --git a/compiler/one-cmds/tests/one-optimize_neg_002.test b/compiler/one-cmds/tests/one-optimize_neg_002.test index 72f306e..a1ef702 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_002.test +++ b/compiler/one-cmds/tests/one-optimize_neg_002.test @@ -39,7 +39,7 @@ rm -rf ${outputfile} rm -rf ${outputfile}.log # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log 2>&1 diff --git a/compiler/one-cmds/tests/one-optimize_neg_003.test b/compiler/one-cmds/tests/one-optimize_neg_003.test index 3fe7d33..668a6c2 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_003.test +++ b/compiler/one-cmds/tests/one-optimize_neg_003.test @@ -44,7 +44,7 @@ if [[ ! -s ${inputfile} ]]; then fi # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --input_path "${inputfile}" > "${filename}.log" 2>&1 echo "${filename_ext} FAILED" diff --git a/compiler/one-cmds/tests/one-optimize_neg_004.test b/compiler/one-cmds/tests/one-optimize_neg_004.test index e73911b..5abd4c5 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_004.test +++ b/compiler/one-cmds/tests/one-optimize_neg_004.test @@ -39,7 +39,7 @@ rm -rf ${outputfile} rm -rf ${filename}.log # run test -one-optimize --O1 \ +one-optimize --resolve_customop_add \ --change_outputs non_existing_node_name \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log 2>&1 diff --git a/compiler/one-cmds/tests/one-partition_001.test b/compiler/one-cmds/tests/one-partition_001.test new file mode 100644 index 0000000..a6fba07 --- /dev/null +++ b/compiler/one-cmds/tests/one-partition_001.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +testmodel="Net_InstanceNorm_003" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="${testmodel}.circle" +partfile="${testmodel}.part" +outputfile="${testmodel}.conn.json" + +rm -rf ${testmodel}.000* +rm -rf ${testmodel}.conn.* +rm -rf ${testmodel}.*.log + +# run test +one-partition \ +--input_file ${inputfile} \ +--part_file ${partfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-partition_neg_001.test b/compiler/one-cmds/tests/one-partition_neg_001.test new file mode 100644 index 0000000..d54a94f --- /dev/null +++ b/compiler/one-cmds/tests/one-partition_neg_001.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# negative usage with invalid .part file (wrong comply value) + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +testmodel="Net_InstanceNorm_003" + +trap_err_onexit() +{ + if grep -q "ERROR" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="${testmodel}.circle" +partfile="${testmodel}.neg.part" +outputfile="${testmodel}.conn.json" + +rm -rf ${testmodel}.000* +rm -rf ${testmodel}.conn.* +rm -rf ${testmodel}.*.log +rm -rf ${filename}.log + +# run test +one-partition \ +--input_file ${inputfile} \ +--part_file ${partfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-partition_neg_002.test b/compiler/one-cmds/tests/one-partition_neg_002.test new file mode 100644 index 0000000..23fe84c --- /dev/null +++ b/compiler/one-cmds/tests/one-partition_neg_002.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# negative usage with invalid .cfg file (no one-partition section) + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +testmodel="Net_InstanceNorm_003" + +trap_err_onexit() +{ + if grep -q "'one-partition' section" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +cfgfile="${testmodel}.neg.cfg" + +rm -rf ${testmodel}.000* +rm -rf ${testmodel}.conn.* +rm -rf ${testmodel}.*.log +rm -rf ${filename}.log + +# run test +one-partition -C ${cfgfile}> ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-quantize_010.test b/compiler/one-cmds/tests/one-quantize_010.test new file mode 100644 index 0000000..1095ba0 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_010.test @@ -0,0 +1,65 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "MPEIR for InceptionV3/Predictions/Reshape_1 is" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_010.q.circle" +datafile="./inception_v3_test_data.h5" + +rm -rf ${outputfile} + +# to create inception_v3.circle +if [[ ! -s ${inputfile} ]]; then + /bin/bash one-import_001.test > /dev/null 2>&1 + return_code=$? + if [[ ${return_code} != 0 ]]; then + trap_err_onexit + fi +fi + +# run test +one-quantize \ +--input_dtype float32 \ +--quantized_dtype uint8 \ +--granularity channel \ +--input_path ${inputfile} \ +--input_data ${datafile} \ +--output_path ${outputfile} \ +--evaluate_result \ +--test_data ${datafile} \ +--print_mpeir > ${filename}.log 2>&1 + +check_message diff --git a/compiler/one-cmds/tests/one-quantize_011.test b/compiler/one-cmds/tests/one-quantize_011.test new file mode 100644 index 0000000..34d7f57 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_011.test @@ -0,0 +1,56 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "Mean Top-5 match ratio for InceptionV3/Predictions/Reshape_1 is" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_011.q.circle" +datafile="./inception_v3_test_data.h5" + +rm -rf ${outputfile} + +# run test +one-quantize \ +--input_dtype float32 \ +--quantized_dtype uint8 \ +--granularity channel \ +--input_path ${inputfile} \ +--input_data ${datafile} \ +--output_path ${outputfile} \ +--evaluate_result \ +--test_data ${datafile} \ +--print_top5_match > ${filename}.log 2>&1 + +check_message diff --git a/compiler/one-cmds/tests/one-quantize_012.qconf.json b/compiler/one-cmds/tests/one-quantize_012.qconf.json new file mode 100644 index 0000000..4a15b04 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_012.qconf.json @@ -0,0 +1,16 @@ +{ + "default_quantization_dtype" : "uint8", + "default_granularity" : "channel", + "layers" : [ + { + "names" : ["InceptionV3/InceptionV3/Conv2d_2b_3x3/Relu;InceptionV3/InceptionV3/Conv2d_2b_3x3/BatchNorm/FusedBatchNorm;InceptionV3/InceptionV3/Mixed_6a/Branch_1/Conv2d_0a_1x1/Conv2D;InceptionV3/InceptionV3/Conv2d_2b_3x3/Conv2D", + "InceptionV3/InceptionV3/MaxPool_5a_3x3/MaxPool", + "InceptionV3/InceptionV3/Mixed_5b/concat", + "InceptionV3/InceptionV3/Mixed_5b/Branch_3/AvgPool_0a_3x3/AvgPool", + "InceptionV3/InceptionV3/Mixed_7c/concat", + "InceptionV3/Predictions/Reshape_1"], + "dtype" : "int16", + "granularity" : "channel" + } + ] +} diff --git a/compiler/one-cmds/tests/one-quantize_012.test b/compiler/one-cmds/tests/one-quantize_012.test new file mode 100644 index 0000000..fba18ac --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_012.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_012.q.circle" + +rm -rf ${outputfile} + +# run test without input data +one-quantize \ +--input_dtype float32 \ +--quantized_dtype uint8 \ +--granularity channel \ +--quant_config one-quantize_012.qconf.json \ +--input_path ${inputfile} \ +--output_path ${outputfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_013.qconf.json b/compiler/one-cmds/tests/one-quantize_013.qconf.json new file mode 100644 index 0000000..4a15b04 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_013.qconf.json @@ -0,0 +1,16 @@ +{ + "default_quantization_dtype" : "uint8", + "default_granularity" : "channel", + "layers" : [ + { + "names" : ["InceptionV3/InceptionV3/Conv2d_2b_3x3/Relu;InceptionV3/InceptionV3/Conv2d_2b_3x3/BatchNorm/FusedBatchNorm;InceptionV3/InceptionV3/Mixed_6a/Branch_1/Conv2d_0a_1x1/Conv2D;InceptionV3/InceptionV3/Conv2d_2b_3x3/Conv2D", + "InceptionV3/InceptionV3/MaxPool_5a_3x3/MaxPool", + "InceptionV3/InceptionV3/Mixed_5b/concat", + "InceptionV3/InceptionV3/Mixed_5b/Branch_3/AvgPool_0a_3x3/AvgPool", + "InceptionV3/InceptionV3/Mixed_7c/concat", + "InceptionV3/Predictions/Reshape_1"], + "dtype" : "int16", + "granularity" : "channel" + } + ] +} diff --git a/compiler/one-cmds/tests/one-quantize_013.test b/compiler/one-cmds/tests/one-quantize_013.test new file mode 100644 index 0000000..fd443d6 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_013.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# quantized_dtype and granularity are given by qconfig file +# (not by command line interface) + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_013.q.circle" + +rm -rf ${outputfile} + +# run test without input data +# quantized_dtype and granularity are not given here +one-quantize \ +--input_dtype float32 \ +--quant_config one-quantize_013.qconf.json \ +--input_path ${inputfile} \ +--output_path ${outputfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_014.test b/compiler/one-cmds/tests/one-quantize_014.test new file mode 100644 index 0000000..518c328 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_014.test @@ -0,0 +1,59 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Test if `circle-eval-diff` supports directory input. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "Mean Top-5 match ratio for InceptionV3/Predictions/Reshape_1 is" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_014.q.circle" +datadir="./raw_files/" + +rm -rf ${outputfile} + +# run test +one-quantize \ +--input_dtype float32 \ +--quantized_dtype uint8 \ +--granularity channel \ +--input_path ${inputfile} \ +--input_data ${datadir} \ +--input_data_format dir \ +--output_path ${outputfile} \ +--evaluate_result \ +--test_data ${datadir} \ +--print_top5_match > ${filename}.log 2>&1 + +check_message diff --git a/compiler/one-cmds/tests/one-quantize_015.test b/compiler/one-cmds/tests/one-quantize_015.test new file mode 100644 index 0000000..bb45b57 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_015.test @@ -0,0 +1,45 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Test if --fake_quantize option works well + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.mat.q8.circle" +outputfile="./inception_v3.one-quantize_015.fq.circle" + +rm -rf ${outputfile} + +# run test +one-quantize \ +--fake_quantize \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_neg_019.test b/compiler/one-cmds/tests/one-quantize_neg_019.test index ac920a4..e182edf 100644 --- a/compiler/one-cmds/tests/one-quantize_neg_019.test +++ b/compiler/one-cmds/tests/one-quantize_neg_019.test @@ -42,7 +42,7 @@ one-quantize \ --input_dtype float32 \ --quantized_dtype int16 \ --granularity channel \ ---input_type float32 \ +--input_type float64 \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log 2>&1 diff --git a/compiler/one-cmds/tests/one-quantize_neg_020.test b/compiler/one-cmds/tests/one-quantize_neg_020.test new file mode 100644 index 0000000..27b11c3 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_neg_020.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# check error message is printed when qconfig file is not json + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Failed to decode" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.quantized.neg_020.circle" + +rm -rf ${outputfile}.log + +# run test +one-quantize \ +--input_dtype float32 \ +--quant_config one-quantize_neg_020.test \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_008.cfg b/compiler/one-cmds/tests/onecc_008.cfg index 0be026e..020e274 100644 --- a/compiler/one-cmds/tests/onecc_008.cfg +++ b/compiler/one-cmds/tests/onecc_008.cfg @@ -15,7 +15,6 @@ output_path=test_onnx_model.circle [one-optimize] input_path=test_onnx_model.circle output_path=test_onnx_model.opt.circle -all=True remove_redundant_transpose=True [one-codegen] diff --git a/compiler/one-cmds/tests/onecc_009.cfg b/compiler/one-cmds/tests/onecc_009.cfg index a17ae59..86121c5 100644 --- a/compiler/one-cmds/tests/onecc_009.cfg +++ b/compiler/one-cmds/tests/onecc_009.cfg @@ -15,7 +15,6 @@ output_path=onnx_conv2d_conv2d.circle [one-optimize] input_path=onnx_conv2d_conv2d.circle output_path=onnx_conv2d_conv2d.opt.circle -all=True remove_redundant_transpose=True convert_nchw_to_nhwc=True diff --git a/compiler/one-cmds/tests/onecc_024.cfg b/compiler/one-cmds/tests/onecc_024.cfg new file mode 100644 index 0000000..7b4b1a8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_024.cfg @@ -0,0 +1,22 @@ +[onecc] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-import-onnx=False +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v1 + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle +make_batchnorm_gamma_positive=False diff --git a/compiler/one-cmds/tests/onecc_024.test b/compiler/one-cmds/tests/onecc_024.test new file mode 100644 index 0000000..1f5daa1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_024.test @@ -0,0 +1,77 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Use `OONECC_024` optimization option + +: ' +This test assumes below directories. + +[one hierarchy] + one + ├── backends + ├── bin + ├── doc + ├── include + ├── lib + ├── optimization + └── test # pwd +' + +OPT_ALREADY_EXIST=true + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +clean_envir() +{ + rm -rf ../optimization/OONECC_024.cfg + if [ "$OPT_ALREADY_EXIST" = false ]; then + rm -rf ../optimization + fi +} + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + clean_envir + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_024.cfg" +outputfile="inception_v3.opt.circle" + +rm -rf ${outputfile} + +if [ ! -d "../optimization" ]; then + mkdir -p ../optimization + OPT_ALREADY_EXIST=false +fi + +cp OONECC_024.cfg ../optimization + +# run test +LUCI_LOG=5 onecc -C ${configfile} -OONECC_024 > ${filename}.log 2>&1 + +clean_envir + +if grep -q "MakeBatchNormGammaPositivePass" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/onecc_025.cfg b/compiler/one-cmds/tests/onecc_025.cfg new file mode 100644 index 0000000..4776ea8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_025.cfg @@ -0,0 +1,20 @@ +[onecc] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v2 + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle diff --git a/compiler/one-cmds/tests/onecc_025.test b/compiler/one-cmds/tests/onecc_025.test new file mode 100644 index 0000000..396f40c --- /dev/null +++ b/compiler/one-cmds/tests/onecc_025.test @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# one-import-tf -> one-optimize with the configuration file that includes `onecc` section + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_001.cfg" +outputfile="inception_v3.opt.circle" + +# run test +onecc -C ${configfile} > /dev/null 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_026.cfg b/compiler/one-cmds/tests/onecc_026.cfg new file mode 100644 index 0000000..c27a136 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_026.cfg @@ -0,0 +1,16 @@ +[onecc] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=True +one-pack=False +one-codegen=False + +[one-quantize] +input_path=inception_v3.circle +output_path=inception_v3.onecc_026.q.circle +input_data=inception_v3_test_data.h5 +evaluate_result=True +test_data=inception_v3_test_data.h5 +print_mpeir=True diff --git a/compiler/one-cmds/tests/onecc_026.test b/compiler/one-cmds/tests/onecc_026.test new file mode 100644 index 0000000..84cfa41 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_026.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "MPEIR for InceptionV3/Predictions/Reshape_1 is" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +configfile="onecc_026.cfg" +outputfile="inception_v3.onecc_026.q.circle" + +rm -rf ${outputfile} + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +check_message diff --git a/compiler/one-cmds/tests/onecc_027.cfg b/compiler/one-cmds/tests/onecc_027.cfg new file mode 100644 index 0000000..d3f6b5e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_027.cfg @@ -0,0 +1,15 @@ +[onecc] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-import-onnx=False +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False +one-profile=False +one-infer=True + +[one-infer] +backend=dummy +command=test_onnx_model.bin diff --git a/compiler/one-cmds/tests/onecc_027.test b/compiler/one-cmds/tests/onecc_027.test new file mode 100644 index 0000000..e727359 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_027.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# one-infer + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_027.cfg" + +# copy dummy-infer to bin folder +cp dummy-infer ../bin/dummy-infer + +# run test +onecc -C ${configfile} > ${filename}.log + +rm -rf ../bin/dummy-infer + +if grep -q "dummy-infer dummy output!!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/onecc_028.test b/compiler/one-cmds/tests/onecc_028.test new file mode 100644 index 0000000..10ce158 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_028.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-optimize -> one-pack + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_028.workflow.json" +outputfile="inception_v3_pkg" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_028.workflow.json b/compiler/one-cmds/tests/onecc_028.workflow.json new file mode 100644 index 0000000..84bfd01 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_028.workflow.json @@ -0,0 +1,37 @@ +{ + "workflows": [ + "MY_WORKFLOW" + ], + "MY_WORKFLOW": { + "steps": [ + "IMPORT_TF", + "OPTIMIZE", + "PACK" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "OPTIMIZE": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + }, + "PACK": { + "one-cmd": "one-pack", + "commands": { + "input_path": "inception_v3.opt.circle", + "output_path": "inception_v3_pkg" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_029.test b/compiler/one-cmds/tests/onecc_029.test new file mode 100644 index 0000000..9bab1a1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_029.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-quantize + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_029.workflow.json" +outputfile="inception_v3.quantized.circle" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_029.workflow.json b/compiler/one-cmds/tests/onecc_029.workflow.json new file mode 100644 index 0000000..65c9ea6 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_029.workflow.json @@ -0,0 +1,30 @@ +{ + "workflows": [ + "QUANTIZE_WORKFLOW" + ], + "QUANTIZE_WORKFLOW": { + "steps": [ + "IMPORT_TF", + "QUANTIZE" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.quantized.circle", + "input_data": "inception_v3_test_data.h5" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_030.test b/compiler/one-cmds/tests/onecc_030.test new file mode 100644 index 0000000..c0aa56a --- /dev/null +++ b/compiler/one-cmds/tests/onecc_030.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_030.workflow.json" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_030.workflow.json b/compiler/one-cmds/tests/onecc_030.workflow.json new file mode 100644 index 0000000..111a1b0 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_030.workflow.json @@ -0,0 +1,29 @@ +{ + "workflows": [ + "codegen_wf" + ], + "codegen_wf": { + "steps": [ + "import_tf", + "codegen" + ], + "import_tf": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "codegen": { + "one-cmd": "one-codegen", + "commands": { + "backend": "dummy", + "command": "-o sample.tvn inception_v3.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_031.test b/compiler/one-cmds/tests/onecc_031.test new file mode 100644 index 0000000..7a1c670 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_031.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tflite -> one-optimize -> one-codgen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_031.workflow.json" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_031.workflow.json b/compiler/one-cmds/tests/onecc_031.workflow.json new file mode 100644 index 0000000..83d52b9 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_031.workflow.json @@ -0,0 +1,33 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import", + "optimize", + "codegen" + ], + "import": { + "one-cmd": "one-import-tflite", + "commands": { + "input_path": "inception_v3.tflite", + "output_path": "inception_v3.circle" + } + }, + "optimize": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + }, + "codegen": { + "one-cmd": "one-codegen", + "commands": { + "backend": "dummy", + "command": "-o sample.tvn inception_v3.opt.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_032.test b/compiler/one-cmds/tests/onecc_032.test new file mode 100644 index 0000000..89b6c41 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_032.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-optimize -> one-quantize -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_032.workflow.json" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_032.workflow.json b/compiler/one-cmds/tests/onecc_032.workflow.json new file mode 100644 index 0000000..08d3f0f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_032.workflow.json @@ -0,0 +1,42 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import", + "optimize", + "quantize", + "codegen" + ], + "import": { + "one-cmd": "one-import-tflite", + "commands": { + "input_path": "inception_v3.tflite", + "output_path": "inception_v3.circle" + } + }, + "optimize": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + }, + "quantize": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.quantized.circle", + "input_data": "inception_v3_test_data.h5" + } + }, + "codegen": { + "one-cmd": "one-codegen", + "commands": { + "backend": "dummy", + "command": "-o sample.tvn inception_v3.quantized.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_033.test b/compiler/one-cmds/tests/onecc_033.test new file mode 100644 index 0000000..635582f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_033.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-optimize -> one-quantize -> one-pack + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_033.workflow.json" +outputfile="inception_v3_pkg" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_033.workflow.json b/compiler/one-cmds/tests/onecc_033.workflow.json new file mode 100644 index 0000000..01233ff --- /dev/null +++ b/compiler/one-cmds/tests/onecc_033.workflow.json @@ -0,0 +1,42 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import", + "optimize", + "quantize", + "pack" + ], + "import": { + "one-cmd": "one-import-tflite", + "commands": { + "input_path": "inception_v3.tflite", + "output_path": "inception_v3.circle" + } + }, + "optimize": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + }, + "quantize": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.quantized.circle", + "input_data": "inception_v3_test_data.h5" + } + }, + "pack": { + "one-cmd": "one-pack", + "commands": { + "input_path": "inception_v3.quantized.circle", + "output_path": "inception_v3_pkg" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_034.test b/compiler/one-cmds/tests/onecc_034.test new file mode 100644 index 0000000..e766548 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_034.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-onnx -> one-optimize -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_034.workflow.json" +outputfile="onnx_conv2d_conv2d.bin" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_034.workflow.json b/compiler/one-cmds/tests/onecc_034.workflow.json new file mode 100644 index 0000000..bc3cbbf --- /dev/null +++ b/compiler/one-cmds/tests/onecc_034.workflow.json @@ -0,0 +1,35 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import", + "optimize", + "codegen" + ], + "import": { + "one-cmd": "one-import-onnx", + "commands": { + "input_path": "onnx_conv2d_conv2d.onnx", + "output_path": "onnx_conv2d_conv2d.circle" + } + }, + "optimize": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "onnx_conv2d_conv2d.circle", + "output_path": "onnx_conv2d_conv2d.opt.circle", + "remove_redundant_transpose": "True", + "convert_nchw_to_nhwc": "True" + } + }, + "codegen": { + "one-cmd": "one-codegen", + "commands": { + "backend": "dummy", + "command": "-o onnx_conv2d_conv2d.bin onnx_conv2d_conv2d.opt.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_035.test b/compiler/one-cmds/tests/onecc_035.test new file mode 100644 index 0000000..762cdd3 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_035.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf generates intermediate files + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_035.workflow.json" +outputfile="inception_v3.alt.circle" +intermfile="inception_v3.alt.tflite" + +rm -rf ${outputfile} +rm -rf ${intermfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi +if [[ ! -s "${intermfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_035.workflow.json b/compiler/one-cmds/tests/onecc_035.workflow.json new file mode 100644 index 0000000..6abf1f3 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_035.workflow.json @@ -0,0 +1,22 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import" + ], + "import": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.alt.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v1", + "save_intermediate": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_036.test b/compiler/one-cmds/tests/onecc_036.test new file mode 100644 index 0000000..865255e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_036.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-onnx generates intermediate files + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_036.workflow.json" +outputfile="test_onnx_model.circle" +intermfile="test_onnx_model.tflite" + +rm -rf ${outputfile} +rm -rf ${intermfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi +if [[ ! -s "${intermfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_036.workflow.json b/compiler/one-cmds/tests/onecc_036.workflow.json new file mode 100644 index 0000000..5fa29ed --- /dev/null +++ b/compiler/one-cmds/tests/onecc_036.workflow.json @@ -0,0 +1,18 @@ +{ + "workflows": [ + "wf" + ], + "wf": { + "steps": [ + "import" + ], + "import": { + "one-cmd": "one-import-onnx", + "commands": { + "input_path": "test_onnx_model.onnx", + "output_path": "test_onnx_model.circle", + "save_intermediate": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_037.test b/compiler/one-cmds/tests/onecc_037.test new file mode 100644 index 0000000..52ea9e4 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_037.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-optimize + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_037.workflow.json" +outputfile="inception_v3.opt.circle" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_037.workflow.json b/compiler/one-cmds/tests/onecc_037.workflow.json new file mode 100644 index 0000000..3317fb2 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_037.workflow.json @@ -0,0 +1,29 @@ +{ + "workflows": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOW": { + "steps": [ + "IMPORT", + "OPTIMIZE" + ], + "IMPORT": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "OPTIMIZE": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_038.test b/compiler/one-cmds/tests/onecc_038.test new file mode 100644 index 0000000..6b8f7cf --- /dev/null +++ b/compiler/one-cmds/tests/onecc_038.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-import-tf -> one-quantize + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_038.workflow.json" +outputfile="inception_v3.list.quantized.circle" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_038.workflow.json b/compiler/one-cmds/tests/onecc_038.workflow.json new file mode 100644 index 0000000..5ac515d --- /dev/null +++ b/compiler/one-cmds/tests/onecc_038.workflow.json @@ -0,0 +1,31 @@ +{ + "workflows": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOW": { + "steps": [ + "IMPORT", + "QUANTIZE" + ], + "IMPORT": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.list.quantized.circle", + "input_data": "datalist.txt", + "input_data_format": "list" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_039.test b/compiler/one-cmds/tests/onecc_039.test new file mode 100644 index 0000000..7db9d90 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_039.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow where one-quantize quantizes the model and evaluates the result + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "MPEIR for InceptionV3/Predictions/Reshape_1 is" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +workflowfile="onecc_039.workflow.json" +outputfile="inception_v3.onecc_039.q.circle" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +check_message diff --git a/compiler/one-cmds/tests/onecc_039.workflow.json b/compiler/one-cmds/tests/onecc_039.workflow.json new file mode 100644 index 0000000..55ef569 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_039.workflow.json @@ -0,0 +1,21 @@ +{ + "workflows": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOW": { + "steps": [ + "QUANTIZE" + ], + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.onecc_026.q.circle", + "input_data": "inception_v3_test_data.h5", + "evaluate_result": "True", + "test_data": "inception_v3_test_data.h5", + "print_mpeir": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_040.cfg b/compiler/one-cmds/tests/onecc_040.cfg new file mode 100644 index 0000000..4776ea8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_040.cfg @@ -0,0 +1,20 @@ +[onecc] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v2 + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle diff --git a/compiler/one-cmds/tests/onecc_040.test b/compiler/one-cmds/tests/onecc_040.test new file mode 100644 index 0000000..2f75677 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_040.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflow with cfg reference + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_040.workflow.json" +outputfile="inception_v3.opt.circle" + +rm -rf ${outputfile} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_040.workflow.json b/compiler/one-cmds/tests/onecc_040.workflow.json new file mode 100644 index 0000000..2d4119b --- /dev/null +++ b/compiler/one-cmds/tests/onecc_040.workflow.json @@ -0,0 +1,10 @@ +{ + "workflows": [ + "MY_WORKFLOW" + ], + "MY_WORKFLOW": { + "cfg-reference": { + "path": "onecc_040.cfg" + } + } +} diff --git a/compiler/one-cmds/tests/onecc_041.cfg b/compiler/one-cmds/tests/onecc_041.cfg new file mode 100644 index 0000000..16135f0 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_041.cfg @@ -0,0 +1,16 @@ +[onecc] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3_without_opt.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v2 diff --git a/compiler/one-cmds/tests/onecc_041.test b/compiler/one-cmds/tests/onecc_041.test new file mode 100644 index 0000000..791dd12 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_041.test @@ -0,0 +1,58 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# run a workflows + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +check_message() +{ + if grep -q "Do inference of inception_v3_without_opt\.circle" "${filename}.log" && + grep -q "Do inference of inception_v3\.opt\.circle" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + trap_err_onexit +} + +trap trap_err_onexit ERR + +workflowfile="onecc_041.workflow.json" +outputfile1="inception_v3_without_opt.circle" +outputfile2="inception_v3.opt.circle" + +cp dummy-inferV2 ../bin/dummy-inferV2 + +rm -rf ${outputfile1} {outputfile2} + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +rm -rf ../bin/dummy-inferV2 + +if [[ ! -s "${outputfile1}" ]] && [[ ! -s "${outputfile2}" ]]; then + trap_err_onexit +fi + +check_message diff --git a/compiler/one-cmds/tests/onecc_041.workflow.json b/compiler/one-cmds/tests/onecc_041.workflow.json new file mode 100644 index 0000000..7dfc1c6 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_041.workflow.json @@ -0,0 +1,61 @@ +{ + "workflows": [ + "WITHOUT_OPT", + "WITH_OPT", + "INFER" + ], + "INFER": { + "run-after": [ + "WITHOUT_OPT", + "WITH_OPT" + ], + "steps": [ + "INFER1", + "INFER2" + ], + "INFER1": { + "one-cmd": "one-infer", + "commands" : { + "driver": "dummy-inferV2", + "command": "inception_v3_without_opt.circle" + } + }, + "INFER2": { + "one-cmd": "one-infer", + "commands": { + "driver": "dummy-inferV2", + "command": "inception_v3.opt.circle" + } + } + }, + "WITHOUT_OPT": { + "cfg-reference": { + "path": "onecc_041.cfg" + } + }, + "WITH_OPT": { + "steps": [ + "IMPORT_TF", + "OPTIMIZE" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "OPTIMIZE": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + } + } + +} diff --git a/compiler/one-cmds/tests/onecc_neg_009.test b/compiler/one-cmds/tests/onecc_neg_009.test new file mode 100644 index 0000000..54dd129 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_009.test @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Valid optimization option but invalid configuration file path + +: ' +This test assumes below directories. + +[one hierarchy] + one + ├── backends + ├── bin + ├── doc + ├── include + ├── lib + ├── optimization + └── test # pwd +' + +OPT_ALREADY_EXIST=true + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + rm -rf ../optimization/OONECC_NEG_009.cfg + if [ "$OPT_ALREADY_EXIST" = false ]; then + rm -rf ../optimization + fi + if grep -q "Not found given configuration file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +if [ ! -d "../optimization" ]; then + mkdir -p ../optimization + OPT_ALREADY_EXIST=false +fi + + +touch ../optimization/OONECC_NEG_009.cfg + +configfile=".." + +# run test +onecc -C ${configfile} -OONECC_NEG_009 > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_010.test b/compiler/one-cmds/tests/onecc_neg_010.test new file mode 100644 index 0000000..ddad5e6 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_010.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Invalid optimization option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Invalid optimization option" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile=".." + +# run test +onecc -C ${configfile} -OONECC_NEG_010 > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_011.cfg b/compiler/one-cmds/tests/onecc_neg_011.cfg new file mode 100644 index 0000000..b587324 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_011.cfg @@ -0,0 +1,13 @@ +[onecc] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=False + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle +wrong_opt=True diff --git a/compiler/one-cmds/tests/onecc_neg_011.test b/compiler/one-cmds/tests/onecc_neg_011.test new file mode 100644 index 0000000..3f043a7 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_011.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# generate error for unrecognized opitmization option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "following arguments are unrecognized" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_011.cfg" + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_012.cfg b/compiler/one-cmds/tests/onecc_neg_012.cfg new file mode 100644 index 0000000..fdc73ef --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_012.cfg @@ -0,0 +1,15 @@ +[onecc] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False +one-profile=False +one-infer=True + +[one-infer] +driver=dummy-infer +backend=dummy +command="dummy arguments" diff --git a/compiler/one-cmds/tests/onecc_neg_012.test b/compiler/one-cmds/tests/onecc_neg_012.test new file mode 100644 index 0000000..9feca5f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_012.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# Check driver and backend option is mutually exclusive + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "\-d and -b options are mutually exclusive" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_012.cfg" + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_013.test b/compiler/one-cmds/tests/onecc_neg_013.test new file mode 100644 index 0000000..0dd8a0f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_013.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# negative usage with missing workflow file + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found given workflow file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_013.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_014.test b/compiler/one-cmds/tests/onecc_neg_014.test new file mode 100644 index 0000000..2ed5dcb --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_014.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# invalid workflow file + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Invalid workflow file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_014.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_014.workflow.json b/compiler/one-cmds/tests/onecc_neg_014.workflow.json new file mode 100644 index 0000000..8d4fd43 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_014.workflow.json @@ -0,0 +1,3 @@ +{ + INVALID JSON FILE +} diff --git a/compiler/one-cmds/tests/onecc_neg_015.test b/compiler/one-cmds/tests/onecc_neg_015.test new file mode 100644 index 0000000..079ba67 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_015.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found" "${filename}.log" && + grep -q "key in workflow file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_015.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_015.workflow.json b/compiler/one-cmds/tests/onecc_neg_015.workflow.json new file mode 100644 index 0000000..4cb752e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_015.workflow.json @@ -0,0 +1,21 @@ +{ + "workflowsssssss": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOW": { + "steps": [ + "QUANTIZE" + ], + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.onecc_026.q.circle", + "input_data": "inception_v3_test_data.h5", + "evaluate_result": "True", + "test_data": "inception_v3_test_data.h5", + "print_mpeir": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_016.test b/compiler/one-cmds/tests/onecc_neg_016.test new file mode 100644 index 0000000..c52763f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_016.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found" "${filename}.log" && + grep -q "key listed in" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_016.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_016.workflow.json b/compiler/one-cmds/tests/onecc_neg_016.workflow.json new file mode 100644 index 0000000..c929cf3 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_016.workflow.json @@ -0,0 +1,21 @@ +{ + "workflows": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOWWWWW": { + "steps": [ + "QUANTIZE" + ], + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.onecc_026.q.circle", + "input_data": "inception_v3_test_data.h5", + "evaluate_result": "True", + "test_data": "inception_v3_test_data.h5", + "print_mpeir": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_017.test b/compiler/one-cmds/tests/onecc_neg_017.test new file mode 100644 index 0000000..2f173d2 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_017.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Each workflow should have either" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_017.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_017.workflow.json b/compiler/one-cmds/tests/onecc_neg_017.workflow.json new file mode 100644 index 0000000..22f1415 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_017.workflow.json @@ -0,0 +1,18 @@ +{ + "workflows": [ + "SIMPLE_WORKFLOW" + ], + "SIMPLE_WORKFLOW": { + "QUANTIZE": { + "one-cmd": "one-quantize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.onecc_026.q.circle", + "input_data": "inception_v3_test_data.h5", + "evaluate_result": "True", + "test_data": "inception_v3_test_data.h5", + "print_mpeir": "True" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_018.test b/compiler/one-cmds/tests/onecc_neg_018.test new file mode 100644 index 0000000..bc2297e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_018.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "are exclusive key" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_018.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_018.workflow.json b/compiler/one-cmds/tests/onecc_neg_018.workflow.json new file mode 100644 index 0000000..58cb88e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_018.workflow.json @@ -0,0 +1,24 @@ +{ + "workflows": [ + "MY_WORKFLOW" + ], + "MY_WORKFLOW": { + "steps": [ + "IMPORT_TF" + ], + "cfg-reference": { + "path": "/path/to/ini/format/file" + }, + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_019.test b/compiler/one-cmds/tests/onecc_neg_019.test new file mode 100644 index 0000000..11ef3a9 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_019.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Each step should have" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_019.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_019.workflow.json b/compiler/one-cmds/tests/onecc_neg_019.workflow.json new file mode 100644 index 0000000..aedeeec --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_019.workflow.json @@ -0,0 +1,21 @@ +{ + "workflows": [ + "MY_WORKFLOW" + ], + "MY_WORKFLOW": { + "steps": [ + "IMPORT_TF" + ], + "IMPORT_TF": { + "one-cmddddddddd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_020.test b/compiler/one-cmds/tests/onecc_neg_020.test new file mode 100644 index 0000000..7f5073d --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_020.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflow file has invalid key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Each step should have" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_020.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_020.workflow.json b/compiler/one-cmds/tests/onecc_neg_020.workflow.json new file mode 100644 index 0000000..d3446d3 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_020.workflow.json @@ -0,0 +1,21 @@ +{ + "workflows": [ + "MY_WORKFLOW" + ], + "MY_WORKFLOW": { + "steps": [ + "IMPORT_TF" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commandssssssssss": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_021.test b/compiler/one-cmds/tests/onecc_neg_021.test new file mode 100644 index 0000000..e9d4baa --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_021.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflows have a cycle + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Workflows should not have a cycle" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_021.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_021.workflow.json b/compiler/one-cmds/tests/onecc_neg_021.workflow.json new file mode 100644 index 0000000..6d21111 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_021.workflow.json @@ -0,0 +1,44 @@ +{ + "workflows": [ + "CYCLE_WF1", + "CYCLE_WF2" + ], + "CYCLE_WF1": { + "run-after": [ + "CYCLE_WF2" + ], + "steps": [ + "IMPORT_TF" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + } + }, + "CYCLE_WF2": { + "run-after": [ + "CYCLE_WF1" + ], + "steps": [ + "IMPORT_TF" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_022.cfg b/compiler/one-cmds/tests/onecc_neg_022.cfg new file mode 100644 index 0000000..16135f0 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_022.cfg @@ -0,0 +1,16 @@ +[onecc] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3_without_opt.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v2 diff --git a/compiler/one-cmds/tests/onecc_neg_022.test b/compiler/one-cmds/tests/onecc_neg_022.test new file mode 100644 index 0000000..5400717 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_022.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflows have a cycle + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Workflows should not have a cycle" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_022.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_022.workflow.json b/compiler/one-cmds/tests/onecc_neg_022.workflow.json new file mode 100644 index 0000000..2e056ac --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_022.workflow.json @@ -0,0 +1,63 @@ +{ + "workflows": [ + "WITHOUT_OPT", + "WITH_OPT", + "INFER" + ], + "INFER": { + "run-after": [ + "WITHOUT_OPT", + "WITH_OPT" + ], + "steps": [ + "INFER1", + "INFER2" + ], + "INFER1": { + "one-cmd": "one-infer", + "commands" : { + "driver": "dummy-inferV2", + "command": "inception_v3_without_opt.circle" + } + }, + "INFER2": { + "one-cmd": "one-infer", + "commands": { + "driver": "dummy-inferV2", + "command": "inception_v3.opt.circle" + } + } + }, + "WITHOUT_OPT": { + "cfg-reference": { + "path": "onecc_041.cfg" + } + }, + "WITH_OPT": { + "run-after": [ + "WITHOUT_OPT" + ], + "steps": [ + "IMPORT_TF", + "OPTIMIZE" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "OPTIMIZE": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_023.test b/compiler/one-cmds/tests/onecc_neg_023.test new file mode 100644 index 0000000..09717e8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_023.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# +# 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. + +# workflows have wrong optimize option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Change outputs failed" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_023.workflow.json" + +# run test +onecc -W ${workflowfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_023.workflow.json b/compiler/one-cmds/tests/onecc_neg_023.workflow.json new file mode 100644 index 0000000..056e704 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_023.workflow.json @@ -0,0 +1,30 @@ +{ + "workflows": [ + "WITH_OPT" + ], + "WITH_OPT": { + "steps": [ + "IMPORT_TF", + "OPTIMIZE" + ], + "IMPORT_TF": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "OPTIMIZE": { + "one-cmd": "one-optimize", + "commands": { + "input_path": "inception_v3.circle", + "output_path": "inception_v3.opt.circle", + "change_outputs": "non_existing_node_name" + } + } + } +} diff --git a/compiler/one-cmds/tests/prepare_test_materials.sh b/compiler/one-cmds/tests/prepare_test_materials.sh index c80c598..c171cfe 100644 --- a/compiler/one-cmds/tests/prepare_test_materials.sh +++ b/compiler/one-cmds/tests/prepare_test_materials.sh @@ -91,6 +91,20 @@ if [[ ! -s "onnx_conv2d_conv2d.onnx" ]]; then # https://github.com/Samsung/ONE/issues/5577#issuecomment-755078444 fi +if [[ ! -s "reshape_matmul.onnx" ]]; then + rm -rf reshape_matmul.zip + wget https://github.com/Samsung/ONE/files/9082878/reshape_matmul.zip + unzip reshape_matmul.zip + # https://github.com/Samsung/ONE/issues/9405#issuecomment-1180198137 +fi + +if [[ ! -s "Net_InstanceNorm_003.part" ]]; then + rm -rf Net_InstanceNorm_003.zip + wget https://github.com/Samsung/ONE/files/8608844/Net_InstanceNorm_003.zip + unzip Net_InstanceNorm_003.zip + # https://github.com/Samsung/ONE/issues/8570#issuecomment-1115804257 +fi + function files_missing() { condition="test " diff --git a/compiler/one-cmds/utils.py b/compiler/one-cmds/utils.py index be0322a..d204447 100644 --- a/compiler/one-cmds/utils.py +++ b/compiler/one-cmds/utils.py @@ -47,6 +47,25 @@ def _add_default_arg(parser): parser.add_argument('-S', '--section', type=str, help=argparse.SUPPRESS) +def _add_default_arg_no_CS(parser): + """ + This adds -v -V args only (no -C nor -S) + """ + # version + parser.add_argument( + '-v', + '--version', + action='store_true', + help='show program\'s version number and exit') + + # verbose + parser.add_argument( + '-V', + '--verbose', + action='store_true', + help='output additional information to stdout or stderr') + + def is_accumulated_arg(arg, driver): if driver == "one-quantize": accumulables = [ @@ -62,6 +81,43 @@ def _is_valid_attr(args, attr): return hasattr(args, attr) and getattr(args, attr) +class Command: + def __init__(self, driver, args, log_file): + self.cmd = [driver] + self.driver = driver + self.args = args + self.log_file = log_file + + # Add option if attrs are valid + # Option values are collected from self.args + def add_option_with_valid_args(self, option, attrs): + for attr in attrs: + if not _is_valid_attr(self.args, attr): + return self + self.cmd.append(option) + for attr in attrs: + self.cmd.append(getattr(self.args, attr)) + return self + + # Add option and values without any condition + def add_option_with_values(self, option, values): + self.cmd.append(option) + for value in values: + self.cmd.append(value) + return self + + # Add option with no argument (ex: --verbose) if attr is valid + def add_noarg_option_if_valid_arg(self, option, attr): + if _is_valid_attr(self.args, attr): + self.cmd.append(option) + return self + + # Run cmd and save logs + def run(self): + self.log_file.write((' '.join(self.cmd) + '\n').encode()) + _run(self.cmd, err_prefix=self.driver, logfile=self.log_file) + + def _parse_cfg_and_overwrite(config_path, section, args): """ parse given section of configuration file and set the values of args. @@ -153,8 +209,7 @@ def _run(cmd, err_prefix=None, logfile=None): err_prefix: prefix to be put before every stderr lines logfile: file stream to which both of stdout and stderr lines will be written """ - with subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1) as p: + with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: import select inputs = set([p.stdout, p.stderr]) while inputs: diff --git a/compiler/onnx-tools/CMakeLists.txt b/compiler/onnx-tools/CMakeLists.txt index ac4500e..5935cdf 100644 --- a/compiler/onnx-tools/CMakeLists.txt +++ b/compiler/onnx-tools/CMakeLists.txt @@ -18,4 +18,10 @@ foreach(ONNX_TOOL IN ITEMS ${ONNX_TOOL_FILES}) add_custom_target(${ONNX_TOOL_TARGET} ALL DEPENDS ${ONNX_TOOL_BIN}) + install(FILES ${ONNX_TOOL_BIN} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION bin) + endforeach(ONNX_TOOL) diff --git a/compiler/pota-quantization-value-test/CMakeLists.txt b/compiler/pota-quantization-value-test/CMakeLists.txt index 51fd9a3..96dfc86 100644 --- a/compiler/pota-quantization-value-test/CMakeLists.txt +++ b/compiler/pota-quantization-value-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + unset(QUANTIZATION_VALUE_TEST) unset(QUANTIZATION_VALUE_TEST_WITH_PARAM) unset(QUANTIZATION_CONFIG_VALUE_TEST) diff --git a/compiler/record-minmax-conversion-test/CMakeLists.txt b/compiler/record-minmax-conversion-test/CMakeLists.txt index 31b9061..6363614 100644 --- a/compiler/record-minmax-conversion-test/CMakeLists.txt +++ b/compiler/record-minmax-conversion-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + unset(RECORD_MINMAX_CONVERSION_TEST) macro(addTest NAME) diff --git a/compiler/record-minmax/driver/Driver.cpp b/compiler/record-minmax/driver/Driver.cpp index c9f1d0c..faa402f 100644 --- a/compiler/record-minmax/driver/Driver.cpp +++ b/compiler/record-minmax/driver/Driver.cpp @@ -34,62 +34,33 @@ int entry(const int argc, char **argv) arser::Arser arser( "Embedding min/max values of activations to the circle model for post-training quantization"); - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument("-V", "--verbose") - .nargs(0) - .required(false) - .default_value(false) - .help("output additional information to stdout or stderr"); + arser::Helper::add_version(arser, print_version); + arser::Helper::add_verbose(arser); - arser.add_argument("--input_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Input model filepath"); + arser.add_argument("--input_model").required(true).help("Input model filepath"); arser.add_argument("--input_data") - .nargs(1) - .type(arser::DataType::STR) - .required(false) .help("Input data filepath. If not given, record-minmax will run with randomly generated data. " "Note that the random dataset does not represent inference workload, leading to poor " "model accuracy."); - arser.add_argument("--output_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Output model filepath"); + arser.add_argument("--output_model").required(true).help("Output model filepath"); arser.add_argument("--min_percentile") - .nargs(1) .type(arser::DataType::FLOAT) .help("Record n'th percentile of min"); arser.add_argument("--max_percentile") - .nargs(1) .type(arser::DataType::FLOAT) .help("Record n'th percentile of max"); - arser.add_argument("--mode") - .nargs(1) - .type(arser::DataType::STR) - .help("Record mode. percentile (default) or moving_average"); + arser.add_argument("--mode").help("Record mode. percentile (default) or moving_average"); arser.add_argument("--input_data_format") - .nargs(1) - .type(arser::DataType::STR) .help("Input data format. h5/hdf5 (default) or list/filelist"); arser.add_argument("--generate_profile_data") .nargs(0) - .required(false) .default_value(false) .help("This will turn on profiling data generation."); diff --git a/compiler/record-minmax/include/RecordFunction.h b/compiler/record-minmax/include/RecordFunction.h index ba199d0..5b993e4 100644 --- a/compiler/record-minmax/include/RecordFunction.h +++ b/compiler/record-minmax/include/RecordFunction.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include namespace record_minmax diff --git a/compiler/record-minmax/src/MinMaxObserver.cpp b/compiler/record-minmax/src/MinMaxObserver.cpp index 8288d3e..e6edbdc 100644 --- a/compiler/record-minmax/src/MinMaxObserver.cpp +++ b/compiler/record-minmax/src/MinMaxObserver.cpp @@ -18,6 +18,7 @@ #include +#include #include using DataType = luci_interpreter::DataType; @@ -75,7 +76,7 @@ void MinMaxObserver::postTensorWrite(const luci::CircleNode *node, // Reshape changes only shape of input tensor, efficiently is it a no-op. return; default: - throw std::runtime_error("Tensor's data type is not float"); + throw std::runtime_error("Tensor's data type is not float. " + node->name()); } } diff --git a/compiler/record-minmax/src/RecordMinMax.cpp b/compiler/record-minmax/src/RecordMinMax.cpp index 10a1451..6dbf98d 100644 --- a/compiler/record-minmax/src/RecordMinMax.cpp +++ b/compiler/record-minmax/src/RecordMinMax.cpp @@ -186,7 +186,13 @@ void RecordMinMax::initialize(const std::string &input_model_path) throw std::runtime_error("Failed to verify circle '" + input_model_path + "'"); } - _module = luci::Importer().importModule(circle::GetModel(model_data.data())); + const circle::Model *circle_model = circle::GetModel(model_data.data()); + if (circle_model == nullptr) + { + throw std::runtime_error("Failed to load '" + input_model_path + "'"); + } + + _module = luci::Importer().importModule(circle_model); if (_module == nullptr) { diff --git a/compiler/souschef/CMakeLists.txt b/compiler/souschef/CMakeLists.txt index f57102f..8dcf4c2 100644 --- a/compiler/souschef/CMakeLists.txt +++ b/compiler/souschef/CMakeLists.txt @@ -1,13 +1,20 @@ nnas_find_package(Protobuf QUIET) +nnas_find_package(Fp16Source QUIET) if(NOT Protobuf_FOUND) message(STATUS "Build souschef: FAILED (missing Protobuf)") return() endif(NOT Protobuf_FOUND) +if(NOT Fp16Source_FOUND) + message(STATUS "Build souschef: FAILED (missing Fp16Source)") + return() +endif(NOT Fp16Source_FOUND) + file(GLOB_RECURSE SOURCES "src/*.cpp") add_library(souschef STATIC ${SOURCES}) set_target_properties(souschef PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(souschef PRIVATE ${Fp16Source_DIR}/include) target_include_directories(souschef PUBLIC include) target_link_libraries(souschef PUBLIC libprotobuf) diff --git a/compiler/souschef/include/souschef/Data/Explicit.h b/compiler/souschef/include/souschef/Data/Explicit.h index 7cbb773..434d0ec 100644 --- a/compiler/souschef/include/souschef/Data/Explicit.h +++ b/compiler/souschef/include/souschef/Data/Explicit.h @@ -96,6 +96,41 @@ template struct ExplicitDataChefFactory : public DataChefFactory } }; +class ExplicitFloat16DataChef final : public DataChef +{ +public: + ExplicitFloat16DataChef() + { + // DO NOTHING + } + +public: + std::vector generate(int32_t count) const override; + +public: + void insert(const float &value) { _values.emplace_back(value); } + +private: + // NOTE store values in float but will convert to uint16_t in generate() + std::vector _values; +}; + +struct ExplicitFloat16DataChefFactory : public DataChefFactory +{ + std::unique_ptr create(const Arguments &args) const + { + std::unique_ptr res{new ExplicitFloat16DataChef}; + + for (uint32_t n = 0; n < args.count(); ++n) + { + auto const value = to_number(args.value(n)); + res->insert(value); + } + + return std::move(res); + } +}; + } // namespace souschef #endif // __SOUSCHEF_DATA_EXPLICIT_H__ diff --git a/compiler/souschef/include/souschef/Data/Gaussian.h b/compiler/souschef/include/souschef/Data/Gaussian.h index 8093b4c..c9ac571 100644 --- a/compiler/souschef/include/souschef/Data/Gaussian.h +++ b/compiler/souschef/include/souschef/Data/Gaussian.h @@ -41,6 +41,22 @@ private: float _stddev; }; +class GaussianFloat16DataChef final : public DataChef +{ +public: + GaussianFloat16DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + class GaussianInt32DataChef final : public DataChef { public: @@ -109,6 +125,11 @@ struct GaussianUint8DataChefFactory : public DataChefFactory std::unique_ptr create(const Arguments &args) const; }; +struct GaussianFloat16DataChefFactory : public DataChefFactory +{ + std::unique_ptr create(const Arguments &args) const; +}; + } // namespace souschef #endif // __SOUSCHEF_DATA_GAUSSIAN_H__ diff --git a/compiler/souschef/src/Explicit.cpp b/compiler/souschef/src/Explicit.cpp index eb36cb7..3278ae3 100644 --- a/compiler/souschef/src/Explicit.cpp +++ b/compiler/souschef/src/Explicit.cpp @@ -19,6 +19,8 @@ #include #include +#include + namespace souschef { @@ -74,4 +76,23 @@ void ExplicitDataChef::write_value(std::vector &res, int32 } } +std::vector ExplicitFloat16DataChef::generate(int32_t count) const +{ + std::vector res; + + for (uint32_t n = 0; n < count; ++n) + { + float const fvalue = (n < _values.size()) ? _values.at(n) : 0.0; + uint16_t const value = fp16_ieee_from_fp32_value(fvalue); + auto const arr = reinterpret_cast(&value); + + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) + { + res.emplace_back(arr[b]); + } + } + + return res; +} + } // namespace souschef diff --git a/compiler/souschef/src/Gaussian.cpp b/compiler/souschef/src/Gaussian.cpp index 32cbcff..53a62ca 100644 --- a/compiler/souschef/src/Gaussian.cpp +++ b/compiler/souschef/src/Gaussian.cpp @@ -23,6 +23,8 @@ #include #include +#include + namespace souschef { @@ -36,7 +38,7 @@ static std::vector generate_gaussian(int32_t count, float mean, float s std::vector res; constexpr float max_cap = std::numeric_limits::max(); - constexpr float min_cap = std::numeric_limits::min(); + constexpr float min_cap = std::numeric_limits::lowest(); for (uint32_t n = 0; n < count; ++n) { float raw_value = dist(rand); @@ -69,6 +71,34 @@ std::vector GaussianFloat32DataChef::generate(int32_t count) const return generate_gaussian(count, _mean, _stddev); } +std::vector GaussianFloat16DataChef::generate(int32_t count) const +{ + auto time_stamp = std::chrono::system_clock::now().time_since_epoch().count(); + auto seed = static_cast(time_stamp); + + std::minstd_rand rand{static_cast(seed)}; + std::normal_distribution dist{_mean, _stddev}; + + std::vector res; + + constexpr float max_cap = 1e9; + constexpr float min_cap = -1e9; + for (uint32_t n = 0; n < count; ++n) + { + float raw_value = dist(rand); + const float capped_value = std::max(min_cap, std::min(max_cap, raw_value)); + const uint16_t value = fp16_ieee_from_fp32_value(capped_value); + auto const arr = reinterpret_cast(&value); + + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) + { + res.emplace_back(arr[b]); + } + } + + return res; +} + std::vector GaussianInt32DataChef::generate(int32_t count) const { return generate_gaussian(count, _mean, _stddev); @@ -136,4 +166,17 @@ std::unique_ptr GaussianUint8DataChefFactory::create(const Arguments & return std::unique_ptr{new GaussianUint8DataChef{mean, stddev}}; } +std::unique_ptr GaussianFloat16DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number(args.value(0)); + auto const stddev = to_number(args.value(1)); + + return std::unique_ptr{new GaussianFloat16DataChef{mean, stddev}}; +} + } // namespace souschef diff --git a/compiler/tf2circle-conversion-test/CMakeLists.txt b/compiler/tf2circle-conversion-test/CMakeLists.txt index 27f2463..79a3987 100644 --- a/compiler/tf2circle-conversion-test/CMakeLists.txt +++ b/compiler/tf2circle-conversion-test/CMakeLists.txt @@ -128,6 +128,10 @@ list(APPEND TEST_DEPS "${TEST_CONFIG}") # This "tf2circle_conversion_test_deps" target enforces CMake to generate all the dependencies during "build" phase add_custom_target(tf2circle_conversion_test_deps ALL DEPENDS ${TEST_DEPS}) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + # Run tests add_test( NAME tf2circle_conversion_test diff --git a/compiler/tf2circle-dredd-pb-test/CMakeLists.txt b/compiler/tf2circle-dredd-pb-test/CMakeLists.txt index 48b098e..83596fa 100644 --- a/compiler/tf2circle-dredd-pb-test/CMakeLists.txt +++ b/compiler/tf2circle-dredd-pb-test/CMakeLists.txt @@ -132,6 +132,10 @@ list(APPEND DEPS "${TARGET_RULE_LIB}") # Generate dependencies add_custom_target(tf2circle_dredd_pb_deps ALL DEPENDS ${DEPS}) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + add_test( NAME tf2circle_dredd_pb_test COMMAND diff --git a/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt b/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt index 789e585..427e575 100644 --- a/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt +++ b/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt @@ -175,6 +175,10 @@ list(APPEND DEPS "${TARGET_RULE_LIB}") # Generate dependencies add_custom_target(tf2circle_dredd_pbtxt_deps ALL DEPENDS ${DEPS}) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + add_test( NAME tf2circle_dredd_pbtxt_test COMMAND diff --git a/compiler/tf2circle-model-test/CMakeLists.txt b/compiler/tf2circle-model-test/CMakeLists.txt index 2fb8223..ad776a6 100644 --- a/compiler/tf2circle-model-test/CMakeLists.txt +++ b/compiler/tf2circle-model-test/CMakeLists.txt @@ -100,6 +100,10 @@ list(APPEND DEPS "${TEST_RUNNER_SCRIPT}") ### Generate dependencies add_custom_target(tf2circle_model_test_deps ALL DEPENDS ${DEPS}) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + # NOTE This target is not built by default add_test( NAME tf2circle_model_test diff --git a/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt b/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt index b75c507..ac9f14d 100644 --- a/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt +++ b/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nnas_include(TargetRequire) unset(REQUIRED_TARGETS) diff --git a/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt b/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt index 87cf783..95a296e 100644 --- a/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt +++ b/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nnas_include(TargetRequire) unset(REQUIRED_TARGETS) diff --git a/compiler/tf2tflite-value-pb-test/CMakeLists.txt b/compiler/tf2tflite-value-pb-test/CMakeLists.txt index 41974f7..a6c451e 100644 --- a/compiler/tf2tflite-value-pb-test/CMakeLists.txt +++ b/compiler/tf2tflite-value-pb-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nnas_include(TargetRequire) unset(REQUIRED_TARGETS) diff --git a/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt b/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt index 2e76e21..fde3e60 100644 --- a/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt +++ b/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nnas_include(TargetRequire) unset(REQUIRED_TARGETS) diff --git a/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt index 0b47393..97aa07f 100644 --- a/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt +++ b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nncc_find_resource(TensorFlowTests) # diff --git a/compiler/tf2tfliteV2/tf2tfliteV2.py b/compiler/tf2tfliteV2/tf2tfliteV2.py index 6b578ad..2bcf553 100755 --- a/compiler/tf2tfliteV2/tf2tfliteV2.py +++ b/compiler/tf2tfliteV2/tf2tfliteV2.py @@ -110,6 +110,12 @@ def _get_parser(): type=str, help="Names of the output arrays, comma-separated.") + # experimental options + parser.add_argument( + "--experimental_disable_batchmatmul_unfold", + action="store_true", + help="Experimental disable BatchMatMul unfold") + # Set default value parser.set_defaults(model_format="graph_def") return parser @@ -228,6 +234,9 @@ def _v2_convert(flags): keras_model = tf.keras.models.load_model(flags.input_path) converter = tf.lite.TFLiteConverter.from_keras_model(keras_model) + if flags.experimental_disable_batchmatmul_unfold: + converter._experimental_disable_batchmatmul_unfold = True + converter.allow_custom_ops = True converter.experimental_new_converter = True diff --git a/compiler/tfl-inspect/CMakeLists.txt b/compiler/tfl-inspect/CMakeLists.txt index 9e1cb72..2c6e3a1 100644 --- a/compiler/tfl-inspect/CMakeLists.txt +++ b/compiler/tfl-inspect/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_tflite) +if(NOT TARGET mio_tflite280) return() -endif(NOT TARGET mio_tflite) +endif(NOT TARGET mio_tflite280) set(DRIVER "driver/Driver.cpp") diff --git a/compiler/tfl-inspect/driver/Driver.cpp b/compiler/tfl-inspect/driver/Driver.cpp index 3e62e0f..8505ff4 100644 --- a/compiler/tfl-inspect/driver/Driver.cpp +++ b/compiler/tfl-inspect/driver/Driver.cpp @@ -35,7 +35,7 @@ int entry(int argc, char **argv) .nargs(0) .help("Dump Conv2D series weight operators in tflite file"); arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in tflite file"); - arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to inspect"); + arser.add_argument("tflite").help("TFLite file to inspect"); try { diff --git a/compiler/tfl-verify/CMakeLists.txt b/compiler/tfl-verify/CMakeLists.txt index 2fba335..5bead5b 100644 --- a/compiler/tfl-verify/CMakeLists.txt +++ b/compiler/tfl-verify/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_tflite) +if(NOT TARGET mio_tflite280) return() -endif(NOT TARGET mio_tflite) +endif(NOT TARGET mio_tflite280) file(GLOB_RECURSE SOURCES "src/*.cpp") diff --git a/compiler/tfl-verify/src/Driver.cpp b/compiler/tfl-verify/src/Driver.cpp index 6d18976..6234549 100644 --- a/compiler/tfl-verify/src/Driver.cpp +++ b/compiler/tfl-verify/src/Driver.cpp @@ -25,7 +25,7 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file path to verify"); + arser.add_argument("tflite").help("TFLite file path to verify"); try { diff --git a/compiler/tflchef/CMakeLists.txt b/compiler/tflchef/CMakeLists.txt index 948b1ce..6205ac6 100644 --- a/compiler/tflchef/CMakeLists.txt +++ b/compiler/tflchef/CMakeLists.txt @@ -20,4 +20,9 @@ add_subdirectory(core) add_subdirectory(tflite) # Tools add_subdirectory(tools) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + add_subdirectory(tests) diff --git a/compiler/tflchef/core/src/Convert.cpp b/compiler/tflchef/core/src/Convert.cpp index 200c71e..f4dd4b3 100644 --- a/compiler/tflchef/core/src/Convert.cpp +++ b/compiler/tflchef/core/src/Convert.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,6 +63,8 @@ tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value) { case tflchef::FLOAT32: return tflite::TensorType_FLOAT32; + case tflchef::FLOAT16: + return tflite::TensorType_FLOAT16; case tflchef::INT32: return tflite::TensorType_INT32; case tflchef::UINT8: @@ -164,3 +167,222 @@ as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb, throw std::runtime_error("Unknown SparseIndexVector type"); } + +// namespace sparsity code referenced from +// https://github.com/tensorflow/tensorflow/blob/3f878cff5b698b82eea85db2b60d65a2e320850e/ +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.cc + +namespace sparsity +{ + +template +FormatConverter::FormatConverter(const std::vector &shape, + const std::vector &traversal_order, + const std::vector &format, + const std::vector &block_size, + const std::vector &block_map) + : dense_shape_(shape), traversal_order_(traversal_order), block_size_(block_size), + block_map_(block_map) +{ + dense_size_ = 1; + int block_dim = 0; + blocked_shape_.resize(shape.size()); + format_.resize(shape.size() + block_map.size()); + for (int i = 0; i < shape.size(); i++) + { + format_[i] = format[traversal_order[i]]; + dense_size_ *= shape[i]; + if (block_dim < block_map.size() && block_map[block_dim] == i) + { + blocked_shape_[i] = shape[i] / block_size[block_dim]; + block_dim++; + } + else + { + blocked_shape_[i] = shape[i]; + } + } + + // Only dense blocks are supported. + for (int i = 0; i < block_map.size(); i++) + { + format_[i + shape.size()] = kTfLiteDimDense; + } +} + +template bool FormatConverter::DenseToSparse(const T *src_data) +{ + int num_original_dims = dense_shape_.size(); + int num_block_dims = block_map_.size(); + int num_expanded_dims = num_original_dims + num_block_dims; + std::vector expanded_shape(num_expanded_dims); + for (int i = 0; i < num_expanded_dims; i++) + { + if (i < num_original_dims) + { + expanded_shape[i] = blocked_shape_[i]; + } + else + { + expanded_shape[i] = block_size_[i - num_original_dims]; + } + } + + std::vector shape_offset(num_original_dims); + shape_offset[shape_offset.size() - 1] = 1; + for (int i = num_original_dims - 1; i > 0; --i) + { + shape_offset[i - 1] = shape_offset[i] * dense_shape_[i]; + } + + std::vector expanded_shape_offset(num_expanded_dims); + for (int i = 0; i < num_original_dims; ++i) + { + expanded_shape_offset[i] = shape_offset[i]; + } + for (int i = 0; i < num_block_dims; ++i) + { + int mapped_dim = block_map_[i]; + expanded_shape_offset[num_original_dims + i] = shape_offset[mapped_dim]; + expanded_shape_offset[mapped_dim] *= block_size_[i]; + } + + std::vector dst_ordered_offset(num_expanded_dims); + for (int i = 0; i < num_expanded_dims; ++i) + { + dst_ordered_offset[i] = expanded_shape_offset[traversal_order_[i]]; + } + + std::vector dst_dim_has_nonzeroes(num_expanded_dims); + std::fill(dst_dim_has_nonzeroes.begin(), dst_dim_has_nonzeroes.end(), false); + std::vector inner_compressed_dim(num_expanded_dims); + int most_recent_compressed_dim = -1; + std::vector num_segments_of_next_compressed_dim(num_expanded_dims); + int segment_count = 1; + for (int i = num_expanded_dims - 1; i >= 0; --i) + { + inner_compressed_dim[i] = most_recent_compressed_dim; + if (format_[i] == kTfLiteDimSparseCSR) + { + most_recent_compressed_dim = i; + num_segments_of_next_compressed_dim[i] = segment_count; + segment_count = 1; + } + else + { + num_segments_of_next_compressed_dim[i] = -1; + segment_count *= expanded_shape[traversal_order_[i]]; + } + } + + dim_metadata_.resize(num_expanded_dims * 2); + std::vector dst_sparse_dims; + dst_sparse_dims.reserve(num_expanded_dims); + for (int i = 0; i < num_expanded_dims; ++i) + { + dim_metadata_[i * 2].clear(); + dim_metadata_[i * 2 + 1].clear(); + if (format_[i] == kTfLiteDimDense) + { + // If dimension is dense, just store the shape. + dim_metadata_[i * 2].push_back(expanded_shape[traversal_order_[i]]); + } + else + { + dim_metadata_[i * 2].push_back(0); // Segment array always begins with 0. + dst_sparse_dims.push_back(i); // Add dimension to the sparse list. + } + } + + // This algorithm assumes that the block size is small enough for all the + // elements to fit in cache, so the strided accesses from different traversal + // order and the write-first-erase-later strategy shouldn't be too slow + int dst_dim_idx = num_expanded_dims; + std::vector coordinate(num_expanded_dims, 0); + int dense_tensor_idx = 0; + while (dst_dim_idx >= 0) + { + if (dst_dim_idx == num_expanded_dims) + { + // We have a complete coordinate. Add the element to the value array if it + // is not zero, or if the last dimension is dense. + if (!IsZero(src_data[dense_tensor_idx])) + { + data_.push_back(src_data[dense_tensor_idx]); + // Mark all sparse dimensions that their current indices have nonzeroes. + for (auto dst_dim : dst_sparse_dims) + { + if (!dst_dim_has_nonzeroes[dst_dim]) + { + // Only add the index to the indices array if the current nonzero + // is the first nonzero of the block. + dim_metadata_[2 * dst_dim + 1].push_back(coordinate[dst_dim]); + dst_dim_has_nonzeroes[dst_dim] = true; + } + } + } + else if (format_[num_expanded_dims - 1] == kTfLiteDimDense) + { + data_.push_back(src_data[dense_tensor_idx]); + } + --dst_dim_idx; + } + else + { + int original_dim_idx = traversal_order_[dst_dim_idx]; + int dim_size = expanded_shape[original_dim_idx]; + if (dst_dim_has_nonzeroes[dst_dim_idx]) + { + // If the previous block has nonzeroes, reset the flag to false since + // we have just moved to a new block. + dst_dim_has_nonzeroes[dst_dim_idx] = false; + } + else if (format_[dst_dim_idx] == kTfLiteDimSparseCSR) + { + // This block is empty. Delete unnecessary values if compressed. + int next_compressed_dim = inner_compressed_dim[dst_dim_idx]; + int erase_offset = dim_metadata_[2 * dst_dim_idx + 1].size() * + num_segments_of_next_compressed_dim[dst_dim_idx]; + if (next_compressed_dim >= 0) + { + auto &segments = dim_metadata_[2 * inner_compressed_dim[dst_dim_idx]]; + segments.erase(segments.begin() + 1 + erase_offset, segments.end()); + } + else + { + data_.erase(data_.begin() + erase_offset, data_.end()); + } + } + if (++coordinate[dst_dim_idx] < dim_size) + { + // The current dst_dim_idx is valid (not out of bound). + dense_tensor_idx += dst_ordered_offset[dst_dim_idx]; + ++dst_dim_idx; + } + else + { + // dst_dim_idx has reached its dim size. Update segment array and go + // back to incrementing the previous dimension (dst_dim_idx - 1). + if (format_[dst_dim_idx] == kTfLiteDimSparseCSR) + { + dim_metadata_[2 * dst_dim_idx].push_back(dim_metadata_[2 * dst_dim_idx + 1].size()); + } + coordinate[dst_dim_idx] = -1; + dense_tensor_idx -= dst_ordered_offset[dst_dim_idx] * dim_size; + --dst_dim_idx; + } + } + } + + return true; +} + +template bool FormatConverter::IsZero(const T val) +{ + return (val == static_cast(0)); +} + +template class FormatConverter; +template class FormatConverter; // float16 + +} // namespace sparsity diff --git a/compiler/tflchef/core/src/Convert.h b/compiler/tflchef/core/src/Convert.h index 45c93d2..6e910ea 100644 --- a/compiler/tflchef/core/src/Convert.h +++ b/compiler/tflchef/core/src/Convert.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,4 +35,52 @@ flatbuffers::Offset as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb, const ::tflchef::TensorSparsity_IndexVec &value); +// codes under namespace sparsity referenced from +// https://github.com/tensorflow/tensorflow/blob/3f878cff5b698b82eea85db2b60d65a2e320850e/ +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.h +// tensorflow/lite/kernels/internal/utils/sparsity_format_converter.cc + +namespace sparsity +{ + +// Storage format of each dimension in a sparse tensor. +typedef enum TfLiteDimensionType +{ + kTfLiteDimDense = 0, + kTfLiteDimSparseCSR, +} TfLiteDimensionType; + +template class FormatConverter +{ +public: + FormatConverter(const std::vector &shape, const std::vector &traversal_order, + const std::vector &format, + const std::vector &block_size = {}, + const std::vector &block_map = {}); + + bool DenseToSparse(const T *src_data); + + const std::vector &GetData() { return data_; } + const std::vector> &GetDimMetadata() { return dim_metadata_; } + +private: + bool IsZero(const T val); + +private: + std::vector dense_shape_; + std::vector blocked_shape_; + size_t dense_size_; + std::vector traversal_order_; + std::vector format_; + std::vector block_size_; + std::vector block_map_; + std::vector> dim_metadata_; + std::vector data_; +}; + +extern template class FormatConverter; +extern template class FormatConverter; // float16 + +} // namespace sparsity + #endif // __CONVERT_H__ diff --git a/compiler/tflchef/core/src/DataChef.def b/compiler/tflchef/core/src/DataChef.def index c634c04..28a5b76 100644 --- a/compiler/tflchef/core/src/DataChef.def +++ b/compiler/tflchef/core/src/DataChef.def @@ -21,3 +21,7 @@ DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory) DATA_CHEF(INT32, gaussian, GaussianInt32DataChefFactory) DATA_CHEF(INT16, gaussian, GaussianInt16DataChefFactory) DATA_CHEF(UINT8, gaussian, GaussianUint8DataChefFactory) + +// FLOAT16 support for only gaussian, explicit for now +DATA_CHEF(FLOAT16, explicit, ExplicitFloat16DataChefFactory) +DATA_CHEF(FLOAT16, gaussian, GaussianFloat16DataChefFactory) diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp index 93b9334..a788adc 100644 --- a/compiler/tflchef/core/src/ModelChef.cpp +++ b/compiler/tflchef/core/src/ModelChef.cpp @@ -92,6 +92,7 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) static DataChefRegistry string; static DataChefRegistry boolean; static DataChefRegistry s16; + static DataChefRegistry fp16; switch (type) { @@ -101,6 +102,8 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) return s64; case tflchef::FLOAT32: return fp32; + case tflchef::FLOAT16: + return fp16; case tflchef::UINT8: return u8; case tflchef::STRING: @@ -207,6 +210,41 @@ struct CookParams std::string noname; }; +std::vector> +make_dim_metadata_vec(flatbuffers::FlatBufferBuilder *flatbuffer_builder, int32_t dims_count, + const std::vector &traversal_order_vec, + const std::vector &format_vec, + const std::vector> &dim_metadata_src) +{ + // Build sparsity parameter. + std::vector> dim_metadata_vec(dims_count); + for (int32_t i = 0; i < dims_count; i++) + { + const int32_t metadata_idx = 2 * i; + if (format_vec[traversal_order_vec[i]] == sparsity::kTfLiteDimSparseCSR) + { + auto array_segments = + tflite::CreateInt32Vector(*flatbuffer_builder, + flatbuffer_builder->CreateVector(dim_metadata_src[metadata_idx])) + .Union(); + auto array_indices = + tflite::CreateInt32Vector( + *flatbuffer_builder, flatbuffer_builder->CreateVector(dim_metadata_src[metadata_idx + 1])) + .Union(); + dim_metadata_vec[i] = + tflite::CreateDimensionMetadata(*flatbuffer_builder, tflite::DimensionType_SPARSE_CSR, 0, + tflite::SparseIndexVector_Int32Vector, array_segments, + tflite::SparseIndexVector_Int32Vector, array_indices); + } + else + { + dim_metadata_vec[i] = tflite::CreateDimensionMetadata( + *flatbuffer_builder, tflite::DimensionType_DENSE, dim_metadata_src[metadata_idx][0]); + } + } + return dim_metadata_vec; +} + template std::map cook_graph(const T &graph, CookParams &cp) { LOGGER(l); @@ -271,6 +309,8 @@ template std::map cook_graph(const T &graph, assert(operand.has_type()); + flatbuffers::Offset sparsity_index; + flatbuffers::Offset> shape; std::vector dims; if (operand.has_shape()) @@ -298,16 +338,125 @@ template std::map cook_graph(const T &graph, // Create Data int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size(); auto data_vec = chef->generate(count); - auto data = flatbuffer_builder->CreateVector(data_vec); - // Create Buffer - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; - buffer_builder.add_data(data); - auto buffer = buffer_builder.Finish(); + if (operand.has_make_sparse() && operand.make_sparse()) + { + assert(not operand.has_sparsity()); + assert(operand.has_shape()); + + const int32_t dims_count = dims.size(); + std::vector traversal_order_vec; + std::vector format_vec; + for (int32_t o = 0; o < dims_count; ++o) + traversal_order_vec.push_back(o); + for (int32_t o = 0; o < dims_count - 1; ++o) + format_vec.push_back(sparsity::kTfLiteDimDense); + format_vec.push_back(sparsity::kTfLiteDimSparseCSR); + + if (operand.type() == tflchef::FLOAT32) + { + ::sparsity::FormatConverter converter(dims, traversal_order_vec, format_vec); + converter.DenseToSparse(reinterpret_cast(data_vec.data())); + const auto &sparse_data = converter.GetData(); + + std::vector sparse_uint8; + for (int c = 0; c < sparse_data.size(); ++c) + { + const float value = sparse_data.at(c); + const uint8_t *arr = reinterpret_cast(&value); + for (uint32_t b = 0; b < sizeof(float); ++b) + { + sparse_uint8.emplace_back(arr[b]); + } + } + auto data = flatbuffer_builder->CreateVector(sparse_uint8); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = buffer_vec.size(); + buffer_vec.emplace_back(buffer); + + // save SparsityParameters + auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec); + + // Create block map + std::vector block_map_vec{}; + auto block_map = flatbuffer_builder->CreateVector(block_map_vec); + + // Create dimension metadata + const auto &dim_metadata_src = converter.GetDimMetadata(); + auto dim_metadata_vec = + make_dim_metadata_vec(flatbuffer_builder.get(), dims_count, traversal_order_vec, + format_vec, dim_metadata_src); + auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order, + block_map, dim_metadata); + } + else if (operand.type() == tflchef::FLOAT16) + { + ::sparsity::FormatConverter converter(dims, traversal_order_vec, format_vec); + converter.DenseToSparse(reinterpret_cast(data_vec.data())); + const auto &sparse_data = converter.GetData(); + + std::vector sparse_uint8; + for (int c = 0; c < sparse_data.size(); ++c) + { + const uint16_t value = sparse_data.at(c); + const uint8_t *arr = reinterpret_cast(&value); + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) + { + sparse_uint8.emplace_back(arr[b]); + } + } + auto data = flatbuffer_builder->CreateVector(sparse_uint8); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = buffer_vec.size(); + buffer_vec.emplace_back(buffer); + + // save SparsityParameters + auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec); + + // Create block map + std::vector block_map_vec{}; + auto block_map = flatbuffer_builder->CreateVector(block_map_vec); + + // Create dimension metadata + const auto &dim_metadata_src = converter.GetDimMetadata(); + auto dim_metadata_vec = + make_dim_metadata_vec(flatbuffer_builder.get(), dims_count, traversal_order_vec, + format_vec, dim_metadata_src); + auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order, + block_map, dim_metadata); + } + else + { + throw std::runtime_error{"NYI: unsupported operand type"}; + } + } + else + { + auto data = flatbuffer_builder->CreateVector(data_vec); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); - // Update Buffer Index & Vector - buffer_index = buffer_vec.size(); - buffer_vec.emplace_back(buffer); + // Update Buffer Index & Vector + buffer_index = buffer_vec.size(); + buffer_vec.emplace_back(buffer); + } } else { @@ -384,8 +533,6 @@ template std::map cook_graph(const T &graph, quant_index = quant_builder.Finish(); } - flatbuffers::Offset sparsity_index; - if (operand.has_sparsity()) { const auto &sparsity = operand.sparsity(); diff --git a/compiler/tflchef/core/src/Op/Densify.cpp b/compiler/tflchef/core/src/Op/Densify.cpp new file mode 100644 index 0000000..63c4e20 --- /dev/null +++ b/compiler/tflchef/core/src/Op/Densify.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "Densify.h" + +flatbuffers::Offset DensifyChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + tflite::DensifyOptionsBuilder options_builder{fbb}; + + return options_builder.Finish().Union(); +} + +std::unique_ptr DensifyChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr{new DensifyChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/Densify.h b/compiler/tflchef/core/src/Op/Densify.h new file mode 100644 index 0000000..f6af693 --- /dev/null +++ b/compiler/tflchef/core/src/Op/Densify.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __OP_DENSIFY_H__ +#define __OP_DENSIFY_H__ + +#include "OpChef.h" + +class DensifyChef final : public OpChef +{ +public: + explicit DensifyChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_DENSIFY; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_DensifyOptions; } + + flatbuffers::Offset value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct DensifyChefFactory final : public OpChefFactory +{ + std::unique_ptr create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_DENSIFY_H__ diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def index beebd35..c19d00d 100644 --- a/compiler/tflchef/core/src/OpChef.def +++ b/compiler/tflchef/core/src/OpChef.def @@ -18,6 +18,7 @@ OP_CHEF(Ceil, CeilChefFactory) OP_CHEF(Concatenation, ConcatenationChefFactory) OP_CHEF(Conv2D, Conv2DChefFactory) OP_CHEF(Cos, CosChefFactory) +OP_CHEF(Densify, DensifyChefFactory) OP_CHEF(DepthToSpace, DepthToSpaceChefFactory) OP_CHEF(DepthwiseConv2D, DepthwiseConv2DChefFactory) OP_CHEF(Dequantize, DequantizeChefFactory) diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h index 159019a..3cd3be5 100644 --- a/compiler/tflchef/core/src/OpChefs.h +++ b/compiler/tflchef/core/src/OpChefs.h @@ -31,6 +31,7 @@ #include "Op/Concatenation.h" #include "Op/Conv2D.h" #include "Op/Cos.h" +#include "Op/Densify.h" #include "Op/DepthToSpace.h" #include "Op/DepthwiseConv2D.h" #include "Op/Dequantize.h" diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto index 1abefaf..da4b692 100644 --- a/compiler/tflchef/proto/tflchef.proto +++ b/compiler/tflchef/proto/tflchef.proto @@ -15,6 +15,7 @@ package tflchef; // This enum value corresponds to TensorType in TensorFlow Lite schema enum TensorType { FLOAT32 = 0; + FLOAT16 = 1; INT32 = 2; UINT8 = 3; INT64 = 4; @@ -88,6 +89,12 @@ message Operand { optional TensorSparsity sparsity = 6; optional bool is_variable = 7 [default = false]; optional ShapeSignature shape_signature = 8; + // 'make_sparse' is to tell tflchef to make a sparse tensor + // as filling 'TensorSparsity' by hand can be difficult + // for now, last dimension will be SPARSE_CSR + // ex) shape [2, 3, 4] will have + // TraversalOrder [0, 1, 2] with [DENSE, DENSE, SPARSE_CSR] + optional bool make_sparse = 9 [default = false]; } // This enum value corresponds to Padding in TensorFlow Lite schema @@ -534,6 +541,10 @@ message FakeQuantOptions { optional bool narrow_range = 4 [default = false]; } +message DensifyOptions { + // NONE +} + message Operation { optional string type = 1; repeated string input = 2; @@ -650,6 +661,7 @@ message Operation { optional AddNOptions add_n_options = 207; optional MatMulOptions matmul_options = 208; optional MaxPoolWithArgmaxOptions max_pool_with_argmax_options = 209; + optional DensifyOptions densify_options = 210; // NOTE if there are more than two options with same type of Options // use the number not listed in the above reserve list } diff --git a/compiler/tflchef/tests/make_sparse/test.recipe b/compiler/tflchef/tests/make_sparse/test.recipe new file mode 100644 index 0000000..15cc93a --- /dev/null +++ b/compiler/tflchef/tests/make_sparse/test.recipe @@ -0,0 +1,44 @@ +operand { + name: "in" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operand { + name: "sparse" + type: FLOAT32 + shape { dim: 4 dim: 4 } + filler { + tag: "explicit" + arg: "2" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "3" + } + make_sparse: true +} +operand { + name: "dense" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operand { + name: "out" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operation { + type: "Densify" + input: "sparse" + output: "dense" +} +operation { + type: "Add" + input: "in" + input: "dense" + output: "out" + add_options { + activation: NONE + } +} +input: "in" +output: "out" diff --git a/compiler/tflchef/tests/make_sparse_f16/test.recipe b/compiler/tflchef/tests/make_sparse_f16/test.recipe new file mode 100644 index 0000000..5977a1d --- /dev/null +++ b/compiler/tflchef/tests/make_sparse_f16/test.recipe @@ -0,0 +1,54 @@ +operand { + name: "in" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operand { + name: "sparse16" + type: FLOAT16 + shape { dim: 4 dim: 4 } + filler { + tag: "explicit" + arg: "2" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "0" + arg: "0" arg: "0" arg: "0" arg: "3" + } + make_sparse: true +} +operand { + name: "dense16" + type: FLOAT16 + shape { dim: 4 dim: 4 } +} +operand { + name: "dense32" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operand { + name: "out" + type: FLOAT32 + shape { dim: 4 dim: 4 } +} +operation { + type: "Densify" + input: "sparse16" + output: "dense16" +} +operation { + type: "Dequantize" + input: "dense16" + output: "dense32" +} +operation { + type: "Add" + input: "in" + input: "dense32" + output: "out" + add_options { + activation: NONE + } +} +input: "in" +output: "out" diff --git a/compiler/tflchef/tflite/CMakeLists.txt b/compiler/tflchef/tflite/CMakeLists.txt index 3c3352b..d9a20a2 100644 --- a/compiler/tflchef/tflite/CMakeLists.txt +++ b/compiler/tflchef/tflite/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_library(tflchef_tflite STATIC ${SOURCES}) target_include_directories(tflchef_tflite PUBLIC include) target_include_directories(tflchef_tflite PRIVATE src) +target_include_directories(tflchef_tflite PRIVATE src/Op/include) target_link_libraries(tflchef_tflite tflchef_proto) target_link_libraries(tflchef_tflite mio_tflite280) target_link_libraries(tflchef_tflite mio_tflite280_helper) diff --git a/compiler/tflchef/tflite/src/Convert.cpp b/compiler/tflchef/tflite/src/Convert.cpp index f47e51d..2429876 100644 --- a/compiler/tflchef/tflite/src/Convert.cpp +++ b/compiler/tflchef/tflite/src/Convert.cpp @@ -35,8 +35,9 @@ tflchef::TensorType as_tflchef_type(const tflite::TensorType type) return tflchef::BOOL; case tflite::TensorType_INT16: return tflchef::INT16; + case tflite::TensorType_FLOAT16: + return tflchef::FLOAT16; // TODO handle other types - // TensorType_FLOAT16 // TensorType_STRING // TensorType_COMPLEX64 default: diff --git a/compiler/tflchef/tflite/src/FillerHelper.cpp b/compiler/tflchef/tflite/src/FillerHelper.cpp index cf96d2e..1ac99ad 100644 --- a/compiler/tflchef/tflite/src/FillerHelper.cpp +++ b/compiler/tflchef/tflite/src/FillerHelper.cpp @@ -48,3 +48,18 @@ void fill_tensor_to_import(int32_t idx, TFliteImport *import) } } // namespace tflchef + +// helpers of common codes for filling inputs +namespace tflchef +{ + +void fill_two_inputs(const tflite::Operator *op, TFliteImport *import) +{ + const std::vector &inputs = as_index_vector(op->inputs()); + assert(inputs.size() == 2); + + fill_tensor_to_import(inputs[0], import); + fill_tensor_to_import(inputs[1], import); +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/FillerHelper.h b/compiler/tflchef/tflite/src/FillerHelper.h index 053a5c1..e96ae73 100644 --- a/compiler/tflchef/tflite/src/FillerHelper.h +++ b/compiler/tflchef/tflite/src/FillerHelper.h @@ -28,4 +28,12 @@ void fill_tensor_to_import(int32_t idx, TFliteImport *import); } // namespace tflchef +// helpers of common codes for filling inputs +namespace tflchef +{ + +void fill_two_inputs(const tflite::Operator *op, TFliteImport *import); + +} // namespace tflchef + #endif // __FILLER_HELPER_H__ diff --git a/compiler/tflchef/tflite/src/Op/Add.cpp b/compiler/tflchef/tflite/src/Op/Add.cpp index 3e880a6..23d3606 100644 --- a/compiler/tflchef/tflite/src/Op/Add.cpp +++ b/compiler/tflchef/tflite/src/Op/Add.cpp @@ -27,11 +27,7 @@ void TFliteOpAdd::filler(const tflite::Operator *op, TFliteImport *import, { // Add may have constant input - const std::vector &inputs = as_index_vector(op->inputs()); - assert(inputs.size() == 2); - - fill_tensor_to_import(inputs[0], import); - fill_tensor_to_import(inputs[1], import); + fill_two_inputs(op, import); } tflchef::Operation *TFliteOpAdd::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Maximum.cpp b/compiler/tflchef/tflite/src/Op/Maximum.cpp index d52caf0..65e4c2c 100644 --- a/compiler/tflchef/tflite/src/Op/Maximum.cpp +++ b/compiler/tflchef/tflite/src/Op/Maximum.cpp @@ -25,11 +25,7 @@ namespace tflchef void TFliteOpMaximum::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { - const std::vector &inputs = as_index_vector(op->inputs()); - assert(inputs.size() == 2); - - fill_tensor_to_import(inputs[0], import); - fill_tensor_to_import(inputs[1], import); + fill_two_inputs(op, import); } tflchef::Operation *TFliteOpMaximum::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Minimum.cpp b/compiler/tflchef/tflite/src/Op/Minimum.cpp index 6440f1d..b4d255c 100644 --- a/compiler/tflchef/tflite/src/Op/Minimum.cpp +++ b/compiler/tflchef/tflite/src/Op/Minimum.cpp @@ -25,11 +25,7 @@ namespace tflchef void TFliteOpMinimum::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { - const std::vector &inputs = as_index_vector(op->inputs()); - assert(inputs.size() == 2); - - fill_tensor_to_import(inputs[0], import); - fill_tensor_to_import(inputs[1], import); + fill_two_inputs(op, import); } tflchef::Operation *TFliteOpMinimum::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Mul.cpp b/compiler/tflchef/tflite/src/Op/Mul.cpp index 9faa4ac..1145ff7 100644 --- a/compiler/tflchef/tflite/src/Op/Mul.cpp +++ b/compiler/tflchef/tflite/src/Op/Mul.cpp @@ -27,11 +27,7 @@ void TFliteOpMul::filler(const tflite::Operator *op, TFliteImport *import, { // Mul may have constant input - const std::vector &inputs = as_index_vector(op->inputs()); - assert(inputs.size() == 2); - - fill_tensor_to_import(inputs[0], import); - fill_tensor_to_import(inputs[1], import); + fill_two_inputs(op, import); } tflchef::Operation *TFliteOpMul::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp index ad99219..4f096ce 100644 --- a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp +++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp @@ -38,7 +38,7 @@ void TFliteOpNonMaxSuppressionV4::filler(const tflite::Operator *op, TFliteImpor for (int32_t index = 2; index < 5; ++index) { - fill_tensor_to_import(index, import); + fill_tensor_to_import(inputs[index], import); } } diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp index db7f4c9..332cba0 100644 --- a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp +++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp @@ -41,7 +41,7 @@ void TFliteOpNonMaxSuppressionV5::filler(const tflite::Operator *op, TFliteImpor for (int32_t index = 2; index < 6; ++index) { - fill_tensor_to_import(index, import); + fill_tensor_to_import(inputs[index], import); } } diff --git a/compiler/tflchef/tflite/src/Op/PadV2.cpp b/compiler/tflchef/tflite/src/Op/PadV2.cpp index 0b1c9f3..a6b657f 100644 --- a/compiler/tflchef/tflite/src/Op/PadV2.cpp +++ b/compiler/tflchef/tflite/src/Op/PadV2.cpp @@ -16,6 +16,7 @@ #include "PadV2.h" +#include "Convert.h" #include "FillerHelper.h" namespace tflchef @@ -24,9 +25,11 @@ namespace tflchef void TFliteOpPadV2::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { + const std::vector &inputs = as_index_vector(op->inputs()); + // Filler for paddings and constant_values - fill_tensor_to_import(1, import); - fill_tensor_to_import(2, import); + fill_tensor_to_import(inputs[1], import); + fill_tensor_to_import(inputs[2], import); } tflchef::Operation *TFliteOpPadV2::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/ScatterNd.cpp b/compiler/tflchef/tflite/src/Op/ScatterNd.cpp index 548a09a..ec09a69 100644 --- a/compiler/tflchef/tflite/src/Op/ScatterNd.cpp +++ b/compiler/tflchef/tflite/src/Op/ScatterNd.cpp @@ -25,9 +25,11 @@ namespace tflchef void TFliteOpScatterNd::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { + const std::vector &inputs = as_index_vector(op->inputs()); + // Filler for indices and shape - fill_tensor_to_import(0, import); - fill_tensor_to_import(2, import); + fill_tensor_to_import(inputs[0], import); + fill_tensor_to_import(inputs[2], import); } tflchef::Operation *TFliteOpScatterNd::build(const tflite::Operator *, TFliteImport *, diff --git a/compiler/tflchef/tflite/src/Op/SegmentSum.cpp b/compiler/tflchef/tflite/src/Op/SegmentSum.cpp index a975ca4..bc45a94 100644 --- a/compiler/tflchef/tflite/src/Op/SegmentSum.cpp +++ b/compiler/tflchef/tflite/src/Op/SegmentSum.cpp @@ -16,6 +16,7 @@ #include "SegmentSum.h" +#include "Convert.h" #include "FillerHelper.h" namespace tflchef @@ -24,8 +25,10 @@ namespace tflchef void TFliteOpSegmentSum::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { - // Filler for indices and shape - fill_tensor_to_import(1, import); + const std::vector &inputs = as_index_vector(op->inputs()); + + // Filler for segment_ids + fill_tensor_to_import(inputs[1], import); } tflchef::Operation *TFliteOpSegmentSum::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Sub.cpp b/compiler/tflchef/tflite/src/Op/Sub.cpp index 0a08bbf..584be0a 100644 --- a/compiler/tflchef/tflite/src/Op/Sub.cpp +++ b/compiler/tflchef/tflite/src/Op/Sub.cpp @@ -27,11 +27,7 @@ void TFliteOpSub::filler(const tflite::Operator *op, TFliteImport *import, { // Sub may have constant input - const std::vector &inputs = as_index_vector(op->inputs()); - assert(inputs.size() == 2); - - fill_tensor_to_import(inputs[0], import); - fill_tensor_to_import(inputs[1], import); + fill_two_inputs(op, import); } tflchef::Operation *TFliteOpSub::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Abs.h b/compiler/tflchef/tflite/src/Op/include/Abs.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Abs.h rename to compiler/tflchef/tflite/src/Op/include/Abs.h diff --git a/compiler/tflchef/tflite/src/Op/Add.h b/compiler/tflchef/tflite/src/Op/include/Add.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Add.h rename to compiler/tflchef/tflite/src/Op/include/Add.h diff --git a/compiler/tflchef/tflite/src/Op/AddN.h b/compiler/tflchef/tflite/src/Op/include/AddN.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/AddN.h rename to compiler/tflchef/tflite/src/Op/include/AddN.h diff --git a/compiler/tflchef/tflite/src/Op/ArgMax.h b/compiler/tflchef/tflite/src/Op/include/ArgMax.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ArgMax.h rename to compiler/tflchef/tflite/src/Op/include/ArgMax.h diff --git a/compiler/tflchef/tflite/src/Op/ArgMin.h b/compiler/tflchef/tflite/src/Op/include/ArgMin.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ArgMin.h rename to compiler/tflchef/tflite/src/Op/include/ArgMin.h diff --git a/compiler/tflchef/tflite/src/Op/AveragePool2D.h b/compiler/tflchef/tflite/src/Op/include/AveragePool2D.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/AveragePool2D.h rename to compiler/tflchef/tflite/src/Op/include/AveragePool2D.h diff --git a/compiler/tflchef/tflite/src/Op/BatchMatMul.h b/compiler/tflchef/tflite/src/Op/include/BatchMatMul.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/BatchMatMul.h rename to compiler/tflchef/tflite/src/Op/include/BatchMatMul.h diff --git a/compiler/tflchef/tflite/src/Op/BatchToSpaceND.h b/compiler/tflchef/tflite/src/Op/include/BatchToSpaceND.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/BatchToSpaceND.h rename to compiler/tflchef/tflite/src/Op/include/BatchToSpaceND.h diff --git a/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h b/compiler/tflchef/tflite/src/Op/include/BidirectionalSequenceLSTM.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h rename to compiler/tflchef/tflite/src/Op/include/BidirectionalSequenceLSTM.h diff --git a/compiler/tflchef/tflite/src/Op/Cast.h b/compiler/tflchef/tflite/src/Op/include/Cast.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Cast.h rename to compiler/tflchef/tflite/src/Op/include/Cast.h diff --git a/compiler/tflchef/tflite/src/Op/Ceil.h b/compiler/tflchef/tflite/src/Op/include/Ceil.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Ceil.h rename to compiler/tflchef/tflite/src/Op/include/Ceil.h diff --git a/compiler/tflchef/tflite/src/Op/Concatenation.h b/compiler/tflchef/tflite/src/Op/include/Concatenation.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Concatenation.h rename to compiler/tflchef/tflite/src/Op/include/Concatenation.h diff --git a/compiler/tflchef/tflite/src/Op/Conv2D.h b/compiler/tflchef/tflite/src/Op/include/Conv2D.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Conv2D.h rename to compiler/tflchef/tflite/src/Op/include/Conv2D.h diff --git a/compiler/tflchef/tflite/src/Op/Cos.h b/compiler/tflchef/tflite/src/Op/include/Cos.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Cos.h rename to compiler/tflchef/tflite/src/Op/include/Cos.h diff --git a/compiler/tflchef/tflite/src/Op/DepthToSpace.h b/compiler/tflchef/tflite/src/Op/include/DepthToSpace.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/DepthToSpace.h rename to compiler/tflchef/tflite/src/Op/include/DepthToSpace.h diff --git a/compiler/tflchef/tflite/src/Op/DepthwiseConv2D.h b/compiler/tflchef/tflite/src/Op/include/DepthwiseConv2D.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/DepthwiseConv2D.h rename to compiler/tflchef/tflite/src/Op/include/DepthwiseConv2D.h diff --git a/compiler/tflchef/tflite/src/Op/Dequantize.h b/compiler/tflchef/tflite/src/Op/include/Dequantize.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Dequantize.h rename to compiler/tflchef/tflite/src/Op/include/Dequantize.h diff --git a/compiler/tflchef/tflite/src/Op/Div.h b/compiler/tflchef/tflite/src/Op/include/Div.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Div.h rename to compiler/tflchef/tflite/src/Op/include/Div.h diff --git a/compiler/tflchef/tflite/src/Op/ELU.h b/compiler/tflchef/tflite/src/Op/include/ELU.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ELU.h rename to compiler/tflchef/tflite/src/Op/include/ELU.h diff --git a/compiler/tflchef/tflite/src/Op/Equal.h b/compiler/tflchef/tflite/src/Op/include/Equal.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Equal.h rename to compiler/tflchef/tflite/src/Op/include/Equal.h diff --git a/compiler/tflchef/tflite/src/Op/Exp.h b/compiler/tflchef/tflite/src/Op/include/Exp.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Exp.h rename to compiler/tflchef/tflite/src/Op/include/Exp.h diff --git a/compiler/tflchef/tflite/src/Op/ExpandDims.h b/compiler/tflchef/tflite/src/Op/include/ExpandDims.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ExpandDims.h rename to compiler/tflchef/tflite/src/Op/include/ExpandDims.h diff --git a/compiler/tflchef/tflite/src/Op/FakeQuant.h b/compiler/tflchef/tflite/src/Op/include/FakeQuant.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/FakeQuant.h rename to compiler/tflchef/tflite/src/Op/include/FakeQuant.h diff --git a/compiler/tflchef/tflite/src/Op/Fill.h b/compiler/tflchef/tflite/src/Op/include/Fill.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Fill.h rename to compiler/tflchef/tflite/src/Op/include/Fill.h diff --git a/compiler/tflchef/tflite/src/Op/Floor.h b/compiler/tflchef/tflite/src/Op/include/Floor.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Floor.h rename to compiler/tflchef/tflite/src/Op/include/Floor.h diff --git a/compiler/tflchef/tflite/src/Op/FloorDiv.h b/compiler/tflchef/tflite/src/Op/include/FloorDiv.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/FloorDiv.h rename to compiler/tflchef/tflite/src/Op/include/FloorDiv.h diff --git a/compiler/tflchef/tflite/src/Op/FloorMod.h b/compiler/tflchef/tflite/src/Op/include/FloorMod.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/FloorMod.h rename to compiler/tflchef/tflite/src/Op/include/FloorMod.h diff --git a/compiler/tflchef/tflite/src/Op/FullyConnected.h b/compiler/tflchef/tflite/src/Op/include/FullyConnected.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/FullyConnected.h rename to compiler/tflchef/tflite/src/Op/include/FullyConnected.h diff --git a/compiler/tflchef/tflite/src/Op/Gather.h b/compiler/tflchef/tflite/src/Op/include/Gather.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Gather.h rename to compiler/tflchef/tflite/src/Op/include/Gather.h diff --git a/compiler/tflchef/tflite/src/Op/GatherNd.h b/compiler/tflchef/tflite/src/Op/include/GatherNd.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/GatherNd.h rename to compiler/tflchef/tflite/src/Op/include/GatherNd.h diff --git a/compiler/tflchef/tflite/src/Op/Greater.h b/compiler/tflchef/tflite/src/Op/include/Greater.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Greater.h rename to compiler/tflchef/tflite/src/Op/include/Greater.h diff --git a/compiler/tflchef/tflite/src/Op/GreaterEqual.h b/compiler/tflchef/tflite/src/Op/include/GreaterEqual.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/GreaterEqual.h rename to compiler/tflchef/tflite/src/Op/include/GreaterEqual.h diff --git a/compiler/tflchef/tflite/src/Op/L2Normalize.h b/compiler/tflchef/tflite/src/Op/include/L2Normalize.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/L2Normalize.h rename to compiler/tflchef/tflite/src/Op/include/L2Normalize.h diff --git a/compiler/tflchef/tflite/src/Op/L2Pool2D.h b/compiler/tflchef/tflite/src/Op/include/L2Pool2D.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/L2Pool2D.h rename to compiler/tflchef/tflite/src/Op/include/L2Pool2D.h diff --git a/compiler/tflchef/tflite/src/Op/LeakyRelu.h b/compiler/tflchef/tflite/src/Op/include/LeakyRelu.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LeakyRelu.h rename to compiler/tflchef/tflite/src/Op/include/LeakyRelu.h diff --git a/compiler/tflchef/tflite/src/Op/Less.h b/compiler/tflchef/tflite/src/Op/include/Less.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Less.h rename to compiler/tflchef/tflite/src/Op/include/Less.h diff --git a/compiler/tflchef/tflite/src/Op/LessEqual.h b/compiler/tflchef/tflite/src/Op/include/LessEqual.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LessEqual.h rename to compiler/tflchef/tflite/src/Op/include/LessEqual.h diff --git a/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h b/compiler/tflchef/tflite/src/Op/include/LocalResponseNormalization.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h rename to compiler/tflchef/tflite/src/Op/include/LocalResponseNormalization.h diff --git a/compiler/tflchef/tflite/src/Op/Log.h b/compiler/tflchef/tflite/src/Op/include/Log.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Log.h rename to compiler/tflchef/tflite/src/Op/include/Log.h diff --git a/compiler/tflchef/tflite/src/Op/LogSoftmax.h b/compiler/tflchef/tflite/src/Op/include/LogSoftmax.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LogSoftmax.h rename to compiler/tflchef/tflite/src/Op/include/LogSoftmax.h diff --git a/compiler/tflchef/tflite/src/Op/LogicalAnd.h b/compiler/tflchef/tflite/src/Op/include/LogicalAnd.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LogicalAnd.h rename to compiler/tflchef/tflite/src/Op/include/LogicalAnd.h diff --git a/compiler/tflchef/tflite/src/Op/LogicalNot.h b/compiler/tflchef/tflite/src/Op/include/LogicalNot.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LogicalNot.h rename to compiler/tflchef/tflite/src/Op/include/LogicalNot.h diff --git a/compiler/tflchef/tflite/src/Op/LogicalOr.h b/compiler/tflchef/tflite/src/Op/include/LogicalOr.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/LogicalOr.h rename to compiler/tflchef/tflite/src/Op/include/LogicalOr.h diff --git a/compiler/tflchef/tflite/src/Op/Logistic.h b/compiler/tflchef/tflite/src/Op/include/Logistic.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Logistic.h rename to compiler/tflchef/tflite/src/Op/include/Logistic.h diff --git a/compiler/tflchef/tflite/src/Op/MatrixDiag.h b/compiler/tflchef/tflite/src/Op/include/MatrixDiag.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/MatrixDiag.h rename to compiler/tflchef/tflite/src/Op/include/MatrixDiag.h diff --git a/compiler/tflchef/tflite/src/Op/MatrixSetDiag.h b/compiler/tflchef/tflite/src/Op/include/MatrixSetDiag.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/MatrixSetDiag.h rename to compiler/tflchef/tflite/src/Op/include/MatrixSetDiag.h diff --git a/compiler/tflchef/tflite/src/Op/MaxPool2D.h b/compiler/tflchef/tflite/src/Op/include/MaxPool2D.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/MaxPool2D.h rename to compiler/tflchef/tflite/src/Op/include/MaxPool2D.h diff --git a/compiler/tflchef/tflite/src/Op/Maximum.h b/compiler/tflchef/tflite/src/Op/include/Maximum.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Maximum.h rename to compiler/tflchef/tflite/src/Op/include/Maximum.h diff --git a/compiler/tflchef/tflite/src/Op/Mean.h b/compiler/tflchef/tflite/src/Op/include/Mean.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Mean.h rename to compiler/tflchef/tflite/src/Op/include/Mean.h diff --git a/compiler/tflchef/tflite/src/Op/Minimum.h b/compiler/tflchef/tflite/src/Op/include/Minimum.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Minimum.h rename to compiler/tflchef/tflite/src/Op/include/Minimum.h diff --git a/compiler/tflchef/tflite/src/Op/MirrorPad.h b/compiler/tflchef/tflite/src/Op/include/MirrorPad.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/MirrorPad.h rename to compiler/tflchef/tflite/src/Op/include/MirrorPad.h diff --git a/compiler/tflchef/tflite/src/Op/Mul.h b/compiler/tflchef/tflite/src/Op/include/Mul.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Mul.h rename to compiler/tflchef/tflite/src/Op/include/Mul.h diff --git a/compiler/tflchef/tflite/src/Op/Neg.h b/compiler/tflchef/tflite/src/Op/include/Neg.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Neg.h rename to compiler/tflchef/tflite/src/Op/include/Neg.h diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h b/compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV4.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h rename to compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV4.h diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h b/compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV5.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h rename to compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV5.h diff --git a/compiler/tflchef/tflite/src/Op/NotEqual.h b/compiler/tflchef/tflite/src/Op/include/NotEqual.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/NotEqual.h rename to compiler/tflchef/tflite/src/Op/include/NotEqual.h diff --git a/compiler/tflchef/tflite/src/Op/OneHot.h b/compiler/tflchef/tflite/src/Op/include/OneHot.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/OneHot.h rename to compiler/tflchef/tflite/src/Op/include/OneHot.h diff --git a/compiler/tflchef/tflite/src/Op/PRelu.h b/compiler/tflchef/tflite/src/Op/include/PRelu.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/PRelu.h rename to compiler/tflchef/tflite/src/Op/include/PRelu.h diff --git a/compiler/tflchef/tflite/src/Op/Pack.h b/compiler/tflchef/tflite/src/Op/include/Pack.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Pack.h rename to compiler/tflchef/tflite/src/Op/include/Pack.h diff --git a/compiler/tflchef/tflite/src/Op/Pad.h b/compiler/tflchef/tflite/src/Op/include/Pad.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Pad.h rename to compiler/tflchef/tflite/src/Op/include/Pad.h diff --git a/compiler/tflchef/tflite/src/Op/PadV2.h b/compiler/tflchef/tflite/src/Op/include/PadV2.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/PadV2.h rename to compiler/tflchef/tflite/src/Op/include/PadV2.h diff --git a/compiler/tflchef/tflite/src/Op/Pow.h b/compiler/tflchef/tflite/src/Op/include/Pow.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Pow.h rename to compiler/tflchef/tflite/src/Op/include/Pow.h diff --git a/compiler/tflchef/tflite/src/Op/Quantize.h b/compiler/tflchef/tflite/src/Op/include/Quantize.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Quantize.h rename to compiler/tflchef/tflite/src/Op/include/Quantize.h diff --git a/compiler/tflchef/tflite/src/Op/Range.h b/compiler/tflchef/tflite/src/Op/include/Range.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Range.h rename to compiler/tflchef/tflite/src/Op/include/Range.h diff --git a/compiler/tflchef/tflite/src/Op/Rank.h b/compiler/tflchef/tflite/src/Op/include/Rank.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Rank.h rename to compiler/tflchef/tflite/src/Op/include/Rank.h diff --git a/compiler/tflchef/tflite/src/Op/ReLU.h b/compiler/tflchef/tflite/src/Op/include/ReLU.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReLU.h rename to compiler/tflchef/tflite/src/Op/include/ReLU.h diff --git a/compiler/tflchef/tflite/src/Op/ReLU6.h b/compiler/tflchef/tflite/src/Op/include/ReLU6.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReLU6.h rename to compiler/tflchef/tflite/src/Op/include/ReLU6.h diff --git a/compiler/tflchef/tflite/src/Op/ReLUN1To1.h b/compiler/tflchef/tflite/src/Op/include/ReLUN1To1.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReLUN1To1.h rename to compiler/tflchef/tflite/src/Op/include/ReLUN1To1.h diff --git a/compiler/tflchef/tflite/src/Op/ReduceAny.h b/compiler/tflchef/tflite/src/Op/include/ReduceAny.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReduceAny.h rename to compiler/tflchef/tflite/src/Op/include/ReduceAny.h diff --git a/compiler/tflchef/tflite/src/Op/ReduceMax.h b/compiler/tflchef/tflite/src/Op/include/ReduceMax.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReduceMax.h rename to compiler/tflchef/tflite/src/Op/include/ReduceMax.h diff --git a/compiler/tflchef/tflite/src/Op/ReduceMin.h b/compiler/tflchef/tflite/src/Op/include/ReduceMin.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReduceMin.h rename to compiler/tflchef/tflite/src/Op/include/ReduceMin.h diff --git a/compiler/tflchef/tflite/src/Op/ReduceProd.h b/compiler/tflchef/tflite/src/Op/include/ReduceProd.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReduceProd.h rename to compiler/tflchef/tflite/src/Op/include/ReduceProd.h diff --git a/compiler/tflchef/tflite/src/Op/Reshape.h b/compiler/tflchef/tflite/src/Op/include/Reshape.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Reshape.h rename to compiler/tflchef/tflite/src/Op/include/Reshape.h diff --git a/compiler/tflchef/tflite/src/Op/ResizeBilinear.h b/compiler/tflchef/tflite/src/Op/include/ResizeBilinear.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ResizeBilinear.h rename to compiler/tflchef/tflite/src/Op/include/ResizeBilinear.h diff --git a/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h b/compiler/tflchef/tflite/src/Op/include/ResizeNearestNeighbor.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h rename to compiler/tflchef/tflite/src/Op/include/ResizeNearestNeighbor.h diff --git a/compiler/tflchef/tflite/src/Op/ReverseSequence.h b/compiler/tflchef/tflite/src/Op/include/ReverseSequence.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReverseSequence.h rename to compiler/tflchef/tflite/src/Op/include/ReverseSequence.h diff --git a/compiler/tflchef/tflite/src/Op/ReverseV2.h b/compiler/tflchef/tflite/src/Op/include/ReverseV2.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ReverseV2.h rename to compiler/tflchef/tflite/src/Op/include/ReverseV2.h diff --git a/compiler/tflchef/tflite/src/Op/Round.h b/compiler/tflchef/tflite/src/Op/include/Round.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Round.h rename to compiler/tflchef/tflite/src/Op/include/Round.h diff --git a/compiler/tflchef/tflite/src/Op/Rsqrt.h b/compiler/tflchef/tflite/src/Op/include/Rsqrt.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Rsqrt.h rename to compiler/tflchef/tflite/src/Op/include/Rsqrt.h diff --git a/compiler/tflchef/tflite/src/Op/SVDF.h b/compiler/tflchef/tflite/src/Op/include/SVDF.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SVDF.h rename to compiler/tflchef/tflite/src/Op/include/SVDF.h diff --git a/compiler/tflchef/tflite/src/Op/ScatterNd.h b/compiler/tflchef/tflite/src/Op/include/ScatterNd.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ScatterNd.h rename to compiler/tflchef/tflite/src/Op/include/ScatterNd.h diff --git a/compiler/tflchef/tflite/src/Op/SegmentSum.h b/compiler/tflchef/tflite/src/Op/include/SegmentSum.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SegmentSum.h rename to compiler/tflchef/tflite/src/Op/include/SegmentSum.h diff --git a/compiler/tflchef/tflite/src/Op/Select.h b/compiler/tflchef/tflite/src/Op/include/Select.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Select.h rename to compiler/tflchef/tflite/src/Op/include/Select.h diff --git a/compiler/tflchef/tflite/src/Op/SelectV2.h b/compiler/tflchef/tflite/src/Op/include/SelectV2.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SelectV2.h rename to compiler/tflchef/tflite/src/Op/include/SelectV2.h diff --git a/compiler/tflchef/tflite/src/Op/Shape.h b/compiler/tflchef/tflite/src/Op/include/Shape.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Shape.h rename to compiler/tflchef/tflite/src/Op/include/Shape.h diff --git a/compiler/tflchef/tflite/src/Op/Sin.h b/compiler/tflchef/tflite/src/Op/include/Sin.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Sin.h rename to compiler/tflchef/tflite/src/Op/include/Sin.h diff --git a/compiler/tflchef/tflite/src/Op/Slice.h b/compiler/tflchef/tflite/src/Op/include/Slice.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Slice.h rename to compiler/tflchef/tflite/src/Op/include/Slice.h diff --git a/compiler/tflchef/tflite/src/Op/Softmax.h b/compiler/tflchef/tflite/src/Op/include/Softmax.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Softmax.h rename to compiler/tflchef/tflite/src/Op/include/Softmax.h diff --git a/compiler/tflchef/tflite/src/Op/SpaceToBatchND.h b/compiler/tflchef/tflite/src/Op/include/SpaceToBatchND.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SpaceToBatchND.h rename to compiler/tflchef/tflite/src/Op/include/SpaceToBatchND.h diff --git a/compiler/tflchef/tflite/src/Op/SpaceToDepth.h b/compiler/tflchef/tflite/src/Op/include/SpaceToDepth.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SpaceToDepth.h rename to compiler/tflchef/tflite/src/Op/include/SpaceToDepth.h diff --git a/compiler/tflchef/tflite/src/Op/SparseToDense.h b/compiler/tflchef/tflite/src/Op/include/SparseToDense.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SparseToDense.h rename to compiler/tflchef/tflite/src/Op/include/SparseToDense.h diff --git a/compiler/tflchef/tflite/src/Op/Split.h b/compiler/tflchef/tflite/src/Op/include/Split.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Split.h rename to compiler/tflchef/tflite/src/Op/include/Split.h diff --git a/compiler/tflchef/tflite/src/Op/SplitV.h b/compiler/tflchef/tflite/src/Op/include/SplitV.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SplitV.h rename to compiler/tflchef/tflite/src/Op/include/SplitV.h diff --git a/compiler/tflchef/tflite/src/Op/Sqrt.h b/compiler/tflchef/tflite/src/Op/include/Sqrt.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Sqrt.h rename to compiler/tflchef/tflite/src/Op/include/Sqrt.h diff --git a/compiler/tflchef/tflite/src/Op/Square.h b/compiler/tflchef/tflite/src/Op/include/Square.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Square.h rename to compiler/tflchef/tflite/src/Op/include/Square.h diff --git a/compiler/tflchef/tflite/src/Op/SquaredDifference.h b/compiler/tflchef/tflite/src/Op/include/SquaredDifference.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/SquaredDifference.h rename to compiler/tflchef/tflite/src/Op/include/SquaredDifference.h diff --git a/compiler/tflchef/tflite/src/Op/Squeeze.h b/compiler/tflchef/tflite/src/Op/include/Squeeze.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Squeeze.h rename to compiler/tflchef/tflite/src/Op/include/Squeeze.h diff --git a/compiler/tflchef/tflite/src/Op/StridedSlice.h b/compiler/tflchef/tflite/src/Op/include/StridedSlice.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/StridedSlice.h rename to compiler/tflchef/tflite/src/Op/include/StridedSlice.h diff --git a/compiler/tflchef/tflite/src/Op/Sub.h b/compiler/tflchef/tflite/src/Op/include/Sub.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Sub.h rename to compiler/tflchef/tflite/src/Op/include/Sub.h diff --git a/compiler/tflchef/tflite/src/Op/Sum.h b/compiler/tflchef/tflite/src/Op/include/Sum.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Sum.h rename to compiler/tflchef/tflite/src/Op/include/Sum.h diff --git a/compiler/tflchef/tflite/src/Op/Tanh.h b/compiler/tflchef/tflite/src/Op/include/Tanh.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Tanh.h rename to compiler/tflchef/tflite/src/Op/include/Tanh.h diff --git a/compiler/tflchef/tflite/src/Op/Tile.h b/compiler/tflchef/tflite/src/Op/include/Tile.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Tile.h rename to compiler/tflchef/tflite/src/Op/include/Tile.h diff --git a/compiler/tflchef/tflite/src/Op/TopKV2.h b/compiler/tflchef/tflite/src/Op/include/TopKV2.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/TopKV2.h rename to compiler/tflchef/tflite/src/Op/include/TopKV2.h diff --git a/compiler/tflchef/tflite/src/Op/Transpose.h b/compiler/tflchef/tflite/src/Op/include/Transpose.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Transpose.h rename to compiler/tflchef/tflite/src/Op/include/Transpose.h diff --git a/compiler/tflchef/tflite/src/Op/TransposeConv.h b/compiler/tflchef/tflite/src/Op/include/TransposeConv.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/TransposeConv.h rename to compiler/tflchef/tflite/src/Op/include/TransposeConv.h diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/tflite/src/Op/include/UnidirectionalSequenceLSTM.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h rename to compiler/tflchef/tflite/src/Op/include/UnidirectionalSequenceLSTM.h diff --git a/compiler/tflchef/tflite/src/Op/Unique.h b/compiler/tflchef/tflite/src/Op/include/Unique.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Unique.h rename to compiler/tflchef/tflite/src/Op/include/Unique.h diff --git a/compiler/tflchef/tflite/src/Op/Unpack.h b/compiler/tflchef/tflite/src/Op/include/Unpack.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Unpack.h rename to compiler/tflchef/tflite/src/Op/include/Unpack.h diff --git a/compiler/tflchef/tflite/src/Op/Where.h b/compiler/tflchef/tflite/src/Op/include/Where.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/Where.h rename to compiler/tflchef/tflite/src/Op/include/Where.h diff --git a/compiler/tflchef/tflite/src/Op/ZerosLike.h b/compiler/tflchef/tflite/src/Op/include/ZerosLike.h similarity index 100% rename from compiler/tflchef/tflite/src/Op/ZerosLike.h rename to compiler/tflchef/tflite/src/Op/include/ZerosLike.h diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h index b38b35a..1b9d420 100644 --- a/compiler/tflchef/tflite/src/TFliteOpChefs.h +++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h @@ -18,115 +18,115 @@ #define __TFLITE_OP_CHEFS_H__ // In alphabet order -#include "Op/Abs.h" -#include "Op/Add.h" -#include "Op/AddN.h" -#include "Op/ArgMax.h" -#include "Op/ArgMin.h" -#include "Op/AveragePool2D.h" -#include "Op/BatchMatMul.h" -#include "Op/BatchToSpaceND.h" -#include "Op/BidirectionalSequenceLSTM.h" -#include "Op/Cast.h" -#include "Op/Ceil.h" -#include "Op/Concatenation.h" -#include "Op/Conv2D.h" -#include "Op/Cos.h" -#include "Op/DepthToSpace.h" -#include "Op/DepthwiseConv2D.h" -#include "Op/Dequantize.h" -#include "Op/Div.h" -#include "Op/ELU.h" -#include "Op/Equal.h" -#include "Op/Exp.h" -#include "Op/ExpandDims.h" -#include "Op/FakeQuant.h" -#include "Op/Fill.h" -#include "Op/Floor.h" -#include "Op/FloorDiv.h" -#include "Op/FloorMod.h" -#include "Op/FullyConnected.h" -#include "Op/Gather.h" -#include "Op/GatherNd.h" -#include "Op/Greater.h" -#include "Op/GreaterEqual.h" -#include "Op/L2Normalize.h" -#include "Op/L2Pool2D.h" -#include "Op/LeakyRelu.h" -#include "Op/Less.h" -#include "Op/LessEqual.h" -#include "Op/LocalResponseNormalization.h" -#include "Op/Log.h" -#include "Op/LogicalAnd.h" -#include "Op/LogicalNot.h" -#include "Op/LogicalOr.h" -#include "Op/Logistic.h" -#include "Op/LogSoftmax.h" -#include "Op/MatrixDiag.h" -#include "Op/MatrixSetDiag.h" -#include "Op/Maximum.h" -#include "Op/MaxPool2D.h" -#include "Op/Mean.h" -#include "Op/Minimum.h" -#include "Op/MirrorPad.h" -#include "Op/Mul.h" -#include "Op/Neg.h" -#include "Op/NonMaxSuppressionV4.h" -#include "Op/NonMaxSuppressionV5.h" -#include "Op/NotEqual.h" -#include "Op/OneHot.h" -#include "Op/Pack.h" -#include "Op/Pad.h" -#include "Op/PadV2.h" -#include "Op/Pow.h" -#include "Op/PRelu.h" -#include "Op/Quantize.h" -#include "Op/Range.h" -#include "Op/Rank.h" -#include "Op/ReduceAny.h" -#include "Op/ReduceMax.h" -#include "Op/ReduceMin.h" -#include "Op/ReduceProd.h" -#include "Op/ReLU.h" -#include "Op/ReLU6.h" -#include "Op/ReLUN1To1.h" -#include "Op/Reshape.h" -#include "Op/ResizeBilinear.h" -#include "Op/ResizeNearestNeighbor.h" -#include "Op/ReverseSequence.h" -#include "Op/ReverseV2.h" -#include "Op/Round.h" -#include "Op/Rsqrt.h" -#include "Op/ScatterNd.h" -#include "Op/SegmentSum.h" -#include "Op/Select.h" -#include "Op/SelectV2.h" -#include "Op/Shape.h" -#include "Op/Sin.h" -#include "Op/Slice.h" -#include "Op/Softmax.h" -#include "Op/SpaceToBatchND.h" -#include "Op/SpaceToDepth.h" -#include "Op/SparseToDense.h" -#include "Op/Split.h" -#include "Op/SplitV.h" -#include "Op/Sqrt.h" -#include "Op/Square.h" -#include "Op/SquaredDifference.h" -#include "Op/Squeeze.h" -#include "Op/StridedSlice.h" -#include "Op/Sub.h" -#include "Op/Sum.h" -#include "Op/SVDF.h" -#include "Op/Tanh.h" -#include "Op/Tile.h" -#include "Op/TopKV2.h" -#include "Op/Transpose.h" -#include "Op/TransposeConv.h" -#include "Op/UnidirectionalSequenceLSTM.h" -#include "Op/Unique.h" -#include "Op/Unpack.h" -#include "Op/Where.h" -#include "Op/ZerosLike.h" +#include "Op/include/Abs.h" +#include "Op/include/Add.h" +#include "Op/include/AddN.h" +#include "Op/include/ArgMax.h" +#include "Op/include/ArgMin.h" +#include "Op/include/AveragePool2D.h" +#include "Op/include/BatchMatMul.h" +#include "Op/include/BatchToSpaceND.h" +#include "Op/include/BidirectionalSequenceLSTM.h" +#include "Op/include/Cast.h" +#include "Op/include/Ceil.h" +#include "Op/include/Concatenation.h" +#include "Op/include/Conv2D.h" +#include "Op/include/Cos.h" +#include "Op/include/DepthToSpace.h" +#include "Op/include/DepthwiseConv2D.h" +#include "Op/include/Dequantize.h" +#include "Op/include/Div.h" +#include "Op/include/ELU.h" +#include "Op/include/Equal.h" +#include "Op/include/Exp.h" +#include "Op/include/ExpandDims.h" +#include "Op/include/FakeQuant.h" +#include "Op/include/Fill.h" +#include "Op/include/Floor.h" +#include "Op/include/FloorDiv.h" +#include "Op/include/FloorMod.h" +#include "Op/include/FullyConnected.h" +#include "Op/include/Gather.h" +#include "Op/include/GatherNd.h" +#include "Op/include/Greater.h" +#include "Op/include/GreaterEqual.h" +#include "Op/include/L2Normalize.h" +#include "Op/include/L2Pool2D.h" +#include "Op/include/LeakyRelu.h" +#include "Op/include/Less.h" +#include "Op/include/LessEqual.h" +#include "Op/include/LocalResponseNormalization.h" +#include "Op/include/Log.h" +#include "Op/include/LogicalAnd.h" +#include "Op/include/LogicalNot.h" +#include "Op/include/LogicalOr.h" +#include "Op/include/Logistic.h" +#include "Op/include/LogSoftmax.h" +#include "Op/include/MatrixDiag.h" +#include "Op/include/MatrixSetDiag.h" +#include "Op/include/Maximum.h" +#include "Op/include/MaxPool2D.h" +#include "Op/include/Mean.h" +#include "Op/include/Minimum.h" +#include "Op/include/MirrorPad.h" +#include "Op/include/Mul.h" +#include "Op/include/Neg.h" +#include "Op/include/NonMaxSuppressionV4.h" +#include "Op/include/NonMaxSuppressionV5.h" +#include "Op/include/NotEqual.h" +#include "Op/include/OneHot.h" +#include "Op/include/Pack.h" +#include "Op/include/Pad.h" +#include "Op/include/PadV2.h" +#include "Op/include/Pow.h" +#include "Op/include/PRelu.h" +#include "Op/include/Quantize.h" +#include "Op/include/Range.h" +#include "Op/include/Rank.h" +#include "Op/include/ReduceAny.h" +#include "Op/include/ReduceMax.h" +#include "Op/include/ReduceMin.h" +#include "Op/include/ReduceProd.h" +#include "Op/include/ReLU.h" +#include "Op/include/ReLU6.h" +#include "Op/include/ReLUN1To1.h" +#include "Op/include/Reshape.h" +#include "Op/include/ResizeBilinear.h" +#include "Op/include/ResizeNearestNeighbor.h" +#include "Op/include/ReverseSequence.h" +#include "Op/include/ReverseV2.h" +#include "Op/include/Round.h" +#include "Op/include/Rsqrt.h" +#include "Op/include/ScatterNd.h" +#include "Op/include/SegmentSum.h" +#include "Op/include/Select.h" +#include "Op/include/SelectV2.h" +#include "Op/include/Shape.h" +#include "Op/include/Sin.h" +#include "Op/include/Slice.h" +#include "Op/include/Softmax.h" +#include "Op/include/SpaceToBatchND.h" +#include "Op/include/SpaceToDepth.h" +#include "Op/include/SparseToDense.h" +#include "Op/include/Split.h" +#include "Op/include/SplitV.h" +#include "Op/include/Sqrt.h" +#include "Op/include/Square.h" +#include "Op/include/SquaredDifference.h" +#include "Op/include/Squeeze.h" +#include "Op/include/StridedSlice.h" +#include "Op/include/Sub.h" +#include "Op/include/Sum.h" +#include "Op/include/SVDF.h" +#include "Op/include/Tanh.h" +#include "Op/include/Tile.h" +#include "Op/include/TopKV2.h" +#include "Op/include/Transpose.h" +#include "Op/include/TransposeConv.h" +#include "Op/include/UnidirectionalSequenceLSTM.h" +#include "Op/include/Unique.h" +#include "Op/include/Unpack.h" +#include "Op/include/Where.h" +#include "Op/include/ZerosLike.h" #endif // __TFLITE_OP_CHEFS_H__ diff --git a/compiler/tflchef/tools/file/Driver.cpp b/compiler/tflchef/tools/file/Driver.cpp index d4605ce..f6c6789 100644 --- a/compiler/tflchef/tools/file/Driver.cpp +++ b/compiler/tflchef/tools/file/Driver.cpp @@ -28,10 +28,8 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("recipe") - .type(arser::DataType::STR) - .help("Source recipe file path to convert"); - arser.add_argument("tflite").type(arser::DataType::STR).help("Target tflite file path"); + arser.add_argument("recipe").help("Source recipe file path to convert"); + arser.add_argument("tflite").help("Target tflite file path"); try { diff --git a/compiler/tflchef/tools/reverse/Driver.cpp b/compiler/tflchef/tools/reverse/Driver.cpp index 1451e8b..119bee6 100644 --- a/compiler/tflchef/tools/reverse/Driver.cpp +++ b/compiler/tflchef/tools/reverse/Driver.cpp @@ -25,10 +25,8 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("tflite") - .type(arser::DataType::STR) - .help("Source tflite file path to convert"); - arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path"); + arser.add_argument("tflite").help("Source tflite file path to convert"); + arser.add_argument("recipe").help("Target recipe file path"); try { diff --git a/compiler/tfldump/CMakeLists.txt b/compiler/tfldump/CMakeLists.txt index fac0be6..4102326 100644 --- a/compiler/tfldump/CMakeLists.txt +++ b/compiler/tfldump/CMakeLists.txt @@ -10,6 +10,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(tfldump ${DRIVER} ${SOURCES}) target_include_directories(tfldump PRIVATE include) target_link_libraries(tfldump arser) +target_link_libraries(tfldump foder) target_link_libraries(tfldump mio_tflite280) target_link_libraries(tfldump mio_tflite280_helper) target_link_libraries(tfldump safemain) diff --git a/compiler/tfldump/driver/Driver.cpp b/compiler/tfldump/driver/Driver.cpp index 38c9c06..a3e748b 100644 --- a/compiler/tfldump/driver/Driver.cpp +++ b/compiler/tfldump/driver/Driver.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include #include @@ -23,7 +23,7 @@ int entry(int argc, char **argv) { arser::Arser arser; - arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to dump"); + arser.add_argument("tflite").help("TFLite file to dump"); try { @@ -38,14 +38,9 @@ int entry(int argc, char **argv) std::string tflite_path = arser.get("tflite"); // Load TF lite model from a tflite file - std::unique_ptr model = tflread::load_tflite(tflite_path); - if (model == nullptr) - { - std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl; - return 255; - } - - const tflite::Model *tflmodel = model->model(); + foder::FileLoader fileLoader{tflite_path}; + std::vector modelData = fileLoader.load(); + const tflite::Model *tflmodel = tflite::GetModel(modelData.data()); if (tflmodel == nullptr) { std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl; diff --git a/compiler/tfldump/include/tflread/Model.h b/compiler/tfldump/include/tflread/Model.h deleted file mode 100644 index c6e4a94..0000000 --- a/compiler/tfldump/include/tflread/Model.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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. - */ - -#ifndef __TFLREAD_MODEL_H__ -#define __TFLREAD_MODEL_H__ - -#include - -#include - -namespace tflread -{ - -struct Model -{ - virtual ~Model() = default; - - virtual const ::tflite::Model *model(void) const = 0; -}; - -/** - * @brief Load TensorFlow Lite model (as a raw Model) from a given path - * - * @note May return a nullptr - */ -std::unique_ptr load_tflite(const std::string &path); - -} // namespace tflread - -#endif // __TFLREAD_MODEL_H__ diff --git a/compiler/tfldump/requires.cmake b/compiler/tfldump/requires.cmake index b1abf94..a11f6b2 100644 --- a/compiler/tfldump/requires.cmake +++ b/compiler/tfldump/requires.cmake @@ -1,3 +1,4 @@ require("arser") +require("foder") require("mio-tflite280") require("safemain") diff --git a/compiler/tfldump/src/Dump.cpp b/compiler/tfldump/src/Dump.cpp index 2a87e47..4388fcd 100644 --- a/compiler/tfldump/src/Dump.cpp +++ b/compiler/tfldump/src/Dump.cpp @@ -33,7 +33,7 @@ void dump_buffer(std::ostream &os, const uint8_t *buffer, size_t size, size_t am std::ios_base::fmtflags saveflags(os.flags()); bool second = false; - bool ellipsis = amount > 0 && size > 4; + bool ellipsis = amount > 0 && size > 8; size_t count = ellipsis ? std::min(size, amount) : size; for (size_t i = 0; i < count; i++) @@ -103,8 +103,8 @@ std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector *fbvect) if (fbvect == nullptr) return os; - bool ellipsis = (fbvect->size() > 4); - auto limit_size = ellipsis ? 4 : fbvect->size(); + bool ellipsis = (fbvect->size() > 8); + auto limit_size = ellipsis ? 8 : fbvect->size(); if (ellipsis) { diff --git a/compiler/tfldump/src/Load.cpp b/compiler/tfldump/src/Load.cpp deleted file mode 100644 index d2f6e06..0000000 --- a/compiler/tfldump/src/Load.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved - * - * 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 - -#include -#include -#include -#include - -namespace -{ - -class MemoryMappedModel final : public tflread::Model -{ -public: - /** - * @require fd and data SHOULD be valid - */ - explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size} - { - // DO NOTHING - } - -public: - ~MemoryMappedModel() - { - munmap(_data, _size); - close(_fd); - } - -public: - MemoryMappedModel(const MemoryMappedModel &) = delete; - MemoryMappedModel(MemoryMappedModel &&) = delete; - -public: - const ::tflite::Model *model(void) const override { return ::tflite::GetModel(_data); } - -private: - int _fd = -1; - void *_data = nullptr; - size_t _size = 0; -}; - -class FileDescriptor final -{ -public: - FileDescriptor(int value) : _value{value} - { - // DO NOTHING - } - -public: - // NOTE Copy is not allowed - FileDescriptor(const FileDescriptor &) = delete; - -public: - // NOTE Move is allowed - FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); } - -public: - ~FileDescriptor() - { - if (_value != -1) - { - // Close on destructor - close(_value); - } - } - -public: - int value(void) const { return _value; } - -public: - int release(void) - { - auto res = _value; - _value = -1; - return res; - } - -private: - int _value = -1; -}; - -} // namespace - -namespace tflread -{ - -std::unique_ptr load_tflite(const std::string &path) -{ - FileDescriptor fd = open(path.c_str(), O_RDONLY); - - if (fd.value() == -1) - { - // Return nullptr on open failure - return nullptr; - } - - struct stat st; - if (fstat(fd.value(), &st) == -1) - { - // Return nullptr on fstat failure - return nullptr; - } - - auto size = st.st_size; - auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0); - - if (data == MAP_FAILED) - { - // Return nullptr on mmap failure - return nullptr; - } - - return std::unique_ptr{new MemoryMappedModel(fd.release(), data, size)}; -} - -} // namespace tflread diff --git a/compiler/tfldump/src/OpPrinter.cpp b/compiler/tfldump/src/OpPrinter.cpp index 47edcb0..2e8e713 100644 --- a/compiler/tfldump/src/OpPrinter.cpp +++ b/compiler/tfldump/src/OpPrinter.cpp @@ -736,6 +736,7 @@ OpPrinterRegistry::OpPrinterRegistry() // There is no Option for CEIL _op_map[tflite::BuiltinOperator_CONCATENATION] = make_unique(); _op_map[tflite::BuiltinOperator_CONV_2D] = make_unique(); + // There is no Option for DENSIFY _op_map[tflite::BuiltinOperator_DEPTH_TO_SPACE] = make_unique(); _op_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique(); // There is no Option for DEQUANTIZE diff --git a/compiler/tflite2circle-conversion-test/CMakeLists.txt b/compiler/tflite2circle-conversion-test/CMakeLists.txt index 83fe23a..2e67d48 100644 --- a/compiler/tflite2circle-conversion-test/CMakeLists.txt +++ b/compiler/tflite2circle-conversion-test/CMakeLists.txt @@ -1,3 +1,7 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + nnas_include(TargetRequire) unset(REQUIRED_TARGETS) diff --git a/compiler/tflite2circle/driver/Driver.cpp b/compiler/tflite2circle/driver/Driver.cpp index fb8c211..6afe1b0 100644 --- a/compiler/tflite2circle/driver/Driver.cpp +++ b/compiler/tflite2circle/driver/Driver.cpp @@ -36,24 +36,11 @@ int entry(int argc, char **argv) { arser::Arser arser{"tflite2circle is a Tensorflow lite to circle model converter"}; - arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument("-V", "--verbose") - .nargs(0) - .required(false) - .default_value(false) - .help("output additional information to stdout or stderr"); - - arser.add_argument("tflite") - .nargs(1) - .type(arser::DataType::STR) - .help("Source tflite file path to convert"); - arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Target circle file path"); + arser::Helper::add_version(arser, print_version); + arser::Helper::add_verbose(arser); + + arser.add_argument("tflite").help("Source tflite file path to convert"); + arser.add_argument("circle").help("Target circle file path"); try { diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions.h index 88a4f71..8149197 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions.h +++ b/compiler/tflite2circle/src/BuildBuiltinOptions.h @@ -31,8 +31,10 @@ #include "BuildBuiltinOptions/ConcatenationOptions.h" #include "BuildBuiltinOptions/Conv2DOptions.h" #include "BuildBuiltinOptions/CosOptions.h" +#include "BuildBuiltinOptions/DensifyOptions.h" #include "BuildBuiltinOptions/DepthToSpaceOptions.h" #include "BuildBuiltinOptions/DepthwiseConv2DOptions.h" +#include "BuildBuiltinOptions/DequantizeOptions.h" #include "BuildBuiltinOptions/DivOptions.h" #include "BuildBuiltinOptions/EqualOptions.h" #include "BuildBuiltinOptions/ExpandDimsOptions.h" diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.cpp new file mode 100644 index 0000000..4e58635 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "DensifyOptions.h" + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_DensifyOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *) +{ + circle::DensifyOptionsBuilder builtin_options_builder{fb}; + return builtin_options_builder.Finish(); +} + +} // namespace tflite2circle diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.h new file mode 100644 index 0000000..b6126c4 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DensifyOptions.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __BBO_DENSIFY_OPTIONS_H__ +#define __BBO_DENSIFY_OPTIONS_H__ + +#include +#include + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_DensifyOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op); + +} // namespace tflite2circle + +#endif // __BBO_DENSIFY_OPTIONS_H__ diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.cpp new file mode 100644 index 0000000..eeacece --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "DequantizeOptions.h" +#include "DataLookup.h" + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_DequantizeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op) +{ + circle::DequantizeOptionsBuilder builtin_options_builder{fb}; + return builtin_options_builder.Finish(); +} + +} // namespace tflite2circle diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.h new file mode 100644 index 0000000..1cb9f9c --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DequantizeOptions.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __BBO_DEQUANTIZE_OPTIONS_H__ +#define __BBO_DEQUANTIZE_OPTIONS_H__ + +#include +#include + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_DequantizeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op); + +} // namespace tflite2circle + +#endif // __BBO_DEQUANTIZE_OPTIONS_H__ diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp index d2d2888..db88d3e 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp @@ -25,8 +25,6 @@ namespace tflite2circle flatbuffers::Offset build_circle_MaximumMinimumOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op) { - auto tflite_builtin_options = op->builtin_options_as_MaximumMinimumOptions(); - assert(tflite_builtin_options); circle::MaximumMinimumOptionsBuilder builtin_options_builder{fb}; return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp index d483b28..ac017b8 100644 --- a/compiler/tflite2circle/src/CircleModel.cpp +++ b/compiler/tflite2circle/src/CircleModel.cpp @@ -344,8 +344,13 @@ template <> void Offset::build(const TFLFlatBufVec *tflite_fla circle::OperatorCodeBuilder operator_code_builder{*_fb}; auto de_code = it->deprecated_builtin_code(); auto bt_code = it->builtin_code(); - operator_code_builder.add_deprecated_builtin_code(get_circle_builtin_code(de_code)); - operator_code_builder.add_builtin_code(get_circle_builtin_code(bt_code)); + auto cir_de_code = get_circle_builtin_code(de_code); + auto cir_bt_code = get_circle_builtin_code(bt_code); + // correct bt_code where bt_code == 0 for old tflite format + if (cir_bt_code == 0) + cir_bt_code = static_cast(cir_de_code); + operator_code_builder.add_deprecated_builtin_code(cir_de_code); + operator_code_builder.add_builtin_code(cir_bt_code); operator_code_builder.add_custom_code(custom_code); operator_code_builder.add_version(it->version()); auto code = operator_code_builder.Finish(); diff --git a/compiler/tflite2circle/src/TFLBuiltinOptions.lst b/compiler/tflite2circle/src/TFLBuiltinOptions.lst index d55ba46..9cbf803 100644 --- a/compiler/tflite2circle/src/TFLBuiltinOptions.lst +++ b/compiler/tflite2circle/src/TFLBuiltinOptions.lst @@ -42,7 +42,7 @@ TFL_BUILTIN_OPTIONS(TopKV2Options) TFL_BUILTIN_OPTIONS(SplitOptions) TFL_BUILTIN_OPTIONS(LogSoftmaxOptions) TFL_BUILTIN_OPTIONS(CastOptions) -//TFL_BUILTIN_OPTIONS(DequantizeOptions) +TFL_BUILTIN_OPTIONS(DequantizeOptions) TFL_BUILTIN_OPTIONS(MaximumMinimumOptions) TFL_BUILTIN_OPTIONS(ArgMaxOptions) TFL_BUILTIN_OPTIONS(LessOptions) @@ -106,3 +106,4 @@ TFL_BUILTIN_OPTIONS(RankOptions) TFL_BUILTIN_OPTIONS(ScatterNdOptions) TFL_BUILTIN_OPTIONS(SegmentSumOptions) TFL_BUILTIN_OPTIONS(BatchMatMulOptions) +TFL_BUILTIN_OPTIONS(DensifyOptions) diff --git a/compiler/vconone/CMakeLists.txt b/compiler/vconone/CMakeLists.txt index 3841a1b..93c33cd 100644 --- a/compiler/vconone/CMakeLists.txt +++ b/compiler/vconone/CMakeLists.txt @@ -1,5 +1,5 @@ if (NOT VCONONE_VERSION) - set(VCONONE_VERSION 0x0000000000140001) + set(VCONONE_VERSION 0x0000000000150001) # NOTE order is [build patch minor major] # if VCONONE_VERSION is set with -D option, it will be cached # you may have to remove cache file if you remove -D option diff --git a/compiler/vconone/src/version.cpp b/compiler/vconone/src/version.cpp index d94a7ad..cebf7d9 100644 --- a/compiler/vconone/src/version.cpp +++ b/compiler/vconone/src/version.cpp @@ -54,7 +54,7 @@ std::string get_string(void) std::string get_copyright(void) { std::string str; - str = "Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; + str = "Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; str += "Licensed under the Apache License, Version 2.0\r\n"; str += "https://github.com/Samsung/ONE"; return str; diff --git a/compute/ARMComputeEx/CMakeLists.txt b/compute/ARMComputeEx/CMakeLists.txt index 58f558d..c8d12c2 100644 --- a/compute/ARMComputeEx/CMakeLists.txt +++ b/compute/ARMComputeEx/CMakeLists.txt @@ -14,7 +14,7 @@ file(GLOB_RECURSE ACL_EX_SRCS "${ACL_EX_BASE}/*.cpp") # generate embeded cl_kernel execute_process ( WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND bash -c "python resolve_includes.py" + COMMAND bash -c "python3 resolve_includes.py" ) add_library(arm_compute_ex SHARED ${ACL_EX_SRCS}) diff --git a/compute/cker/CMakeLists.txt b/compute/cker/CMakeLists.txt index 09f6725..9b3cd4f 100644 --- a/compute/cker/CMakeLists.txt +++ b/compute/cker/CMakeLists.txt @@ -17,3 +17,20 @@ target_include_directories(nnfw_lib_cker INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/i # Workaround to avoid warning # TODO Resolve warning target_compile_options(nnfw_lib_cker INTERFACE -Wno-attributes) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +set(TEST_CKER test_cker) + +file(GLOB_RECURSE TESTS "src/*.test.cc") + +add_executable(${TEST_CKER} ${TESTS}) + +target_link_libraries(${TEST_CKER} nnfw_lib_cker) +target_link_libraries(${TEST_CKER} nnfw_coverage) +target_link_libraries(${TEST_CKER} gtest gtest_main ${LIB_PTHREAD}) + +add_test(${TEST_CKER} ${TEST_CKER}) +install(TARGETS ${TEST_CKER} DESTINATION unittest_standalone) diff --git a/compute/cker/include/cker/CpuBackendThreadpool.h b/compute/cker/include/cker/CpuBackendThreadpool.h index cc6a9db..8ec6140 100644 --- a/compute/cker/include/cker/CpuBackendThreadpool.h +++ b/compute/cker/include/cker/CpuBackendThreadpool.h @@ -21,6 +21,8 @@ #include // from @ruy #include // from @ruy +#include + namespace nnfw { namespace cker @@ -33,7 +35,12 @@ using Task = ruy::Task; template void Execute(int tasks_count, TaskType *tasks, ruy::Context *ruy_context) { + assert(ruy_context != nullptr); assert(tasks_count <= ruy_context->max_num_threads()); + if (ruy_context == nullptr) + { + throw std::runtime_error("CpuBackendThreadpool.h: ruy::Context is null"); + } ruy_context->mutable_thread_pool()->Execute(tasks_count, tasks); } diff --git a/compute/cker/include/cker/NeonTensorUtils.h b/compute/cker/include/cker/NeonTensorUtils.h index 8bf0bee..45ad969 100644 --- a/compute/cker/include/cker/NeonTensorUtils.h +++ b/compute/cker/include/cker/NeonTensorUtils.h @@ -632,7 +632,7 @@ inline void NeonCpuBackendGemm(const int8_t *input, const int32_t *bias, ruy_support::MakeRuyMatrix(rhs_params, input, &ruy_rhs, true); ruy_support::MakeRuyMatrix(dst_params, scratch, &ruy_dst); - ruy::BasicSpec ruy_mul_params; + ruy::MulParams ruy_mul_params; ruy_support::MakeRuyMulParams(gemm_params, &ruy_mul_params); ruy::Mul(ruy_lhs, ruy_rhs, ruy_mul_params, ruy_context, &ruy_dst); diff --git a/compute/cker/include/cker/operation/Conv.h b/compute/cker/include/cker/operation/Conv.h index 16c937a..7cd54dc 100644 --- a/compute/cker/include/cker/operation/Conv.h +++ b/compute/cker/include/cker/operation/Conv.h @@ -57,9 +57,9 @@ class Conv public: Conv() : _modified_filter_data(), _im2col_shape(4), _need_im2col(false), _prepared(false) {} - void prepare(const Shape &filter_shape, const float *filter_data, PaddingType padding_type, - bool &is_replaced_weights, uint32_t dilationWidthFactor, - uint32_t dilationHeightFactor) + void prepareF32(const Shape &filter_shape, const float *filter_data, PaddingType padding_type, + bool &is_replaced_weights, uint32_t dilationWidthFactor, + uint32_t dilationHeightFactor) { if (!_prepared) { @@ -71,9 +71,9 @@ public: } } - void prepareQuant(const Shape &input_shape, const Shape &kernel_shape, const Shape &output_shape, - uint32_t stride_width, uint32_t stride_height, uint32_t dilation_width_factor, - uint32_t dilation_height_factor) + void prepareQ8uPerTensor(const Shape &input_shape, const Shape &kernel_shape, + const Shape &output_shape, uint32_t stride_width, uint32_t stride_height, + uint32_t dilation_width_factor, uint32_t dilation_height_factor) { if (!_prepared) { @@ -138,13 +138,25 @@ public: } } + void operator()(const ConvParams ¶ms, const Shape &input_shape, const uint8_t *input_data, + const Shape &filter_shape, const uint8_t *filter_data, + const int32_t *filter_zero_point, const Shape &bias_shape, + const int32_t *bias_data, const Shape &output_shape, uint8_t *output_data) + { + reference::Conv(params, _per_channel_output_multiplier.data(), + _per_channel_output_shift.data(), input_shape, input_data, + filter_shape, filter_data, filter_zero_point, bias_shape, + bias_data, output_shape, output_data); + } + void operator()(const ConvParams ¶ms, const Shape &input_shape, const int8_t *input_data, const Shape &filter_shape, const int8_t *filter_data, const Shape &bias_shape, const int32_t *bias_data, const Shape &output_shape, int8_t *output_data) { - reference::Conv(params, _per_channel_output_multiplier.data(), _per_channel_output_shift.data(), - input_shape, input_data, filter_shape, filter_data, bias_shape, bias_data, - output_shape, output_data); + reference::Conv(params, _per_channel_output_multiplier.data(), + _per_channel_output_shift.data(), input_shape, input_data, + filter_shape, filter_data, nullptr /* filter_zero_point */, + bias_shape, bias_data, output_shape, output_data); } std::vector &per_channel_output_multiplier() { return _per_channel_output_multiplier; } std::vector &per_channel_output_shift() { return _per_channel_output_shift; } diff --git a/compute/cker/include/cker/operation/DepthwiseConv.h b/compute/cker/include/cker/operation/DepthwiseConv.h index 06ee780..ed1f93d 100644 --- a/compute/cker/include/cker/operation/DepthwiseConv.h +++ b/compute/cker/include/cker/operation/DepthwiseConv.h @@ -25,6 +25,7 @@ #include "cker/operation/optimized/DepthwiseConvFloat.h" #include "cker/operation/optimized/DepthwiseConvUint8.h" #include "cker/operation/optimized/integer_ops/DepthwiseConvInt8.h" +#include "cker/operation/reference/integer_ops/DepthwiseConvUInt8.h" #include "cker/CpuBackendThreadpool.h" namespace nnfw diff --git a/compute/cker/include/cker/operation/reference/Conv.h b/compute/cker/include/cker/operation/reference/Conv.h index 4474754..8bfd469 100644 --- a/compute/cker/include/cker/operation/reference/Conv.h +++ b/compute/cker/include/cker/operation/reference/Conv.h @@ -190,10 +190,13 @@ inline void Conv(const ConvParams ¶ms, const Shape &input_shape, const uint8 } } +template inline void Conv(const ConvParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const Shape &input_shape, const int8_t *input_data, - const Shape &filter_shape, const int8_t *filter_data, const Shape &bias_shape, - const int32_t *bias_data, const Shape &output_shape, int8_t *output_data) + const int32_t *output_shift, const Shape &input_shape, const T *input_data, + const Shape &filter_shape, const T *filter_data, const int32_t *filter_zeropoint, + const Shape &bias_shape, const int32_t *bias_data, const Shape &output_shape, + T *output_data) + { UNUSED_RELEASE(bias_shape); // Get parameters. @@ -259,26 +262,35 @@ inline void Conv(const ConvParams ¶ms, const int32_t *output_multiplier, for (int in_channel = 0; in_channel < input_depth; ++in_channel) { - int32_t input_val = input_data[Offset(input_shape, batch, in_y, in_x, in_channel)]; - int32_t filter_val = + const T input_val = input_data[Offset(input_shape, batch, in_y, in_x, in_channel)]; + const T filter_val = filter_data[Offset(filter_shape, out_channel, filter_y, filter_x, in_channel)]; - // Accumulate with 32 bits accumulator. - // In the nudging process during model quantization, we force - // real value of 0.0 be represented by a quantized value. This - // guarantees that the input_offset is a int8_t, even though - // it is represented using int32_t. int32_t += int8_t * - // (int8_t - int8_t) so the highest value we can get from each - // accumulation is [-127, 127] * ([-128, 127] - - // [-128, 127]), which is [-32512, 32512]. log2(32512) - // = 14.98, which means we can accumulate at least 2^16 - // multiplications without overflow. The accumulator is - // applied to a filter so the accumulation logic will hold as - // long as the filter size (filter_y * filter_x * in_channel) - // does not exceed 2^16, which is the case in all the models - // we have seen so far. - // TODO(jianlijianli): Add a check to make sure the - // accumulator depth is smaller than 2^16. - acc += filter_val * (input_val + input_offset); + if (is_asymmetric) + { + const int32_t filter_offset = -filter_zeropoint[out_channel]; + acc += (filter_val + filter_offset) * (input_val + input_offset); + } + else + { + // Accumulate with 32 bits accumulator. + // In the nudging process during model quantization, we force + // real value of 0.0 be represented by a quantized value. This + // guarantees that the input_offset is a int8_t, even though + // it is represented using int32_t. int32_t += int8_t * + // (int8_t - int8_t) so the highest value we can get from each + // accumulation is [-127, 127] * ([-128, 127] - + // [-128, 127]), which is [-32512, 32512]. log2(32512) + // = 14.98, which means we can accumulate at least 2^16 + // multiplications without overflow. The accumulator is + // applied to a filter so the accumulation logic will hold as + // long as the filter size (filter_y * filter_x * in_channel) + // does not exceed 2^16, which is the case in all the models + // we have seen so far. + // TODO(jianlijianli): Add a check to make sure the + // accumulator depth is smaller than 2^16. + acc += filter_val * (input_val + input_offset); + UNUSED_RELEASE(filter_zeropoint); + } } } } @@ -292,8 +304,7 @@ inline void Conv(const ConvParams ¶ms, const int32_t *output_multiplier, acc += output_offset; acc = std::max(acc, output_activation_min); acc = std::min(acc, output_activation_max); - output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] = - static_cast(acc); + output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] = static_cast(acc); } } } diff --git a/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvUInt8.h b/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvUInt8.h new file mode 100644 index 0000000..025e407 --- /dev/null +++ b/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvUInt8.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * 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. + */ + +#ifndef __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_UINT8_H__ +#define __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_UINT8_H__ + +#include "cker/Shape.h" +#include "cker/Types.h" +#include "cker/Utils.h" + +namespace nnfw +{ +namespace cker +{ +namespace reference_integer_ops +{ +inline void DepthwiseConvPerChannel(const DepthwiseConvParams ¶ms, + const int32_t *output_multiplier, const int32_t *output_shift, + const Shape &input_shape, const uint8_t *input_data, + const Shape &filter_shape, const uint8_t *filter_data, + const int32_t *filter_zeropoint, const Shape &bias_shape, + const int32_t *bias_data, const Shape &output_shape, + uint8_t *output_data) +{ + // Get parameters. + // TODO(b/141565753): Re-introduce ScopedProfilingLabel on Micro. + const int stride_width = params.stride_width; + const int stride_height = params.stride_height; + const int dilation_width_factor = params.dilation_width_factor; + const int dilation_height_factor = params.dilation_height_factor; + const int pad_width = params.padding_values.width; + const int pad_height = params.padding_values.height; + const int depth_multiplier = params.depth_multiplier; + const int32_t input_offset = params.input_offset; + const int32_t output_offset = params.output_offset; + const int32_t output_activation_min = params.quantized_activation_min; + const int32_t output_activation_max = params.quantized_activation_max; + + // Check dimensions of the tensors. + assert(input_shape.DimensionsCount() == 4); + assert(filter_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + + assert(output_activation_min <= output_activation_max); + const int batches = MatchingDim(input_shape, 0, output_shape, 0); + const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3); + const int input_height = input_shape.Dims(1); + const int input_width = input_shape.Dims(2); + const int input_depth = input_shape.Dims(3); + const int filter_height = filter_shape.Dims(1); + const int filter_width = filter_shape.Dims(2); + const int output_height = output_shape.Dims(1); + const int output_width = output_shape.Dims(2); + UNUSED_RELEASE(output_depth); + UNUSED_RELEASE(bias_shape); + assert(output_depth == input_depth * depth_multiplier); + assert(bias_shape.FlatSize() == output_depth); + + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int in_channel = 0; in_channel < input_depth; ++in_channel) + { + for (int m = 0; m < depth_multiplier; ++m) + { + const int output_channel = m + in_channel * depth_multiplier; + const int in_x_origin = (out_x * stride_width) - pad_width; + const int in_y_origin = (out_y * stride_height) - pad_height; + int32_t acc = 0; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + const int in_y = in_y_origin + dilation_height_factor * filter_y; + // Zero padding by omitting the areas outside the image. + const bool is_point_inside_image = + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + if (is_point_inside_image) + { + uint8_t input_val = + input_data[Offset(input_shape, batch, in_y, in_x, in_channel)]; + uint8_t filter_val = + filter_data[Offset(filter_shape, 0, filter_y, filter_x, output_channel)]; + + // { for per-channel + // NOTE: The following comment is copied from tflite int8 implementation + // It may not be 100% true for uint8 per-channel. + // + // Accumulate with 32 bits accumulator. + // In the nudging process during model quantization, we force + // real value of 0.0 be represented by a quantized value. This + // guarantees that the input_offset is a int8, even though it + // is represented using int32_t. + // int32 += int8 * (int8 - int8) so the highest value we can + // get from each accumulation is [-127, 127] * ([-128, 127] - + // [-128, 127]), which is [-32512, 32512]. log2(32512) + // = 14.98, which means we can accumulate at least 2^16 + // multiplications without overflow. The accumulator is + // applied to a filter so the accumulation logic will hold as + // long as the filter size (filter_y * filter_x * in_channel) + // does not exceed 2^16, which is the case in all the models + // we have seen so far. + // TODO(jianlijianli): Add a check to make sure the + // accumulator depth is smaller than 2^16. + const int32_t filter_offset = -filter_zeropoint[output_channel]; + acc += (filter_val + filter_offset) * (input_val + input_offset); + // } for per-channel + } + } + } + if (bias_data) + { + acc += bias_data[output_channel]; + } + acc = MultiplyByQuantizedMultiplier(acc, output_multiplier[output_channel], + output_shift[output_channel]); + acc += output_offset; + acc = std::max(acc, output_activation_min); + acc = std::min(acc, output_activation_max); + // For q8u per-channel, int8_t -> uint8_t + output_data[Offset(output_shape, batch, out_y, out_x, output_channel)] = + static_cast(acc); + } + } + } + } + } +} + +} // namespace reference_integer_ops +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_UINT8_H__ diff --git a/compute/cker/include/cker/ruy/RuySupport.h b/compute/cker/include/cker/ruy/RuySupport.h index 62eeaf6..14489a8 100644 --- a/compute/cker/include/cker/ruy/RuySupport.h +++ b/compute/cker/include/cker/ruy/RuySupport.h @@ -64,23 +64,35 @@ void MakeRuyMatrix(const MatrixParams ¶ms, DataPointer data_ptr, } } -template -void MakeRuyMulParams(const GemmParamsType ¶ms, RuySpecType *ruy_mul_params) +// Integer-quantized case with destination type narrower than int32 +template +void MakeRuyMulParams(const GemmParams ¶ms, + ruy::MulParams *ruy_mul_params) { - // This validation has already been performed by the Gemm API entry point, - // but it doesn't hurt to test specifically this again here, where it's - // being used. - ValidateGemmParams(params); - - ruy_mul_params->set_multiplier_fixedpoint(params.multiplier_fixedpoint); - ruy_mul_params->set_multiplier_exponent(params.multiplier_exponent); - ruy_mul_params->set_multiplier_fixedpoint_perchannel(params.multiplier_fixedpoint_perchannel); - ruy_mul_params->set_multiplier_exponent_perchannel(params.multiplier_exponent_perchannel); + static_assert(sizeof(DstScalar) < sizeof(std::int32_t), ""); + if (quantization_flavor == QuantizationFlavor::kIntegerWithUniformMultiplier) + { + ruy_mul_params->set_multiplier_fixedpoint(params.multiplier_fixedpoint); + ruy_mul_params->set_multiplier_exponent(params.multiplier_exponent); + } + if (quantization_flavor == QuantizationFlavor::kIntegerWithPerRowMultiplier) + { + ruy_mul_params->set_multiplier_fixedpoint_perchannel(params.multiplier_fixedpoint_perchannel); + ruy_mul_params->set_multiplier_exponent_perchannel(params.multiplier_exponent_perchannel); + } ruy_mul_params->set_bias(params.bias); ruy_mul_params->set_clamp_min(params.clamp_min); ruy_mul_params->set_clamp_max(params.clamp_max); } +// Raw-integer case with destination type int32. +template +void MakeRuyMulParams(const GemmParams ¶ms, + ruy::MulParams *ruy_mul_params) +{ + ruy_mul_params->set_bias(params.bias); +} + } // namespace ruy_support } // namespace cker } // namespace nnfw diff --git a/compute/test/cker/Range.cc b/compute/cker/src/Range.test.cc similarity index 100% rename from compute/test/cker/Range.cc rename to compute/cker/src/Range.test.cc diff --git a/compute/ruy/include/ruy/RuySupport.h b/compute/ruy/include/ruy/RuySupport.h index 7086a96..2f9ed74 100644 --- a/compute/ruy/include/ruy/RuySupport.h +++ b/compute/ruy/include/ruy/RuySupport.h @@ -64,23 +64,46 @@ void MakeRuyMatrix(const MatrixParams ¶ms, DataPointer data_ptr, } } -template -void MakeRuyMulParams(const GemmParamsType ¶ms, RuySpecType *ruy_mul_params) +// Floating-point case. +template +void MakeRuyMulParams(const GemmParams ¶ms, + ::ruy::MulParams *ruy_mul_params) { - // This validation has already been performed by the Gemm API entry point, - // but it doesn't hurt to test specifically this again here, where it's - // being used. - ValidateGemmParams(params); + static_assert(quantization_flavor == QuantizationFlavor::kFloatingPoint, ""); + ruy_mul_params->set_bias(params.bias); + ruy_mul_params->set_clamp_min(params.clamp_min); + ruy_mul_params->set_clamp_max(params.clamp_max); +} - ruy_mul_params->set_multiplier_fixedpoint(params.multiplier_fixedpoint); - ruy_mul_params->set_multiplier_exponent(params.multiplier_exponent); - ruy_mul_params->set_multiplier_fixedpoint_perchannel(params.multiplier_fixedpoint_perchannel); - ruy_mul_params->set_multiplier_exponent_perchannel(params.multiplier_exponent_perchannel); +// Integer-quantized case with destination type narrower than int32 +template +void MakeRuyMulParams(const GemmParams ¶ms, + ::ruy::MulParams *ruy_mul_params) +{ + static_assert(sizeof(DstScalar) < sizeof(std::int32_t), ""); + if (quantization_flavor == QuantizationFlavor::kIntegerWithUniformMultiplier) + { + ruy_mul_params->set_multiplier_fixedpoint(params.multiplier_fixedpoint); + ruy_mul_params->set_multiplier_exponent(params.multiplier_exponent); + } + if (quantization_flavor == QuantizationFlavor::kIntegerWithPerRowMultiplier) + { + ruy_mul_params->set_multiplier_fixedpoint_perchannel(params.multiplier_fixedpoint_perchannel); + ruy_mul_params->set_multiplier_exponent_perchannel(params.multiplier_exponent_perchannel); + } ruy_mul_params->set_bias(params.bias); ruy_mul_params->set_clamp_min(params.clamp_min); ruy_mul_params->set_clamp_max(params.clamp_max); } +// Raw-integer case with destination type int32. +template +void MakeRuyMulParams(const GemmParams ¶ms, + ::ruy::MulParams *ruy_mul_params) +{ + ruy_mul_params->set_bias(params.bias); +} + } // namespace ruy_support } // namespace ruy } // namespace nnfw diff --git a/compute/ruy/include/ruy/operation/Conv.h b/compute/ruy/include/ruy/operation/Conv.h index 2b9c8c3..3f03694 100644 --- a/compute/ruy/include/ruy/operation/Conv.h +++ b/compute/ruy/include/ruy/operation/Conv.h @@ -169,7 +169,7 @@ private: ruy_support::MakeRuyMatrix(rhs_params, gemm_input_data, &ruy_rhs, true); ruy_support::MakeRuyMatrix(dst_params, output_data, &ruy_dst); - ::ruy::BasicSpec ruy_mul_params; + ::ruy::MulParams ruy_mul_params; ruy_support::MakeRuyMulParams(gemm_params, &ruy_mul_params); ::ruy::Mul(ruy_lhs, ruy_rhs, ruy_mul_params, ruy_context, &ruy_dst); diff --git a/compute/ruy/include/ruy/operation/FullyConnected.h b/compute/ruy/include/ruy/operation/FullyConnected.h index 59facdb..1d686b6 100644 --- a/compute/ruy/include/ruy/operation/FullyConnected.h +++ b/compute/ruy/include/ruy/operation/FullyConnected.h @@ -68,7 +68,7 @@ inline void FullyConnected(const FullyConnectedParams ¶ms, const Shape &inpu ruy_support::MakeRuyMatrix(rhs_params, input_data, &ruy_rhs, true); ruy_support::MakeRuyMatrix(dst_params, output_data, &ruy_dst); - ::ruy::BasicSpec ruy_mul_params; + ::ruy::MulParams ruy_mul_params; ruy_support::MakeRuyMulParams(gemm_params, &ruy_mul_params); ::ruy::Mul(ruy_lhs, ruy_rhs, ruy_mul_params, ruy_context, &ruy_dst); diff --git a/compute/test/CMakeLists.txt b/compute/test/CMakeLists.txt deleted file mode 100644 index 92aac3e..0000000 --- a/compute/test/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -if(NOT ENABLE_TEST) - return() -endif(NOT ENABLE_TEST) - -set(TEST_COMPUTE test_compute) - -file(GLOB_RECURSE TESTS "*.cc") - -add_executable(${TEST_COMPUTE} ${TESTS}) - -target_link_libraries(${TEST_COMPUTE} nnfw_lib_cker) -target_link_libraries(${TEST_COMPUTE} gtest) -target_link_libraries(${TEST_COMPUTE} gtest_main) -target_link_libraries(${TEST_COMPUTE} ${LIB_PTHREAD} dl) -add_test(${TEST_COMPUTE} ${TEST_COMPUTE}) - -install(TARGETS ${TEST_COMPUTE} DESTINATION unittest_standalone) diff --git a/docs/conf.py b/docs/conf.py index 84197e6..409e5f7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ copyright = '2020, Samsung Research & contributors' author = 'Samsung Research & contributors' # The full version, including alpha/beta/rc tags -release = '1.20.0' +release = '1.21.0' # -- General configuration --------------------------------------------------- diff --git a/docs/howto/how-to-build-runtime-tizen-gbs-rpi4.md b/docs/howto/how-to-build-runtime-tizen-gbs-rpi4.md index 1f8c0c2..57b2b78 100644 --- a/docs/howto/how-to-build-runtime-tizen-gbs-rpi4.md +++ b/docs/howto/how-to-build-runtime-tizen-gbs-rpi4.md @@ -174,34 +174,26 @@ $ vi j2/etc/systemd/system/ip.service and set as like: ``` [Service] -Type=simple Restart=always RestartSec=1 User=root -ExecStart=/bin/sh /bin/ip.sh +ExecStart=/bin/sh -c "ifconfig eth0 192.168.x.y netmask 255.255.255.0 up" [Install] WantedBy=multi-user.target ``` +Replace 192.168.x.y to your actual ip address. -(5-3) Add a new file -``` -$ vi j2/bin/ip.sh -``` -and set with IP address for your RPi4: -``` -ifconfig eth0 192.168.x.y netmask 255.255.255.0 up -``` -where you should update `192.168.x.y` part to your actual IP address. -(5-4) Add a symbolic link +(5-3) Add a symbolic link ``` +$ sudo mkdir -p j2/etc/systemd/system/multi-user.target.wants/ $ pushd j2/etc/systemd/system/multi-user.target.wants/ $ sudo ln -s ../../system/ip.service . $ popd ``` -(5-5) Now that every thing is ready, unmount and unplug your memory card and plug into +(5-4) Now that every thing is ready, unmount and unplug your memory card and plug into RPi4, turn on the power. ``` $ sync diff --git a/docs/release/1.20/index.rst b/docs/release/1.20/index.rst new file mode 100644 index 0000000..082d867 --- /dev/null +++ b/docs/release/1.20/index.rst @@ -0,0 +1,13 @@ +.. ONE documentation master file, created by + sphinx-quickstart on Tue Apr 26 10:18:12 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +1.20 +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + ./release-note-1.20.0.md diff --git a/docs/release/1.20/release-note-1.20.0.md b/docs/release/1.20/release-note-1.20.0.md new file mode 100644 index 0000000..2c75e06 --- /dev/null +++ b/docs/release/1.20/release-note-1.20.0.md @@ -0,0 +1,34 @@ +# Release Note 1.20.0 + +## ONE Compiler + +### Compiler Frontend + +- luci-interpreter supports multiple kernels with PAL layer including Cortext-M +- luci-interpreter supports integer tensor for partly kernels +- luci import support constant without coping to reduce memory for luci-interpreter +- Reduce duplicate codes to package released modules +- Limited support for ONNX LSTM/RNN unrolling while importing +- Limited support for ARM32 cross build +- Support new operator: SVDF +- New virtual CircleVariable to support tensor with variable +- Support quantization of BatchMatMul Op +- Support mixed(UINT8 + INT16) quantization +- Support backward propagation of quantization parameters +- Upgrade default python to version 3.8 +- Support TensorFlow 2.8.0, ONNX-TF 1.10.0, ONNX 1.11.0 +- Upgrade circle schema to follow tflite schema v3b +- Refactor to mio-tflite280, mio-circle04 with version and helpers methods +- Use one flatbuffers 2.0 version +- Drop support for TensorFlow 1.x +- Fix for several bugs, performance enhancements, and typos + +## ONE Runtime + +### Introduce TRIX backend +- TRIX backend supports trix binary with NHWC layout +- TRIX backend supports trix binary with input/output of Q8 and Q16 type + +### API supports new data type +- Symmetric Quantized int16 type named "NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED" + diff --git a/docs/release/1.21/index.rst b/docs/release/1.21/index.rst new file mode 100644 index 0000000..587065f --- /dev/null +++ b/docs/release/1.21/index.rst @@ -0,0 +1,13 @@ +.. ONE documentation master file, created by + sphinx-quickstart on Wed Sep 06 12:18:12 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +1.21 +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + ./release-note-1.21.0.md diff --git a/docs/release/1.21/release-note_1.21.0.md b/docs/release/1.21/release-note_1.21.0.md new file mode 100644 index 0000000..49bf074 --- /dev/null +++ b/docs/release/1.21/release-note_1.21.0.md @@ -0,0 +1,35 @@ +# Release Note 1.21.0 + +## ONE Compiler + +- Support unrolling of LSTM and RNN Ops in `one-import-onnx` tool +- Introduced new tools `one-infer`, `circle-operator`, `circle-interpreter` +- Introduced `Workflow`(WIP) in `one-cmds` +- New option `quant_config` in `one-quantize` +- New option `fake_quantize` in `one-quantize` +- More Ops supported: Densify +- More Ops for quantization: ReduceMax +- More Ops for mixed-precision quantization (MPQ): LeakyRelu, Neg, Relu6, Squeeze +- More Ops for `convert_nchw_to_nhwc` option: LogSoftmax, ReduceMax, SplitV, Softmax +- New optimization options in `one-optimize`: `replace_non_const_fc_with_bmm`, `resolve_customop_splitv`, `fold_densify` +- Improved reshape elimination in `convert_nchw_to_nhwc` option. +- Support fusion of Channel-wise Add + Relu with TConv +- Support negative axis in ArgMin/Max +- Show errors for unrecognized options in `one-optimize` +- Fix shape inference for `StridedSlice` +- Fix FuseBatchNormWithTConvPass to support TConv with bias +- Deprecate `--O1` option in `circle2circle` +- Support gcc-11 +- Support limited Float16 for kernels constants with dequantization to Float32 + +## ONE Runtime + +### Basic Multimodel nnpackage +- Runtime supports to run nnpackage with two models + +### Channel Wise Quantization on Conv2D and Depthwise Conv2D +- Conv2D and Depthwise Conv2D supports per-channel quantization of uint8 type. + +### Batch Execution with TRIX backend +- TRIX backend supports batch execution which run in parallel with multicore + diff --git a/infra/cmake/modules/IdentifyPlatform.cmake b/infra/cmake/modules/IdentifyPlatform.cmake index 6616283..890055f 100644 --- a/infra/cmake/modules/IdentifyPlatform.cmake +++ b/infra/cmake/modules/IdentifyPlatform.cmake @@ -35,6 +35,8 @@ endif() if("${HOST_ARCH}" STREQUAL "x86_64") set(HOST_ARCH_BASE ${HOST_ARCH}) +elseif("${HOST_ARCH}" STREQUAL "armv7em") + set(HOST_ARCH_BASE "arm") elseif("${HOST_ARCH}" STREQUAL "armv7l") set(HOST_ARCH_BASE "arm") elseif("${HOST_ARCH}" STREQUAL "armv7hl") @@ -49,6 +51,8 @@ endif() if("${TARGET_ARCH}" STREQUAL "x86_64") set(TARGET_ARCH_BASE ${TARGET_ARCH}) +elseif("${TARGET_ARCH}" STREQUAL "armv7em") + set(TARGET_ARCH_BASE "arm") elseif("${TARGET_ARCH}" STREQUAL "armv7l") set(TARGET_ARCH_BASE "arm") elseif("${TARGET_ARCH}" STREQUAL "armv7hl") diff --git a/infra/cmake/packages/AbseilConfig.cmake b/infra/cmake/packages/AbseilConfig.cmake index 6fae721..b3cb364 100644 --- a/infra/cmake/packages/AbseilConfig.cmake +++ b/infra/cmake/packages/AbseilConfig.cmake @@ -12,11 +12,18 @@ function(_Abseil_import) # NOTE Turn off abseil testing set(BUILD_TESTING OFF) + # Set -fPIC property because Abseil-cpp can be used for shared library + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + # Abseil-cpp 20211102.0 show warning without below setting + set(ABSL_PROPAGATE_CXX_STD ON) + add_extdirectory("${AbseilSource_DIR}" ABSEIL) add_library(abseil INTERFACE) + target_link_libraries(abseil INTERFACE # From "Available Abseil CMake Public Targets" in CMake/README.md + # Add absl::status (It is not listed in CMake/README.md) absl::algorithm absl::base absl::debugging @@ -27,19 +34,14 @@ function(_Abseil_import) absl::numeric absl::random_random absl::strings - absl::status absl::synchronization absl::time absl::utility + absl::status ) endif(NOT TARGET abseil) set(Abseil_FOUND TRUE PARENT_SCOPE) endfunction(_Abseil_import) -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fPIC") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fPIC") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fPIC") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC") - _Abseil_import() diff --git a/infra/cmake/packages/AbseilSourceConfig.cmake b/infra/cmake/packages/AbseilSourceConfig.cmake index 8aeb86d..0297c08 100644 --- a/infra/cmake/packages/AbseilSourceConfig.cmake +++ b/infra/cmake/packages/AbseilSourceConfig.cmake @@ -7,14 +7,13 @@ function(_AbseilSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - # NOTE TensorFlow 2.3 downloads abseil from the following URL + # NOTE TensorFlow 2.9 downloads abseil 20211102.0 envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") - envoption(ABSEIL_URL ${EXTERNAL_DOWNLOAD_SERVER}/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz) - + envoption(ABSEIL_URL ${EXTERNAL_DOWNLOAD_SERVER}/abseil/abseil-cpp/archive/20211102.0.tar.gz) ExternalSource_Download(ABSEIL DIRNAME ABSEIL URL ${ABSEIL_URL} - CHECKSUM MD5=4d9aa7e757adf48fef171c85f0d88552) + CHECKSUM MD5=bdca561519192543378b7cade101ec43) set(AbseilSource_DIR ${ABSEIL_SOURCE_DIR} PARENT_SCOPE) set(AbseilSource_FOUND TRUE PARENT_SCOPE) diff --git a/infra/cmake/packages/CMSISSource-5.8.0/CMSISSourceConfig.cmake b/infra/cmake/packages/CMSISSource-5.8.0/CMSISSourceConfig.cmake index 99118c5..d1588d3 100644 --- a/infra/cmake/packages/CMSISSource-5.8.0/CMSISSourceConfig.cmake +++ b/infra/cmake/packages/CMSISSource-5.8.0/CMSISSourceConfig.cmake @@ -2,7 +2,8 @@ function(_CMSISSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(CMSIS_5_8_0_URL https://github.com/ARM-software/CMSIS_5/archive/refs/tags/5.8.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(CMSIS_5_8_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/ARM-software/CMSIS_5/archive/refs/tags/5.8.0.tar.gz) set(CMSIS_5_8_0_SHA256 fe6b697b8782e7fd6131034b7646a3b65c83018774abf7f9f94901a3bc7c82ad) ExternalSource_Download(CMSIS DIRNAME CMSIS-5.8.0 ${CMSIS_5_8_0_URL} diff --git a/infra/cmake/packages/CaffeSourceConfig.cmake b/infra/cmake/packages/CaffeSourceConfig.cmake index 41cc2c9..05eb5b3 100644 --- a/infra/cmake/packages/CaffeSourceConfig.cmake +++ b/infra/cmake/packages/CaffeSourceConfig.cmake @@ -7,7 +7,8 @@ function(_CaffeSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(CAFFE_URL https://github.com/BVLC/caffe/archive/1.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(CAFFE_URL ${EXTERNAL_DOWNLOAD_SERVER}/BVLC/caffe/archive/1.0.tar.gz) ExternalSource_Download(CAFFE ${CAFFE_URL}) diff --git a/infra/cmake/packages/CpuInfoSourceConfig.cmake b/infra/cmake/packages/CpuInfoSourceConfig.cmake index 60419ad..b93a6a2 100644 --- a/infra/cmake/packages/CpuInfoSourceConfig.cmake +++ b/infra/cmake/packages/CpuInfoSourceConfig.cmake @@ -8,8 +8,8 @@ function(_CpuInfoSource_import) nnas_include(OptionTools) envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") - # CPUINFO commit including patch from tflite v2.3 - envoption(CPUINFO_URL ${EXTERNAL_DOWNLOAD_SERVER}/pytorch/cpuinfo/archive/63b254577ed77a8004a9be6ac707f3dccc4e1fd9.tar.gz) + # CPUINFO commit from tflite v2.8 + envoption(CPUINFO_URL ${EXTERNAL_DOWNLOAD_SERVER}/pytorch/cpuinfo/archive/5916273f79a21551890fd3d56fc5375a78d1598d.tar.gz) ExternalSource_Download(CPUINFO DIRNAME CPUINFO URL ${CPUINFO_URL}) diff --git a/infra/cmake/packages/Egl_HeadersSourceConfig.cmake b/infra/cmake/packages/Egl_HeadersSourceConfig.cmake new file mode 100644 index 0000000..fae57f6 --- /dev/null +++ b/infra/cmake/packages/Egl_HeadersSourceConfig.cmake @@ -0,0 +1,21 @@ +function(_Egl_HeadersSource_import) + if(NOT DOWNLOAD_EGL_HEADERS) + set(Egl_HeadersSource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT DOWNLOAD_EGL_HEADERS) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(EGL_HEADERS_URL ${EXTERNAL_DOWNLOAD_SERVER}/KhronosGroup/EGL-Registry/archive/649981109e263b737e7735933c90626c29a306f2.zip) + + ExternalSource_Download(EGL_HEADERS + DIRNAME EGL_HEADERS + URL ${EGL_HEADERS_URL}) + + set(Egl_HeadersSource_DIR ${EGL_HEADERS_SOURCE_DIR} PARENT_SCOPE) + set(Egl_HeadersSource_FOUND TRUE PARENT_SCOPE) +endfunction(_Egl_HeadersSource_import) + +_Egl_HeadersSource_import() diff --git a/infra/cmake/packages/FarmhashSourceConfig.cmake b/infra/cmake/packages/FarmhashSourceConfig.cmake index a19c8b9..fa1867c 100644 --- a/infra/cmake/packages/FarmhashSourceConfig.cmake +++ b/infra/cmake/packages/FarmhashSourceConfig.cmake @@ -10,7 +10,8 @@ function(_FarmhashSource_import) # NOTE TensorFlow 1.12 downloads farmhash from the following URL # TensorFlow 1.13.1 downloads farmhash from the following URL # TensorFlow 2.3.0 downloads farmhash from the following URL - envoption(FARMHASH_1_12_URL https://github.com/google/farmhash/archive/816a4ae622e964763ca0862d9dbd19324a1eaf45.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(FARMHASH_1_12_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/farmhash/archive/816a4ae622e964763ca0862d9dbd19324a1eaf45.tar.gz) ExternalSource_Download(FARMHASH ${FARMHASH_1_12_URL}) diff --git a/infra/cmake/packages/FlatBuffersSource-2.0/FlatBuffersSourceConfig.cmake b/infra/cmake/packages/FlatBuffersSource-2.0/FlatBuffersSourceConfig.cmake index a0a32aa..e094055 100644 --- a/infra/cmake/packages/FlatBuffersSource-2.0/FlatBuffersSourceConfig.cmake +++ b/infra/cmake/packages/FlatBuffersSource-2.0/FlatBuffersSourceConfig.cmake @@ -7,7 +7,8 @@ function(_FlatBuffersSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(FLATBUFFERS_2_0_URL https://github.com/google/flatbuffers/archive/v2.0.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(FLATBUFFERS_2_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/flatbuffers/archive/v2.0.0.tar.gz) ExternalSource_Download(FLATBUFFERS DIRNAME FLATBUFFERS-2.0 CHECKSUM MD5=a27992324c3cbf86dd888268a23d17bd diff --git a/infra/cmake/packages/Fp16SourceConfig.cmake b/infra/cmake/packages/Fp16SourceConfig.cmake index 3623fd2..3df4e4c 100644 --- a/infra/cmake/packages/Fp16SourceConfig.cmake +++ b/infra/cmake/packages/Fp16SourceConfig.cmake @@ -9,7 +9,7 @@ function(_Fp16Source_import) envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") # fp16 commit in xnnpack 8b283aa30a31 - envoption(FP16_URL ${EXTERNAL_DOWNLOAD_SERVER}/Maratyszcza/FP16/archive/3c54eacb74f6f5e39077300c5564156c424d77ba.tar.gz) + envoption(FP16_URL ${EXTERNAL_DOWNLOAD_SERVER}/Maratyszcza/FP16/archive/4dfe081cf6bcd15db339cf2680b9281b8451eeb3.tar.gz) ExternalSource_Download(FP16 DIRNAME FP16 URL ${FP16_URL}) diff --git a/infra/cmake/packages/GEMMLowpSourceConfig.cmake b/infra/cmake/packages/GEMMLowpSourceConfig.cmake index 6e1cfa9..3b35603 100644 --- a/infra/cmake/packages/GEMMLowpSourceConfig.cmake +++ b/infra/cmake/packages/GEMMLowpSourceConfig.cmake @@ -9,7 +9,8 @@ function(_GEMMLowpSource_import) # NOTE TensorFlow 1.12 uses the following URL # TensorFlow 1.13.1 uses the following URL - envoption(GEMMLOWP_URL https://github.com/google/gemmlowp/archive/38ebac7b059e84692f53e5938f97a9943c120d98.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(GEMMLOWP_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/gemmlowp/archive/38ebac7b059e84692f53e5938f97a9943c120d98.tar.gz) ExternalSource_Download(GEMMLOWP ${GEMMLOWP_URL}) diff --git a/infra/cmake/packages/GFlagsSourceConfig.cmake b/infra/cmake/packages/GFlagsSourceConfig.cmake index 3e70d89..2f9b753 100644 --- a/infra/cmake/packages/GFlagsSourceConfig.cmake +++ b/infra/cmake/packages/GFlagsSourceConfig.cmake @@ -7,7 +7,8 @@ function(_GFlagsSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(GFLAGS_URL https://github.com/gflags/gflags/archive/v2.2.1.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(GFLAGS_URL ${EXTERNAL_DOWNLOAD_SERVER}/gflags/gflags/archive/v2.2.1.tar.gz) ExternalSource_Download(GFLAGS ${GFLAGS_URL}) diff --git a/infra/cmake/packages/GTestSourceConfig.cmake b/infra/cmake/packages/GTestSourceConfig.cmake index e57d096..643c3d1 100644 --- a/infra/cmake/packages/GTestSourceConfig.cmake +++ b/infra/cmake/packages/GTestSourceConfig.cmake @@ -7,7 +7,8 @@ function(_GTestSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(GTEST_URL https://github.com/google/googletest/archive/release-1.11.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(GTEST_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/googletest/archive/release-1.11.0.tar.gz) ExternalSource_Download(GTEST ${GTEST_URL}) diff --git a/infra/cmake/packages/HDF5SourceConfig.cmake b/infra/cmake/packages/HDF5SourceConfig.cmake index 9db048c..3440dbd 100644 --- a/infra/cmake/packages/HDF5SourceConfig.cmake +++ b/infra/cmake/packages/HDF5SourceConfig.cmake @@ -7,7 +7,8 @@ function(_HDF5Source_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(HDF5_URL https://github.com/HDFGroup/hdf5/archive/hdf5-1_8_16.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(HDF5_URL ${EXTERNAL_DOWNLOAD_SERVER}/HDFGroup/hdf5/archive/hdf5-1_8_16.tar.gz) ExternalSource_Download(HDF5 ${HDF5_URL} PATCH ${CMAKE_CURRENT_LIST_DIR}/HDF5Source.patch) diff --git a/infra/cmake/packages/JsoncppSourceConfig.cmake b/infra/cmake/packages/JsoncppSourceConfig.cmake index 3195ea4..8d67285 100644 --- a/infra/cmake/packages/JsoncppSourceConfig.cmake +++ b/infra/cmake/packages/JsoncppSourceConfig.cmake @@ -7,7 +7,8 @@ function(_JsoncppSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(JSONCPP_URL https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(JSONCPP_URL ${EXTERNAL_DOWNLOAD_SERVER}/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.tar.gz) ExternalSource_Download(JSONCPP ${JSONCPP_URL}) diff --git a/infra/cmake/packages/MbedOSSource-6.15/MbedOSSourceConfig.cmake b/infra/cmake/packages/MbedOSSource-6.15/MbedOSSourceConfig.cmake index 8055545..e55647d 100644 --- a/infra/cmake/packages/MbedOSSource-6.15/MbedOSSourceConfig.cmake +++ b/infra/cmake/packages/MbedOSSource-6.15/MbedOSSourceConfig.cmake @@ -2,7 +2,8 @@ function(_MbedOSSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(MBEDOS_6_15_URL https://github.com/ARMmbed/mbed-os/archive/refs/tags/mbed-os-6.15.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(MBEDOS_6_15_URL ${EXTERNAL_DOWNLOAD_SERVER}/ARMmbed/mbed-os/archive/refs/tags/mbed-os-6.15.0.tar.gz) set(MBEDOS_6_15_SHA256 529b04c41f3020ed8a62f12d47f2d3de87e1b07fb13708534534a587f7ea048e) ExternalSource_Download(MBEDOS DIRNAME MBEDOS-6.15 ${MBEDOS_6_15_URL} diff --git a/infra/cmake/packages/NEON2SSESourceConfig.cmake b/infra/cmake/packages/NEON2SSESourceConfig.cmake index bd40267..82c71e2 100644 --- a/infra/cmake/packages/NEON2SSESourceConfig.cmake +++ b/infra/cmake/packages/NEON2SSESourceConfig.cmake @@ -8,10 +8,10 @@ function(_NEON2SSESource_import) nnas_include(OptionTools) # NOTE TensorFlow 1.13.1 downloads NEON2SSE from the following URL - # NOTE TensorFlow 2.1 downloads NEON2SSE from the following URL - # NOTE TensorFlow 2.2 downloads NEON2SSE from the following URL - # NOTE TensorFlow 2.3 downloads NEON2SSE from the following URL - envoption(NEON2SSE_URL https://github.com/intel/ARM_NEON_2_x86_SSE/archive/1200fe90bb174a6224a525ee60148671a786a71f.tar.gz) + # NOTE TensorFlow 2.8.0 downloads NEON2SSE from the following URL + # NOTE commit c12f8932c3be5aebaf35562d699f645686c4e2c3 will resolve build fail on debug build + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(NEON2SSE_URL ${EXTERNAL_DOWNLOAD_SERVER}/intel/ARM_NEON_2_x86_SSE/archive/1200fe90bb174a6224a525ee60148671a786a71f.tar.gz) ExternalSource_Download(NEON2SSE ${NEON2SSE_URL}) diff --git a/infra/cmake/packages/ONNXSource-1.4.1/ONNXSourceConfig.cmake b/infra/cmake/packages/ONNXSource-1.4.1/ONNXSourceConfig.cmake index c9fb5e4..fe21f6d 100644 --- a/infra/cmake/packages/ONNXSource-1.4.1/ONNXSourceConfig.cmake +++ b/infra/cmake/packages/ONNXSource-1.4.1/ONNXSourceConfig.cmake @@ -7,7 +7,8 @@ function(_ONNXSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(ONNX_1_4_1_URL https://github.com/onnx/onnx/archive/v1.4.1.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(ONNX_1_4_1_URL ${EXTERNAL_DOWNLOAD_SERVER}/onnx/onnx/archive/v1.4.1.zip) ExternalSource_Download(ONNX DIRNAME ONNX-1.4.1 CHECKSUM MD5=604b43a22fbc758f32ae9f3a4fb9d397 diff --git a/infra/cmake/packages/ONNXSource-1.6.0/ONNXSourceConfig.cmake b/infra/cmake/packages/ONNXSource-1.6.0/ONNXSourceConfig.cmake index ef903f8..b2ad08b 100644 --- a/infra/cmake/packages/ONNXSource-1.6.0/ONNXSourceConfig.cmake +++ b/infra/cmake/packages/ONNXSource-1.6.0/ONNXSourceConfig.cmake @@ -7,7 +7,8 @@ function(_ONNXSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(ONNX_1_6_0_URL https://github.com/onnx/onnx/archive/v1.6.0.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(ONNX_1_6_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/onnx/onnx/archive/v1.6.0.zip) ExternalSource_Download(ONNX DIRNAME ONNX-1.6.0 CHECKSUM MD5=cbdc547a527f1b59c7f066c8d258b966 diff --git a/infra/cmake/packages/OouraFFTSourceConfig.cmake b/infra/cmake/packages/OouraFFTSourceConfig.cmake index be551fb..d84b5b2 100644 --- a/infra/cmake/packages/OouraFFTSourceConfig.cmake +++ b/infra/cmake/packages/OouraFFTSourceConfig.cmake @@ -8,7 +8,8 @@ function(_OouraFFTSource_import) nnas_include(OptionTools) # NOTE TensorFlow 2.3 downloads OOURAFFT from the following URL - envoption(OOURAFFT_URL https://github.com/petewarden/OouraFFT/archive/v1.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(OOURAFFT_URL ${EXTERNAL_DOWNLOAD_SERVER}/petewarden/OouraFFT/archive/v1.0.tar.gz) ExternalSource_Download(OOURAFFT ${OOURAFFT_URL}) diff --git a/infra/cmake/packages/Opengl_HeadersSourceConfig.cmake b/infra/cmake/packages/Opengl_HeadersSourceConfig.cmake new file mode 100644 index 0000000..c5a774a --- /dev/null +++ b/infra/cmake/packages/Opengl_HeadersSourceConfig.cmake @@ -0,0 +1,21 @@ +function(_Opengl_HeadersSource_import) + if(NOT DOWNLOAD_OPENGL_HEADERS) + set(Opengl_HeadersSource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT DOWNLOAD_OPENGL_HEADERS) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(OPENGL_HEADERS_URL ${EXTERNAL_DOWNLOAD_SERVER}/KhronosGroup/OpenGL-Registry/archive/0cb0880d91581d34f96899c86fc1bf35627b4b81.zip) + + ExternalSource_Download(OPENGL_HEADERS + DIRNAME OPENGL_HEADERS + URL ${OPENGL_HEADERS_URL}) + + set(Opengl_HeadersSource_DIR ${OPENGL_HEADERS_SOURCE_DIR} PARENT_SCOPE) + set(Opengl_HeadersSource_FOUND TRUE PARENT_SCOPE) +endfunction(_Opengl_HeadersSource_import) + +_Opengl_HeadersSource_import() diff --git a/infra/cmake/packages/ProtobufSourceConfig.cmake b/infra/cmake/packages/ProtobufSourceConfig.cmake index baa49ee..a1704e5 100644 --- a/infra/cmake/packages/ProtobufSourceConfig.cmake +++ b/infra/cmake/packages/ProtobufSourceConfig.cmake @@ -7,7 +7,8 @@ function(_ProtobufSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(PROTOBUF_URL https://github.com/protocolbuffers/protobuf/archive/v3.5.2.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(PROTOBUF_URL ${EXTERNAL_DOWNLOAD_SERVER}/protocolbuffers/protobuf/archive/v3.5.2.tar.gz) ExternalSource_Download(PROTOBUF ${PROTOBUF_URL} PATCH ${CMAKE_CURRENT_LIST_DIR}/ProtobufSource.patch) diff --git a/infra/cmake/packages/Pybind11SourceConfig.cmake b/infra/cmake/packages/Pybind11SourceConfig.cmake index 76f51e4..2f64253 100644 --- a/infra/cmake/packages/Pybind11SourceConfig.cmake +++ b/infra/cmake/packages/Pybind11SourceConfig.cmake @@ -7,7 +7,8 @@ function(_Pybind11Source_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(PYBIND11_URL https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(PYBIND11_URL ${EXTERNAL_DOWNLOAD_SERVER}/pybind/pybind11/archive/v2.5.0.tar.gz) ExternalSource_Download(PYBIND11 ${PYBIND11_URL}) diff --git a/infra/cmake/packages/PytorchSourceConfig.cmake b/infra/cmake/packages/PytorchSourceConfig.cmake index 0212f2f..94757f8 100644 --- a/infra/cmake/packages/PytorchSourceConfig.cmake +++ b/infra/cmake/packages/PytorchSourceConfig.cmake @@ -7,7 +7,8 @@ function(_PytorchSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(PYTORCH_URL https://github.com/pytorch/pytorch/archive/v0.4.1.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(PYTORCH_URL ${EXTERNAL_DOWNLOAD_SERVER}/pytorch/pytorch/archive/v0.4.1.tar.gz) ExternalSource_Download(PYTORCH ${PYTORCH_URL}) diff --git a/infra/cmake/packages/TensorFlowEigenSource-2.1.0/TensorFlowEigenSourceConfig.cmake b/infra/cmake/packages/TensorFlowEigenSource-2.1.0/TensorFlowEigenSourceConfig.cmake index f846755..8120ebc 100644 --- a/infra/cmake/packages/TensorFlowEigenSource-2.1.0/TensorFlowEigenSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowEigenSource-2.1.0/TensorFlowEigenSourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowEigenSource_import) # Exact version used by TensorFlow v2.1.0. # See tensorflow/tensorflow/workspace.bzl. - envoption(TENSORFLOW_2_1_0_EIGEN_URL https://gitlab.com/libeigen/eigen/-/archive/4e696901f873a2347f76d931cf2f701e31e15d05/eigen-4e696901f873a2347f76d931cf2f701e31e15d05.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://gitlab.com") + envoption(TENSORFLOW_2_1_0_EIGEN_URL ${EXTERNAL_DOWNLOAD_SERVER}/libeigen/eigen/-/archive/4e696901f873a2347f76d931cf2f701e31e15d05/eigen-4e696901f873a2347f76d931cf2f701e31e15d05.tar.gz) ExternalSource_Download(EIGEN DIRNAME TENSORFLOW-2.1.0-EIGEN ${TENSORFLOW_2_1_0_EIGEN_URL}) diff --git a/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfig.cmake b/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfig.cmake new file mode 100644 index 0000000..6f59f07 --- /dev/null +++ b/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfig.cmake @@ -0,0 +1,21 @@ +function(_TensorFlowEigenSource_import) + if(NOT DOWNLOAD_EIGEN) + set(TensorFlowEigenSource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT DOWNLOAD_EIGEN) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + # Exact version used by TensorFlow v2.8.0. + # See tensorflow/third_party/eigen3/workspace.bzl. + envoption(EXTERNAL_DOWNLOAD_SERVER "https://gitlab.com") + envoption(TENSORFLOW_2_8_0_EIGEN_URL ${EXTERNAL_DOWNLOAD_SERVER}/libeigen/eigen/-/archive/008ff3483a8c5604639e1c4d204eae30ad737af6/eigen-e1dd31ce174c3d26fbe38388f64b09d2adbd7557a59e90e6f545a288cc1755fc.tar.gz) + + ExternalSource_Download(EIGEN DIRNAME TENSORFLOW-2.8.0-EIGEN ${TENSORFLOW_2_8_0_EIGEN_URL}) + + set(TensorFlowEigenSource_DIR ${EIGEN_SOURCE_DIR} PARENT_SCOPE) + set(TensorFlowEigenSource_FOUND TRUE PARENT_SCOPE) +endfunction(_TensorFlowEigenSource_import) + +_TensorFlowEigenSource_import() diff --git a/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfigVersion.cmake b/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfigVersion.cmake new file mode 100644 index 0000000..2ad2e24 --- /dev/null +++ b/infra/cmake/packages/TensorFlowEigenSource-2.8.0/TensorFlowEigenSourceConfigVersion.cmake @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION "2.8.0") +set(PACKAGE_VERSION_EXACT FALSE) +set(PACKAGE_VERSION_COMPATIBLE FALSE) +set(PACKAGE_VERSION_UNSUITABLE TRUE) + +if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + set(PACKAGE_VERSION_UNSUITABLE FALSE) +endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.1.0/TensorFlowGEMMLowpSourceConfig.cmake b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.1.0/TensorFlowGEMMLowpSourceConfig.cmake index 035264f..421be6c 100644 --- a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.1.0/TensorFlowGEMMLowpSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.1.0/TensorFlowGEMMLowpSourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowGEMMLowpSource_import) # Exact version used by TensorFlow v2.1.0. # See tensorflow/tensorflow/workspace.bzl. - envoption(TENSORFLOW_2_1_0_GEMMLOWP_URL https://github.com/google/gemmlowp/archive/12fed0cd7cfcd9e169bf1925bc3a7a58725fdcc3.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_1_0_GEMMLOWP_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/gemmlowp/archive/12fed0cd7cfcd9e169bf1925bc3a7a58725fdcc3.zip) ExternalSource_Download(GEMMLOWP DIRNAME TENSORFLOW-2.1.0-GEMMLOWP ${TENSORFLOW_2_1_0_GEMMLOWP_URL}) diff --git a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.3.0/TensorFlowGEMMLowpSourceConfig.cmake b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.3.0/TensorFlowGEMMLowpSourceConfig.cmake index bc13d62..44c56a6 100644 --- a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.3.0/TensorFlowGEMMLowpSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.3.0/TensorFlowGEMMLowpSourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowGEMMLowpSource_import) # Exact version used by TensorFlow v2.3.0. # See tensorflow/tensorflow/workspace.bzl. - envoption(TENSORFLOW_2_3_0_GEMMLOWP_URL https://github.com/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_3_0_GEMMLOWP_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) ExternalSource_Download(GEMMLOWP DIRNAME TENSORFLOW-2.3.0-GEMMLOWP ${TENSORFLOW_2_3_0_GEMMLOWP_URL}) diff --git a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.6.0/TensorFlowGEMMLowpSourceConfig.cmake b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.6.0/TensorFlowGEMMLowpSourceConfig.cmake index b7f3148..76cdfdd 100644 --- a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.6.0/TensorFlowGEMMLowpSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.6.0/TensorFlowGEMMLowpSourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowGEMMLowpSource_import) # Exact version used by TensorFlow v2.6.0. # See tensorflow/third_party/gemmlowp/workspace.bzl. - envoption(TENSORFLOW_2_6_0_GEMMLOWP_URL https://github.com/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_6_0_GEMMLOWP_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) ExternalSource_Download(GEMMLOWP DIRNAME TENSORFLOW-2.6.0-GEMMLOWP ${TENSORFLOW_2_6_0_GEMMLOWP_URL}) diff --git a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.8.0/TensorFlowGEMMLowpSourceConfig.cmake b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.8.0/TensorFlowGEMMLowpSourceConfig.cmake index f3663cc..3e17490 100644 --- a/infra/cmake/packages/TensorFlowGEMMLowpSource-2.8.0/TensorFlowGEMMLowpSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowGEMMLowpSource-2.8.0/TensorFlowGEMMLowpSourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowGEMMLowpSource_import) # Exact version used by TensorFlow v2.8.0. # See tensorflow/third_party/gemmlowp/workspace.bzl. - envoption(TENSORFLOW_2_8_0_GEMMLOWP_URL https://github.com/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_8_0_GEMMLOWP_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/gemmlowp/archive/fda83bdc38b118cc6b56753bd540caa49e570745.zip) ExternalSource_Download(GEMMLOWP DIRNAME TENSORFLOW-2.8.0-GEMMLOWP ${TENSORFLOW_2_8_0_GEMMLOWP_URL}) diff --git a/infra/cmake/packages/TensorFlowGpuSourceConfig.cmake b/infra/cmake/packages/TensorFlowGpuSourceConfig.cmake index f1debe7..369816a 100644 --- a/infra/cmake/packages/TensorFlowGpuSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowGpuSourceConfig.cmake @@ -13,7 +13,7 @@ function(_TensorFlowGpuSource_Import) set(PATCH_DONE "TRUE") endif() endif() - + if(${PATCH_DONE} STREQUAL "TRUE") message(STATUS "Skip downloading TensorFlowGpuSource") set(TENSORFLOWGPU_SOURCE_DIR "${NNAS_EXTERNALS_DIR}/TENSORFLOW_GPU" PARENT_SCOPE) @@ -28,7 +28,8 @@ function(_TensorFlowGpuSource_Import) # Download TFLite Source Code nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_4_1_URL https://github.com/tensorflow/tensorflow/archive/v2.4.1.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_4_1_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.4.1.tar.gz) ExternalSource_Download(TFLITE_GPU_DELEGATE DIRNAME TENSORFLOW-2.4.1 ${TENSORFLOW_2_4_1_URL}) # Patch for non used codes on onert backend/gpu_cl diff --git a/infra/cmake/packages/TensorFlowRuySource-2.3.0/TensorFlowRuySourceConfig.cmake b/infra/cmake/packages/TensorFlowRuySource-2.3.0/TensorFlowRuySourceConfig.cmake index 3dbf05e..3a7dc89 100644 --- a/infra/cmake/packages/TensorFlowRuySource-2.3.0/TensorFlowRuySourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowRuySource-2.3.0/TensorFlowRuySourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowRuySource_import) # Exact version used by TensorFlow v2.3.0. # See tensorflow/third_party/ruy/workspace.bzl - envoption(TENSORFLOW_2_3_0_RUY_URL https://github.com/google/ruy/archive/34ea9f4993955fa1ff4eb58e504421806b7f2e8f.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_3_0_RUY_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/ruy/archive/34ea9f4993955fa1ff4eb58e504421806b7f2e8f.zip) ExternalSource_Download(RUY DIRNAME TENSORFLOW-2.3.0-RUY ${TENSORFLOW_2_3_0_RUY_URL}) diff --git a/infra/cmake/packages/TensorFlowRuySource-2.6.0/TensorFlowRuySourceConfig.cmake b/infra/cmake/packages/TensorFlowRuySource-2.6.0/TensorFlowRuySourceConfig.cmake index b4dee91..e4dd4f2 100644 --- a/infra/cmake/packages/TensorFlowRuySource-2.6.0/TensorFlowRuySourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowRuySource-2.6.0/TensorFlowRuySourceConfig.cmake @@ -9,7 +9,8 @@ function(_TensorFlowRuySource_import) # Exact version used by TensorFlow v2.6.0. # See tensorflow/third_party/ruy/workspace.bzl - envoption(TENSORFLOW_2_6_0_RUY_URL https://github.com/google/ruy/archive/e6c1b8dc8a8b00ee74e7268aac8b18d7260ab1ce.zip) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_6_0_RUY_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/ruy/archive/e6c1b8dc8a8b00ee74e7268aac8b18d7260ab1ce.zip) ExternalSource_Download(RUY DIRNAME TENSORFLOW-2.6.0-RUY ${TENSORFLOW_2_6_0_RUY_URL}) diff --git a/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfig.cmake b/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfig.cmake new file mode 100644 index 0000000..2ead7cd --- /dev/null +++ b/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfig.cmake @@ -0,0 +1,21 @@ +function(_TensorFlowRuySource_import) + if(NOT DOWNLOAD_RUY) + set(TensorFlowRuySource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT DOWNLOAD_RUY) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + # Exact version used by TensorFlow v2.8.0. + # See tensorflow/third_party/ruy/workspace.bzl + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_8_0_RUY_URL ${EXTERNAL_DOWNLOAD_SERVER}/google/ruy/archive/e6c1b8dc8a8b00ee74e7268aac8b18d7260ab1ce.zip) + + ExternalSource_Download(RUY DIRNAME TENSORFLOW-2.8.0-RUY ${TENSORFLOW_2_8_0_RUY_URL}) + + set(TensorFlowRuySource_DIR ${RUY_SOURCE_DIR} PARENT_SCOPE) + set(TensorFlowRuySource_FOUND TRUE PARENT_SCOPE) +endfunction(_TensorFlowRuySource_import) + +_TensorFlowRuySource_import() diff --git a/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfigVersion.cmake b/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfigVersion.cmake new file mode 100644 index 0000000..2ad2e24 --- /dev/null +++ b/infra/cmake/packages/TensorFlowRuySource-2.8.0/TensorFlowRuySourceConfigVersion.cmake @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION "2.8.0") +set(PACKAGE_VERSION_EXACT FALSE) +set(PACKAGE_VERSION_COMPATIBLE FALSE) +set(PACKAGE_VERSION_UNSUITABLE TRUE) + +if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + set(PACKAGE_VERSION_UNSUITABLE FALSE) +endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/cmake/packages/TensorFlowSource-1.14/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-1.14/TensorFlowSourceConfig.cmake index bcdf9f2..33538c2 100644 --- a/infra/cmake/packages/TensorFlowSource-1.14/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-1.14/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_1_14_URL https://github.com/tensorflow/tensorflow/archive/v1.14.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_1_14_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v1.14.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-1.14 ${TENSORFLOW_1_14_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfig.cmake index 0d2a950..aabc22f 100644 --- a/infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.1.0/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_1_0_URL https://github.com/tensorflow/tensorflow/archive/v2.1.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_1_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.1.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.1.0 ${TENSORFLOW_2_1_0_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.2.0/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.2.0/TensorFlowSourceConfig.cmake index 71220d7..7dabf88 100644 --- a/infra/cmake/packages/TensorFlowSource-2.2.0/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.2.0/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_2_0_URL https://github.com/tensorflow/tensorflow/archive/v2.2.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_2_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.2.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.2.0 ${TENSORFLOW_2_2_0_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.3.0-rc0Config.cmake b/infra/cmake/packages/TensorFlowSource-2.3.0-rc0Config.cmake index 82df579..967d49e 100644 --- a/infra/cmake/packages/TensorFlowSource-2.3.0-rc0Config.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.3.0-rc0Config.cmake @@ -10,7 +10,8 @@ function(_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_3_0_RC0_URL https://github.com/tensorflow/tensorflow/archive/v2.3.0-rc0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_3_0_RC0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.3.0-rc0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.3.0-RC0 ${TENSORFLOW_2_3_0_RC0_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.3.0/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.3.0/TensorFlowSourceConfig.cmake index 5c3a0f8..0ad0cda 100644 --- a/infra/cmake/packages/TensorFlowSource-2.3.0/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.3.0/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_3_0_URL https://github.com/tensorflow/tensorflow/archive/v2.3.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_3_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.3.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.3.0 ${TENSORFLOW_2_3_0_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.6.0/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.6.0/TensorFlowSourceConfig.cmake index 611c7c8..9a7af17 100644 --- a/infra/cmake/packages/TensorFlowSource-2.6.0/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.6.0/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_6_0_URL https://github.com/tensorflow/tensorflow/archive/v2.6.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_6_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.6.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.6.0 ${TENSORFLOW_2_6_0_URL}) diff --git a/infra/cmake/packages/TensorFlowSource-2.8.0/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.8.0/TensorFlowSourceConfig.cmake index 4abe2ea..988a0f4 100644 --- a/infra/cmake/packages/TensorFlowSource-2.8.0/TensorFlowSourceConfig.cmake +++ b/infra/cmake/packages/TensorFlowSource-2.8.0/TensorFlowSourceConfig.cmake @@ -7,7 +7,8 @@ function(_TensorFlowSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - envoption(TENSORFLOW_2_8_0_URL https://github.com/tensorflow/tensorflow/archive/v2.8.0.tar.gz) + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_8_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.8.0.tar.gz) ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.8.0 ${TENSORFLOW_2_8_0_URL}) diff --git a/infra/cmake/packages/VulkanSourceConfig.cmake b/infra/cmake/packages/VulkanSourceConfig.cmake new file mode 100644 index 0000000..76b6989 --- /dev/null +++ b/infra/cmake/packages/VulkanSourceConfig.cmake @@ -0,0 +1,20 @@ +function(_VulkanSource_import) + if(NOT ${DOWNLOAD_VULKAN}) + set(VulkanSource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT ${DOWNLOAD_VULKAN}) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(VULKAN_URL ${EXTERNAL_DOWNLOAD_SERVER}/KhronosGroup/Vulkan-Headers/archive/ec2db85225ab410bc6829251bef6c578aaed5868.tar.gz) + ExternalSource_Download(VULKAN + DIRNAME VULKAN + URL ${VULKAN_URL}) + + set(VulkanSource_DIR ${VULKAN_SOURCE_DIR} PARENT_SCOPE) + set(VulkanSource_FOUND TRUE PARENT_SCOPE) +endfunction(_VulkanSource_import) + +_VulkanSource_import() diff --git a/infra/command/format b/infra/command/format index 5cf9606..993a6ad 100644 --- a/infra/command/format +++ b/infra/command/format @@ -154,11 +154,9 @@ function check_python_files() { fi # Check python files - FILES_TO_CHECK_PYTHON=`echo "$FILES_TO_CHECK" | tr ' ' '\n' | egrep '\.py$'` + FILES_TO_CHECK_PYTHON=(`echo "$FILES_TO_CHECK" | tr ' ' '\n' | egrep '\.py$'`) # Exceptional case: one-cmds don't have '.py' extension: ignore non-python source (cmake, etc) and ignore shell script: one-prepare-venv - FILES_TO_CHECK_PYTHON=`echo "$FILES_TO_CHECK_PYTHON" | egrep -v '^compiler/one-cmds/.*\..*$' | egrep -v '^compiler/one-cmds/one-prepare-venv$'` - # Transform to array - FILES_TO_CHECK_PYTHON=($FILES_TO_CHECK_PYTHON) + FILES_TO_CHECK_PYTHON+=(`echo "$FILES_TO_CHECK" | tr ' ' '\n' | egrep '^compiler/one-cmds/[^(\./)]*$' | egrep -v '^compiler/one-cmds/one-prepare-venv$'`) for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do skip=${s#'.'/}/ diff --git a/infra/command/gen-coverage-report b/infra/command/gen-coverage-report index 3058aee..df6377d 100644 --- a/infra/command/gen-coverage-report +++ b/infra/command/gen-coverage-report @@ -69,10 +69,10 @@ done opencl_files=($(find ./runtime/onert/backend/gpu_cl/open_cl/ \( -name "*.cc" -o -name "*.h" \) -exec realpath {} \; )) -# Exclude *.test.cpp files from coverage report +# Exclude test files from coverage report # Exclude flatbuffer generated files from coverage report "${LCOV_PATH}" -r "${EXTRACTED_COVERAGE_INFO_PATH}" -o "${EXCLUDED_COVERAGE_INFO_PATH}" \ - '*.test.cpp' '*_schema_generated.h' "${opencl_files[@]}" + '*.test.cpp' '*.test.cc' '*/test/*' '*/tests/*' '*_schema_generated.h' "${opencl_files[@]}" # Final coverage data cp -v ${EXCLUDED_COVERAGE_INFO_PATH} ${COVERAGE_INFO_PATH} diff --git a/infra/debian/compiler/changelog b/infra/debian/compiler/changelog index 2763ac5..ddca70a 100644 --- a/infra/debian/compiler/changelog +++ b/infra/debian/compiler/changelog @@ -1,3 +1,50 @@ +one (1.21.0) bionic; urgency=medium + + * Support unrolling of LSTM and RNN Ops in `one-import-onnx` tool + * Introduced new tools `one-infer`, `circle-operator`, `circle-interpreter` + * Introduced `Workflow`(WIP) in `one-cmds` + * New option `quant_config` in `one-quantize` + * New option `fake_quantize` in `one-quantize` + * More Ops supported: Densify + * More Ops for quantization: ReduceMax + * More Ops for mixed-precision quantization (MPQ): LeakyRelu, Neg, Relu6, Squeeze + * More Ops for `convert_nchw_to_nhwc` option: LogSoftmax, ReduceMax, SplitV, Softmax + * New optimization options in `one-optimize`: `replace_non_const_fc_with_bmm`, `resolve_customop_splitv`, `fold_densify` + * Improved reshape elimination in `convert_nchw_to_nhwc` option. + * Support fusion of Channel-wise Add + Relu with TConv + * Support negative axis in ArgMin/Max + * Show errors for unrecognized options in `one-optimize` + * Fix shape inference for `StridedSlice` + * Fix FuseBatchNormWithTConvPass to support TConv with bias + * Deprecate `--O1` option in `circle2circle` + * Support gcc-11 + * Support limited Float16 for kernels constants with dequantization to Float32 + + -- seongwoo Wed, 06 Sep 2022 12:00:00 +0900 + +one (1.20.0) bionic; urgency=medium + + * luci-interpreter supports multiple kernels with PAL layer including Cortext-M + * luci-interpreter supports integer tensor for partly kernels + * luci import support constant without coping to reduce memory for luci-interpreter + * Reduce duplicate codes to package released modules + * Limited support for ONNX LSTM/RNN unrolling while importing + * Limited support for ARM32 cross build + * Support new operator: SVDF + * New virtual CircleVariable to support tensor with variable + * Support quantization of BatchMatMul Op + * Support mixed(UINT8 + INT16) quantization + * Support backward propagation of quantization parameters + * Upgrade default python to version 3.8 + * Support TensorFlow 2.8.0, ONNX-TF 1.10.0, ONNX 1.11.0 + * Upgrade circle schema to follow tflite schema v3b + * Refactor to mio-tflite280, mio-circle04 with version and helpers methods + * Use one flatbuffers 2.0 version + * Drop support for TensorFlow 1.x + * Fix for several bugs, performance enhancements, and typos + + -- seongwoo Tue, 26 Apr 2022 12:00:00 +0900 + one (1.19.0) bionic; urgency=medium * `circle-quantizer` supports input/output type option diff --git a/infra/debian/compiler/docs/one-infer.1 b/infra/debian/compiler/docs/one-infer.1 new file mode 100644 index 0000000..a1bafbb --- /dev/null +++ b/infra/debian/compiler/docs/one-infer.1 @@ -0,0 +1,46 @@ +.TH ONE-INFER "1" "July 2022" "one-infer version 1.21.0" "User Commands" +.SH NAME +one-infer \- manual page for one-infer version 1.21.0 +.SH DESCRIPTION +usage: one\-infer [\-h] [\-v] [\-C CONFIG] [\-d DRIVER | \fB\-b\fR BACKEND] [\-\-post\-process POST_PROCESS] [\-\-] [COMMANDS FOR BACKEND DRIVER] +.PP +command line tool to infer model +.SS "optional arguments:" +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-v\fR, \fB\-\-version\fR +show program's version number and exit +.TP +\fB\-V\fR, \fB\-\-verbose\fR +output additional information to stdout or stderr +.TP +\fB\-C\fR CONFIG, \fB\-\-config\fR CONFIG +run with configuation file +.TP +\fB\-d\fR DRIVER, \fB\-\-driver\fR DRIVER +backend inference driver name to execute +.TP +\fB\-b\fR BACKEND, \fB\-\-backend\fR BACKEND +backend name to use +.TP +\fB\-\-post\-process\fR POST_PROCESS +post processing script to convert I/O data to standard +format +.SH COPYRIGHT +Copyright \(co 2020\-2022 Samsung Electronics Co., Ltd. All Rights Reserved +Licensed under the Apache License, Version 2.0 +https://github.com/Samsung/ONE +.SH "SEE ALSO" +The full documentation for +.B one-infer +is maintained as a Texinfo manual. If the +.B info +and +.B one-infer +programs are properly installed at your site, the command +.IP +.B info one-infer +.PP +should give you access to the complete manual. diff --git a/infra/debian/compiler/docs/one-partition.1 b/infra/debian/compiler/docs/one-partition.1 new file mode 100644 index 0000000..5b6fe93 --- /dev/null +++ b/infra/debian/compiler/docs/one-partition.1 @@ -0,0 +1,56 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. +.TH ONE-PARTITION "1" "June 2022" "one-partition version 1.21.0" "User Commands" +.SH NAME +one-partition \- manual page for one-partition version 1.21.0 +.SH DESCRIPTION +usage: one\-partition [\-h] [\-v] [\-V] [\-C CONFIG] [\-\-backends BACKENDS] +.TP +[\-\-default DEFAULT] [\-\-part_file PART_FILE] +[\-\-input_file INPUT_FILE] [\-\-work_path WORK_PATH] +.PP +command line tool to partition circle model by multiple backends +.SS "optional arguments:" +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-v\fR, \fB\-\-version\fR +show program's version number and exit +.TP +\fB\-V\fR, \fB\-\-verbose\fR +output additional information to stdout or stderr +.TP +\fB\-C\fR CONFIG, \fB\-\-config\fR CONFIG +run with configuation file +.TP +\fB\-\-backends\fR BACKENDS +backends in CSV to use for partitioning +.TP +\fB\-\-default\fR DEFAULT +default backend to assign +.TP +\fB\-\-part_file\fR PART_FILE +partition file which provides backend to assign +.TP +\fB\-\-input_file\fR INPUT_FILE +input circle model filename +.TP +\fB\-\-work_path\fR WORK_PATH +work path of partition, input files exist and output +files are produced +.SH COPYRIGHT +Copyright \(co 2020\-2022 Samsung Electronics Co., Ltd. All Rights Reserved +Licensed under the Apache License, Version 2.0 +https://github.com/Samsung/ONE +.SH "SEE ALSO" +The full documentation for +.B one-partition +is maintained as a Texinfo manual. If the +.B info +and +.B one-partition +programs are properly installed at your site, the command +.IP +.B info one-partition +.PP +should give you access to the complete manual. diff --git a/infra/debian/compiler/one-compiler.install b/infra/debian/compiler/one-compiler.install index 805ba86..65e46d1 100644 --- a/infra/debian/compiler/one-compiler.install +++ b/infra/debian/compiler/one-compiler.install @@ -1,6 +1,8 @@ # {FILES_TO_INSTALL} {DEST_DIR} # bin usr/bin/circle2circle usr/share/one/bin/ +usr/bin/circle-eval-diff usr/share/one/bin/ +usr/bin/circle-operator usr/share/one/bin/ usr/bin/circle-partitioner usr/share/one/bin/ usr/bin/circle-quantizer usr/share/one/bin/ usr/bin/generate_bcq_metadata.py usr/share/one/bin/ @@ -16,14 +18,21 @@ usr/bin/one-import-bcq usr/share/one/bin/ usr/bin/one-import-onnx usr/share/one/bin/ usr/bin/one-import-tf usr/share/one/bin/ usr/bin/one-import-tflite usr/share/one/bin/ +usr/bin/one-infer usr/share/one/bin/ usr/bin/one-optimize usr/share/one/bin/ usr/bin/one-pack usr/share/one/bin/ +usr/bin/one-partition usr/share/one/bin/ usr/bin/one-prepare-venv usr/share/one/bin/ usr/bin/one-profile usr/share/one/bin/ usr/bin/one-quantize usr/share/one/bin/ usr/bin/one-version usr/share/one/bin/ usr/bin/onelib/constant.py usr/share/one/bin/onelib/ usr/bin/onelib/make_cmd.py usr/share/one/bin/onelib/ +usr/bin/onelib/CfgRunner.py usr/share/one/bin/onelib/ +usr/bin/onelib/OptionBuilder.py usr/share/one/bin/onelib/ +usr/bin/onelib/TopologicalSortHelper.py usr/share/one/bin/onelib/ +usr/bin/onelib/WorkflowRunner.py usr/share/one/bin/onelib/ +usr/bin/onnx_legalizer.py usr/share/one/bin/ usr/bin/rawdata2hdf5 usr/share/one/bin/ usr/bin/record-minmax usr/share/one/bin/ usr/bin/tf2nnpkg usr/share/one/bin/ diff --git a/infra/debian/compiler/one-compiler.manpages b/infra/debian/compiler/one-compiler.manpages index 77f2f4e..e0284ae 100644 --- a/infra/debian/compiler/one-compiler.manpages +++ b/infra/debian/compiler/one-compiler.manpages @@ -1,5 +1,6 @@ debian/docs/one-build.1 debian/docs/one-codegen.1 +debian/docs/one-infer.1 debian/docs/one-import.1 debian/docs/one-import-bcq.1 debian/docs/one-import-onnx.1 @@ -7,6 +8,7 @@ debian/docs/one-import-tf.1 debian/docs/one-import-tflite.1 debian/docs/one-optimize.1 debian/docs/one-pack.1 +debian/docs/one-partition.1 debian/docs/one-profile.1 debian/docs/one-quantize.1 debian/docs/onecc.1 diff --git a/infra/debian/runtime/changelog b/infra/debian/runtime/changelog index 4cf0abc..e07c50c 100644 --- a/infra/debian/runtime/changelog +++ b/infra/debian/runtime/changelog @@ -1,3 +1,18 @@ +one (1.21.0) bionic; urgency=low + + * Runtime supports to run nnpackage with two models + * Conv2D and Depthwise Conv2D supports per-channel quantization of uint8 type. + * TRIX backend supports batch execution which run in parallel with multicore + + -- Chunseok Lee Tue, 06 Sep 2022 12:00:00 +0900 + +one (1.20.0) bionic; urgency=low + + * Introduce TRIX backend + * API supports new data type NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED + + -- Chunseok Lee Wed, 26 Apr 2022 12:00:00 +0900 + one (1.19.0) bionic; urgency=low * Synch up version with ONE Compiler diff --git a/infra/debian/runtime/rules b/infra/debian/runtime/rules index dee87a9..97170ee 100755 --- a/infra/debian/runtime/rules +++ b/infra/debian/runtime/rules @@ -3,7 +3,7 @@ DEBVER := $(shell dpkg-parsechangelog -SVersion) export DH_VERBOSE = 1 export _DESTDIR = debian/tmp/ export BUILD_TYPE=release -export OPTIONS=-DBUILD_LOGGING=0 -DBUILD_TFLITE_COMPARATOR_TEST_TOOL=0 -DBUILD_NNPACKAGE_RUN=0 -DBUILD_TFLITE_RUN=0 -DBUILD_NNAPI_TEST=0 -DBUILD_RUNTIME_NNAPI_TEST=0 -DBUILD_TFLITE_BENCHMARK_MODEL=0 -DBUILD_TFLITE_VANILLA_RUN=0 -DBUILD_TENSORFLOW_LITE_2_3_0=0 -DBUILD_TENSORFLOW_LITE=0 +export OPTIONS=-DBUILD_LOGGING=0 -DBUILD_TFLITE_COMPARATOR_TEST_TOOL=0 -DBUILD_NNPACKAGE_RUN=0 -DBUILD_TFLITE_RUN=0 -DBUILD_NNAPI_TEST=0 -DBUILD_RUNTIME_NNAPI_TEST=0 -DBUILD_TFLITE_BENCHMARK_MODEL=0 -DBUILD_TFLITE_VANILLA_RUN=0 -DBUILD_TENSORFLOW_LITE_2_8_0=0 -DBUILD_TENSORFLOW_LITE=0 export DEBIAN_BUILD=1 export INSTALL_PATH=debian/tmp/usr/ %: diff --git a/infra/docker/bionic/Dockerfile b/infra/docker/bionic/Dockerfile index dbc22a6..f7ffc73 100644 --- a/infra/docker/bionic/Dockerfile +++ b/infra/docker/bionic/Dockerfile @@ -86,7 +86,7 @@ RUN echo 'deb [trusted=yes] http://download.tizen.org/tools/latest-release/Ubunt RUN apt-get update && apt-get -qqy install gbs RUN wget http://download.tizen.org/sdk/tizenstudio/official/binary/sdb_3.1.4_ubuntu-64.zip -O sdb.zip RUN unzip -d tmp sdb.zip && rm sdb.zip -RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp +RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp/* # Install java RUN apt-get install -y --no-install-recommends openjdk-8-jdk diff --git a/infra/docker/focal/Dockerfile b/infra/docker/focal/Dockerfile index 6f3cd9b..1cdeffb 100644 --- a/infra/docker/focal/Dockerfile +++ b/infra/docker/focal/Dockerfile @@ -46,7 +46,7 @@ RUN echo 'deb [trusted=yes] http://download.tizen.org/tools/latest-release/Ubunt RUN apt-get update && apt-get -qqy install gbs RUN wget http://download.tizen.org/sdk/tizenstudio/official/binary/sdb_4.2.19_ubuntu-64.zip -O sdb.zip RUN unzip -d tmp sdb.zip && rm sdb.zip -RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp +RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp/* # Clean archives (to reduce image size) RUN apt-get clean -y diff --git a/infra/nncc/CMakeLists.txt b/infra/nncc/CMakeLists.txt index 2ff5a5f..768d797 100644 --- a/infra/nncc/CMakeLists.txt +++ b/infra/nncc/CMakeLists.txt @@ -1,4 +1,7 @@ -cmake_minimum_required(VERSION 3.1) +# The libboost 1.74 uses IN_LIST operator, which requires the policy CMP0057, in a CMake file. +# This policy requires ``cmake_minimum_required(VERSION 3.3)``. +# Run "cmake --help-policy CMP0057" for policy details. +cmake_minimum_required(VERSION 3.3) project(nncc) diff --git a/infra/nncc/cmake/options/options_armv7em-generic.cmake b/infra/nncc/cmake/options/options_armv7em-generic.cmake new file mode 100644 index 0000000..d671b73 --- /dev/null +++ b/infra/nncc/cmake/options/options_armv7em-generic.cmake @@ -0,0 +1,3 @@ +# +# armv7em generic cmake options +# diff --git a/infra/nnfw/CMakeLists.txt b/infra/nnfw/CMakeLists.txt index 897a16f..2a27eee 100644 --- a/infra/nnfw/CMakeLists.txt +++ b/infra/nnfw/CMakeLists.txt @@ -55,6 +55,12 @@ macro(nnas_find_package PREFIX) ) endmacro(nnas_find_package) +# C++14 feature requires 5 or later +# Using std::unordered_map shows build fail under 6.2 +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2) + message(FATAL "Runtime build requires GNU Compiler version 6.2 or later.") +endif() + set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/infra/nnfw/cmake/ApplyCompileFlags.cmake b/infra/nnfw/cmake/ApplyCompileFlags.cmake index b042b0c..b1c7ff5 100644 --- a/infra/nnfw/cmake/ApplyCompileFlags.cmake +++ b/infra/nnfw/cmake/ApplyCompileFlags.cmake @@ -31,3 +31,13 @@ endforeach() foreach(FLAG ${FLAGS_CXXONLY}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") endforeach() + +# lib pthread as a variable (finding pthread build option must be disabled on android) +# Define here to use on external lib build +set(LIB_PTHREAD lib_pthread) +add_library(${LIB_PTHREAD} INTERFACE) +if(NOT TARGET_OS STREQUAL "android") + # Get compile option (ex. "-pthread" on linux GNU build tool) + find_package(Threads) + target_link_libraries(${LIB_PTHREAD} INTERFACE Threads::Threads) +endif() diff --git a/infra/nnfw/cmake/CfgOptionFlags.cmake b/infra/nnfw/cmake/CfgOptionFlags.cmake index 5371120..440f185 100644 --- a/infra/nnfw/cmake/CfgOptionFlags.cmake +++ b/infra/nnfw/cmake/CfgOptionFlags.cmake @@ -31,6 +31,8 @@ option(GENERATE_RUNTIME_NNAPI_TESTS "Generate NNAPI operation gtest" ON) option(ENVVAR_ONERT_CONFIG "Use environment variable for onert configuration" ON) option(INSTALL_TEST_SCRIPTS "Install test scripts" ON) option(BUILD_GPU_CL "Build gpu_cl backend" OFF) +option(BUILD_NPUD "Build NPU daemon" ON) +option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" ON) # # Default build configuration for contrib # @@ -72,9 +74,10 @@ option(DOWNLOAD_OOURAFFT "Download Ooura FFT source" ON) option(DOWNLOAD_GTEST "Download Google Test source and build Google Test" ON) option(BUILD_BOOST "Build boost source" OFF) option(BUILD_TENSORFLOW_LITE "Build TensorFlow Lite from the downloaded source" ON) -option(BUILD_TENSORFLOW_LITE_2_3_0 "Build TensorFlow Lite 2.3.0 from the downloaded source" OFF) +option(BUILD_TENSORFLOW_LITE_2_8_0 "Build TensorFlow Lite 2.8.0 from the downloaded source" OFF) option(BUILD_TENSORFLOW_LITE_GPU "Build TensorFlow Lite GPU delegate from the downloaded source" OFF) option(BUILD_ARMCOMPUTE "Build ARM Compute from the downloaded source" ON) +option(DEBUG_ARMCOMPUTE "Build ARM Compute as debug type" OFF) option(BUILD_RUY "Build ruy library from the downloaded source" ON) option(BUILD_CPUINFO "Build cpuinfo library from the downloaded source" ON) option(PROFILE_RUY "Enable ruy library profiling" OFF) diff --git a/infra/nnfw/cmake/buildtool/config/config_aarch64-android.cmake b/infra/nnfw/cmake/buildtool/config/config_aarch64-android.cmake index e0c81de..fb63b3c 100644 --- a/infra/nnfw/cmake/buildtool/config/config_aarch64-android.cmake +++ b/infra/nnfw/cmake/buildtool/config/config_aarch64-android.cmake @@ -1,8 +1,5 @@ include("cmake/buildtool/config/config_linux.cmake") -# On Android, pthread is contained in bionic(libc) -set(LIB_PTHREAD "") - # SIMD for aarch64 set(FLAGS_COMMON ${FLAGS_COMMON} "-ftree-vectorize" diff --git a/infra/nnfw/cmake/buildtool/config/config_linux.cmake b/infra/nnfw/cmake/buildtool/config/config_linux.cmake index 86dd0f2..01b47ef 100644 --- a/infra/nnfw/cmake/buildtool/config/config_linux.cmake +++ b/infra/nnfw/cmake/buildtool/config/config_linux.cmake @@ -2,20 +2,11 @@ # linux common compile options # -# remove warning from arm cl +# Remove warning: ignoring attributes on template argument (ACL, Eigen, etc) # https://github.com/ARM-software/ComputeLibrary/issues/330 -set(GCC_VERSION_DISABLE_WARNING 6.0) -if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER GCC_VERSION_DISABLE_WARNING) - message(STATUS "GCC version higher than ${GCC_VERSION_DISABLE_WARNING}") - set(FLAGS_CXXONLY ${FLAGS_CXXONLY} - "-Wno-ignored-attributes" - ) -endif() +set(FLAGS_CXXONLY ${FLAGS_CXXONLY} "-Wno-ignored-attributes") # Disable annoying ABI compatibility warning. if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) list(APPEND FLAGS_CXXONLY "-Wno-psabi") endif() - -# lib pthread as a variable (pthread must be disabled on android) -set(LIB_PTHREAD pthread) diff --git a/infra/nnfw/cmake/buildtool/config/config_x86_64-darwin.cmake b/infra/nnfw/cmake/buildtool/config/config_x86_64-darwin.cmake index dbd45fc..52d6c6b 100644 --- a/infra/nnfw/cmake/buildtool/config/config_x86_64-darwin.cmake +++ b/infra/nnfw/cmake/buildtool/config/config_x86_64-darwin.cmake @@ -7,6 +7,3 @@ message(STATUS "Building for x86-64 Darwin") set(FLAGS_COMMON ${FLAGS_COMMON} "-msse4" ) - -# lib pthread as a variable (pthread must be disabled on android) -set(LIB_PTHREAD pthread) diff --git a/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-linux.cmake b/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-linux.cmake index 3356aa7..07b26a9 100644 --- a/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-linux.cmake +++ b/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-linux.cmake @@ -21,12 +21,6 @@ endif() set(CMAKE_SYSROOT ${ROOTFS_DIR}) set(CMAKE_FIND_ROOT_PATH ${ROOTFS_DIR}) -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) -set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) # search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-tizen.cmake b/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-tizen.cmake index 4d5d7ac..cab7325 100644 --- a/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-tizen.cmake +++ b/infra/nnfw/cmake/buildtool/cross/toolchain_aarch64-tizen.cmake @@ -23,12 +23,6 @@ endif() set(CMAKE_SYSROOT ${ROOTFS_DIR}) set(CMAKE_FIND_ROOT_PATH ${ROOTFS_DIR}) -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) -set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) # search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-linux.cmake b/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-linux.cmake index 8f2cb67..c69259f 100644 --- a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-linux.cmake +++ b/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-linux.cmake @@ -21,12 +21,6 @@ endif() set(CMAKE_SYSROOT ${ROOTFS_DIR}) set(CMAKE_FIND_ROOT_PATH ${ROOTFS_DIR}) -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) -set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) # search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake b/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake index 72513cd..181415d 100644 --- a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake +++ b/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake @@ -23,12 +23,6 @@ endif() set(CMAKE_SYSROOT ${ROOTFS_DIR}) set(CMAKE_FIND_ROOT_PATH ${ROOTFS_DIR}) -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) -set(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}" - CACHE INTERNAL "" FORCE) # search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) @@ -46,10 +40,6 @@ add_compile_options(-mfpu=neon-vfpv4) add_compile_options(-mfloat-abi=softfp) add_compile_options(--sysroot=${ROOTFS_DIR}) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}") - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${ROOTFS_DIR}") - include_directories(SYSTEM ${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) include_directories(SYSTEM ${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7l-tizen-linux-gnueabi) add_compile_options(-Wno-deprecated-declarations) # compile-time option diff --git a/infra/nnfw/cmake/options/options_aarch64-android.cmake b/infra/nnfw/cmake/options/options_aarch64-android.cmake index 9332f52..e95ccca 100644 --- a/infra/nnfw/cmake/options/options_aarch64-android.cmake +++ b/infra/nnfw/cmake/options/options_aarch64-android.cmake @@ -10,3 +10,5 @@ option(DOWNLOAD_NEON2SSE "Download NEON2SSE library source" OFF) option(DOWNLOAD_BOOST "Download boost source" ON) option(BUILD_BOOST "Build boost source" ON) option(BUILD_LOGGING "Build logging runtime" OFF) +# Do not support npud +option(BUILD_NPUD "Build NPU daemon" OFF) diff --git a/infra/nnfw/cmake/options/options_armv7l-tizen.cmake b/infra/nnfw/cmake/options/options_armv7l-tizen.cmake index eab3b0a..9b487d9 100644 --- a/infra/nnfw/cmake/options/options_armv7l-tizen.cmake +++ b/infra/nnfw/cmake/options/options_armv7l-tizen.cmake @@ -9,6 +9,7 @@ option(DOWNLOAD_GTEST "Download Google Test source and build Google Test" OFF) option(BUILD_LOGGING "Build logging runtime" OFF) option(GENERATE_RUNTIME_NNAPI_TESTS "Generate NNAPI operation gtest" OFF) option(ENVVAR_ONERT_CONFIG "Use environment variable for onert configuration" OFF) +option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) option(DOWNLOAD_OPENCL_HEADERS "Download Opencl_headers source" ON) option(DOWNLOAD_TENSORFLOW_GPU "Download Tensorflow GPU delegate source" ON) diff --git a/infra/nnfw/cmake/options/options_x86_64-tizen.cmake b/infra/nnfw/cmake/options/options_x86_64-tizen.cmake index 31b7fd6..eea3722 100644 --- a/infra/nnfw/cmake/options/options_x86_64-tizen.cmake +++ b/infra/nnfw/cmake/options/options_x86_64-tizen.cmake @@ -2,6 +2,7 @@ # x86_64 linux cmake options # option(BUILD_ARMCOMPUTE "Build ARM Compute from the downloaded source" OFF) +option(BUILD_TENSORFLOW_LITE "Build TensorFlow Lite from the downloaded source" OFF) option(DOWNLOAD_ARMCOMPUTE "Download ARM Compute source" OFF) option(DOWNLOAD_GTEST "Download Google Test source and build Google Test" OFF) diff --git a/infra/nnfw/cmake/packages/ARMComputeConfig.cmake b/infra/nnfw/cmake/packages/ARMComputeConfig.cmake index 6ae7dea..f6a4efd 100644 --- a/infra/nnfw/cmake/packages/ARMComputeConfig.cmake +++ b/infra/nnfw/cmake/packages/ARMComputeConfig.cmake @@ -90,11 +90,11 @@ function(_ARMCompute_Build ARMComputeInstall_DIR) return() endif(NOT SCONS_PATH) - if(CMAKE_BUILD_TYPE) - string(TOLOWER "${CMAKE_BUILD_TYPE}" SCON_BUILD_TYPE) - else(CMAKE_BUILD_TYPE) + if(DEBUG_ARMCOMPUTE) + set(SCON_BUILD_TYPE "debug") + else(DEBUG_ARMCOMPUTE) set(SCON_BUILD_TYPE "release") - endif(CMAKE_BUILD_TYPE) + endif(DEBUG_ARMCOMPUTE) #### Architecture-specific configurations diff --git a/infra/nnfw/cmake/packages/CpuInfoConfig.cmake b/infra/nnfw/cmake/packages/CpuInfoConfig.cmake index 878026d..dddec89 100644 --- a/infra/nnfw/cmake/packages/CpuInfoConfig.cmake +++ b/infra/nnfw/cmake/packages/CpuInfoConfig.cmake @@ -16,14 +16,18 @@ function(_CpuInfo_Build) nnas_include(ExternalProjectTools) - set(CPUINFO_BUILD_TOOLS OFF CACHE BOOL "Build command-line tools") - set(CPUINFO_BUILD_BENCHMARKS OFF CACHE BOOL "Build cpuinfo unit tests") - set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE BOOL "Build cpuinfo mock tests") - set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE BOOL "Build cpuinfo micro-benchmarks") + # Set build option + # - Static (position independent) + # - No logging + # - Library only (CPUINFO_RUNTIME_TYPE is not used) + set(CPUINFO_LIBRARY_TYPE "static" CACHE STRING "") + set(CPUINFO_LOG_LEVEL "none" CACHE STRING "") + set(CPUINFO_BUILD_TOOLS OFF CACHE BOOL "") + set(CPUINFO_BUILD_BENCHMARKS OFF CACHE BOOL "") + set(CPUINFO_BUILD_UNIT_TESTS OFF CACHE BOOL "") + set(CPUINFO_BUILD_MOCK_TESTS OFF CACHE BOOL "") add_extdirectory("${CpuInfoSource_DIR}" cpuinfo EXCLUDE_FROM_ALL) set_target_properties(cpuinfo PROPERTIES POSITION_INDEPENDENT_CODE ON) - # Suppress warnings generated by clog - set_target_properties(clog PROPERTIES COMPILE_FLAGS "-Wno-unused-result") set(CpuInfoSource_DIR ${CpuInfoSource_DIR} PARENT_SCOPE) set(CpuInfo_FOUND TRUE PARENT_SCOPE) endfunction(_CpuInfo_Build) diff --git a/infra/nnfw/cmake/packages/GLib2.0Config.cmake b/infra/nnfw/cmake/packages/GLib2.0Config.cmake new file mode 100644 index 0000000..d4c6bf2 --- /dev/null +++ b/infra/nnfw/cmake/packages/GLib2.0Config.cmake @@ -0,0 +1,41 @@ +function(_GLIB_2_0_import) + find_library(GLIB_LIBRARIES + NAMES glib-2.0) + + get_filename_component(GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} DIRECTORY) + find_path(GLIBCONFIG_INCLUDE_DIR + NAMES glibconfig.h + PATHS ${GLIB_LIBRARY_DIR} + PATH_SUFFIXES glib-2.0/include + NO_CMAKE_FIND_ROOT_PATH) + + find_path(GLIB_INCLUDE_DIR + NAMES glib.h + PATH_SUFFIXES glib-2.0) + + set(GLIB_FOUND TRUE) + + if(NOT GLIB_LIBRARIES) + set(GLIB_FOUND FALSE) + endif(NOT GLIB_LIBRARIES) + + if(NOT GLIBCONFIG_INCLUDE_DIR) + set(GLIB_FOUND FALSE) + endif(NOT GLIBCONFIG_INCLUDE_DIR) + + if(NOT GLIB_INCLUDE_DIR) + set(GLIB_FOUND FALSE) + endif(NOT GLIB_INCLUDE_DIR) + + set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR}) + + if(NOT GLIB_FOUND) + message(STATUS "Failed to find GLib 2.0") + endif(NOT GLIB_FOUND) + + set(GLIB2.0_FOUND ${GLIB_FOUND} PARENT_SCOPE) + set(GLIB2.0_INCLUDE_DIRS ${GLIB_INCLUDE_DIRS} PARENT_SCOPE) + set(GLIB2.0_LIBRARIES ${GLIB_LIBRARIES} PARENT_SCOPE) +endfunction(_GLIB_2_0_import) + +_GLIB_2_0_import() diff --git a/infra/nnfw/cmake/packages/Ruy/CMakeLists.txt b/infra/nnfw/cmake/packages/Ruy/CMakeLists.txt index 9140a17..a1c4656 100644 --- a/infra/nnfw/cmake/packages/Ruy/CMakeLists.txt +++ b/infra/nnfw/cmake/packages/Ruy/CMakeLists.txt @@ -1,4 +1,4 @@ -set(RUY_BASE ${RuySource_DIR}/ruy) +set(RUY_BASE ${TensorFlowRuySource_DIR}/ruy) # # Ruy library @@ -14,7 +14,6 @@ list(REMOVE_ITEM RUY_SRCS "${RUY_BASE}/example_advanced.cc") list(REMOVE_ITEM RUY_SRCS "${RUY_BASE}/tune_tool.cc") list(REMOVE_ITEM RUY_SRCS "${RUY_BASE}/pmu.cc") list(REMOVE_ITEM RUY_SRCS "${RUY_BASE}/create_trmul_params.cc") -list(REMOVE_ITEM RUY_SRCS "${RUY_BASE}/prepare_packed_matrices.cc") list(APPEND RUY_INSTRUMENTATION_SRCS "${RUY_BASE}/profiler/instrumentation.cc") @@ -23,7 +22,7 @@ if(PROFILE_RUY) list(APPEND RUY_PROFILER_SRCS "${RUY_BASE}/profiler/treeview.cc") endif(PROFILE_RUY) -list(APPEND RUY_INCLUDES "${RuySource_DIR}") +list(APPEND RUY_INCLUDES "${TensorFlowRuySource_DIR}") add_library(ruy STATIC ${RUY_SRCS}) target_include_directories(ruy SYSTEM PUBLIC ${RUY_INCLUDES}) diff --git a/infra/nnfw/cmake/packages/RuyConfig.cmake b/infra/nnfw/cmake/packages/RuyConfig.cmake index 4e7cc24a..6f5f4b7 100644 --- a/infra/nnfw/cmake/packages/RuyConfig.cmake +++ b/infra/nnfw/cmake/packages/RuyConfig.cmake @@ -5,14 +5,14 @@ function(_Ruy_Build) return() endif(TARGET ruy) - nnas_find_package(RuySource QUIET) + nnas_find_package(TensorFlowRuySource EXACT 2.8 QUIET) nnfw_find_package(CpuInfo QUIET) - if(NOT RuySource_FOUND) + if(NOT TensorFlowRuySource_FOUND) message(STATUS "RUY: Source not found") set(Ruy_FOUND FALSE PARENT_SCOPE) return() - endif(NOT RuySource_FOUND) + endif(NOT TensorFlowRuySource_FOUND) if (NOT CpuInfo_FOUND) message(STATUS "RUY: CPUINFO not found") @@ -20,6 +20,17 @@ function(_Ruy_Build) return() endif(NOT CpuInfo_FOUND) + # Ruy's cmake requires cmake >= 3.14 + # If we ready cmake >= 3.14, enable below comment out code + #if(PROFILE_RUY) + # # Will be used on ruy build + # set(RUY_PROFILER ON) + #endif(PROFILE_RUY) + #add_extdirectory("${RuySource_DIR}" Ruy) + # + ## Ignore warning from ruy + #target_compile_options(ruy INTERFACE -Wno-comment) + add_extdirectory("${CMAKE_CURRENT_LIST_DIR}/Ruy" ruy) set(Ruy_FOUND TRUE PARENT_SCOPE) endfunction(_Ruy_Build) diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-1.13.1/TensorFlowLite/CMakeLists.txt b/infra/nnfw/cmake/packages/TensorFlowLite-1.13.1/TensorFlowLite/CMakeLists.txt index 9a7b240..f872b88 100644 --- a/infra/nnfw/cmake/packages/TensorFlowLite-1.13.1/TensorFlowLite/CMakeLists.txt +++ b/infra/nnfw/cmake/packages/TensorFlowLite-1.13.1/TensorFlowLite/CMakeLists.txt @@ -52,6 +52,12 @@ target_compile_definitions(tensorflow-lite PUBLIC "GEMMLOWP_ALLOW_SLOW_SCALAR_FA set_property(TARGET tensorflow-lite PROPERTY POSITION_INDEPENDENT_CODE ON) target_link_libraries(tensorflow-lite eigen-tf-1.13.1 flatbuffers::flatbuffers ${LIB_PTHREAD} dl) +# Define TF_LITE_DISABLE_X86_NEON for debug build +# If we upgrade NEON2SSE version, we can remove below line +if(NEON2SSESource_FOUND) + target_compile_definitions(tensorflow-lite PRIVATE $<$:TF_LITE_DISABLE_X86_NEON>) +endif(NEON2SSESource_FOUND) + if(ANDROID) target_link_libraries(tensorflow-lite log) target_include_directories(tensorflow-lite PUBLIC "${NDK_DIR}/..") diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLite/CMakeLists.txt b/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLite/CMakeLists.txt deleted file mode 100644 index afee6e1..0000000 --- a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLite/CMakeLists.txt +++ /dev/null @@ -1,96 +0,0 @@ -# Reference: https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/lite/tools/make/Makefile -# -# Tensorflow Lite library 2.3.0 -# -set(TENSORFLOW_LITE_BASE ${TensorFlowSource_DIR}/tensorflow/lite) - -file(GLOB TFLITE_CORE_SRCS "${TENSORFLOW_LITE_BASE}/*.c" - "${TENSORFLOW_LITE_BASE}/*.cc" - "${TENSORFLOW_LITE_BASE}/core/*.cc") - -file(GLOB_RECURSE TFLITE_KERNEL_SRCS "${TENSORFLOW_LITE_BASE}/kernels/*.cc") - -file(GLOB TFLITE_LIB_SRCS "${TENSORFLOW_LITE_BASE}/c/*.c" "${TENSORFLOW_LITE_BASE}/c/*.cc") - -file(GLOB TFLITE_API_SRCS "${TENSORFLOW_LITE_BASE}/core/api/*.c" - "${TENSORFLOW_LITE_BASE}/core/api/*.cc") - -list(APPEND TFLITE_PROFILING_SRCS "${TENSORFLOW_LITE_BASE}/profiling/memory_info.cc") -list(APPEND TFLITE_PROFILING_SRCS "${TENSORFLOW_LITE_BASE}/profiling/time.cc") - -file(GLOB TFLITE_EXPERIMENTAL_SRCS "${TENSORFLOW_LITE_BASE}/experimental/resource/*.cc") - -file(GLOB TFLITE_SPARSITY_SRCS "${TENSORFLOW_LITE_BASE}/tools/optimize/sparsity/*.cc") - -list(APPEND TFLITE_SRCS ${TFLITE_CORE_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_KERNEL_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_LIB_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_API_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_PROFILING_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_EXPERIMENTAL_SRCS}) -list(APPEND TFLITE_SRCS ${TFLITE_SPARSITY_SRCS}) - -# externals -list(APPEND TFLITE_SRCS "${OouraFFTSource_DIR}/fftsg.c") -list(APPEND TFLITE_SRCS "${OouraFFTSource_DIR}/fftsg2d.c") - -# Build with mmap? true -# caution: v2.3.0's Makefile has wrong code on this part. This is fixed on master branch. -set(BUILD_WITH_MMAP TRUE) -if(${BUILD_WITH_MMAP}) - list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/mmap_allocation_disabled.cc") -else() - list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/mmap_allocation.cc") -endif() - -# Build with nnapi? true -# caution: this nnapi delegate comes from tflite, not ours. -set(BUILD_WITH_NNAPI TRUE) -if(${BUILD_WITH_NNAPI}) - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/nnapi_delegate.cc") - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/quant_lstm_sup.cc") - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_implementation.cc") - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_util.cc") -else() - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/nnapi_delegate_disabled.cc") - list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_implementation_disabled.cc") -endif() - -# ios: we don't support ios -list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/minimal_logging_ios.cc") - -# android -if(NOT ANDROID) - list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/minimal_logging_android.cc") -endif() - -# exclude some source files -file(GLOB_RECURSE TFLITE_EXCLS "${TENSORFLOW_LITE_BASE}/*test*.cc" - "${TENSORFLOW_LITE_BASE}/*benchmark*.cc" - "${TENSORFLOW_LITE_BASE}/*example*.cc" - "${TENSORFLOW_LITE_BASE}/*tool*.cc") -list(REMOVE_ITEM TFLITE_SRCS ${TFLITE_EXCLS}) - -# include headers -list(APPEND TFLITE_INCLUDES "${TensorFlowSource_DIR}") -list(APPEND TFLITE_INCLUDES "${TensorFlowGEMMLowpSource_DIR}") -list(APPEND TFLITE_INCLUDES "${Fp16Source_DIR}/include") - -if(NEON2SSESource_FOUND) - list(APPEND TFLITE_INCLUDES "${NEON2SSESource_DIR}") -endif(NEON2SSESource_FOUND) - -add_library(tensorflow-lite-2.3.0 STATIC ${TFLITE_SRCS}) -target_include_directories(tensorflow-lite-2.3.0 SYSTEM PUBLIC ${TFLITE_INCLUDES}) -target_include_directories(tensorflow-lite-2.3.0 PRIVATE ${CpuInfoSource_DIR}) -target_compile_definitions(tensorflow-lite-2.3.0 PUBLIC "GEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK -DTFLITE_WITH_RUY -DTFLITE_WITH_RUY_GEMV -DRUY_HAVE_CPUINFO") -set_property(TARGET tensorflow-lite-2.3.0 PROPERTY POSITION_INDEPENDENT_CODE ON) -target_link_libraries(tensorflow-lite-2.3.0 eigen flatbuffers::flatbuffers ruy abseil farmhash ${LIB_PTHREAD} dl) -if(NOT ANDROID AND ${BUILD_WITH_NNAPI}) - target_link_libraries(tensorflow-lite-2.3.0 rt) -endif() - -if(ANDROID) - target_link_libraries(tensorflow-lite-2.3.0 log) - target_include_directories(tensorflow-lite-2.3.0 PUBLIC "${NDK_DIR}/..") -endif() diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfig.cmake b/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfig.cmake deleted file mode 100644 index c81958c..0000000 --- a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfig.cmake +++ /dev/null @@ -1,44 +0,0 @@ -if(BUILD_TENSORFLOW_LITE_2_3_0) - macro(return_unless VAR) - if(NOT ${VAR}) - message("TFLiteVanillaRun: ${VAR} NOT TRUE") - set(TensorFlowLite_2_3_0_FOUND FALSE PARENT_SCOPE) - return() - endif(NOT ${VAR}) - endmacro(return_unless) - - nnas_include(ExternalSourceTools) - nnas_include(OptionTools) - - nnas_find_package(TensorFlowSource EXACT 2.3.0 QUIET) - return_unless(TensorFlowSource_FOUND) - - # Below urls come from https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/tensorflow/workspace.bzl - nnas_find_package(AbseilSource QUIET) - return_unless(AbseilSource_FOUND) - nnfw_find_package(Eigen QUIET) - return_unless(Eigen_FOUND) - nnas_find_package(Farmhash QUIET) - return_unless(Farmhash_FOUND) - nnfw_find_package(FlatBuffers QUIET) - return_unless(FlatBuffers_FOUND) - nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.3.0 QUIET) - return_unless(TensorFlowGEMMLowpSource_FOUND) - nnas_find_package(OouraFFTSource QUIET) - return_unless(OouraFFTSource_FOUND) - nnfw_find_package(Ruy QUIET) - return_unless(Ruy_FOUND) - - # TensorFlow Lite requires FP16 library's header only - nnas_find_package(Fp16Source QUIET) - return_unless(Fp16Source_FOUND) - - # Optional packages - nnas_find_package(NEON2SSESource QUIET) - - nnas_include(ExternalProjectTools) - add_extdirectory("${CMAKE_CURRENT_LIST_DIR}/TensorFlowLite" tflite-2.3.0) - - set(TensorFlowLite_2_3_0_FOUND TRUE) - return() -endif() diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfigVersion.cmake b/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfigVersion.cmake deleted file mode 100644 index 08e6374..0000000 --- a/infra/nnfw/cmake/packages/TensorFlowLite-2.3.0/TensorFlowLiteConfigVersion.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set(PACKAGE_VERSION "2.3.0") -set(PACKAGE_VERSION_EXACT FALSE) -set(PACKAGE_VERSION_COMPATIBLE FALSE) -set(PACKAGE_VERSION_UNSUITABLE TRUE) - -if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) - set(PACKAGE_VERSION_EXACT TRUE) - set(PACKAGE_VERSION_UNSUITABLE FALSE) -endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLite/CMakeLists.txt b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLite/CMakeLists.txt new file mode 100644 index 0000000..d7e1d06 --- /dev/null +++ b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLite/CMakeLists.txt @@ -0,0 +1,121 @@ +# Reference: https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/lite/tools/make/Makefile +# +# Tensorflow Lite library 2.3.0 +# +set(TENSORFLOW_LITE_BASE ${TensorFlowSource_DIR}/tensorflow/lite) + +file(GLOB TFLITE_CORE_SRCS "${TENSORFLOW_LITE_BASE}/*.c" + "${TENSORFLOW_LITE_BASE}/*.cc" + "${TENSORFLOW_LITE_BASE}/core/*.cc") + +file(GLOB_RECURSE TFLITE_KERNEL_SRCS "${TENSORFLOW_LITE_BASE}/kernels/*.cc") + +file(GLOB TFLITE_LIB_SRCS "${TENSORFLOW_LITE_BASE}/c/*.c" "${TENSORFLOW_LITE_BASE}/c/*.cc") + +file(GLOB TFLITE_API_SRCS "${TENSORFLOW_LITE_BASE}/core/api/*.c" + "${TENSORFLOW_LITE_BASE}/core/api/*.cc") + +list(APPEND TFLITE_PROFILING_SRCS "${TENSORFLOW_LITE_BASE}/profiling/memory_info.cc") +list(APPEND TFLITE_PROFILING_SRCS "${TENSORFLOW_LITE_BASE}/profiling/time.cc") +list(APPEND TFLITE_PROFILING_SRCS "${TENSORFLOW_LITE_BASE}/profiling/platform_profiler.cc") + +file(GLOB TFLITE_EXPERIMENTAL_SRCS "${TENSORFLOW_LITE_BASE}/experimental/resource/*.cc") + +file(GLOB TFLITE_SCHEMA_UTIL_SRCS "${TENSORFLOW_LITE_BASE}/schema/*.cc") + +# Moved to kerenls/internal/utils +#file(GLOB TFLITE_SPARSITY_SRCS "${TENSORFLOW_LITE_BASE}/tools/optimize/sparsity/*.cc") + +list(APPEND TFLITE_SRCS ${TFLITE_CORE_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_KERNEL_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_LIB_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_API_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_PROFILING_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_EXPERIMENTAL_SRCS}) +#list(APPEND TFLITE_SRCS ${TFLITE_SPARSITY_SRCS}) +list(APPEND TFLITE_SRCS ${TFLITE_SCHEMA_UTIL_SRCS}) + +# externals +list(APPEND TFLITE_SRCS "${OouraFFTSource_DIR}/fftsg.c") +list(APPEND TFLITE_SRCS "${OouraFFTSource_DIR}/fftsg2d.c") + +# Build with mmap? true +# caution: v2.3.0's Makefile has wrong code on this part. This is fixed on master branch. +set(BUILD_WITH_MMAP TRUE) +if(${BUILD_WITH_MMAP}) + list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/mmap_allocation_disabled.cc") +else() + list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/mmap_allocation.cc") +endif() + +# Build with nnapi? true +# caution: this nnapi delegate comes from tflite, not ours. +set(BUILD_WITH_NNAPI TRUE) +if(${BUILD_WITH_NNAPI}) + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/nnapi_delegate.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/quant_lstm_sup.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/utils.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/serialization.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_implementation.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_util.cc") +else() + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/delegates/nnapi/nnapi_delegate_disabled.cc") + list(APPEND TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/nnapi/nnapi_implementation_disabled.cc") +endif() + +# ios: we don't support ios +list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/minimal_logging_ios.cc") + +# android +if(NOT ANDROID) + list(REMOVE_ITEM TFLITE_SRCS "${TENSORFLOW_LITE_BASE}/minimal_logging_android.cc") +endif() + +# exclude some source files +file(GLOB_RECURSE TFLITE_EXCLS "${TENSORFLOW_LITE_BASE}/*test*.cc" + "${TENSORFLOW_LITE_BASE}/*benchmark*.cc" + "${TENSORFLOW_LITE_BASE}/*example*.cc" + "${TENSORFLOW_LITE_BASE}/*tool*.cc") +list(REMOVE_ITEM TFLITE_SRCS ${TFLITE_EXCLS}) + +# exclude some kernels (requires python3-dev package) +# TODO Enable these kernels by installing package on build system +file(GLOB_RECURSE TFLITE_KERNEL_EXCLS "${TENSORFLOW_LITE_BASE}/kernels/variable_ops_wrapper.cc" + "${TENSORFLOW_LITE_BASE}/kernels/gradient/*.cc" + "${TENSORFLOW_LITE_BASE}/kernels/perception/*.cc") +list(REMOVE_ITEM TFLITE_SRCS ${TFLITE_KERNEL_EXCLS}) + +# exclude kernel shim +file(GLOB_RECURSE TFLITE_SHIM_EXCLS "${TENSORFLOW_LITE_BASE}/kernels/shim/*.cc") +list(REMOVE_ITEM TFLITE_SRCS ${TFLITE_SHIM_EXCLS}) + +# include headers +list(APPEND TFLITE_INCLUDES "${TensorFlowSource_DIR}") +list(APPEND TFLITE_INCLUDES "${TensorFlowGEMMLowpSource_DIR}") +list(APPEND TFLITE_INCLUDES "${Fp16Source_DIR}/include") +#list(APPEND TFLITE_INCLUDES "${Pybind11Source_DIR}/include") + +if(NEON2SSESource_FOUND) + list(APPEND TFLITE_INCLUDES "${NEON2SSESource_DIR}") +endif(NEON2SSESource_FOUND) + +add_library(tensorflow-lite-2.8.0 STATIC ${TFLITE_SRCS}) +target_include_directories(tensorflow-lite-2.8.0 SYSTEM PUBLIC ${TFLITE_INCLUDES}) +target_include_directories(tensorflow-lite-2.8.0 PRIVATE ${CpuInfoSource_DIR}) +target_compile_definitions(tensorflow-lite-2.8.0 PUBLIC "GEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK -DTFLITE_WITH_RUY -DTFLITE_WITH_RUY_GEMV -DRUY_HAVE_CPUINFO") +set_property(TARGET tensorflow-lite-2.8.0 PROPERTY POSITION_INDEPENDENT_CODE ON) +target_link_libraries(tensorflow-lite-2.8.0 eigen flatbuffers::flatbuffers ruy abseil farmhash ${LIB_PTHREAD} dl) +if(NOT ANDROID AND ${BUILD_WITH_NNAPI}) + target_link_libraries(tensorflow-lite-2.8.0 rt) +endif() + +# Define TF_LITE_DISABLE_X86_NEON for debug build +# If we upgrade NEON2SSE version, we can remove below line +if(NEON2SSESource_FOUND) + target_compile_definitions(tensorflow-lite-2.8.0 PRIVATE $<$:TF_LITE_DISABLE_X86_NEON>) +endif(NEON2SSESource_FOUND) + +if(ANDROID) + target_link_libraries(tensorflow-lite-2.8.0 log) + target_include_directories(tensorflow-lite-2.8.0 PUBLIC "${NDK_DIR}/..") +endif() diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfig.cmake b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfig.cmake new file mode 100644 index 0000000..1c80618 --- /dev/null +++ b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfig.cmake @@ -0,0 +1,50 @@ +if(BUILD_TENSORFLOW_LITE_2_8_0) + macro(return_unless VAR) + if(NOT ${VAR}) + message("TFLite 2.8: ${VAR} NOT TRUE") + set(TensorFlowLite_2_8_0_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT ${VAR}) + endmacro(return_unless) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + nnas_find_package(TensorFlowSource EXACT 2.8.0 QUIET) + return_unless(TensorFlowSource_FOUND) + + # Below urls come from https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/tensorflow/workspace.bzl + nnas_find_package(AbseilSource QUIET) + return_unless(AbseilSource_FOUND) + nnfw_find_package(Eigen QUIET) + return_unless(Eigen_FOUND) + nnas_find_package(Farmhash QUIET) + return_unless(Farmhash_FOUND) + nnfw_find_package(FlatBuffers QUIET) + return_unless(FlatBuffers_FOUND) + nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.8.0 QUIET) + return_unless(TensorFlowGEMMLowpSource_FOUND) + nnas_find_package(OouraFFTSource QUIET) + return_unless(OouraFFTSource_FOUND) + nnfw_find_package(Ruy QUIET) + return_unless(Ruy_FOUND) + + # TensorFlow Lite requires FP16 library's header only + nnas_find_package(Fp16Source QUIET) + return_unless(Fp16Source_FOUND) + + # TensorFlow Lite requires Pybind11 library's header only + # But Pybind11 requires python3-dev package + # TODO Enable below by installing package on build system + #nnas_find_package(Pybind11Source QUIET) + #return_unless(Pybind11Source_FOUND) + + # Optional packages + nnas_find_package(NEON2SSESource QUIET) + + nnas_include(ExternalProjectTools) + add_extdirectory("${CMAKE_CURRENT_LIST_DIR}/TensorFlowLite" tflite-2.8.0) + + set(TensorFlowLite_2_8_0_FOUND TRUE) + return() +endif() diff --git a/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfigVersion.cmake b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfigVersion.cmake new file mode 100644 index 0000000..cd49d7b --- /dev/null +++ b/infra/nnfw/cmake/packages/TensorFlowLite-2.8.0/TensorFlowLiteConfigVersion.cmake @@ -0,0 +1,9 @@ +set(PACKAGE_VERSION "2.8.0") +set(PACKAGE_VERSION_EXACT FALSE) +set(PACKAGE_VERSION_COMPATIBLE FALSE) +set(PACKAGE_VERSION_UNSUITABLE TRUE) + +if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + set(PACKAGE_VERSION_UNSUITABLE FALSE) +endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/nnfw/config/gbs.conf b/infra/nnfw/config/gbs.conf index 1150a5f..2b5994a 100644 --- a/infra/nnfw/config/gbs.conf +++ b/infra/nnfw/config/gbs.conf @@ -3,20 +3,11 @@ profile = profile.tizen [profile.tizen] -user=obs_viewer -obs = obs.tizen -repos = repo.tizen_one,repo.tizen_base,repo.tizen_mobile +repos = repo.tizen_base,repo.tizen_mobile buildroot = /home/GBS-ROOT/ -[obs.tizen] -url = http://api.tizen.org - [repo.tizen_mobile] url = http://download.tizen.org/snapshots/tizen/unified/latest/repos/standard/packages/ [repo.tizen_base] url = http://download.tizen.org/snapshots/tizen/base/latest/repos/standard/packages/ - -[repo.tizen_one] -url = http://13.125.34.93/archive/tizen/ - diff --git a/infra/packaging/preset/20220323 b/infra/packaging/preset/20220323 index 421106c..0eac106 100644 --- a/infra/packaging/preset/20220323 +++ b/infra/packaging/preset/20220323 @@ -20,21 +20,26 @@ function preset_configure() # loco IR and related utilities REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") # Flatbuffer I/O - REQUIRED_UNITS+=("mio-tflite" "mio-tflite260" "mio-tflite280" "mio-circle04") + REQUIRED_UNITS+=("mio-tflite280" "mio-circle04") # Data I/O REQUIRED_UNITS+=("dio-hdf5") # Circle compiler library (.circle -> .circle) REQUIRED_UNITS+=("luci") # Tools - REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef" "circlechef") + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") REQUIRED_UNITS+=("circle-tensordump" "circledump") - REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter" "circle-verify") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") REQUIRED_UNITS+=("luci-eval-driver") REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5") - REQUIRED_UNITS+=("circle-partitioner") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") REQUIRED_UNITS+=("one-cmds") REQUIRED_UNITS+=("bcq-tools") + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + NPROC=${NPROC:-$(cat /proc/cpuinfo | grep -c processor)} # TODO Use "nncc configure" and "nncc build" diff --git a/infra/packaging/preset/20220323_windows b/infra/packaging/preset/20220323_windows index 60500b1..14917b3 100644 --- a/infra/packaging/preset/20220323_windows +++ b/infra/packaging/preset/20220323_windows @@ -15,20 +15,26 @@ function preset_configure() # loco IR and related utilities REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") # Flatbuffer I/O - REQUIRED_UNITS+=("mio-tflite" "mio-tflite260" "mio-tflite280" "mio-circle04") + REQUIRED_UNITS+=("mio-tflite280" "mio-circle04") # Data I/O REQUIRED_UNITS+=("dio-hdf5") # Circle compiler library (.circle -> .circle) REQUIRED_UNITS+=("luci") # Tools - REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef" "circlechef") - REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter" "circle-verify") + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") + REQUIRED_UNITS+=("circle-tensordump" "circledump") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") REQUIRED_UNITS+=("luci-eval-driver") REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5") - REQUIRED_UNITS+=("circle-partitioner") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") REQUIRED_UNITS+=("one-cmds") REQUIRED_UNITS+=("bcq-tools") + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + NPROC=$(cat /proc/cpuinfo | grep -c processor) # TODO Use "nncc configure" and "nncc build" diff --git a/infra/packaging/res/tf2nnpkg.20220323 b/infra/packaging/res/tf2nnpkg.20220323 index 0d44818..5f43b23 100644 --- a/infra/packaging/res/tf2nnpkg.20220323 +++ b/infra/packaging/res/tf2nnpkg.20220323 @@ -104,6 +104,6 @@ fi ${ONE_IMPORT_BCQ_SCRIPT} # optimize -"${ROOT}/bin/circle2circle" --O1 "${TMPDIR}/${MODEL_NAME}.tmp.circle" "${TMPDIR}/${MODEL_NAME}.circle" +"${ROOT}/bin/circle2circle" --resolve_customop_add "${TMPDIR}/${MODEL_NAME}.tmp.circle" "${TMPDIR}/${MODEL_NAME}.circle" "${ROOT}/bin/model2nnpkg.sh" -o "${OUTPUT_DIR}" "${TMPDIR}/${MODEL_NAME}.circle" diff --git a/infra/scripts/compiler_modules.sh b/infra/scripts/compiler_modules.sh index 6a857d2..51cba92 100644 --- a/infra/scripts/compiler_modules.sh +++ b/infra/scripts/compiler_modules.sh @@ -1,5 +1,8 @@ #!/bin/bash +# NOTE this file is sourced from, for the purpose of +# - configure_compiler_coverage.sh: to get test coverage for release criteria + # Don't run this script [[ "${BASH_SOURCE[0]}" == "${0}" ]] && echo "Please don't execute ${BASH_SOURCE[0]}, source it" && return @@ -8,13 +11,14 @@ DEBUG_BUILD_ITEMS+=";oops;pepper-assert;pepper-csv2vec" DEBUG_BUILD_ITEMS+=";hermes;hermes-std" DEBUG_BUILD_ITEMS+=";loco;locop;locomotiv;logo-core;logo" DEBUG_BUILD_ITEMS+=";foder;crew;souschef;arser;vconone" -DEBUG_BUILD_ITEMS+=";safemain;mio-circle04;mio-tflite;mio-tflite260;mio-tflite280" +DEBUG_BUILD_ITEMS+=";safemain;mio-circle04;mio-tflite280;dio-hdf5" DEBUG_BUILD_ITEMS+=";tflite2circle" DEBUG_BUILD_ITEMS+=";luci" DEBUG_BUILD_ITEMS+=";luci-interpreter" DEBUG_BUILD_ITEMS+=";luci-eval-driver;luci-pass-value-test;luci-value-test" DEBUG_BUILD_ITEMS+=";circle2circle;record-minmax;circle-quantizer" -DEBUG_BUILD_ITEMS+=";circle-partitioner;circle-part-driver" +DEBUG_BUILD_ITEMS+=";circle-eval-diff" +DEBUG_BUILD_ITEMS+=";circle-partitioner;circle-part-driver;circle-operator" DEBUG_BUILD_ITEMS+=";circle-verify" DEBUG_BUILD_ITEMS+=";circle-tensordump" DEBUG_BUILD_ITEMS+=";tflchef;circlechef" @@ -25,3 +29,5 @@ DEBUG_BUILD_ITEMS+=";tf2tfliteV2;tf2tfliteV2-conversion-test" DEBUG_BUILD_ITEMS+=";tflite2circle-conversion-test" DEBUG_BUILD_ITEMS+=";pota-quantization-value-test" DEBUG_BUILD_ITEMS+=";circle-part-value-test" +DEBUG_BUILD_ITEMS+=";circle-quantizer-dredd-recipe-test" +DEBUG_BUILD_ITEMS+=";circle-operator-test" diff --git a/infra/scripts/docker_build_nncc.sh b/infra/scripts/docker_build_nncc.sh index 7146141..2e603b5 100755 --- a/infra/scripts/docker_build_nncc.sh +++ b/infra/scripts/docker_build_nncc.sh @@ -27,13 +27,13 @@ else fi # prepare tensorflow -if [ -d $TENSORFLOW_PREFIX ]; then +if [ -n "$TENSORFLOW_PREFIX" ]; then DOCKER_OPTS+=" -v $TENSORFLOW_PREFIX:/opt/tensorflow" CONFIG_OPTIONS+=" -DTENSORFLOW_PREFIX=/opt/tensorflow" fi # prepare onnx -if [ -d $ONNXRUNTIME_PREFIX ]; then +if [ -n "$ONNXRUNTIME_PREFIX" ]; then DOCKER_OPTS+=" -v $ONNXRUNTIME_PREFIX:/opt/onnxruntime" CONFIG_OPTIONS+=" -DONNXRUNTIME_PREFIX=/opt/onnxruntime" fi diff --git a/infra/scripts/docker_build_test_x64.sh b/infra/scripts/docker_build_test_x64.sh index 26d8de4..b3428e0 100755 --- a/infra/scripts/docker_build_test_x64.sh +++ b/infra/scripts/docker_build_test_x64.sh @@ -32,8 +32,8 @@ pushd $ROOT_PATH > /dev/null export DOCKER_ENV_VARS export DOCKER_VOLUMES export BUILD_OPTIONS -# Disable nnpackage_run build: mismatch between buildtool for CI and installed hdf5 -CMD="export OPTIONS='-DBUILD_NNPACKAGE_RUN=OFF $BUILD_OPTIONS' && \ + +CMD="export OPTIONS='$BUILD_OPTIONS' && \ export BUILD_TYPE=Release && \ cp -nv Makefile.template Makefile && \ make all install build_test_suite" diff --git a/infra/scripts/docker_collect_nnpkg_resources.sh b/infra/scripts/docker_collect_nnpkg_resources.sh index 06cf880..afdd3b9 100755 --- a/infra/scripts/docker_collect_nnpkg_resources.sh +++ b/infra/scripts/docker_collect_nnpkg_resources.sh @@ -28,13 +28,13 @@ else fi # prepare tensorflow -if [ -d $TENSORFLOW_PREFIX ]; then +if [ -n "$TENSORFLOW_PREFIX" ]; then DOCKER_OPTS+=" -v $TENSORFLOW_PREFIX:/opt/tensorflow" CONFIG_OPTIONS+=" -DTENSORFLOW_PREFIX=/opt/tensorflow" fi # prepare onnx -if [ -d $ONNXRUNTIME_PREFIX ]; then +if [ -n "$ONNXRUNTIME_PREFIX" ]; then DOCKER_OPTS+=" -v $ONNXRUNTIME_PREFIX:/opt/onnxruntime" CONFIG_OPTIONS+=" -DONNXRUNTIME_PREFIX=/opt/onnxruntime" fi @@ -71,7 +71,7 @@ REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") # Circle compiler library (.circle -> .circle) REQUIRED_UNITS+=("luci") # Flatbuffer I/O -REQUIRED_UNITS+=("mio-tflite" "mio-tflite260" "mio-tflite280" "mio-circle04") +REQUIRED_UNITS+=("mio-tflite280" "mio-circle04") # Tools REQUIRED_UNITS+=("tflite2circle" "circle2circle" "luci-interpreter") REQUIRED_UNITS+=("souschef" "tflchef" "circlechef" "circle-verify") diff --git a/infra/scripts/test_ubuntu_runtime_mixed.sh b/infra/scripts/test_ubuntu_runtime_mixed.sh index 697fed8..2510d9c 100755 --- a/infra/scripts/test_ubuntu_runtime_mixed.sh +++ b/infra/scripts/test_ubuntu_runtime_mixed.sh @@ -55,8 +55,8 @@ echo "GeneratedTests.squeeze_relaxed" >> $SKIPLIST_PREFIX.union # Run the test export OP_BACKEND_Conv2D="cpu" -export OP_BACKEND_MaxPool2D="acl_cl" -export OP_BACKEND_AvgPool2D="acl_neon" +export OP_BACKEND_Pool2D="acl_cl" +export OP_BACKEND_FullyConnected="acl_neon" export ACL_LAYOUT="NCHW" export RUY_THREADS=4 NNAPIGTest "acl_cl;acl_neon;cpu" "Product/out/unittest/nnapi_gtest.skip.${TEST_ARCH}-${TEST_OS}.union" "report/mixed" diff --git a/infra/scripts/unittest_compiler_xml.sh b/infra/scripts/unittest_compiler_xml.sh index 46d3bc8..6e9e8ad 100755 --- a/infra/scripts/unittest_compiler_xml.sh +++ b/infra/scripts/unittest_compiler_xml.sh @@ -7,7 +7,9 @@ set -eo pipefail CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_PATH="$CURRENT_PATH/../../" NNCC_WORKSPACE=${NNCC_WORKSPACE:-${ROOT_PATH}build} -UNITTEST_REPORT_DIR=${NNCC_WORKSPACE}/unittest_compiler_xml + +# Use fixed absolute report dir for CI +UNITTEST_REPORT_DIR=${ROOT_PATH}build/unittest_compiler_xml for i in "$@" do @@ -25,5 +27,10 @@ fi for TEST_BIN in `find ${NNCC_WORKSPACE}/compiler -type f -executable -name *_test`; do TEST_NAME="$(basename -- $TEST_BIN)" - LUGI_LOG=999 $TEST_BIN --gtest_output="xml:$UNITTEST_REPORT_DIR/$TEST_NAME.xml" + TEST_DIR="$(dirname $TEST_BIN)" + + # Execute on test directory to find related file + pushd $TEST_DIR > /dev/null + LUGI_LOG=999 ./$TEST_NAME --gtest_output="xml:$UNITTEST_REPORT_DIR/$TEST_NAME.xml" + popd > /dev/null done diff --git a/nnpackage/examples/README.md b/nnpackage/examples/README.md index fb0bae3..951048b 100644 --- a/nnpackage/examples/README.md +++ b/nnpackage/examples/README.md @@ -1,5 +1,12 @@ # NNPackage example +## Package version 1.3.0 + +### two_tflites + +- Model file: two TensorFlow Lite models +- It has two tflite models with pkg-input, pkg-output and model-connect fields. + ## Package version 1.1.0 ### one_op_in_tflite diff --git a/nnpackage/examples/v1.3.0/two_tflites/README.md b/nnpackage/examples/v1.3.0/two_tflites/README.md new file mode 100644 index 0000000..3fcbe2d --- /dev/null +++ b/nnpackage/examples/v1.3.0/two_tflites/README.md @@ -0,0 +1,28 @@ +## How to create + +``` +$ wget https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz +$ tar -zxf mobilenet_v1_1.0_224.tgz + +$ python tools/tflitefile_tool/select_operator.py mobilenet_v1_1.0_224.tflite <( echo 0-1 ) mv1.0_1.tflite +$ python tools/tflitefile_tool/select_operator.py mv1.0_1.tflite <( echo 0 ) mv1.0.tflite +$ python tools/tflitefile_tool/select_operator.py mv1.0_1.tflite <( echo 1 ) mv1.1.tflite + +# make sure three tflite is valid +$ ./Product/out/bin/tflite_comparator mv1.0_1.tflite +$ ./Product/out/bin/tflite_comparator mv1.0.tflite +$ ./Product/out/bin/tflite_comparator mv1.1.tflite + +$ tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh -m mv1.0.tflite mv1.1.tflite -p two_tflites +$ cat two_tflites/metadata/MANIFEST +{ + "major-version" : "1", + "minor-version" : "2", + "patch-version" : "0", + "configs" : [ ], + "models" : [ "mv1.0.tflite", "mv1.1.tflite" ], + "model-types" : [ "tflite", "tflite" ] +} + +# update minor-version, and add additional fields manually +``` diff --git a/nnpackage/examples/v1.3.0/two_tflites/metadata/MANIFEST b/nnpackage/examples/v1.3.0/two_tflites/metadata/MANIFEST new file mode 100644 index 0000000..9d9e21a --- /dev/null +++ b/nnpackage/examples/v1.3.0/two_tflites/metadata/MANIFEST @@ -0,0 +1,11 @@ +{ + "major-version" : "1", + "minor-version" : "3", + "patch-version" : "0", + "configs" : [ ], + "models" : [ "mv1.0.tflite", "mv1.1.tflite" ], + "model-types" : [ "tflite", "tflite" ], + "pkg-inputs" : [ "0:0:0" ], + "pkg-outputs" : [ "1:0:0" ], + "model-connect" : [ { "from" : "0:0:0", "to" : [ "1:0:0" ] } ] +} diff --git a/nnpackage/examples/v1.3.0/two_tflites/metadata/tc/expected.h5 b/nnpackage/examples/v1.3.0/two_tflites/metadata/tc/expected.h5 new file mode 100644 index 0000000000000000000000000000000000000000..59a6b904095f4a73391a98881912e269f9cab43a GIT binary patch literal 1614584 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
`sD1NEi-Dc@ zf$p2tl!othmP4)jvzdRj~a86*%tM1BWV&$ znp2OoD%-gj+H_XddWe6V$Nb}WIgniz?65u>tBk*eHj{Xbf| zUw7$Q2W)&ZMdpXc>F>!7y`z;eh3Of5&c%~Ku25sx39_Tv#vIbjKl3)9AIg(p(!2fe zcji|3T0NWdJP+uF~uMbI7Q4>K4qc}hodWb|%$wmuQoIIrXK#!C)}wa4zLuYb-7OM~XJ zFM1hrEQg|B72kh7*4@KpdTa1FZ(*IZXJ!kCiK%P64#udRlPQhjFV)g%D`x8bY{wz7m{6YPBJ&{ng zf734$;7<2Yto-OGVYsxNjp}bdQ*`}xU$s|RRx~i!?dN}TVTl0QqBjC{v|g4evci-Y!}aKE<(dXDK0zZ&LlgD z%n#3&HKaV)T{uLVgxW&_3p$4={4Rf5!^h#$|Br`{mphu@cP2|k~p5X*rY+l{ikySIhM(s?3Y7Fb-ham7|Q&ZAoNwoOr-s1 zm>G|A$xHgYP!c~BO@@5ua(7j0ptE%p+TGqF)}7B({Jb1_{A1TK72p4yGHJ?X68Ca! zqBKrgR*AG&d(%nbk94$$)7zx=clxJpWS0fj*{Q!{eo_Z zuLo4r}u$7%i9C67Nba=wn(dPREve__*avM<-S!ha9EKHdgR zS8!dJEDXifh*9Wz);q}7Y1C@I!e72>Fxg=bK2!X_nfnBco>%<;O^({< z&G9fk{{atC_9cx2dul=cI87KgLfLDGb{DRp+xF9{Yxf_+l22dRZ$dS;i+-KWWPa4W z?7_CV%?e4AV33UsX|ZQ#GpLlYo6;ok=Vu+}AL|q-A<_};o1I4k+f7*YRbBSe%uE$E z7I~?kzr8CS^-Wa2ms46pn+N%_{n`{0nIC?%kz!`Zjk#Ay)P!O|c8g|BtQfj_djR{z^)vBs(HALbA%}ea}%OkzLsmqO2k47HQ$c$km@SO=phF^ z#n9LBD8By!yCkkV+d0pc$LFlvdy0S5Y21`_P+R_=1-5hW4kxbJ+w3}2H2%QYg0+dn z+5ceosU#}|Lz+xS<7GR+WI4w$I8tyM46g=A*I#|0TR;QF4g)V{kPO{@)b$Umzwb;K zdRF4LW4V`KK5-(@Neg7YxOOPK65rXs&)7w*{CmLq6GE}V%Kq4`J@1QgscR8byDEJ= zcxWnAw|7!(alzhmimcY(q(UrVaQwzqMTa)gdlgw3|4`WUJ}k`n$^Je4|2zJNP7Z@V zx%-qnIAy4p$O?FXPVE9f%Pt8PY~yoC?E3E*vVl`K%Afzx@0v*%_G-;@l;v<=-bX;K zm`$qx6Vr!5^sV>of6f-}XR;;2`1plTa=$l*jBw_3lgZk`>vbu1d3Q_X6dJP;*?|qW z1tjK{zyEo##YN7e?NL*;%lgWF4$W!~SN!A5Qa3oePrm;WsB59v5{Ds=LZtuvxDJIO z9Vv~KmPU&BTg}kJc7|r*>Z@4M62J5qmupJBev4Fxf(;4f%OPO&$0|*omqlCURM)GYV=2!hgBI5 zc^6I`v;#a<|AmivFDlI?DYdCbz4s_P@o)io)iOeEvJF_h`kIINGMPf~Va& zqkR+h14oAp()dO@U=OX!8k>sYa3WrA_esm!kUboWP|rW%wN)mUkxNIJaMoaX|7|%U z3-o;V5DLGnTSJKZTCV@p+kAfT*IatubLno>ZBPN$ObS&n&B-+}=-R|k?0el9oBAIj z93u*N{$u6ug-ZRUZ^JZTUG;WPi4);J2U`<|8sj5D>nHaqrHR=8D1F`^%ZOy(tWitU zPC0Eq`Exd5sCCOqnC(tQcu*f&?C1Rr!XE~RSlAWe^ z70{i`u^!fyG(f%4AKAaZ?SK8}O16)>UqYt|jVK>R^syEiy}8)EQ?|H200|?)aGO)i zjSd}G>tAvFaK>gGQU?I$5+c?0T~iy-(Op9_u6^4J#+ZAvf8Vpogee3Mqt~LLdnK4( z-$=p4$$HaKQ?D-63hb|7xL|^YWE}qSr0QQgOx}|?wn$LzcE_LYcbeI@*jAZC}$N_ZI+*n`j*a2*@DTVz{Z_jI%YCpJi7!V^V>$f6JF#k2 zK6WhX19hek#zE)aa~bn@P9+W=Ba2l3ced|jU9B1&h!bIa68A6EpPD89Z0<*xNDfO8 z_dMUp{a3`TCR!^P(-B)nivj3@@4P?vmBUU~=OL|^0i4d{wW+K##7>DBL2GDWn z0VR8v5{?Z{XcLD+P0PRk@Yybwu@(Bgi4&>*^CGG!{$bYwfECidjpAR>R<5UPE!i*r zJR4q}zKgn(?hytH#lK*ABoBK`n<=hNj)q5bHxiDHjyx8nd+Fu-&#$fJ@qPRl@A=to zUy3DhSZHpn_~*3OqPOfn@Sp#VzxvGwLSAGrtVsEX>@+$S*Ts$LozeBwDb2*~i=_WX zl;>SkRX@_BO+xwo&!10t&4e*`9*~X*=lO0YF4opjzdtM94@v;f^}E@>Z@ZtQR|r#% zeT1JQhC=#SdHr`wE)beGGtl|aP%*Ju2I^Miw#7kHcrHL#qKz{Cu*TnSLs@01dnz11 zg!crXz0C|N=5g!<*M%X_!cqSIy4NsuO`_j`iw>AVaYIY2)Jkx==I$Aqqj{Cl>C9vC zEvrOR?689I(eg7M;=_*fs{h&(JxK?v&8HD3!uVh~ZFDnC7}Nt_Qj$fb{{;hF~p?KlQw!jpI zkL6T@%f!WzQ!k-HO)ittL~{R6+a;&uBSci#2JiEh<0QNBgkb;fdYbTlqp{zmxuRO7 zd(w5kT8z7_Kgzs%o3Z8o@0ZDWri~#^gbAbC6BkQ$r0*|Zv1V-Cw{7s!zY6=G-s!*f zPnuN$bruIfNMm^}b^f?gvnQYhI&`rW#pyb*K4C1EVdrp$ILxpr-~U~2YR1^UCRd0P z;pU$5np5#}M``>wlu;?)fTe%fzfbdJ3dZ);umIj|9SrZzaSX!^9WRPu-OX@tyI z!sxs$#GyjHreOGm^>F#)7qnh;j{OH8Qrn8{E_Y3af>Z%NJQ6vbP z{`A0vrAr7$$Cwt({hF**{TuiMa~{oZjsvWZn8NFXaFO2sXg8b348-kiM>_UDbJZh~ z6~YZwYh%^!KjG_pj$0UEWiHlS)y9_6_bV;$*A(+xo0BcZAIu;QjoTF{{^3HnCt>Ke zzAZmKaAr|cg>7!l}u%Q4&gQZXFY9?A8OEdQoE z*_i`l;FB+GB|7z>ce^?iKnByGF zyLK#QJ6m_@{;&N08`ipP0Y@*dVEKg_yrz)XY4CmPtoVmtMbAmc{{1gZBUvHL zI&uIW&%djtCrQsYbU)S$T@qi24SR;dhJoIs!#>sI{eg+`U8VogSR=>!4@>{3`G-_; zAz5U#>8tq10jGXoCAWd>e?mmAVoMx*xj({+p--g$|Ko86z2__tPXp?s^O?G$UL;>% zW4rl2J&40mxAObn6$3f`pj-b}q$5I2&#S~m#-hDopqoQ79869WE+fyf|L6gq2~!A{ z-q`~ed`4oQ94*4o@YEM^Zf*tXI}+#1W%^e-$$$Ik=B!~=RbK~G+nBN`szvbtt>QvLrL!?6VX9(s|E2&a1R{DyXx zJCyz>9G2Cq$o2QTp#E02_doIqo?orK99;*_wu>_JK9vyUgVANix+R>{}LKqaF2Fw|!wD zaU%43%`pi&I+!Z;m;Q&U2aLJm#{PrMU(1&A5mQ``Lf&vcG<4+{3bhmNXqKn6!2b1D zlLZ*`KF9oxo4h`HbXT80My-7u>%{qf92>|E)7o*&hMIlqi{g9oxkHOzS)^nCbN>7N zJ*>B*2Ykw_4VSLXCmZPdwWk<#+#4Muw+R2J%7hW&%t1+ zL>T{X6IqhVQtrPMu4pITFTKY8rw{sX{2MsGHxw1SW83g9oWG{@ob2?NN9fklrt6ZY z2i3Cnk9kNkOs!c*^3NbO4$G8bk&yaTlM#W zThYrogLJ?~gKLZ9vF&hRgoPMg-BQ8fgf90rbe7%={QLYRR*R1(JLl-Z$QEO7 z@*WFo4a!jb!;_=(`r|(Qt6E;4?K~I7vgjJ(%SL&9yldbkI`87RSWN`v8Z*<=;O#^Yk$3;qIvNdE(Ol8e!tj znP8>=F=mML{%zc6u77A0k8`piqWv!X`1lCkgzaTOI*4;+S!lERmN@&PKN!?}KsZLo z-|awgTD|@Pr{4@^oliSnBUm>5#(e<}=i|lCwQ}D1%iP7ntAC%rdUllT6oQ=>KcUw1 z%kbvT2L%()gAZcoCbK}Vp%;#e=P`y}=EbCk#;eQkzd!0EUz@FzR33jOKfA|vN$wj# z_rWQpOqeNsH<8xA8E)KX3XblN4dA2xWJvtxNSL%A)E0N*w_vB$(x1kmVHW;(f8^W$`-AzUn#HxkoKgF)9c?_VpxNobURbcsJ?uHLCwxo1YmU#}23UdTeI@;ccE$S|QMQ^@aEN9p`qPL>R_Bw9wqo&Xyj?br-iBn%QU!)(w|$4Vsk z*Fm@G@)%1g$yV~H{=clxBMc{vVa{?c9i8dhHxT|f8RdTfh%)3Ziz?I{iU65`>@A6OU?1GYoOSF z9NP`nxl0@_wkW^1vWuO~dot)^ zeg|~t#3(on8_|sYPwA|!V8k&)iv3^j#bTcXdrHHg~jmO@u~30 zk?Xl6We*rt`;L7&{1N}!6e+g2Ac@Bkt{a`K`hVsq&&|nooK?Hbr~I4X^3ZTFcK*gb zawY#uBQDGR|B~m7YQMQ{5>}{m0R4WqXWMBOKg8YfZL!s~gW^R7pOZ5lc)SL2$gcWD z^?$A!_cb)`e3NuUm{!1R9!xlJP~`5E=cnGE$M9qBM)n_YeW7eAAJMJ$Gx)X686M`D za{h#59g%P9gbwRFio>%!G;8MHVBB``ed1sk)I;@uslhXC==@#$aCByNM0ZO z+|~1MI5ur5m(kfztrL!Wzk&;v4xp9yA7AX>zbcPU#RfBU9HskM{-~S9eF!;2_lnTH zk=V6`p19xL0{*!#kFmggyv9S+fJC+aKYLbT|4mcM*WZBbi%Qv`yL~w8YZ-2YBlG97 z{|UbiD|*%~GfY6;_3G~z;}Gq^~XYo-Opf1n%plfceWvWcyeBS{}gp5%WHN* z`8!WIcV>066SrRmgW)+|3(>`Vu88vK#r|isdP;hQaA?+g`0bXB7Q6ls22mrO#QuOZ zZ0Fh_RMjS@?sL$U?&s4a&wg!XPt7MBF_l^*U9b+1+ z^)Kwg^8=%f^&uS*_A8Rd-?3O5)&I0+(s@J^dH)v~Um{z|N8HrCFFY-+vFaUu-K!Z7j86p^vx2|BIDt{0zu!dYP{gv0xqJP+Y$lh&&L z8@G6G1i$CaBpV_e<2qiJq&$`O=#VV#XZOWMhhDP(gbM0!B^@k^ZUWyoXyF9;xn0=d zdPLmp@D4kNg$WD8N7#J?$40D_I)m)7@tpGSf9{aq<;P~&opeOl%CIYOF@JYosABqz zWQEv&=yG016pZE{>U8J;t48n`rnD&T7lk&HyrGu+KdN2Y?nsg$@MwAaAFs-O#gDa? zs@))+O>7rxSO>Ju^1Q^&_m7~rg|&3Z1<#k8a&ruj6+`B;dE@(RU)~~Ms#`X zrb$>;1-)L5ARK2m<9wp#!t(Kd`}q*o?SBEpiRkwe!J=-iV7Q3aCrXPGm*nfeiGI9i zA&dwYJr9JpRkYDGn`0fs4s9nowTY1K|Cnhq6T4xp_p%R%OpZU;JXpQ|0~_Rb;&4+A zj}0y_myVMEs^TE%Se_DQ zBD&2Xy+RnXtqdMcxQ7$>@E#H}qHAaj%TC}>$JJy5A;sLE*fV?@rLkM5F^YfGX*!az z=3cSHiLmKKb$&tRMXMMa(6Rvp)%(Tu4?32?hJO#3dblxsZRDh2SfzeFp?6~wItJ*A z9m}&|^d;_hT&Qs;8`xG_e*e#8Zymqtkr;U2tuppXlzk1v{Kx0s4{U<2o*Ovf@LjKRk`R%w>2S^-**f zeLRt5aBemL^k2#`CD6AT#2u60{|IaGRem0QPW9McGqshI|F>#t` zGyN%U&7Q)z)4Qs~;YF=}s{f4-jLE}PSRmmOkm z-n0Dv>&Yjw|B17@k&X!0nIsX1*6C)@p|3H^dPWK0QAvLOc5b6U!W2S>Q)kh1V@Djc znD>QfFnFqPYZ!<&d4t7$<1)B3_KF7{|KVcN~ZlVJ<8V$sYAAK8SrN>b2F0#s{ad-a(o+}=cJU0>22ga!FS&t z;(H+~I4qc+#PtubuBl+eafMbZc(W%IYbLDXbX5JOqT+Mu{}-AL6+3&{V@8fV{sS+_ z>yYz#ZPovYHu9cfvqOebCiXX}PqNr)cpJ3t3obKa+$4ZI^8N3~ds&1jgehlArRSeo zqE6XH!o-&cmcs7kNp#vNUH|-OZ!Zq~9!;*mtig#~|Hz#Fgein+IVSMZ*9t8mj4+s6f3T?Dyc>4;Ib2*aA1DTv zwjdnmEwyER$d-83f8hv@br{p98R>}7pn-ubNp<<7TSBtth<*r|FP7(DU=WWZ#Rlt) zs4G4Das)m_%VQ}r!(EIYlaG#f<3vjJ!{V{ODcPduFpgW0+Eji13ks@pT~TYzM72Cz ztM0Fq^#{!dJw>z6=3M{TPdH|(_PJ7>KfEc1{qH{zhArX_itWeRVegOAgvqV>nzY*6 z7!S?r%Dli!pvPETW98)H5XU^&Uaia3$t zU!?iwfX~R5T>l9M(`BuE#D%}Tp>)-6ShFRR5S+cZk96J09^IBq6az{}L(!jggu~aZ zJPw8FDRuvgIv08WgS&dXA{!#yJ&xli$%=oL6K!qTzyGLnlvW6=!mGf`Iw#=nO`f+{ zZDV7xBgaYl`=Ky#ZQ4(894GGsQ)`c)Jm}i2{Qlc5$KHgA*DLoECqgISaUvRcr7Qk1 z>Btv&+s=&r2N;c%E#)J{D=2-NiZ(T_5JG7Y7260!$W-CdtQ2n7kmp^{GI=jyrK`Sw z1&^MTQ66{@$8&>Zv~4Y~KO0unQ2keV$9>EGLrO1^okFmybpt5f5DSO%P7{XBEn~#_ zUOTa4@G5b$VmQtVvSEC}`zYc<%cMO1UNYsmhN6_)j_r1xyUKPES5*I$hRpap_CLFO zKBX1H%KKvB^)!DpohR3GgY^GnFY7hLUebHUO9KkUnfnLG7Mq>q-vNi5&D8myw^*K= zH77Mt%bTZnkz_H$x4ivV4cZUC>v^&N;L#jsRr^&fR>3c$_fXpMp@NBQ+rwyoYmUh3 z))lVL97H%;n0#h#ynUbQ|KAE6OE7x>Bqc9)a`7Wsb27(MX@5#XRK!H~KW4DnSA?VA z&FfgPXIC^&=l&;51m#{q=No}wXsFI598kF}>0o)L+WuJa`3uJWcmI>p?gwMdyQtrP z!b&Sfi@miX*}wk^W6~=GhvprIynDvt;_9)4X&$Ug7Y8>tL07NqVi9tDB^5#6>=4|C7%BXZ*wayQ5L>+%Pm)mBslA z7lw&4zpLobu~2*}J4P50whOt(+;C5X>R;<~E5Xhuyb$id4T$bc&HLz)`~3#SiK<`PT4uB{+}5%Aq*?;|aH_q0|2ybuj$+H@wyx;}rM6!&X4FnfL)wwenz>F( z(Zw@gEKRag+7m}vtzdgiQu+56K5du#CMNI~>4qgW6B!xHvG6#|`Rra8TM`dTvV|GiM5?DP>~%gI**H zl)itZJb&qarT|_{$mQ`DF`N5GwO@C`3JU$3;kv++ghA#HYcVFh2I9qGVxy6(aQ*vU zc%s|Cq!-%`s?WbbaVobpM)!D1IwGaLz-wu&>fd1Z8@PHy!~RDdo+DezNAWJl7_}ep zIzou_EQ=5o&%efgw)H@J&S1qB-L&0F#_1i^@sCEMIA&sE&`Z_s*xz@;-DW;l{ZEh2 z1#f6A*WdC#^Phe%RE}u(@-cSL`XrLJ`D6Xf952yz;7`(Fjc$Y0_SZfU z%~;pMMB+r4VxB-8x;5*n_=gQWCflz~InVw>tSm{d5NsS?NxHVV7CZkOuV5l3`#bi_ zJx6J=t)09s&!5pn)ho~c=yt0@nDlJR4n>CpExj3QVwDBjQ+lfSpkVp@H?YV+!HDDH z13lo4^sSQ>kG66;SMu5G#ZmO=FhVoCXDvwl!TSmH{>pnev^iTrslW8iEN*v<`uv3Q z5@F&^ZVRxUn+v9kd3{jE1lK<@iuZ@IwPc?w{r=B?4#D%+LljKZYcL7z>+TXc&my6; zY$)OAx1R@f9@R^%~=0#JP%o3?=+RThp!!l1snc7pivgW=X#lN<%A8_wS{B$vGBN`JkE2a z@-`jZhNG(RzC*Q}eoaF%l=cWz{9~o{?+L?gH7*b*Qu-P~k5rcae`XJ3>1*tu=D0`f zf118v!@mc#IpmHN%wNLEV~itM*Alhf)xcgZ%|xWnZN(NxtmSnBeW%S*{p&X7@eOgu z*$?~j8+?&CY`9ejO*U~|G3w+@)Hg9@|6>m|Q_5uffpd1Fp>-!H_U0Ih0XzLQXTP7u zw)MY49p|bz;j}l~X-bNjSGd_r@eemwo@T7k_hZC~Fv>)qIM|$-1XV-cvaG54fH?b) zSmsKyLO8^H4E)OV#1?ZF6NdAeUJzDc&e*l2k|?cx71s}0%XmPG<;-==T~+@F58PvH ztQGeI%Og*1CoViwvcX8c*A0Dg9KpRTn*GnpZc2KEF!^U^_>earI#w_w3{5uriVTlj z>~d$H*jTm#(C-^$N_2HS*_o%rTC6tt%HCK;RlIs%KFd~D-9>O`XMps5Y~E{N{b`9J zxpfolRj-PuviPuS*Id6j$(r0%S&Dxs=)iLp;_InvGe&9g`YsMPP~X2qw~K#Zdvp2u z&xkqClro89-^VlH%f~6`F2@Ks;pe3pw@cc8?y93H%DxVjEk3fH?KRGaM%T`%{`ZWZ z$-2ei8{U35~*TX2-y2^J6kNAVB&9X4a-w8QK_GO>eV%jM4b z(hHuHRDvHPIsU_uiTy;C^*Y!q2<)$%_^H_9)J(ZwFCJ8%e}*3w+Ho0?f7Ejd8oxwv z&|7-{=f2!NExcXD)5mSu|Cop^vW0xaWeZZ#=xnTl!MHG`|m&~S>nR$_0H z6J!H**Xk(kk2-%+2}5UFo@XrYJ#R)FR%D+Cy}un z@mcaWenq}`s?;A1D^?~Q%wOn4oCsI#!n=($rHB8mhur7 zJwSM~s{khazR&p$2W=HzM`Ce^Uq3PLUURHxEswDUujE)>Gjy}+-?;A#&a>xPno_1Z z{xjeQRA^G0aLwb2M$quzH|*c5IsazW-mgX;R;>LEF6iG>P)JDO#^ zt6@5F+{0LF-it%of%5l%p6kt~JaEBzIdLLPeA$LL=$bYMqkMUNu41`Z`o@w0`yW5g zf%FQ|Z-)P|i?@>G#DNV%wCuj4Rlo83^vVI`E=<^ZlkGo44E`Iq!*zXvDZr`mm z8+ydUv^zBzAGTpVb9?LOYX6&u_GfIU*jPWcHjwLI$mJ+D z*sansC=En#Zp!tPp07VCJla>r!S|<#=`ZR)gU%eU(JOf(<$>p~%J)BpDcrx1gS-y1 zKB}F%zgPXY$Vwm``*-{DpX(1eu1*nrjJ3mlw}z1oZcG>=T#x7B(6*uCpTvczG2(cE zy@Q&tK36J7x&B}wKkEk@d{aqB1ks_~kFa)$m*O8X%X~=3{-b_z|4^C;ldR6c?fmbU ze*X-oXV%-t=fB-8nrp%X%g-xP`*Q!24f%(Fua&WKMS1_7c3f>!44=#K8k|m)x4((K zIT*gG#{T23ou)Dr!e(pR!8`9GXm@fnVKCFNzvhW+A`WQ#h|~A}-uLcrbcoW}$G80Y z->$ct7>f(#-_G)4_cz3$;-&{+R40gK3k#kT?0?1<9l{jCbk8;L>anSq7k7#<40L%R zk~6ZiXl`V-A;i5lxh>PcS>ws-WSH|2= ze!%M8_1S-LQ?4iBMDS;@H|jstU{3>{L-6~~NpW;aIJR?{EUtq(u5jtTePk~htXrt` zKV+m>5r)oRuM;Q2u-EH|i;UTRQ1ct-0sY1KBBP)&`;WdAPI`r~*Yy#Q|EL-cxx0)o zociac*luj9_1ti{cO@I%TwBDpyB^z%B&!zK$NH@%>-!VflP>Qus&(OcgO-QX z|KEW7$*H8`bv~yLFCQRGCv?PqLmkAtrOUD7@_B?~a5S$E@aM4l z{UvzPSl(YRSe`>RL>TF#B}-C0z9yG4)~4Vy+#9%-{YT#5Ijh*<{`pSuw8#j~%oYkJ zHdZOXfz4Wq>WOb~<)ADsW2CV!^BVQ8sP+H(nEM|cBKUW(zFKW{Edp(So*#rmm5cKC zufz3nDXkFtyzUO)YJb5Bw|)?Ytp{xpA0}pEm!>viQRpPjo?Rk0pvq_PB*+M#GShFXxnq8_-&m9 zM$4}-E-pPL4jUS%@4sQCwLI5x=8tQnBf^cR)%Xe>dd+97?R)8a!j^{YKe*lwHvD_Q zjnywm``9=PZYYnli1UNRw3F4)W_Vkuv1GDhD?N|S`#^BXvRB)Gnb&aA!}eCms$Iey zmc`R+m7#{u3BqyP@Q1M7`=ng|8a$sAd)V695+1d#4Vj0>5(bOzmWb-b7U*=;MR-gs zRP1n6ti0FQ@-bWW|Nc<`>2buvC8}NW!x)l9#`uGx==)2S*Ein^o1NtI&q;RA6kFox zbk!OwW(mkx=f>%l+qA^IVdqhdUn4Huc!K}zljr~FlVeB^>u;Ao|5R|JE$4A54J1y4 zv;6N77nzUZLH~d!$qLE!f2ppOgrm>7C@B8<3Ohvc_{Y^}=ZW|lXR*z-=iU z7q*)|hT|Icc5A5C|76WF(!me^Na94euc#w&s9aPB8ZOEr8J8~#f#gl{^9KO~_YkHK zrd(QphV46`Ro_X3Y3g=Ro>Q{hXmZCnA5Vu?ts4 zWRjicLdkV7@jK0V2AfuZbGq{SGez^C@dw_vXP|7;H0hluKG(!aCO3r9(65NSpMin- zG2GbvBb9++Y4ZB_b$g*&{}&xPv2Kb;Cr*Sh2N%l{wLy$pR{;l{xcy^VbDt?TxXdpf zZn;}yBjX~@ukRNwJa%Vemrr*^voj_bHO_mBIMlP?BL%mw~Xqwfy~`c^799rV!4bVv0I$3ov>ruVJ|I?o=_Qw;hV-NgBsT zC*l6@f5{eOn(_P~S@j?D(1X(8=HXAA2;0t>NF2HyF9Dr9`ixb!IYyl8ADuRYWQA~O z_wn#7`Mf z!r68YnAgi(q56NPeVZ}kmx;uQRR7l&O8;XU%kdYr^`A)JS&+X!9XX=4Y$+eH;ho;_ zJ>&(h7%TVhCB2K#YRF6M(sqFe)aO{j<<9@nfw}(;_52UAs`9u)b0cmW)~COd=gGD- zbLriU0>*NsHrThzo9iDHHiIyQFiyKSJnU1fq=`eXPFGO#c$7$S_Jt~i9NRE-GshUL zd90RN|8G+okUhE=hY=^j#j(7;i?!BMph>^4GAhl-gx2=#f6gi1vnV)*erSM(N1Edl zZ8O3kwRUG{GRh9y9;zdb9ORfsIF263<51LexUTxY+?dyLv@_vzWVTBT{YG4TY+(=8 zqq(m!0i0pUV zw`$NC%|Y+;9c*o%F1}Z?$4(1N$QGl5I14=Zs;m0nKGl|WmS0j>Np?FDD=9M6dwG~- zXj|9~;#SXQ|G`ciXA~O@401+;-_kPx?>JV9(!*Cp&amd#DYOJEug#F&i{P>`qGdLh zmDqKF>i@{gT*7daKhHCk6UKfaP6P%mc+Ft>RKNiCKR%D^!v@j;V-9SCU&&f<&$S_8 z7+VoV!ro8Vy~{kP>vtNfUzX#ae-8UclV9&t|8E{P<~&-V3B-voFpm8TUl$Y5x^bB> zcx^va({ZxA{~Xg}F<}Z}dwh%)Ka7INivtORh7;e2_fmfwgt5gM6YBray{w}BcVycS{Y3tu4e$ULrp*qk|}R|sRyPlu-s zW{XwN_Ywv_^Q^_^Nl&rg$-biYTYFfRwvBM~{fE~&Fxjl`f3W(?ORP&C(StYNifqv>d>wJf2|%^|_kNJ~B;#gI zBOMW@rf_>fN9_{j{D*KzxGdlQo3ViRGlUW0z*r5uopTv>K6U2w_-Xq^&A?NLoub9{ zo`-PM>=BH+&EmBTPFHE6`oD2f{>|Gh)G{&6Je2Ik?V$g8{_JZjcyMzP*WdSNXVNPK zN6)9DPQhrji{*NPuU3xcccKgSNF56Lsju6?;jRq*I{ho#WbZn zjJniRk^i^;gt7m*kJLRaVYFVs`F-m(L#*66Z8p+OvtssP^vFZ0_JE~|e{?##8L~ROVE^;>=8|3^_&v)Fo(*q;_BuZlOeB~% zqsyo9N}4!&n&m5Lwf!Hr=lF*GBPJ+1Ov?@=84h<;|9?Tz$>XopNF~j7nRoZWudcUY z|5Ew<+P3m2=pbE_?mFL53`mTI)otdH9X6=V^8?158n61_y(x=z6G`9m)+G*; zma5O6!N+mRiC}t zu5(jfpDBmAit9wLq!PS}TAGv>!qGx+O(>JJP zQ4e0o&leFUF0`70w$r+cf&-hdhoijy#cUYL_AV3E`ya47`!!=BBh)=L#tBm{&>EY?rJm%AOso!70u95E`_2+5!KjoDb=@mlhZ#Cgnu)0p+)R9Ia zyJs8hZtN_2cAEe%4$1r3cXMng4{U8;e*W<-A)GKYFW5+&2#xO*5Qo^Z%S!!G$F)6N zn=60+D<*0Z=@r8HZ@0j2m$A5i?-Ku{M{om`Ka9|O~ed!wA zdPRpT9d?rpT}$pL{>8s5Okk9?{QgPI8r7EVo;8~fcc+_)pyD%>#ul|&Y7*P0pktc{ zn!1mDV3ixk0bDehV=OEUw^7^w_Fg_G!0g}h=7jR+QYn&u1G}N1v)h4X)4(fGZFCs7 ze?(u7V{9ZHaL&JP;P>g1IItV9?~rMRQ+4zk_EYV!WPY-+aZ|4}LL`*2vJElQafmE4(Ru(-Drbmmqj zSyO0UV4r6jAp1A&LYP9>eS0;yf6y5wR+>o|wrMy>v-AFE9DZnpM)wo93FFqOiOegx zs_l;^<9ZPWu|@I3iLl@E;l!cug8-%farVH6Xc+&H{m;40F@tP~(0aY}|HrQ@!z{;> zGD7Fh>AkW4(|h(~dwAlQGf8ac_Ut-ws1<%)sXywve`8&jj#Y^hVeHcr#3AiWG#CY_ zaS{9-AO653amO*#B<}BX~ybQy}%l98cDvp9t76X~ z+*jz^#7z@7@C({x#fm>8e?Z0L3uKFtuXugJw*4L}^@rsr-?46qXEWkNczg@D1(+XP z2(_2-ISbtA8VEb?>T&ze>(4PmwwCPE+U3DZpT6+y^f1C;+J(28Mb%HB{lr|cVAyNb zZo!eyB%@)4^5^fiMb;tA{(R+3;zZcGVI*-_8az(z{{&}C$ou>Lf%irp$7DlI z@mc`qI*kP_4~}0#t1ZWNu7B{btFonh#OB$@;nkIOkad&C7jCk6A}W7Ags!9Y#p3%j zVeEBzUQTNc~ZXq;@SMa?ccd$0vfJ426g2BjiWL|w$^F1x%dDY#dd)Y zV{UP|Jv$Fz-s7u_Qh%&8>=9wG?hWs`SU;?k_lt0;RwvM{#(M^cFx-Sji8RX$qTjS{KNi(w)7#rLb&6Q^zMoCRPpL!p@L~bZf7EvP7+IJ z)>SY}y2Ei*y zC1c>pE>rZe!aUzyv$Kkmvur3o(=jq$Nl7pOKJ57>*mwYZS}T z2R0EGHD+c*wZ@Et_t4AG=bL=}Ijog0=@mk6o$pwwlM@bn%VP+BMjR5BdzxZ9?QtUe zU>r7j(28tv<^VV1aA#}z_m7s1lIP^Xu4SKK~6hKTdWE!Hv>ncxTlP+Agd`7_8srEIzdQj&8xD zMWtmr7@O!sI67t(Gw(jSf!h8BUnUU-E22gcCz8H}vyZqYH^@`*PiZ2qzuzg22{Izt zM-42(N|upW@fgQa9Gz+ct((_C`;J@1A8RdanH)j37@Ouo9M&`pRs3VcKx@`{%GaD& zKDI@!N5k_lG^{lkH=T|B;%!9f zjn#@BYA3fQ8Cn|uQ2p!wme;qlciO9VtBxFGyL21r{OiCAk}>nvW~}DFhU@Q>lcd

I=LKvcDY3_V1FUbijBGJ($#*4>TK^rb?yyb_;B}DYt!~|j3poEn@ee;f zHIv@el;1y}>vfj&3Q6BzkAY7&+~MjPE(_eA%n=rscVq9A$|BLP63)uxH3ofdJ5nAr zI#+)G^Q^@e#?ls)uYWH7S%g7-2lf0@Z`~OB2(kp~V3K5F(%&|jb9>X}Y zU70vHqz^j98Hi@d<1jOuaVSmLN_jBmSNZsR>3NSa?Jup=@|Gm=oP|i&1hxLF>zF}f zdoA`qy=b0d%Xz!q-vNcg9>JBKJcck=YP+k}!D#RC5*qwi1^s4jVms5p2bd2oe5khn z+qMq~gN>Ie6DPvNsXQhiwoX&UKb48qs%~WeV{0s6!@mctIr=$jr`n2(bJ!2T;>>{C z*!ijzRA_36ExkDIU|`60(rMC)vQ+06%XGq_)$badU(2GASZ^cU|1sGleqXV}kepSF52=++T)Ix%Pw|g+ig|s) z%!<0CBf{hvJQiT4wmSZ?dYAL0~X6peW$ zb{q1#EX|KIq=$qQ7C|*GgoI&P6=`AU3Rg|G9s+DeJbMCVu{Vi8v8Po|ntfb8QZKQ4?fT!cC3}uK(PB-Y6Jx z_&nYje)!bK&QpF<8kf2M7Ewhzu;XSMfh&fi!ERf|M+CBeth2BD`^#%wqFHA;h0iZo z4tsEjI3(Wa0hP}3UIbSq4HtvsrgKQv%8 z=@r5uCD~9|+#IKdo+1oqom;B868Hn1w{#FKS`CKB13ej^(QPJiBDMWLm>SDS`4N|2 zwP%}iwbl2p(4f_O=~Q|B z6! z@i;FJ;xKO5O11vljpX??%!B6$+rredlpJ>wYJo^ut_x7(TGaT0G3OwG}8}eu9 zalREpvxM%-_0lsn`IN?p-4B@$Tq0QSyJM5;zs#UAVgE}wXhwTV!}!@YP;tATq65ou z^7EH7*8R8si>g0>{J}M_g0Z~*4%xp=*u*`;0iv>)zMvQGo66%AwVsWmve3@bRPm3M zLU=4;rdhb+2g8U_Ven+({=Y7 z@1gc%JD7KR9hcF_lJ{*mD%DW+Z+I%6b)qky=do;jj_0@VjNBytOgO@r`MtBC`%%9B z9#-0bFoiHKJreF-oCDv>cn=3P2J{peKhx3SXtEe6JfP)wAX}W=iRU57s{hIpc`U=7 zBku_(!ssr`h}$>tjSyw+gGnZW8^0a6{=v9XM&u(-PArzbxs-@~>ht+0Y8NC4_cfmp zt^XB!R_w+>Cp^g#z5ZU;xi(BF{^5y%ye8z5g-n<$bEw(C8|t=lrWB66UyC^VpBQLK zvO?H9_Xk#OC4HaGjORZL*WE9!Z2XRzc_YE8UIez8#^)s%yu^s|V29#z|LnuF&a9>;>w3qFs>30yPw0`~{ zRo5Mm)BDAhwzi#+?3Jyd_c;fZY|55Ol9a5H6b+S9Q4+EevNsu-*?X_-jO>{imEU{L zb#K4#`Qx7F`JD4SpZD3%z3&~^(@R5rAAl8%%VrPoOyf(=Kzum-6vn-+7AYRMZFYp-8tJpT!KxzM7p z4qx5`z_Ul(FQB#KV{z(fDT-Ps=Gjljxr2BP!z#j?Qh>If^8Y_P{-Hw{uFq*pngr)2 z4N$X>N?IRhj|dd=f_^an^jF?wmj&%zTfpt=XP^^nI7 zhd1j*Vso77{VA@=3)YG00n4K+<`bgkYdWs8y#GPdliY`xf68$l=aQE?U-Kr`@a|w9nm5xY44cI2isbf< zu_!?=l9l`KP{&0Y zn`2Vr$wq?nFCl4&pRKO{+b=o1^v*Dt^rI^Cdmn92m@IJQNEDQn1i-m&0|`Utp{>Q0 zA&s$5nwxlFGY%JC)?j>o4DY{CD_6PxfhI2+lMS*P@*b7_rWcH0tWUa)!rxja500tc zGQZ!52V|Fp`U`4!WWcG^0>Z?z%`e2&%Nw!VxOAwu<^^Wle@8e*x8Z!zr1^2B{%dxS z#>V#VY-7KCI;wwDb&X=s8+TNSsjpi+%az{0>~T*eW2AA=?;YxIO7k9v;_&py3E{JA zq56B+I?#RHBf>~9A)ym#Se|i9;WzS-=ERL2kI6=YX?vBq5RBIITmv&(8AIdX8(jX8 z^|?Pu-s=2z-rS4&pKMTT1fO4^&D#vs*ZNnm-R%g_+cyP$PxHEfvnNfYG*F&jasSm@ z?Qp_y&GKfXNl?EQCHHVJPXza{}|v?(H^1)>26AYY=#!F&(q-Xq#v(@i*sw zEc;+BixT*IvMc-!*C7n@^ri}P^+%AqJbfXcJV@~iU8?MH<@$$%ZgynH%vwzpzg3f- z%3-i}SW98JZWRP>6NWN>KuNT0CygOp`=WNURXAi^J-IfOxz;)!5Vo!3;j5P2TDZadBVft_8*E(^Bn z9s~E&-V2XRBn+FcJ0dC-+TsB1LNVg^BE@fH%4xQnT)r-sAN5UmErpzViHhHzDcTf< zdcD-oe@o*Zz4M>L((8fDAL7nqR`K6EXbaqY*$8^y&yg`<_~(lHn|_N`+Xi`ym{r`j zQ5=cld`C^PQ}}DYXk7O}RVnZj?eo!2hr@BNeWQT(xJI95)*gKjDD zGNy7;-<9c6=qTE{I?EU~_Wej<%vaA5^7DVqu#~Y?KY5Pe@Ged6lMr>ez040!M{_<> z``9Nec8i~Q5f1kSDruQxMsC*=8v`4l6^_z=y5;c`ii%3Tl$tGHgTOV8YINy zfH7Yk+dt#|4g1wuzK(UR#{=c^!<%{Q8SC53oHPlpSkG&*_%g7M_`52Vu>moQ!E#js z^G|U(&5r*L*x~&NtkmQ@U>jcl2^MZT>k)RBi2aU7!J}X&VOYHmbD-f^<@!5nG|6O} z{zI+@4zKuoh&0Uquox`T_j34CpB!j=#-I78FX8dU(~ZWOZ@ z{zcmZeMRY%))4I)&3+4;|0XS3SSr_F;o&howoTg4YZr&}>8R%4Q>q2kUh(gPgelwM z*N)jz`Mr%L59x@B-WO1F5un*<&L8d1jS?p7Cty2U6H#Jh0nOaFJu%{3bxMIY!<6ek zuzBA%#*zyv+9{t9HUE`HlfmRL*D0>ly#fAiHJCrb*GKY@j%f387?dTvN1fw*-b|=i zc(xyQZqP~`x%w2nocVkKBeceo4O?~zRoeg945|FOlZ(klf`=MxBn_KfE6e>~iZ}Vq z{4tNhC@u@Gd>92^It_%Yv6h5kqpCH;fvxSZt5305cj^XO_Te=UGivi53)(F^r|_RN z=5d7!zA0-ejZYE?T2%4;VU10jM81AJ^GB8qmOP{*rmFA%Eu4N47R5+=^={L3#JCx6 z(e=T9$7x#md`cl|@A6jo|6Dpi7`Cp(?abkPI;#0Kf)EVX^4=BZoS6f@o2CE1W*=`V zc}Pci?@D8u-KBem2!W*b()E{EKV`4V__ZV>)L(6hGuqyuIC`0l1HFZxvFVxX;*a)o z!bmX6rzPv|5!niVYWWn#>K6fN5**W+$E&FQHX0fntj$;tz0*)blu7;n#!Ghmcfg$7 zeAHfOg&pfi{Xg>5L-D0bH|*rO8%(wzK+`TgIgf!cOIhnSs_6e4u3liQX>Xf<*;Rte zULBOMMI#QPS!xoR>1#;s|5Mp3aoVit4REii7cAS(zY8rrzl&)1E9iLnp4g=~4Qrq8 z&VG&Kc^nFx`#Y8LAN==yJKLf+GAHhEe@|i2(Jfzmd-_Vo#exyHn1AYdw#zuWPa6Qg zdb**O$r!?L_TRmt<5=}`d5ik0at8NT{Pb0xY{ybFh5z|=X>98Cs-^fH7^6jD2x;sA zdcDVSntXM9;#Fk+>9=|iCJUw(yhlxwx{z0#PMEmY6#jTZG|dB927qhF_FU9 zyevlHe{@GWCmsHc_bzOY7Dc4RvESL?V7H4g!yiScIiNlB`+mwNOcp5di$wjyGqCNN zzl4eXHrqw&`}f$!uM>38Q|2lRZQ)9G$T%LN@bAr;K^S%{<#RW-=eoO*7HJPQL*2PN zhT*D;*CFPQcE~2XEV#PsN%#<~4Iy=SuZLYe{183~m$7TcH}T+fKAN0wNWR$R?|LqN5#%$IVkS4+I`aHitL~OJu8_HuEmUp&9Bmdn}`J+!rp3)KhB3i?rxVvcZ zK^pI?8k>k}84Iyz&o1J~OU5|gVClLI*taQ^`BBG{`LWL(Q#nsOP{8|mIBVwyI_G(; zhJFwg3;~?vM)y0|H<~r zh0SDsC|Vg!7%r@Lmo$ky|G)x28_>(<{h8`@yZYE5_ZRa=?st(qr6a6g;SHypPQmK! zUW7oGRq0|}d>r=v^-$Dqe;4aNYeqQ6W;SQ-*uX>KzahO>8PmJ3AR7tp+wg%jL~Se( zrKV*Z-u>nrY35H#ROV{JF!$L7D5`q^d+PES!g>L{#0~r1=+ue#Td?|N6#LDKkk%Rp zLl=ertdlgpjDMUZ9}*lNc#O2zTqy(eYOx)5<*tDBC(@XIhT!W%t-W;*Afj*mTjw>m;` zbi2GnTwaRUG0FtAE{qXdbZ0UiFglsENXdAu@L%!##WsuMTz4EE5$H~u1Wf+$_{YkJ z+ricQxy+w>jO$18Q1gxZRRCukeqhXa?km`7xrs=$-i5YxmpNXF_rNX9mXR;Eaa8J9 z;ZJ?U6Z|*MmqlvJO1DHpYPrnb=CKI)eKtC>E}C}3$q_< zP&LO-v@_y9$hfbDJ!{=g72jXouFZ7`e`9zZV7uAhXwoE5wdN(#xWQ`~%x~R=%OBM4 zzwO_qTz!wz{2P$*m+K!kJh2r{?HZxO)!CrAjr$zuF|Ex-($IBR#rk)4Vhgs}`79+( zf^K1pNUPAllK4~Fm%=c^X94`MoyPpZbB7To3r_y(3_o`oLhNsz!*NaCR^ivY2)lPO z66*(ULAZLAd=WgjEO2g}^8PWn7tV7DrgR8Y^2?V(;L}Tet=c1=V%T`qcldr;djCOK zyV{b6bj0BYpF&CM6P%pQeUwnuh{imgEV%6;vO|N`75l$u)lE3fo{3*blijQe+tCJj|{E8c&WuQ!*l{})Ui$KyyG z%zO{}Km5oBXFHa{=iceeA36TifBfU`J%^%wmRMCIk8n&LP(%EkvKF0APF1C9brd?i z4H=)Xjn^Nve6L*pf-M7ieL}AX!E&A$xSXra{Gq8zJ9EDN zD)r}K+bx3FP15>v&eceyZCj7MFPn?VsXV^fFMOG_*Y0Z4P#OQ_SyR|Ha6Gp;hc`c# z+TZQXYtW6H$5@-PIJhdxncvHR=S;#$(C1?*lxLlQ2~L|OMBP^D9W4?1JOr&!?tAPP zc#g*ntX{K5=Eusxygp#9G-XWV)QiUC4~E5;Kzl<1r**HdzNcr~Wagjzg7-&+lb~gl zQYcAU3Xl8_aNI`|#hyLt|3mNHhN`+D>_mRSJIBglgl&jxcx~kLiDM#2tKuU} zmHuDN#u!ZsUNZkA&Hs+SroDZju&O1t9JPSc2h^J-8eeUS9{oS6Y}dcQW0emw?rn6F zwb@7I`76}x-;FWL%~eQ~U_L88DEy7=9)ZSfZ|090?Ns5R_Frg~2zR|l$(Z;$I7GCc zWsbeKaeH9GHQqa@zel}>d~n^W8FKld{E<6j^BS*`)8pdwObUZZ-aHxWIr_97{rzWy54qM3OhffZlWq#B-HJj|B z-}r}$-%#MSSmex5-+$uIdwN)Vx&sWhug~T8fBK*KSLB98pk9(YRL$Y}mGXgxS7NZk z5(j8%!fOIyn6M|8G)V=2%ik2o^i*DFIXq&^Gt%O+zWV!f@0U}U1T}UK?T@RFq=MkJg<0G`B@i{p`+5f_`<62}BK65Mj>E`_o zjO}^c5p=KfT!UNn*21Rn!^|IQ-J9&P;4JmG(F%9XfvexSpAjyKtC%6w?pwy;`;wnLkqSg)k-2wf86;lhNA9CxgtBO3L(pnjv=A#tp+23k3By<x&uA3d{n7AC$K{^4Z_jQ zgy#)xczBG=kGk25*_Ldpv;!VGY(^N2btqKYf1z1VSg#i%^}lf5&yf!ax~{N=yMr3R z+r7If4&RebhVGIbJDM+J8;6mi9WZCcA=PTl!6!0ZNr|b|wlPC0=DCz54(xY)&|0V^eLqW8+aOZ_jU(ZwO7W}-_Cx7 z_71NAs(nEJSq%xtw!7n5xAe1?`LVJ-_htB0P016N-#$QokZs{F^TU3VaZvMw^!}YO z`fbTB3!GKip=NwOqz=14m}hsnh{Cvhr&Ry9Hkg3M@YjTqz{}G)T>hvs<+l=!8No&H(d!IiBc5Mz<=+#c#i=Uj zu;ZN=-_8xnZck*tqLQrh2U+X~S6)tK$QYt2A|sVkU2xcMx?WWm%$7EnAt5mrZRAxtT*3|12sh$Z`gKVD3_DN z`E*qKrR{hNCMT;=7~S7|R=qs;ipw7`VJTsGmc^}!{e%gCJiJnwO_B;01Cst6ibEwYoatZ z1M00+{6-maov0G4MuTpjTb!ot5-SmN?G5u!+)+jLC5;UYZbNzVt!T4xB*$YWpBLw@ z&%{ncpNiiFEoEN}{`Vc0ssR<>AN)9k`yJ#<--zM#J)l1SjT%H@5?H*`nfb@Y^E@k|YX6ADDEO@rf{hyWW52BrB2*7c zO3~3v6KW(}Qhx`G$1`@+=De`S%i9Y7U5y2l2K(Hss^piwjN2c^WM_f))=18CKwLG_ z%pdf=r|iqN?XQoipMMz$nLR#H9Aj*|3onaA>|NugxVUj0tQFF}#J(TzYtT;nn9Ps5 zfre5ZbsCW-!K^dmNy8@J_wxLU4cltM&Y!oL-!C|y?6P3KI*rz!WlG%8`I1;%_zZjO zc`j~!9}E}QO(z>J+0dD^DE+_xKmS_xU|VWs57H#K>>iSa%S(}D-gRYp0yD7);gd|mG0v7iOR6(R|NCwnjL1{e}j?Ck7ncb2!oAf%K8KSEeK+f z`61YO6PVvQB=NW9c9ea%{k5mvhl5etgkh_ykz!ZgOmxyug~|!X6u&_=Cs0_mvavG$ zP(M)Gqi0@euK0y!wvfXzKl+UyislJr%%8BbOtzDTTEC{kwZ-qTejDDaq5c6cG5f^` zblTd`an;F0Y<8VNgBvhs+PJ?&m{s@IB_Ac9Yk6@*%-&>m+y`-dBpL|*vc~;;vndR-tY(6Ke=aL}zC8s_s$}M${pzLcOByZx;^1qm zIJA4k=U*_`!A`t+ldj(X4-?%7)s%hJ_hHwdFr;p)IRC!!C7LkmL$cufOzwc{zYA`G z_Hy2{;L)~svFqIX%pZN{w(Luq`(G8qzwpjisRQwGN^5NY{3z%|G{$BITnE@=TRi*s znR!5||Fq7$W?<~D{c@g|)}7Z;@ww<47zEvranj5mF@44VW6=3(BD^~`2%T?AeYw_< zF!5q^b8P+BMSNX+5l2YKej8~xvaaI(n@8RK2@|hFcpjoK##H0+D%9Oslva}F z{q2(k6rNryl|Mv~pDa{<_)r=OKOXQ}2AkiU5_fEtq00Jkc|pg?P-|5a<235)Vvm_O>MvZo?H>~-W5{A$=6Cs;{+W!*_< zkvKIMZS&m4_IAE#uk(cc#)NTS!0P_d3jZMoWvr183GRJ-fHcJaL75+`j;SLKKI_W- z-g|f+As-SHBa5)o)(28doo@J$8shy{Yjla~DK2h)ifzyH+J;VF?y!G{)5`phdTvtR zSbXT3oF4ZL<-Pz*9H+_rFs1ukF>{l2|B=^dt~14dthfPXI}d_YtHzvvrRM7IcFi=! z9w%l&bJNF)pMS^u6jm*(fpYoLBIY+?a4V9}O(=}pUAe!2!$kG<$A9-<}yr@48?YK{j$l>urzUW-WWkjtn%JZjaK1W)A z7N_wZk^OS-EhP<|=PJ+NV{%-$YOd!8uK%EAIb@dweTQ|0mqkm!x6xI?uuYv3vCY^8 zonD<18*QdxznRPnpNB0WJ6tMCQ_62RSSdHzNN|Whw}}|g)>SFL|D^Vi(`_sB&(7vO zljN=Tw}0^-ZiSko(-1ztBwXY#_s3DUe^D4N8;>T9Hl}_Y&kDJ(@ayd1`p0~-NP;VB z@H`@J-!@eE?;bb@1;6_;|MOYIaMfsQ>HKH(T4k*!92~A)K^;?H zfXu-Z$D!Azfacn}*z1!S=wHsk>8ENlK01Z_BJA&?e18G!4ohPjluRN`qVTu-YxV#9 zWRuEo#d8+LNwAaoc+_qA22H;3brEP4qJpZYucAYxBG9c=3|Y5%yrS^u@egPFSt#Y# zSuX9FwzlB3oZq(OCZu7ax%&B^fBk=9>13>LJe~O?w*Ghi-+rnSJn_oIn%7@&`h4~8 zTvfXaUHknKtG{iAzpd;UANgSjX-IGGBlE*e6Yfipe()XHNN|QKpR|}}`5CnGc&>w4 zHGCPP_y0_MrR-@4L-h*-;A`Lq_%%#g-+gySifv`O*k|y5vF^%lINE0i`_0=XwL@F~ zBAFj6_YPv4(V7y{BxoyrGfI`w~E%+6!Se+O(*9)}vM&Vt?< zFBumfb6}N>C!Pyn@&_%2-?*+8+Z>WnPKyI0?@?HF({?AA+|HMA z$T-}X`D0J1oNei7puAnnFkg-W_PrxMTqhrx`n+QQ^VMS+VO?Go?j%iuE?q38klNSeuu`Y6 zQb#)fo4o43=N}suc16vr-gvU4p1UM?? zFWS_T(;SLW_Pn@w-&VpLkA~z!C4+Jb!^iz`;$GfpuK(~W;e^Qo9fQ`w`%kTKwjb94 zPMO$Q>>Pgr?RJ=|pS9YKBN83R7u#LF%X)H3#rvP~!nwolVH$v&P(j7vK8v?lJ=}Q5tmS7bPDrN(kzfXr6W%E+n|2`@)u0j zoF!az`;a33NvmLr#MujJKDb z&x5j+Idb{oNT4()wK~OPgZ<15l<^HZTzrDn|G(an{{Kihme0Vy18&y(24Bw1#-M+F z<=NpzP}y$>cB;BT%pK2jDY;_nX3|*d;hU`RCymNy+rqDNNs~Z+0Jp#6U6W|>^|?1; z*rQ)_(p>(yis!i5);+tgdjEeMYu@7DjZ=FEi48j*qis;QnAXyhd`PhO8)>XJJf*CE zke|*N6ujVdfczkLRwL44dT9aZtTCW4PS*0pHm4ghf6|)_!eqgGb(*L3mSDecS%ks< zBd0|h=Y8mqy;wN(-v{PHdA-1d`#f*J;5wg`@;~ZtMQO0bWNsr4t1dhwO#+%pO1n^; z`KRhxP*@fmvbrZ~WTc6G#}f>_r9Y136D$I{&Ch+nI9`Z9hvUXrx_&*-JBmE?z;ugru`K}Fbcml@~(Vw{Vrq|!_5bI(r9 zPT*^P>=)aZ*DZ*yT`~VW?=y(gd=5NInglB!njwYMWs#r%>0()`{%)gm{V}vff7#Cd ziH+3H->&giMaTPb-0GRG$k{&?2Y$>_rJi{&`{KOY2^5Ct(2DW*F*BLd1nBB3eqLH_ zD6ASBSi$dJ?|}M$RO$YkNdfm|U(!P3OCr3T-yeIN{Y!CNkTOGTtz3qElaqv5atRFb zl;*wZV|f38y5puR^>1dkpVGjs<^0<@eMkWBiNt`?LeTj+f$ejQpTfGmZruKXrd-d2 zlTiP|O6lNu-nlWw;i}gJv3jh)fqjC-)r;=fV#E}}ak4XyKlMK0rNZCPF^O#>8}iuU za8mht(qdhcTcDrnN?|CUYX%NMb(udT{lEL4ncJG8R{1K}b(H%Wv;`+IarH~=T7M_h zJ6^G_4Y$ z1#TUg0`L5`qPs2kHPpLkDJ;%vVvjk~g_p-+*$>@+ZI#1v`QgG$AHvXSM@2tbdyD59 z=($n7e!j3`d(WpfknbYhf05AXzvrLJ`>%$ZMhzhAgS5}zwt1cE+^qLF!ea@g72Wpm zdWnNBFQqsDZlpUxsE|*Aa&CV>49lJ;K%d!pUOao%L9AO>f5QyLMphR^7d#{Fjar21(QYkS0O> z`uuxE_OxqIsqRz8qMjGQ_5|tqo8Y`*?D+41IaMs+-PfU5Z^r=kZQN~zYE955>|@nh zG;%qJaS68=_l$L5ZSP(&{(ojHW2|fO2+|~Ib~cVQ=&sR`$3MJk@fWIEOV?ilzAh%a zEI4;cH+a1&PrRB|pD>)$;gHJJ`XKg$(W0hHRm=?J`2v&2Tq8R~XReUTkER6|IgR}l z?&lm%&E<6z9HwN*{9;N29-mzQ{YyrYpDZ~3j(UrG=?nVq2q6spGtBw>dxOj7i_A&u z;7J0nF&O4z!}ff2pOBybtGgkRFqm^Kg)|BFI60X#I6FO)`LRi>M8cRqVB~-Hzp%vc z02Cj-f-Ye^)*$QCE8%IO{y(JTs(9UWH8xMSr?j|mSvS%o#1E|-{K8rd^(c<5HdXY$ z=s$G{1BWc-`^Oyr(p9?uZ>EK^RuPVKt2~4EaqigJX$8lSXlJ%f-^J&S9A2=S=NGY9RQ&(mw2m|<(fLnEX+!dp1-+-eQ$N!k z34e2>@$a&xx9Srv#m?4$phk3x`rC3_$QOG{`!5{hrYYAyT-nHD5*^l*D*1W7<#rMU zzf;7s*f)g3Qk_2V>H={6&*;jS;(vI2EBN}s8XH8L5QgKk+lp3Wr(&0JUB#KcPa#%6 zfboT&rTVGz_MXE3vV*i{POGb<`$#QfKpgu(i(MqI zJ4pMV>=%XNtuC)=qR=AU@nnhg{+rX%Ru|^GN&o+b9v&)Lr6b(X zIt%Z|-NKsYrwPHpOBw3>--59F8f&rl^$yte{UGD>PV)LoVY&S9{xXkQalMu@{$TM? zclHUI&{pOLgC$PT{jjwEo9h@#c3E&t;ybuMF%GBdaUJ3YjlN>(;l0>x&{b9A_$RQX z@=5Z=qz|)6gXhc)g@3p65XPqFRFuEjNiW8hRI(Q3UwN&=z>`^E{^1F?fAmddtgzqr zYj*HOeY_!XFW(!D(K#%v18)e=nc0&S%p_i4L#DSf{Bwj4JKN{81b0 z$-bm@v@19-T0kix&VgVbjh zO^78Q5^(CIAFe{`F#o^saPvL9qfljfn z#maMaF{SVd`=tf9WbM@Joy?CKb(?USKTh3AlVH{<{w)w?{u&yc;zFTj<_-`w`!N6b zeI=5obVS#MIcR472=?5VAmieLpM(0l0B+DVIT+(Jcn-qee*TJGzW?RcJ|qk?59G); zdA))e>ht81I=rVN8~i;R%ly&BPh?-x7!cDMUXN{oLwzDRo@8Mx0_K^Z?JY~O`}SKj z_2YhlXnKk5{VyDr%MV+(0b?0AkTi+HpY$YLef@^(g)p)6i#PK}HMC&Ie+QhgdLewe z^bJ2S)oYo^8=RF$Eb|?>%`Qdk+y=-%Q!D~K;X%zq$3E`B-DB3i28kcvR}rxB&Zy>4?87~6y@DKgxiPOgyW*k z&Qc0>UUL6?e#C$j7Qpc{puvZ^oEFO|MOH^DTFFj+9<&LY&! zYL26}G$ahyon0zQ>wdu2T~eX%u={A;(UI{U+8(T%t=y>aZ|^OQ$?S?fJ?5FbB0p$w zY&Mu8&x=^?;b&ED@I)@Zx4$OYWx;mN4^Usz655&B%9!x4(iBy0*P&CTA8_o~62h_j zOk>vdUz^JOs8=P8F`MaJM;y*OQ%G852G#mc{~Hp}g89ep=QW9ZNN8+erJYq^WmDdl z;HE*=BB=5j?B1+9H2cs3h96zXegj`}J;1DO2V{Qu8X=TYnI%4qr>u~XTFuZdJl5y3^ z6H{?`khHga7}SbzjNTM3+m-S^E4s|K{`Yw9=5W5`1=>3ORJ+pUBIZhf! zc|LLHt(Bo{;SoZiPs(+dXwi=i1XpK0|yV`@rjkcR6PIvWQVjiWSVk6;QTV0 zZXylFEvL%-*k5M^)_7dP{ByqX`b<6~Xn1lCTyrr;(}puS-cIYN7~Ag?4sBsiaWF0) z$+%B4EV$Kd8oZo& z6a#)r^W zZ!l&Nj~(U@$>woPJ|yV9Y6-kBP{ss&I+&xn{7U`&%Z%M3+JCO(rS=P593zKiel%M8 zP{w4931vSh3|)Jw@4vdgmBR~iwm@mZ0OpVGpsX`w!#Oj3plD1J98~Qr$GgP%iw(ac z(b;fOmrY)Spw@B=#@*-fzCqQzcC^e7+qSG{+s+=`H#j_JMpe=f^JFvV>0~jM^>+qn z;dkbr=%CCqWD_SIe?aY|_Kzq_JLoV4T8#bHHcH2iC?YV-vgX=Q4|*RaZcb?XF};kB$36m@JsDwj24B!Iek6zk%YdcH-;Pn(Fs&xPo)97TC$SANi`kM;FT4G}l+* zKmFk#W06ZYlP1BHPhXRUz20v?W7j$kfBMREKJ(8i^(9OeSUqwH{BAZDEBWdXhL-l* z!St~Q+L^TivzUr`)uHEZIgP@f8_Q*ZO^G};{({dx6eK1B$?xdoOYPpFPYU z?48LD{|-3Hry+cBFoU(*q_!M3&{6EpJArVsuQ=?&y?<3hSs*NUS@8Y=@y&SGXoAN8nKc(>kvdf~rH^>J{*UiDs+0r_fKK7z&d}w=g zY`;$&`J4sAevQ0I&(+sFL00N;-8%s<+>kT6*=^3N-*X1f`(*7LfFn@@O(DXaC- zZps2^YBLVnc<}iuI%^!KG;m7&eolG+bLATEiLjOKQzgHADFovmxPg&RG{taAn~#{d za3J$XP2M4SNJr}ba6iHvj%)oS1oi5qh>hDb(B=0*F<-A9JUuJ*aqCAsma+ASiuYf> z$l|#aS563%a#N?@|0jyVu&7yI&|Cb7!q{~u&-u(h_VQxFWP$2V7O1)G8hpMb)m8Mt zXJFL83vD|+5TB!Fvn$G9n6J#+WFx^vOS!*5m1=W9&)|iGN=N3O zbfVt>V_4U7G-|cziIsQr`8RHlJ|LdGu*IIG-r{-AJ{&hCf&9^FwA9zywHhGTKWhBD zHngHU+t_dM%4pJHZsj4be_*HJ~scpW~sGE(qLw8r_^u ztKRMAxrFhqdrq*{>Z@G;MV)sa3HyJ+ZKLZ@T*UUPxc@hMWi7bYYABa~^3gm0?@wX) zzTOtjEv_NsVohEXbeU!?mL6*;%1(439Orl2&U)Z;<^4CPRg-@o9`LN_e|~;f$xrkP z{0ur75ga~#yRNuCRQmtHui<>cWWi0YsqniY_Y)ZtG0r=&kI5vFsd|i6tMS@~Q~IqY zJG|JU?EkRJ1`Eb6ziT6>g>UO`Qdspi7Qw)s_ot%Vpo=Op;5wH-cIIK(mo#GO3HAN& zZ85!x)R$W)ZxNwcH_$%pk2w1404&y6!+wK5^IQjAtt$S1e3&K8*#VQf$$6rNmgHyJ zH50VG-*H;~wd!x|%xuQ|UfUMRzHG}^m+6Ud3TT!Yay&B5Ol*1ShORJ7gf2e`PuiVg zeAp**(y;wlVnzSc;IRQI=l+n51Q(t;Pg;x)Q?CDFgVH8q?O*BoOEl~!yDaK=6nBRQ zxlJK)|8l}m)w+k+b~y$I@3B$sx7sZV%>o&pFWQlY!h03>pBQ?LWm{O8vNqwI0pkdB z*y2=i{js`s5CnIV-v1P{p)J{EL6?i}a6hyLI!tpR44XF|AUf%8L$_Db+A(Ql5#w_g zdXk1fbLIRWE5E$P*h+0D(j@4a$a``M%j1v3M?#u#{fF&1OL1AyyvXCbCu~3C=cbhZtHuqsjn2{`O@ebZdAx$%QT6kO zqon@XYw2vLxlP)C#)vwSr*y>rlfvQN03Yx@dWjJDc|Bc}r2;y)yDT=WekRJoxoxrG zr$n-&#=Mm>KkBDGV{CLmI|AvM3@>q(56cb)?y=bCf*$9L!`+u1gA_eCh) z{uMHA-J&?0s5MB~dpt%|-{-qobD0p`TCrc?zwas}q@7dvOAOOE&6uOgo)StPeI|@V z9)H+j)^hR3=r;4m*GZME(h(l}b%x`2YQwq<+*h&N_EPbz-~XS4gXB^-#Y9u(*W6aPZ(2XmbBG^Cw-;CrlQsbMZ91u}#IP z^>|*!rLHDmIqL~xOnrwK^)vi;yruaku^Z1%aNwab|H4s+_mmIzH&^zw=(hEmoHtaz zvxMyq4{t)zS8L{F0Jjv-Nk3%=~r$C9PKRSdvis_`S*u9zMST-`4%O=;4GdK zp`&*+7_{_ad-jD$c(h+?|JmBFWM9(QF#aZd_?d~3E2Va>E#kzYjhWctWjzry0&roI zDE6E4Q-d_P4XL>Pb~>(EVq)9O5Gmm*NoYfwzYVnu`-^Pm|CrP^gG;u+&cpVVGXD!DeW4OMs%dA|oW6LuamHJ;iTTZqs>an=G~Oj8_4w`RzYrudVJX z?PCWqU-HE6zq2R=-MqaO{#(;|EJ4dFBP1{N?@N1TL}AtJ6+1zH+E5vXn42@W{l`7$ zF(c!YKa@^+C;IB1APlEinTi%$6A-=Lh;^RPgppv1i6v>+y`bX!`~9aCZ1Zt9Ax(l= zo^GTeruP}pJy(sfe06+A_!;Jpk^W~UoCJd#?trJauA+U3be=P?e;-v*6IX;I$s+MF zuSx7TC+-02CVMLWf4Ww+9%GZ+za&i}*B4y%y#zH^N#k#R`>{z;mUzbR_p> z=hIPL-uYXafniHtGdQllY9aHFO>4sr{|=aO`3@A%y(vC7iY5pf%*_?OhL6EM6a2*P zZIOhLV4wTa@2FDvRBnINsneEh@M4qFr!W8Iz&{ObQr3fMOPEZaD|cL?`+=)A^C;V+uF2-eRwV}9Rl%2;9lh@Q<+qu)F1 z;xvWg@W4ngN{!_p9a!#}3*C5g)xc^Fh(n52m z^8S6swc|Q6|F{c<6qg0phqi~}%|BsI0k0e2UL{C~T3@m6j;^4;o3G`PFAjXp^9tlR zT$9U>CIhA4Hfe3Ll3%_YQkO-(|NeBPS3>u2FXo@-#BC;f!Slgo@XWpdTFh|-yh{mdvoJ< zyUB+HJJ(azLb?3tpXNd~<`2KzkHWIRh1h-Y=C%$T^xQxg#CY5h!R8j|kvu{b72QhQ zc>jlRY!H~m+N!cgH>% zJSV~YZSz5UF>spQpB{*F$38HBye9972q)qCFSHtigN{v?Ee(j<7$oXZcPXOf`m;BX0*j#B%t&i{Xm!tixt3^e|M z6vmk*N5%WnHt2lFU0CeUL(g{7?~1F>a}1cLDg7VL4a?;;&NVKQCc%BhdZbm4#(jp~ zPW<~Q4t|wNnLniYf5(5el|Sm0SH{E=Y5(WhVW)WeMo0bqAN4-8WF_c3I8%B|yu|eY zW%ar#<!2d1zq30{+K*RJjg+FgcBImI@=Ll&MT)SyMX%SZ= zTJHaF#jpdc9xna=Fu|AWjC@EidU_|UbmI$LnDK?;s?}Gs#RjKZXcyrk%FMY=*spo& z3f7H`_AC4!rE@vzL$Y9s2lqLOEBvDdB#7{1xm^Be-wv`bX;k$Z1h0=bN1aveINtWz zYjNQ1Z0y_EMz~K+$4(D;4}gPm9L6My=HcafvamNQK;$=uE_Nx6xRYRpK`(mogDhfj<{}l@Vw`N+5 zUE6n1@pEX+<4a`PPL|7$`rq8qy>BF!KVH*M_9cz;wq(P(e$^n?UK;1RMlQlGcR2Q& zRhjIf%C5Vl;bYU=q|x+z#r>z>n{8xlz9#p14mVsV?a2+QdMf;L3~rE(`DZ;nN?}=e z{Qt?wQU5>6`!6socN6Xz`Uo$ci>Ynv!w{q4b3T~>*e-wvvkDf9Agiog_E(-*z?@fCiu?CjLy&#N!j~LqNBKG;9xsdrM?ztk{Nu#^na`?4v5sdok zKygT|Q!cjko`v1h%*6Bco8i|nY5$|qG>`q8S>KiU(IiE!Z}GiL$501J|YU8#)Ytssnh=;&i=l zxJdnfoioK{L4!-0Xz=GSPBr6kj-&pb6dQu>qH~}oXc@FpzY8&(d@;o)fOVCh75Bg1 z+9eosU)+Z@32qJI-vn)*XM=Ii0gTP^+ld}xl~?vYzX@zysoj&pZ%|A+UpD(1gy?Yfd3N1d%$e^xJYBn+I@&oOEJW-e&9YHtHe=7OqOCeZN+EnTPapw=fvkTJy&w)v4b1W&QKgQ+-HkrRoeJS?s(i!gNe&h0c zKIHj~d`PhU@pP;mppQ+P@LYzCmwXq->BZQ(WEHfZcN^CK;<*(^CQ5Z((`d26f5PrN z=OJuAb7qv^*8Y4h1jjnEHJx7!0Yq0;^< zYOx1l|1VhOU}JQ-{g^Q5KYhDc9c6(w?)hNaVGgbs#_fee)^1{Za!5t_H|p}Z!*!|3 zn8Mt_+vL2VX0S3Z!`5fXT>hCKmA*oLxGr=RR@zt_Tco|0(}{UW3Ds8N#O9ibTpdK4QuS3 z%<-QiV#GpMeYE*y37VzPz%ODT;W$~3#~7@M+pX09R_VSo+_yH3d`NJ^8=fb`u0EDv z=24$8QGTwK`W?g4`yV4x9uX!BZY*dA-=ns{`{6SQ6NL*bRiAa9pz7j#u*{f?PVc%A zjv-^4v#xo1nZp0>LNQ|#-?@_}k=tL)({BWo?sD5fM$9ddQ(apBqO2N9p3)JgJ$8lb z&>03=6%r!qnd+!MYKN)sx2WToo@9aMrGE*>Ij-|bLvEIb%#YO%zhv8pCfcM)WPa7n z!#m~qhj6f%pTYGX(!7k~vY^RlZ+M~k1-9+r{w01H28k7W4&tDP^&Bs^?u9M?^dMjK zjB7(0eEK$0_)Sc6*k)T&K$-+&Z#$7z9kf^WA28)jU$N$57v_)u_TTkC@VGC$u9=CM ztGNGTpWBVZmFP6=+y0?r%%<6>nHA%Vr)rX27PNi-5(@Nm)X%H(9tz{TE)Z?^*Tw#yFRM~#9Ki8b690@# zJU*agvGV*UynQWUWy!opq&zX>pf73A3a&W+@jEb3Si)cCk7~vBLp~(`BkRiJa(cdh zds--a_UsDTLwe?%in2u6g+i31k}X2CiHf99mI&F)z6;s=SVQ&@vL{JMS$@ws_j7OG z&+B*oxM${l-gnM3b7#)XT_5;5Clh{L>4UXe@wIByfBRHiEh<4gr6Xd!3+3a9!)GA# zKN{4M+aC(nc@u^qGiQ({L7zbjNyCy$@09v4DBKA*3>UEf8RPjl6Nd!X6c2zuFED-4y@5p6fARw@u~!&jM9H#%g!vG0N%1Z`+fG=(m;r^_y0M zD@O_>|4DzyE(@-Wb%bXri^Oq%0~r&|?m1&`zhu>6vs9e!#p?+i6vTZT9>gf~A8PsX zJ~-evw*&2ukqbMMhH+w}QvYjlW^gk~djEgMSY96yhXm6#W}!h+HjX`(AtCC%yL{y; z?CLQIOvCg=h7ZSUv@qfr3#ZbzEA3z2W;B=KVRuH}7Z3Duq%<_0Q7rpcJvr$lb~KgV zzgu_}WiDx)=-vq4h+%NE0ncwx2k)vTCvL|+W21#%EtSm0=@ssjhNOv=&)+>Qct;p6 zvf4?S1h>3=L>ffcW7$7sg-sXFr}}dHhphVV`G1wf1Ms>a2(>D_cz^3}*TnGTmpE|E z0O7Fg5iD8A^C<@Zmg4V(p#4hy%M$PNK6S@dj(^{ruk63q#>)PGp~W3By_OmK_jo;w z?6P3&)f!OhdMyIp%P>>Q#_;e0m|{(NI6bjU3~e_BdwmTO&n8?2i-*^l=MndoG>Ke)_z)#s{~PCiS+b~q z%Q)azASTyU#vj?hH$yu9GcV`MT+--Xb0BKxAA(O!vN>NTaHHyD?gi{&mIOM(C&*m% zYQVn_4PUKQ{695bLv~0$%>9t}_x`5DVlZ)H9IdubhMz?zxc-3y_2n{|ztj8%>bl;5 zvuIUc}oL6v$hCYsrbKAn~yc_PaP$fiBW^!P+GP9 zNjwa_SB=uJ<}MDMJjMT+-@ImkZ@S7l3%#!Xqm8sy=39boM((Q4fX*=TL{60@fi~J$^ivLTkn-K=t=QK%^;9}TGTAZD? zAGFh@-){D+m-xI)y8jtCq9)m8!A)`M|3BE~q5iCQ0~r(5x9>pFW|k0sJh}b>v8@S{1^bj;g0H&+ zvBhme!eC&`c9r4VaI_vZSX?aI1}4vWJmIW%yU30spDOpCAZyMD#*S+9vEy_WZPn%Y zYpBmZ3#7IS(?-Ii_SZM2v@E#dau9r-bpvX?NFod>Jc?E08y-W~a6d8OEU!rk$ED{k zvhHWDJb$nL&p2(u{#P)vg4YXBW%GBa(uvy)?Q18X`PPZ-KeQd!N8SgwLl5}rGzN_y zeIg7koj!SrBEgP+47 zF!Pi8M}7qJ*uP&vDcNO#aLx8;aCta3s4k6>Q+Y*V`_jc|k@gzezVd*Iy?hRia|THL zzx&YRO8>joUm16Dd6+Wn1*O40dl(qG^7vG3FB=T;leb9z*EE*7q%mgwV$=-j1WzNl z5211UCgRHRLD*|e3A8Heibj(-UZ7dCGU9`6u({&@Q3;QA+;R3J*+|s?QzXql4UO(A z@z?*6GxkY5#{OpxuP!mAEpClazki+34RyC1BLvz^DG{|?{m}V%ylPXcvoaUkbx)x* zwz*$<{p*nn&p(jadbq;FbY3q|9&}DC*M%t0{^xyG#tLDW;stQKS08wkaE9}qIuC_c z-5)s6aF?*uolY1D#>7c;O@|+5a{bY`S`6E|&fsy#>D47X=AmZLB&a@gKV#$l!$fji zCi|bdc^+Z1h->A(sOfD1RgUi_46D6+Y{TIjd3}dk!}G-H znX9l%;!u%q&GCb{IO^Ue(y*!`RQ3;NW^nssio%xL?Zh`m3qadgAAId}ek#G_H{wa1}JVOj_qV#WE-A}65t{au-zn}V;av9o2 z7f6$!+334cN?n#b|Mz@53f?rk&;A!I@(xGjEP?cGq8J|Nb$Vx zTd)h}c!Dd^v&atb`;S-Z|6l{3U*eEhWlqHoTe%oj(YR31HwfU2dLshO2T8}j@&BI1kNiAwxO4e9z2JU+wgE3#we zPjAu?S@Q?zBsg<=UH%aGe6c6{kD6spm@F85ax8rMb`S^ub43DY>$J^$yfBy#moqxO^ z{6bxyEh5+QIF+EjPVil{*`I_xEPjCLekE3*d#eJnW38YiivQC$+weYjecefu;JQF< z($H<G_|a6QP93g7xnN!`G-#w0)HwZ~a!Zk1V+P zMi`V-tbi=lO2Tl|jUTF$D~oYJ&j@imV!W}nK0N?uah$V+|t^jma)|S^9Lo9T^3At_yhL>Jh5+HIAQAl z0r3-~Cbhtkr|l>Ymn4zOrV|06N|Ii5Tv;WJ7nwCwpFho$rRsLaKPl{~LwvyxFt`RQA)l{+1WIeoEDxG6YpTK<$%3e)U{OdNA z+9XTT<64QNMp$!1EWxbR0++*WW+z zIi+QRI~6mb%(*M<^C~6`7EZJRonGD0VbLzo(rYC1)a#E_wzqe!jQ`r$lrZe*RY;md zIsUHO-hiR>tpgk!(;55Dm)<`LiS&_}(iW2z&4H4vcx3E-A<@$HFPCrGGM6;gn6nLQ z+=b5xt;$w)(%pQ_C24GnV4t>?X?d=TK(KWnnYE3`8{b^_&fX zq{aCL51~ek7Gp!s-C&HaKX{E-Vl~^A&dk87M$XWEkkW==F#a8OZW#~tvUPA~Gd`cj zj(>Q44!dW5uk3%_r1Cqy<>SER4Nc~;0A6jcLS5?#gu&&zmRKj~2_OIPei?+xg0n}Q zMcq}eagG_UVc^t_e&U<=25en@7PKh2gKKB<7(?s3S+tLsE-K?sYE@p7Vc(e@+Xrs{Sh2KaR&4q|99}#!f0kOKU^0_>zgn zZIF2*0y%EL_3ycI{UOa-x;Bth^9ONAa8q?p(qN=l47%Tsa60ttaX1ud%Kj%bUgH-CvKZPDz!Jro}sfgQ{=dH)DUJz-n46<~q)fGzoeSi%JqCB3_atg1T;Qie=rT+J# zrM{_qxm+pl=xm;^C=XSnXND;)&TM$a{=Gc^TmQ|w&Y(fmci5cH$6x$eVJ?=NzeU@@ zg<#QtllZ!Uk0JJpTn)3XIXH&kZw5}PUlUs!9&dfC8kl=*%-$)CuG5L!BM}2tS zMbru>8woBp;_}5Z-DG+G#W_#Upkw+7_CNhneTgY;;pzH)sBIO3^-ko=xT>f3d~`VX zRdp-R3>}hs5RNl%c4Xc5^JB&TYwO31O*`jCngo-U-Xtwfc2&Or02_4AFvj=)e?6nL zEO=zn3e;TF7?w!qKUjC<7g4L~3G8mu7HXG9VH?MT#KqVzMXVc6Y^wPGTx!Z#Q#+1L zoZfP%HfgbOjq?2mc)Yh4_?eI7`iEVqMRr**@z@5q_Mj=;ar{CU-28*8%_nnlwB=dO zw`nWI*crc+xGnofogTC;BG>P0_DJ{3Z@JKO%E9(1ZRR753t;i+|6rA#e+~0}V zN$(PLC_Qm!TYSO0JM0>V(wk=KEojz2w~G>PmV zmt;&5ZyL8_|DJPuNmgl#^_w?FUC+7V%5yG@P;ofW2OTY+iMPhf#r<}CK8YjBtB?&F zG*j9ijZSmi!N4~U$wq>`_HrDjwCtbL?aoT;pJ}82+x|(x5%4i=Ct6>)OgxO((Mm)Y z)kUYG$KqF$RB_`^3gcm3tGJw9ciPDHN4?Dh8JoSFk2&)eTv$k2Z0lhw`$vr-wIH{# zbpOM*^&zs$f}1uTh4R3MaBf#7VW=}{pBQy~2RiF@6PcDb6yB_9e<=-ry(_Q3=1!8< z#w%iZZ9sNR(~Du={GIg`|5@t&PMp}p?LXmi5ZPtHSyem0YmKLJUgTdNCSKjRfCH-b z6^E_0?0 zk0cHWPK)>krRw*O*GybPc_^4{D{h9DVejox;^66GG+80VaMvyg%&-5hGXA}}#Qh!< zqE+ja%`IZml^)ZhQmZ7Yqb>TbhnU#@>p z;U`MVf;*0=-@mzUh{O6z^Lv4xy~wS&iLKMWL9Ker6<*&Vp_CTM3o7gXcqQ+HhOxYM zCOgjCwV!#b)APiaf6v+O7xhPz9!=T*)EV2!E(^38xC7p-a)rTDI7VWKNds~Ii2C~9 zEHklrz%AG~n9oDd`H*z})xJyR`KRfA2Py|=dwi42!?0M1XRy2os!yE4b|Zsfu-QJ8 z{Rd7NBXijn{r))WM1O;&^}H$HYjwRWaZlq0Iv#h1>Zc~**7JqT3w>)w8nW9~-hVfa ziDO%xiYuf^FpHIj7}n&S_;q)hlvD3lw%JMQf6jb7WgK@^odN~*UZP7`JYjGedI_87 z=h1GuuShVQ3+2_h-=qCd3$m-9Kkg;>f4KIT*IIaTUK_HJDE^a9tN;Hx{|;mGTHGhi z{ud8Y`X}2mbraylvJvp1W?RZb-XkO7?OlSsTa{2=Oxpa8G+JNGAq|f0mMZ>_ER&9Z z~BkK2@nGk-6@o*w^x|L-Kn5yDBZ#|Be)oEw8(9Y1qE>WYo( zr_~*FINsYfv7j=pPMNfe?5ZrQ%K87S!ym%n+*w!BBy#gwu6)vj3&^suLy)PI-6_&VNZkyVko2L)Qz|s(;$d zMaR;8GKcXAR;8?4FR5IAza2P?F!)=YV;9?3$4l!(^V(g(Ab1dC2kXy;h;k|Zh5lrW za1xyIq#NpddWBvC+$BW)+b!A}pzVdxqN-7ujA2B}EK0+VtW`?=uT7Ntrtbfo|DS#^ zmU&wDJizos4Nf1J^Bx*zO7q{$x5_$#Y*_WzPbh9%i_)?Poo5fRe^E`bI+w>j;n?O$ zJeKu(;GuuTx0*?U!!C$+tyWW2tU1Tuz4)cb8s!rTD9fKceFXMPegTXiqUf$GcP=W z;|8|Ot$hC`@gmP9XxDv;Tqdr$!*c-)TlF2Z_vyJdSdW+U*eH~R<<;zYLxwe=l`xhgST*>kr-C5H1TM3)?H@x!-Olr{(nz zBwU=UiVl$apYK>@uI6&r%C#FDK9}1|EKKPu`$tujy*N2CjF116AKag4KN56({0aUJJ_GaW^1O(t z#mz+KazDg*{?Oj45o~@Ttvh_2G>H$FT2-$9^8fbcGN7|IX%d`l!?9TPe1!V{UjjIO zL;kTCG@3t?{Re(PCjPrY|FTP{ap?vISv4UD$AvamZ3*_o?%HFa_BcN0VSM0{W2}uw z_EYMAd~_jWMnO8HNl^Ua_$C7V9)kW-z7~R6>fgO@tTX%fntzaq|8CIO=L}RFw~{ez zVAocd^)AKk6E}l?=^IQe^dlP%-zzTr0G z{d%>{Xa6&j`8N?pf@u*8)!#qz$G)+AEHR?IP&72DgT2+yi(TBDgYyUSdL1LrDr+po zf5mvN3$~n-E0v}$FY+(*RcE%MeEiu@-Qp7V@7I9)k-~S*PlaFF+Gug78}k-w+N)k4 zQ9u8`P5mBB^S0p6032!#s$K}uXj1Je+(G13Z6{-$o{95e<8ap>iddLs4=Kq{r#^Ggo*TjE{pq- zZxCvA7MJyS%|SSnM{;~dmxOnU|1UN#$%f;`?)@^N;H9pLZl1`=9cs zDWzqBiXeb+-zZv2Kwv^<{UaHKG(EI zw;rP;hP1_m$qy(ciuNn_zfk|YC1FrM&zv*~_MD=OC1}))`#-jsGYHL&&Sw8%r~67w zX^Y{7U7@&a1QciUc?rOTFfqez0S>%bBrNMFu?nL#LTL}Ue$QB`|Fvu$_ZXMKW0UL{ z{O|WC4vIjLSMU$-b0SI?OyaAs|IjY~?f+|Q+ry`+Uhuw@#}GPw&lWj7Qqj@0ndmow z=PxdIcGaq^&8Cl5{J&`Zjj@HTcy8kKHmj?oh1SgcolTkq zS1*>vnz6}v&>iW*n8s9ulAr|kKlcoeGbR&1xPuSees~7%Y|fD};idZwhZ)Yc_17OE zW2my@bt{BEQ`Ud#^VTh7N4uKK6kbM@Eu}^7-<|UKgNE<&)c?V>gZ(cI<=?FEm;Kle zIkWm;!?X5;Vave#s-GU7=-Q~I%wasiW!h%vA3`JYlNdsrjl;&5onc`5;b>YmDxbIJThM{5@oc#og*C z{y!e#K8Y*-@^2$MhWT=Q0rPL_>%Y?TmxPmW{lo9|l5)}(7wxPAKN~;92%js2V13g? zsz(bVv3mnsQP3|z=0f4Vdz6MHMMCkvv4#g>SR=DOX%gIajL$h#{mesU|2Q_<4x3E1 zWdE@>?n_K*3y3(RF+o$gA0N)wnm9yty5)QI^{12K-PR0v@ydqrL|2Y+aJOM){4HxL#q6+-$~u8! z4T*SteHQ3ctuE!%zx(-7>HKd(mo73!8hy^Gzkgno1JABY{VQ_+LDk_{2Xvg%MdXDz zqGq%d;}X)U5Fh^ey=a!fHHbs%G3epQbDwhLq7aHb2_f1=wGvde<# zB2eEX3+r~~7z)kL9~WI0H$(dxFQM9&o2b1yfVdd;K9uz!>z=ZIC^F@>E_N6aOEwa$ zrDaYU+)FCYKUTaiB#iyf4m?3=S@1;ve(-ao6^!}XgD@}@kHkPX_4~gQPlB%L1+)%$ zLO2F%II}%B>yF~T;KdEL%`fG-gVWWmzB8|GVxT zX5v>1?*BOd>1jypIh6f}7%OW<+L!tZYHiBI2GItThc4$@sGom1hPHL>MM82R)|ehm zIK~8DWBc-pmWu!Er?c6%B1SnkK##xN7gU3Tv!FrOV`LYF2?1y@W4mpR@u`QU zzSd-5<@?`tUrKX^p(*!W=B?|(=aJxaVhvOm4zeH6t#zFJhpK#KjQQ(-`9R6(1oRv< zn)7ph^%HqJZXjm16={by$XtxIjFQtz{cj&_$om+qJ}KMq*d|X(!^r2eM2Uygcems` z#%d>ivH!)Z<76&r?B3%jlunodJ`=2DT;%E{pz7#F;bvI}lSd{qZ{`Yae^?u!tiRy$ zjXG>Q-ATy%;_i;Flm;w`fx4k3GOoI({%$bce~ZBX%ztS4tv&ob+X)Bk7(rY-rg>H@ z@y$l-RUgGaU97QY`i5}ssFJRziA(s@QK@j_n-A_6=Me57D9)0e13pc zrUR74N$-CKCH*)4WxTLOopfL9b7dOupQWC=KG}6b%ThZqY_uNU&gFF(h8lk0a^vDF z|9>ob|9r-_Pq<2&1g+fjNsDaQ54H1_QyQ#wR)TwNss9JqDzTny*!BA__%rPQ*gf*# ze7esa)#ZJ&vFG>`V*mc9V#tsVj4z+c^D2CNTzUR`ZbSjwswWkZCP6os2c*F!N{K(X z^U7;+e!~!M|3%0A$Sw6=|N9PRWP>{a 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 z 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

G{o9q??CU27E_SZ@yGvqDQ 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

-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­o~KsMybGzFKVfQZFY*U`qENSf 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 QucZGPI*j192 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 literal 0 HcmV?d00001 diff --git a/nnpackage/examples/v1.3.0/two_tflites/mv1.0.tflite b/nnpackage/examples/v1.3.0/two_tflites/mv1.0.tflite new file mode 100644 index 0000000000000000000000000000000000000000..03f30c7478fc091c546d14409ac46d358783353e GIT binary patch literal 4276 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
f0I&~)AA}1nm?(%m!Q)r7f#G(X=1Oa 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%0AI{ioSqu}M#e^Dq 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 z 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

hLY(#2(0qs43Iu$D}@>cc@}?Le)X=W;QoGUJH4 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

T*4*jSLb?I-zKt(j0<3 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

` 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%0DnN~D@>Z8AY$V%a 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%0D8hSsEQ!mTP%IbB%@&Nm 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

4LRGGQz(|V{o@yD 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

sC+W1N~ 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

jn42dEZ=Dz3Nhncm~Pp_)A8vIwMclfGW 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[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 z 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%0DoI|e&>EIo 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[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%0D9TmKkU%f6sj>8)uP% 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 z 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%0A`-FZ?&zm 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�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