Apply ONE's release version accepted/tizen/unified/20220428.162652 submit/tizen/20220427.061713
authorChunseok Lee <chunseok.lee@samsung.com>
Wed, 27 Apr 2022 06:02:33 +0000 (15:02 +0900)
committerChunseok Lee <chunseok.lee@samsung.com>
Wed, 27 Apr 2022 06:02:33 +0000 (15:02 +0900)
- patch from 390544b into tizen branch

Change-Id: I2a1b4d12e1d0d1fd6a08217e6c03fcd6a3b6de2d
Signed-off-by: Chunseok Lee <chunseok.lee@samsung.com>
330 files changed:
.ahub/sam/exclude.txt
.ahub/tcchecker-tca/config.yaml
compiler/arser/include/arser/arser.h
compiler/circle-eval-diff/driver/Driver.cpp
compiler/circle-eval-diff/include/CircleEvalDiff.h
compiler/circle-eval-diff/src/CircleEvalDiff.cpp
compiler/circle-eval-diff/src/MetricPrinter.cpp
compiler/circle-eval-diff/src/MetricPrinter.h
compiler/circle-eval-diff/src/MetricPrinter.test.cpp
compiler/circle-execution-plan/src/CircleExecutionPlan.cpp
compiler/circle-inspect/driver/Driver.cpp
compiler/circle-inspect/src/Dump.cpp
compiler/circle-inspect/src/Reader.h [deleted file]
compiler/circle-opselector/driver/Driver.cpp
compiler/circle-partitioner/CMakeLists.txt
compiler/circle-partitioner/requires.cmake
compiler/circle-partitioner/src/CirclePartitioner.cpp
compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt
compiler/circle-quantizer-dredd-recipe-test/test.lst
compiler/circle-quantizer/CMakeLists.txt
compiler/circle-quantizer/requires.cmake
compiler/circle-quantizer/src/CircleQuantizer.cpp
compiler/circle-tensordump/driver/Driver.cpp
compiler/circle-tensordump/src/Dump.cpp
compiler/circle-tensordump/src/Reader.cpp [deleted file]
compiler/circle-tensordump/src/Reader.h [deleted file]
compiler/circle-verify/src/Driver.cpp
compiler/circle2circle/CMakeLists.txt
compiler/circle2circle/requires.cmake
compiler/circle2circle/src/Circle2Circle.cpp
compiler/circlechef/tools/file/Driver.cpp
compiler/circlechef/tools/reverse/Driver.cpp
compiler/circledump/CMakeLists.txt
compiler/circledump/driver/Driver.cpp
compiler/circledump/requires.cmake
compiler/circledump/src/Dump.cpp
compiler/circledump/src/Load.cpp [deleted file]
compiler/circledump/src/OpPrinter.cpp
compiler/circledump/src/Read.cpp [deleted file]
compiler/cli/CMakeLists.txt
compiler/common-artifacts/src/TestDataGenerator.cpp
compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp
compiler/luci-eval-driver/src/EvalDriver.cpp
compiler/luci-interpreter/src/loader/nodes/Add.cpp
compiler/luci-interpreter/src/loader/nodes/ArgMax.cpp
compiler/luci-interpreter/src/loader/nodes/AveragePool2D.cpp
compiler/luci-interpreter/src/loader/nodes/BatchMatMul.cpp
compiler/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp
compiler/luci-interpreter/src/loader/nodes/Cast.cpp
compiler/luci-interpreter/src/loader/nodes/Concatenation.cpp
compiler/luci-interpreter/src/loader/nodes/Conv2D.cpp
compiler/luci-interpreter/src/loader/nodes/DepthToSpace.cpp
compiler/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp
compiler/luci-interpreter/src/loader/nodes/Dequantize.cpp
compiler/luci-interpreter/src/loader/nodes/Div.cpp
compiler/luci-interpreter/src/loader/nodes/Elu.cpp
compiler/luci-interpreter/src/loader/nodes/Equal.cpp
compiler/luci-interpreter/src/loader/nodes/Exp.cpp
compiler/luci-interpreter/src/loader/nodes/Floor.cpp
compiler/luci-interpreter/src/loader/nodes/FloorDiv.cpp
compiler/luci-interpreter/src/loader/nodes/FullyConnected.cpp
compiler/luci-interpreter/src/loader/nodes/Gather.cpp
compiler/luci-interpreter/src/loader/nodes/Greater.cpp
compiler/luci-interpreter/src/loader/nodes/GreaterEqual.cpp
compiler/luci-interpreter/src/loader/nodes/If.cpp
compiler/luci-interpreter/src/loader/nodes/InstanceNorm.cpp
compiler/luci-interpreter/src/loader/nodes/L2Normalize.cpp
compiler/luci-interpreter/src/loader/nodes/L2Pool2D.cpp
compiler/luci-interpreter/src/loader/nodes/LeakyRelu.cpp
compiler/luci-interpreter/src/loader/nodes/Less.cpp
compiler/luci-interpreter/src/loader/nodes/LessEqual.cpp
compiler/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp
compiler/luci-interpreter/src/loader/nodes/LogSoftmax.cpp
compiler/luci-interpreter/src/loader/nodes/LogicalAnd.cpp
compiler/luci-interpreter/src/loader/nodes/LogicalNot.cpp
compiler/luci-interpreter/src/loader/nodes/LogicalOr.cpp
compiler/luci-interpreter/src/loader/nodes/Logistic.cpp
compiler/luci-interpreter/src/loader/nodes/MaxPool2D.cpp
compiler/luci-interpreter/src/loader/nodes/Maximum.cpp
compiler/luci-interpreter/src/loader/nodes/Mean.cpp
compiler/luci-interpreter/src/loader/nodes/Minimum.cpp
compiler/luci-interpreter/src/loader/nodes/MirrorPad.cpp
compiler/luci-interpreter/src/loader/nodes/Mul.cpp
compiler/luci-interpreter/src/loader/nodes/Neg.cpp
compiler/luci-interpreter/src/loader/nodes/NotEqual.cpp
compiler/luci-interpreter/src/loader/nodes/PRelu.cpp
compiler/luci-interpreter/src/loader/nodes/Pack.cpp
compiler/luci-interpreter/src/loader/nodes/Pad.cpp
compiler/luci-interpreter/src/loader/nodes/PadV2.cpp
compiler/luci-interpreter/src/loader/nodes/Pow.cpp
compiler/luci-interpreter/src/loader/nodes/Quantize.cpp
compiler/luci-interpreter/src/loader/nodes/Relu.cpp
compiler/luci-interpreter/src/loader/nodes/Relu6.cpp
compiler/luci-interpreter/src/loader/nodes/Reshape.cpp
compiler/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp
compiler/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp
compiler/luci-interpreter/src/loader/nodes/ReverseV2.cpp
compiler/luci-interpreter/src/loader/nodes/Rsqrt.cpp
compiler/luci-interpreter/src/loader/nodes/SVDF.cpp
compiler/luci-interpreter/src/loader/nodes/Slice.cpp
compiler/luci-interpreter/src/loader/nodes/Softmax.cpp
compiler/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp
compiler/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp
compiler/luci-interpreter/src/loader/nodes/Split.cpp
compiler/luci-interpreter/src/loader/nodes/SplitV.cpp
compiler/luci-interpreter/src/loader/nodes/Sqrt.cpp
compiler/luci-interpreter/src/loader/nodes/Square.cpp
compiler/luci-interpreter/src/loader/nodes/SquaredDifference.cpp
compiler/luci-interpreter/src/loader/nodes/Squeeze.cpp
compiler/luci-interpreter/src/loader/nodes/StridedSlice.cpp
compiler/luci-interpreter/src/loader/nodes/Sub.cpp
compiler/luci-interpreter/src/loader/nodes/Tanh.cpp
compiler/luci-interpreter/src/loader/nodes/Transpose.cpp
compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp
compiler/luci-interpreter/src/loader/nodes/Unpack.cpp
compiler/luci-interpreter/src/loader/nodes/While.cpp
compiler/luci/import/CMakeLists.txt
compiler/luci/import/include/luci/ImporterEx.h [moved from compiler/tfldump/include/tflread/Model.h with 54% similarity]
compiler/luci/import/src/ImporterEx.cpp [new file with mode: 0644]
compiler/luci/pass/include/luci/CircleOptimizer.h
compiler/luci/pass/include/luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h [moved from compiler/circledump/include/circleread/Model.h with 50% similarity]
compiler/luci/pass/src/CircleOptimizer.cpp
compiler/luci/pass/src/CircleQuantizer.cpp
compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp
compiler/luci/pass/src/FuseInstanceNormPass.cpp
compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp [new file with mode: 0644]
compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp [new file with mode: 0644]
compiler/luci/requires.cmake
compiler/luci/service/src/CircleShapeInferenceRule.cpp
compiler/mio-circle04/include/mio_circle/Helper.h
compiler/mio-circle04/include/mio_circle/Reader.h [moved from compiler/circledump/src/Read.h with 67% similarity]
compiler/mio-circle04/src/Reader.cpp [moved from compiler/circle-inspect/src/Reader.cpp with 67% similarity]
compiler/mio-circle04/src/Reader.test.cpp [new file with mode: 0644]
compiler/one-cmds/one-quantize
compiler/one-cmds/tests/one-quantize_neg_019.test
compiler/pota-quantization-value-test/CMakeLists.txt
compiler/record-minmax-conversion-test/CMakeLists.txt
compiler/record-minmax/driver/Driver.cpp
compiler/record-minmax/src/RecordMinMax.cpp
compiler/tf2circle-conversion-test/CMakeLists.txt
compiler/tf2circle-dredd-pb-test/CMakeLists.txt
compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt
compiler/tf2circle-model-test/CMakeLists.txt
compiler/tf2tflite-dredd-pb-test/CMakeLists.txt
compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt
compiler/tf2tflite-value-pb-test/CMakeLists.txt
compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt
compiler/tf2tfliteV2-conversion-test/CMakeLists.txt
compiler/tfl-inspect/CMakeLists.txt
compiler/tfl-inspect/driver/Driver.cpp
compiler/tfl-verify/CMakeLists.txt
compiler/tfl-verify/src/Driver.cpp
compiler/tflchef/CMakeLists.txt
compiler/tflchef/tflite/CMakeLists.txt
compiler/tflchef/tflite/src/FillerHelper.cpp
compiler/tflchef/tflite/src/FillerHelper.h
compiler/tflchef/tflite/src/Op/Add.cpp
compiler/tflchef/tflite/src/Op/Maximum.cpp
compiler/tflchef/tflite/src/Op/Minimum.cpp
compiler/tflchef/tflite/src/Op/Mul.cpp
compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp
compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp
compiler/tflchef/tflite/src/Op/PadV2.cpp
compiler/tflchef/tflite/src/Op/ScatterNd.cpp
compiler/tflchef/tflite/src/Op/SegmentSum.cpp
compiler/tflchef/tflite/src/Op/Sub.cpp
compiler/tflchef/tflite/src/Op/include/Abs.h [moved from compiler/tflchef/tflite/src/Op/Abs.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Add.h [moved from compiler/tflchef/tflite/src/Op/Add.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/AddN.h [moved from compiler/tflchef/tflite/src/Op/AddN.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ArgMax.h [moved from compiler/tflchef/tflite/src/Op/ArgMax.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ArgMin.h [moved from compiler/tflchef/tflite/src/Op/ArgMin.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/AveragePool2D.h [moved from compiler/tflchef/tflite/src/Op/AveragePool2D.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/BatchMatMul.h [moved from compiler/tflchef/tflite/src/Op/BatchMatMul.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/BatchToSpaceND.h [moved from compiler/tflchef/tflite/src/Op/BatchToSpaceND.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/BidirectionalSequenceLSTM.h [moved from compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Cast.h [moved from compiler/tflchef/tflite/src/Op/Cast.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Ceil.h [moved from compiler/tflchef/tflite/src/Op/Ceil.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Concatenation.h [moved from compiler/tflchef/tflite/src/Op/Concatenation.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Conv2D.h [moved from compiler/tflchef/tflite/src/Op/Conv2D.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Cos.h [moved from compiler/tflchef/tflite/src/Op/Cos.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/DepthToSpace.h [moved from compiler/tflchef/tflite/src/Op/DepthToSpace.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/DepthwiseConv2D.h [moved from compiler/tflchef/tflite/src/Op/DepthwiseConv2D.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Dequantize.h [moved from compiler/tflchef/tflite/src/Op/Dequantize.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Div.h [moved from compiler/tflchef/tflite/src/Op/Div.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ELU.h [moved from compiler/tflchef/tflite/src/Op/ELU.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Equal.h [moved from compiler/tflchef/tflite/src/Op/Equal.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Exp.h [moved from compiler/tflchef/tflite/src/Op/Exp.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ExpandDims.h [moved from compiler/tflchef/tflite/src/Op/ExpandDims.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/FakeQuant.h [moved from compiler/tflchef/tflite/src/Op/FakeQuant.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Fill.h [moved from compiler/tflchef/tflite/src/Op/Fill.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Floor.h [moved from compiler/tflchef/tflite/src/Op/Floor.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/FloorDiv.h [moved from compiler/tflchef/tflite/src/Op/FloorDiv.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/FloorMod.h [moved from compiler/tflchef/tflite/src/Op/FloorMod.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/FullyConnected.h [moved from compiler/tflchef/tflite/src/Op/FullyConnected.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Gather.h [moved from compiler/tflchef/tflite/src/Op/Gather.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/GatherNd.h [moved from compiler/tflchef/tflite/src/Op/GatherNd.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Greater.h [moved from compiler/tflchef/tflite/src/Op/Greater.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/GreaterEqual.h [moved from compiler/tflchef/tflite/src/Op/GreaterEqual.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/L2Normalize.h [moved from compiler/tflchef/tflite/src/Op/L2Normalize.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/L2Pool2D.h [moved from compiler/tflchef/tflite/src/Op/L2Pool2D.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LeakyRelu.h [moved from compiler/tflchef/tflite/src/Op/LeakyRelu.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Less.h [moved from compiler/tflchef/tflite/src/Op/Less.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LessEqual.h [moved from compiler/tflchef/tflite/src/Op/LessEqual.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LocalResponseNormalization.h [moved from compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Log.h [moved from compiler/tflchef/tflite/src/Op/Log.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LogSoftmax.h [moved from compiler/tflchef/tflite/src/Op/LogSoftmax.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LogicalAnd.h [moved from compiler/tflchef/tflite/src/Op/LogicalAnd.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LogicalNot.h [moved from compiler/tflchef/tflite/src/Op/LogicalNot.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/LogicalOr.h [moved from compiler/tflchef/tflite/src/Op/LogicalOr.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Logistic.h [moved from compiler/tflchef/tflite/src/Op/Logistic.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/MatrixDiag.h [moved from compiler/tflchef/tflite/src/Op/MatrixDiag.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/MatrixSetDiag.h [moved from compiler/tflchef/tflite/src/Op/MatrixSetDiag.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/MaxPool2D.h [moved from compiler/tflchef/tflite/src/Op/MaxPool2D.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Maximum.h [moved from compiler/tflchef/tflite/src/Op/Maximum.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Mean.h [moved from compiler/tflchef/tflite/src/Op/Mean.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Minimum.h [moved from compiler/tflchef/tflite/src/Op/Minimum.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/MirrorPad.h [moved from compiler/tflchef/tflite/src/Op/MirrorPad.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Mul.h [moved from compiler/tflchef/tflite/src/Op/Mul.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Neg.h [moved from compiler/tflchef/tflite/src/Op/Neg.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV4.h [moved from compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/NonMaxSuppressionV5.h [moved from compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/NotEqual.h [moved from compiler/tflchef/tflite/src/Op/NotEqual.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/OneHot.h [moved from compiler/tflchef/tflite/src/Op/OneHot.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/PRelu.h [moved from compiler/tflchef/tflite/src/Op/PRelu.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Pack.h [moved from compiler/tflchef/tflite/src/Op/Pack.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Pad.h [moved from compiler/tflchef/tflite/src/Op/Pad.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/PadV2.h [moved from compiler/tflchef/tflite/src/Op/PadV2.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Pow.h [moved from compiler/tflchef/tflite/src/Op/Pow.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Quantize.h [moved from compiler/tflchef/tflite/src/Op/Quantize.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Range.h [moved from compiler/tflchef/tflite/src/Op/Range.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Rank.h [moved from compiler/tflchef/tflite/src/Op/Rank.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReLU.h [moved from compiler/tflchef/tflite/src/Op/ReLU.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReLU6.h [moved from compiler/tflchef/tflite/src/Op/ReLU6.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReLUN1To1.h [moved from compiler/tflchef/tflite/src/Op/ReLUN1To1.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReduceAny.h [moved from compiler/tflchef/tflite/src/Op/ReduceAny.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReduceMax.h [moved from compiler/tflchef/tflite/src/Op/ReduceMax.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReduceMin.h [moved from compiler/tflchef/tflite/src/Op/ReduceMin.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReduceProd.h [moved from compiler/tflchef/tflite/src/Op/ReduceProd.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Reshape.h [moved from compiler/tflchef/tflite/src/Op/Reshape.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ResizeBilinear.h [moved from compiler/tflchef/tflite/src/Op/ResizeBilinear.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ResizeNearestNeighbor.h [moved from compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReverseSequence.h [moved from compiler/tflchef/tflite/src/Op/ReverseSequence.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ReverseV2.h [moved from compiler/tflchef/tflite/src/Op/ReverseV2.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Round.h [moved from compiler/tflchef/tflite/src/Op/Round.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Rsqrt.h [moved from compiler/tflchef/tflite/src/Op/Rsqrt.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SVDF.h [moved from compiler/tflchef/tflite/src/Op/SVDF.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ScatterNd.h [moved from compiler/tflchef/tflite/src/Op/ScatterNd.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SegmentSum.h [moved from compiler/tflchef/tflite/src/Op/SegmentSum.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Select.h [moved from compiler/tflchef/tflite/src/Op/Select.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SelectV2.h [moved from compiler/tflchef/tflite/src/Op/SelectV2.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Shape.h [moved from compiler/tflchef/tflite/src/Op/Shape.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Sin.h [moved from compiler/tflchef/tflite/src/Op/Sin.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Slice.h [moved from compiler/tflchef/tflite/src/Op/Slice.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Softmax.h [moved from compiler/tflchef/tflite/src/Op/Softmax.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SpaceToBatchND.h [moved from compiler/tflchef/tflite/src/Op/SpaceToBatchND.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SpaceToDepth.h [moved from compiler/tflchef/tflite/src/Op/SpaceToDepth.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SparseToDense.h [moved from compiler/tflchef/tflite/src/Op/SparseToDense.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Split.h [moved from compiler/tflchef/tflite/src/Op/Split.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SplitV.h [moved from compiler/tflchef/tflite/src/Op/SplitV.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Sqrt.h [moved from compiler/tflchef/tflite/src/Op/Sqrt.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Square.h [moved from compiler/tflchef/tflite/src/Op/Square.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/SquaredDifference.h [moved from compiler/tflchef/tflite/src/Op/SquaredDifference.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Squeeze.h [moved from compiler/tflchef/tflite/src/Op/Squeeze.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/StridedSlice.h [moved from compiler/tflchef/tflite/src/Op/StridedSlice.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Sub.h [moved from compiler/tflchef/tflite/src/Op/Sub.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Sum.h [moved from compiler/tflchef/tflite/src/Op/Sum.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Tanh.h [moved from compiler/tflchef/tflite/src/Op/Tanh.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Tile.h [moved from compiler/tflchef/tflite/src/Op/Tile.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/TopKV2.h [moved from compiler/tflchef/tflite/src/Op/TopKV2.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Transpose.h [moved from compiler/tflchef/tflite/src/Op/Transpose.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/TransposeConv.h [moved from compiler/tflchef/tflite/src/Op/TransposeConv.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/UnidirectionalSequenceLSTM.h [moved from compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Unique.h [moved from compiler/tflchef/tflite/src/Op/Unique.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Unpack.h [moved from compiler/tflchef/tflite/src/Op/Unpack.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/Where.h [moved from compiler/tflchef/tflite/src/Op/Where.h with 100% similarity]
compiler/tflchef/tflite/src/Op/include/ZerosLike.h [moved from compiler/tflchef/tflite/src/Op/ZerosLike.h with 100% similarity]
compiler/tflchef/tflite/src/TFliteOpChefs.h
compiler/tflchef/tools/file/Driver.cpp
compiler/tflchef/tools/reverse/Driver.cpp
compiler/tfldump/CMakeLists.txt
compiler/tfldump/driver/Driver.cpp
compiler/tfldump/requires.cmake
compiler/tfldump/src/Load.cpp [deleted file]
compiler/tflite2circle-conversion-test/CMakeLists.txt
compiler/tflite2circle/driver/Driver.cpp
docs/release/1.20/index.rst [new file with mode: 0644]
docs/release/1.20/release-note-1.20.0.md [new file with mode: 0644]
infra/command/gen-coverage-report
infra/debian/compiler/changelog
infra/debian/runtime/changelog
infra/packaging/preset/20220323
infra/packaging/preset/20220323_windows
infra/scripts/compiler_modules.sh
infra/scripts/docker_build_test_x64.sh
infra/scripts/test_ubuntu_runtime_mixed.sh
res/TensorFlowLiteRecipes/FullyConnected_007/test.recipe [new file with mode: 0644]
res/TensorFlowLiteRecipes/FullyConnected_007/test.reverse [new file with mode: 0644]
res/TensorFlowLiteRecipes/FullyConnected_007/test.rule [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_000/test.recipe [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_000/test.rule [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_001/test.recipe [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_001/test.rule [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_002/test.recipe [new file with mode: 0644]
res/TensorFlowLiteRecipes/Quant_Conv_002/test.rule [new file with mode: 0644]
runtime/libs/misc/CMakeLists.txt
runtime/libs/misc/test/CMakeLists.txt [new file with mode: 0644]
runtime/libs/misc/test/IndexEnumerator.test.cpp [new file with mode: 0644]
runtime/libs/ndarray/test/CMakeLists.txt
runtime/libs/ndarray/test/ndarray_test.cpp
runtime/onert/backend/trix/CMakeLists.txt
runtime/onert/core/src/exec/feature/nhwc/View.h
runtime/onert/frontend/base_loader/include/base_loader.h
runtime/onert/frontend/trix/CMakeLists.txt
runtime/onert/test/CMakeLists.txt
runtime/onert/test/core/exec/feature/MockTensor.h [new file with mode: 0644]
runtime/onert/test/core/exec/feature/nchw/Reader.cc [new file with mode: 0644]
runtime/onert/test/core/exec/feature/nchw/View.cc [new file with mode: 0644]
runtime/onert/test/core/exec/feature/nhwc/Reader.cc [new file with mode: 0644]
runtime/onert/test/core/exec/feature/nhwc/View.cc [new file with mode: 0644]
tests/nnfw_api/src/CircleGen.cc
tests/nnfw_api/src/CircleGen.h
tests/nnfw_api/src/one_op_tests/Add.cc
tests/nnfw_api/src/one_op_tests/Cast.cc
tests/nnfw_api/src/one_op_tests/Greater.cc [new file with mode: 0644]
tests/nnfw_api/src/one_op_tests/GreaterEqual.cc [new file with mode: 0644]
tests/nnfw_api/src/one_op_tests/Less.cc [new file with mode: 0644]
tests/nnfw_api/src/one_op_tests/LessEqual.cc [new file with mode: 0644]
tests/nnfw_api/src/one_op_tests/NotEqual.cc [new file with mode: 0644]
tests/tools/nnpackage_run/src/rawformatter.cc

index c9ba5e0..f16f84f 100644 (file)
@@ -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
 
index 95e11d0..010f6e0 100644 (file)
@@ -4,19 +4,13 @@ test:
     testCaseLanguage: CPP
     testFW: GTEST
     testCaseFolder:
-      - /compute/test/cker
+      - /compute/test
+      - /runtime/libs/misc/test
+      - /runtime/libs/ndarray/test
       - /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
+      - /runtime/onert/test
+      - /tests/nnfw_api
 
     testFile:
       - extension: cpp
@@ -28,6 +22,7 @@ test:
         - functionName:
             starts:
               - TEST
+              - TYPED_TEST
         - excludes :
           - Verifier.dag_checker
           - graph_operand_LayoutSet.layout_set_operators
index 1703e42..43f99dc 100644 (file)
@@ -303,7 +303,7 @@ private:
   std::string _long_name;
   std::string _short_name;
   std::vector<std::string> _names;
-  std::string _type;
+  std::string _type = "string";
   std::string _help_message;
   std::function<void(void)> _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<char>(::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<char>(::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<char>(::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<Argument> &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 <typename T> T Arser::get(const std::string &arg_name)
   return get_impl(arg_name, static_cast<T *>(nullptr));
 }
 
+class Helper
+{
+public:
+  static void add_version(Arser &arser, const std::function<void(void)> &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__
index f4a12a4..e748335 100644 (file)
@@ -35,6 +35,9 @@ Metric to_metric(const std::string &str)
   if (to_lower_case(str).compare("mae") == 0)
     return Metric::MAE;
 
+  if (to_lower_case(str).compare("mape") == 0)
+    return Metric::MAPE;
+
   throw std::runtime_error("Unsupported metric.");
 }
 
@@ -58,50 +61,23 @@ 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.add_argument("--first_model")
-    .nargs(1)
-    .type(arser::DataType::STR)
-    .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::Helper::add_version(arser, print_version);
+
+  arser.add_argument("--first_model").required(true).help("First 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("--metric").default_value("MAE").help("Metric for comparison (default: MAE)");
 
   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");
 
index bf6aff4..6fce6b6 100644 (file)
@@ -32,7 +32,8 @@ class ModuleEvalDiff;
 enum class Metric
 {
   Undefined, // For debugging
-  MAE,
+  MAE,       // Mean Absolute Error
+  MAPE,      // Mean Percentage Absolute Error
 };
 
 enum class InputFormat
index c39a113..e493ef4 100644 (file)
@@ -69,6 +69,9 @@ void CircleEvalDiff::init()
     case Metric::MAE:
       metric = std::make_unique<MAEPrinter>();
       break;
+    case Metric::MAPE:
+      metric = std::make_unique<MAPEPrinter>();
+      break;
     default:
       throw std::runtime_error("Unsupported metric.");
   }
index d65eb9b..1077360 100644 (file)
@@ -180,6 +180,111 @@ 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<luci::CircleNode *>(first_output[i]);
+    const auto second_node = loco::must_cast<luci::CircleNode *>(second_output[i]);
+    assert(same_shape(first_node, second_node)); // FIX_CALLER_UNLESS
+
+    // 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<loco::DataType::FLOAT32>(num_elems);
+
+    // Check the buffer is initilized with zero
+    for (uint32_t j = 0; j < num_elems; j++)
+      assert(_intermediate.at(i).at<loco::DataType::FLOAT32>(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<Tensor> &a,
+                                            const std::shared_ptr<Tensor> &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<loco::DataType::FLOAT32>(); i++)
+  {
+    const auto a_val = a->at<loco::DataType::FLOAT32>(i);
+    const auto b_val = b->at<loco::DataType::FLOAT32>(i);
+    _intermediate.at(output_idx).at<loco::DataType::FLOAT32>(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<std::shared_ptr<Tensor>> &first,
+                             const std::vector<std::shared_ptr<Tensor>> &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<loco::DataType::FLOAT32>();
+
+    // Compute MAPE
+    float mape = 0.0;
+    for (uint32_t elem_idx = 0; elem_idx < elem_count; elem_idx++)
+      mape += inter.at<loco::DataType::FLOAT32>(elem_idx);
+
+    mape = mape / elem_count;
+    mape = mape / _num_data;
+    mape *= 100.0;
+
+    os << "MAPE for " << name << " is " << mape << "%" << std::endl;
+  }
+}
+
 } // namespace circle_eval_diff
 
 #undef THROW_UNLESS
index b51581c..55228f7 100644 (file)
@@ -85,6 +85,28 @@ private:
   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<std::shared_ptr<Tensor>> &first,
+                  const std::vector<std::shared_ptr<Tensor>> &second);
+
+  void dump(std::ostream &os) const;
+
+private:
+  void accum_mean_absolute_error(uint32_t index, const std::shared_ptr<Tensor> &a,
+                                 const std::shared_ptr<Tensor> &b);
+
+private:
+  // Store accumulated sum of absolute error for each output
+  std::vector<Tensor> _intermediate;
+  std::vector<std::string> _output_names;
+  uint32_t _num_data = 0;
+};
+
 } // namespace circle_eval_diff
 
 #endif // __CIRCLE_EVAL_DIFF_METRIC_PRINTER_H__
index 51ca897..3502384 100644 (file)
@@ -233,4 +233,52 @@ 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<std::shared_ptr<Tensor>> first_result;
+  {
+    auto output = output_tensor_with_value(&first, 2.0);
+    first_result.emplace_back(output);
+  }
+
+  std::vector<std::shared_ptr<Tensor>> 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));
+}
+
 } // namespace circle_eval_diff
index 1788124..3a0cebd 100644 (file)
@@ -33,14 +33,9 @@ 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)
index 10e185d..318a582 100644 (file)
@@ -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
   {
index bba5e56..aa8fed2 100644 (file)
@@ -15,7 +15,9 @@
  */
 
 #include "Dump.h"
-#include "Reader.h"
+
+#include <mio_circle/Helper.h>
+#include <mio_circle/Reader.h>
 
 #include <ostream>
 
@@ -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<int32_t> &outputs = circleinspect::as_index_vector(op->outputs());
+    const std::vector<int32_t> &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<int32_t> &inputs = circleinspect::as_index_vector(op->inputs());
+        const std::vector<int32_t> &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<std::string, int32_t> 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.h b/compiler/circle-inspect/src/Reader.h
deleted file mode 100644 (file)
index c38ec39..0000000
+++ /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 <mio/circle/schema_generated.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace circleinspect
-{
-
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
-{
-  std::vector<T> 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<flatbuffers::Offset<circle::SubGraph>>;
-  using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
-  using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
-  using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
-
-public:
-  Reader(const circle::Model *model);
-
-  Reader() = delete;
-
-public:
-  const std::vector<const circle::OperatorCode *> &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<int32_t> &inputs() const { return _inputs; }
-  const std::vector<int32_t> &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<const circle::OperatorCode *> _op_codes;
-  std::vector<int32_t> _inputs;
-  std::vector<int32_t> _outputs;
-};
-
-} // namespace circleinspect
-
-#endif // __READER_H__
index a1ace4f..4b39a6d 100644 (file)
@@ -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
   {
index 9b8f5af..79a726a 100644 (file)
@@ -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)
@@ -20,7 +19,6 @@ 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)
index 690d953..82d9c2b 100644 (file)
@@ -1,4 +1,3 @@
-require("foder")
 require("crew")
 require("pepper-csv2vec")
 require("safemain")
index 0151e92..8175041 100644 (file)
@@ -18,9 +18,7 @@
 #include "PartitionExport.h"
 #include "HelperPath.h"
 
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
 #include <luci/Service/Validate.h>
 #include <luci/CircleExporter.h>
 #include <luci/CircleFileExpContract.h>
@@ -53,63 +51,23 @@ 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)
-    .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)
-    .help("Work folder of partition, input files exist and output files are produced");
-}
+  arser::Helper::add_version(arser, print_version);
 
-std::unique_ptr<luci::Module> load_model(const std::string &input_path)
-{
-  // Load model from the file
-  foder::FileLoader file_loader{input_path};
-  std::vector<char> model_data = file_loader.load();
+  arser.add_argument(opt_bks).help("Backends in CSV to use for partitioning");
 
-  // Verify flatbuffers
-  flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
-  if (!circle::VerifyModelBuffer(verifier))
-  {
-    std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
-    return nullptr;
-  }
+  arser.add_argument(opt_def).help("Default backend to assign");
 
-  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;
-  }
+  arser.add_argument(opt_part).help("Partition file which provides backend to assign");
+  arser.add_argument(opt_input).help("Input circle model filename");
+  arser.add_argument(opt_work).help(
+    "Work folder of partition, input files exist and output files are produced");
+}
 
+std::unique_ptr<luci::Module> load_model(const std::string &input_path)
+{
   // Import from input Circle file
-  luci::Importer importer;
-  return importer.importModule(circle_model);
+  luci::ImporterEx importerex;
+  return importerex.importVerifyModule(input_path);
 }
 
 } // namespace
index 5ec8b6e..a3a2902 100644 (file)
@@ -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 $<TARGET_FILE:circle-quantizer> --quantize_dequantize_weights float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${CIRCLE_PATH} ${FAKE_QUANT_CIRCLE_PATH}
     COMMAND $<TARGET_FILE:record-minmax> --input_model ${FAKE_QUANT_CIRCLE_PATH} --output_model ${RECORDED_CIRCLE_PATH}
-    COMMAND $<TARGET_FILE:circle-quantizer> --quantize_with_minmax float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${RECORDED_CIRCLE_PATH} ${QUANT_CIRCLE_PATH}
+    COMMAND $<TARGET_FILE:circle-quantizer>
+      --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
index 1881030..b2e95f0 100644 (file)
@@ -11,5 +11,8 @@ 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)
index 14e0097..16e41a3 100644 (file)
@@ -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)
index c21e28e..4fcee18 100644 (file)
@@ -1,4 +1,3 @@
-require("foder")
 require("loco")
 require("locop")
 require("safemain")
index e0c85cb..e30011b 100644 (file)
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
 #include <luci/CircleQuantizer.h>
 #include <luci/Service/Validate.h>
 #include <luci/CircleExporter.h>
@@ -109,23 +107,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 +120,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 +145,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 +153,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 +355,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<char> model_data = file_loader.load();
-
-  // Verify flatbuffers
-  flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
-  if (!circle::VerifyModelBuffer(verifier))
-  {
-    std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
+  luci::ImporterEx importerex;
+  auto module = importerex.importVerifyModule(input_path);
+  if (module.get() == nullptr)
     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);
 
   for (size_t idx = 0; idx < module->size(); ++idx)
   {
index 70f3c8d..c32dc3f 100644 (file)
@@ -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
index e477a74..49afa73 100644 (file)
@@ -15,7 +15,8 @@
  */
 
 #include "Dump.h"
-#include "Reader.h"
+
+#include <mio_circle/Reader.h>
 
 #include <H5Cpp.h>
 
@@ -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 (file)
index 47b8760..0000000
+++ /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 <mio_circle/Helper.h>
-
-#include <sstream>
-#include <string>
-
-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<const uint8_t *>(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 (file)
index c868bc2..0000000
+++ /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 <mio/circle/schema_generated.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace circletensordump
-{
-
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
-{
-  std::vector<T> 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<flatbuffers::Offset<circle::SubGraph>>;
-  using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
-  using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
-  using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
-
-public:
-  Reader(const circle::Model *model);
-
-  Reader() = delete;
-
-public:
-  const std::vector<const circle::OperatorCode *> &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<int32_t> &inputs() const { return _inputs; }
-  const std::vector<int32_t> &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<const circle::OperatorCode *> _op_codes;
-  std::vector<int32_t> _inputs;
-  std::vector<int32_t> _outputs;
-};
-
-} // namespace circletensordump
-
-#endif // __CIRCLE_TENSORDUMP_READER_H__
index 7a44c65..c3a4147 100644 (file)
@@ -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
   {
index cd79967..dbe485b 100644 (file)
@@ -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)
index b6c6119..4e5ed0d 100644 (file)
@@ -1,4 +1,3 @@
-require("foder")
 require("loco")
 require("locop")
 require("logo-core")
index ae677a3..f8fdb47 100644 (file)
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
 #include <luci/CircleOptimizer.h>
 #include <luci/Service/ChangeOutputs.h>
 #include <luci/Service/Validate.h>
@@ -64,128 +62,100 @@ 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::Helper::add_version(arser, print_version);
+  arser::Helper::add_verbose(arser);
 
   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 "
@@ -193,239 +163,192 @@ int entry(int argc, char **argv)
 
   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("--replace_non_const_fc_with_batch_matmul")
+    .nargs(0)
+    .default_value(false)
+    .help("Replace FullyConnected with BatchMatMul when its weight is non-constant");
+
   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.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");
 
@@ -526,6 +449,8 @@ int entry(int argc, char **argv)
     options->enable(Algorithms::ResolveCustomOpMaxPoolWithArgmax);
   if (arser.get<bool>("--shuffle_weight_to_16x1float32"))
     options->enable(Algorithms::ShuffleWeightTo16x1Float32);
+  if (arser.get<bool>("--replace_non_const_fc_with_batch_matmul"))
+    options->enable(Algorithms::ReplaceNonConstFCWithBatchMatMul);
   if (arser.get<bool>("--substitute_pack_to_reshape"))
     options->enable(Algorithms::SubstitutePackToReshape);
   if (arser.get<bool>("--substitute_padv2_to_pad"))
@@ -595,37 +520,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<char> 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<uint8_t *>(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)
   {
index 76d0f3f..9c4256b 100644 (file)
 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
   {
index 639e0af..c8ef07c 100644 (file)
 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
   {
index b65c066..7485ff8 100644 (file)
@@ -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)
index 657f24f..5b0871a 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 #include <arser/arser.h>
-#include <circleread/Model.h>
+#include <foder/FileLoader.h>
 #include <circledump/Dump.h>
 
 #include <iostream>
@@ -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<std::string>("circle");
   // Load Circle model from a circle file
-  std::unique_ptr<circleread::Model> 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<char> 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;
index 362d67c..183dfe2 100644 (file)
@@ -1,3 +1,4 @@
 require("arser")
+require("foder")
 require("mio-circle04")
 require("safemain")
index 0b256dd..69427a2 100644 (file)
@@ -16,8 +16,8 @@
 
 #include <circledump/Dump.h>
 #include <mio_circle/Helper.h>
+#include <mio_circle/Reader.h>
 
-#include "Read.h"
 #include "OpPrinter.h"
 #include "MetadataPrinter.h"
 
@@ -122,7 +122,7 @@ std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *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<int32_t> 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<int32_t> dims_sig = circleread::as_index_vector(tensor->shape_signature());
+      std::vector<int32_t> 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<int32_t> &inputs = circleread::as_index_vector(op->inputs());
-    const std::vector<int32_t> &outputs = circleread::as_index_vector(op->outputs());
+    const std::vector<int32_t> &inputs = mio::circle::as_index_vector(op->inputs());
+    const std::vector<int32_t> &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 (file)
index 67e7fa5..0000000
+++ /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 <circleread/Model.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-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<Model> 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<circleread::Model>{new MemoryMappedModel(fd.release(), data, size)};
-}
-
-} // namespace circleread
index 02e5c26..36d819d 100644 (file)
@@ -15,7 +15,8 @@
  */
 
 #include "OpPrinter.h"
-#include "Read.h"
+
+#include <mio_circle/Helper.h>
 
 #include <memory>
 
@@ -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;
diff --git a/compiler/circledump/src/Read.cpp b/compiler/circledump/src/Read.cpp
deleted file mode 100644 (file)
index 3a7e98c..0000000
+++ /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 <mio_circle/Helper.h>
-
-#include <sstream>
-#include <string>
-
-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<const uint8_t *>(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
index 0fb99dd..4ab0ea2 100644 (file)
@@ -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)
index 33cecbb..7481050 100644 (file)
@@ -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'");
index 500f086..40ddb13 100644 (file)
@@ -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<loco::FeatureDecode *>(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<loco::MatrixDecode *>(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);
 
index 4762cff..0ed3543 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
 #include <luci_interpreter/Interpreter.h>
 #include <luci/CircleExporter.h>
 #include <luci/CircleFileExpContract.h>
@@ -47,18 +47,6 @@ void writeDataToFile(const std::string &filename, const char *data, size_t data_
   }
 }
 
-std::unique_ptr<luci::Module> 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<char> model_data((std::istreambuf_iterator<char>(fs)),
-                               std::istreambuf_iterator<char>());
-  return luci::Importer().importModule(circle::GetModel(model_data.data()));
-}
-
 template <typename NodeT> 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<luci::Module> module = importModel(filename);
+  luci::ImporterEx importer;
+  std::unique_ptr<luci::Module> module = importer.importVerifyModule(filename);
   if (module == nullptr)
   {
     std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl;
index decccaa..501e847 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleAdd(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleAdd *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleAdd *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index 0ee3677..f3ca557 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleArgMax(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleArgMax *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleArgMax *>(circle_node);
   assert(node->arity() == 2);
   const Tensor *input = helper.getInputTensor(node->input());
   const Tensor *axis = helper.getInputTensor(node->dimension());
index efb0112..a813570 100644 (file)
@@ -25,9 +25,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleAveragePool2D(const luci::CircleNode *circle_node,
                                                          KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleAveragePool2D *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleAveragePool2D *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->value());
index aae3dba..9da2f6d 100644 (file)
@@ -25,9 +25,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleBatchMatMul(const luci::CircleNode *circle_node,
                                                        KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleBatchMatMul *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleBatchMatMul *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *lhs = helper.getInputTensor(node->x());
index 33d0e2d..ac6ebb3 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleBatchToSpaceND(const luci::CircleNode *circle_node,
                                                           KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleBatchToSpaceND *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleBatchToSpaceND *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 21ea5ce..a16354c 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleCast(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleCast *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleCast *>(circle_node);
 
   assert(node->arity() == 1);
 
index 7823a99..ba2564e 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleConcatenation(const luci::CircleNode *circle_node,
                                                          KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleConcatenation *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleConcatenation *>(circle_node);
   std::vector<const Tensor *> inputs(node->numValues());
   for (uint32_t i = 0; i < node->numValues(); ++i)
   {
index b48d97d..218165e 100644 (file)
@@ -25,9 +25,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleConv2D(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleConv2D *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleConv2D *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 0310fb2..1749463 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleDepthToSpace(const luci::CircleNode *circle_node,
                                                         KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleDepthToSpace *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleDepthToSpace *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->input());
index db26ecf..8af1e3b 100644 (file)
@@ -25,9 +25,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleDepthwiseConv2D(const luci::CircleNode *circle_node,
                                                            KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleDepthwiseConv2D *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleDepthwiseConv2D *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 4aae564..787322e 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleDequantize(const luci::CircleNode *circle_node,
                                                       KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleDequantize *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleDequantize *>(circle_node);
 
   const Tensor *input = helper.getInputTensor(node->input());
   Tensor *output = helper.getOutputTensor(node);
index 56c2e98..0611dfd 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleDiv(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleDiv *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleDiv *>(circle_node);
   assert(node->arity() == 2);
   const Tensor *input1 = helper.getInputTensor(node->x());
   const Tensor *input2 = helper.getInputTensor(node->y());
index 98ee78b..a79985e 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleElu(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleElu *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleElu *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->features());
index 649d9bf..5969288 100644 (file)
@@ -25,9 +25,7 @@ std::unique_ptr<Kernel> build_kernel_CircleEqual(const luci::CircleNode *circle_
                                                  KernelBuilderHelper &helper)
 
 {
-  const auto *node = dynamic_cast<const luci::CircleEqual *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleEqual *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index 411d142..30d11cb 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleExp(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleExp *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleExp *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 6d8435f..e0a2231 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleFloor(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleFloor *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleFloor *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index cae2e18..a45d89e 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleFloorDiv(const luci::CircleNode *circle_node,
                                                     KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleFloorDiv *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleFloorDiv *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index 0b8ac44..b7b742b 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleFullyConnected(const luci::CircleNode *circle_node,
                                                           KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleFullyConnected *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleFullyConnected *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 9df9775..2ee2906 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleGather(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleGather *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleGather *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *params = helper.getInputTensor(node->params());
index 3db11b8..80aa63c 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleGreater(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleGreater *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleGreater *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index dbe051d..272f284 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleGreaterEqual(const luci::CircleNode *circle_node,
                                                         KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleGreaterEqual *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleGreaterEqual *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index 5983f4d..3ac7d49 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleIf(const luci::CircleNode *circle_node,
                                               KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleIf *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleIf *>(circle_node);
   auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
   assert(node->arity() == 1 + node->input_count());
   assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
index 0a8fb85..06031e5 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleInstanceNorm(const luci::CircleNode *circle_node,
                                                         KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleInstanceNorm *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleInstanceNorm *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 05f9202..6e22e6d 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleL2Normalize(const luci::CircleNode *circle_node,
                                                        KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleL2Normalize *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleL2Normalize *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 0e70afa..95b5589 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleL2Pool2D(const luci::CircleNode *circle_node,
                                                     KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleL2Pool2D *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleL2Pool2D *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->value());
index 7b229ad..bbf5067 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLeakyRelu(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLeakyRelu *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLeakyRelu *>(circle_node);
   assert(node->arity() == 1);
   const Tensor *input = helper.getInputTensor(node->features());
   Tensor *output = helper.getOutputTensor(node);
index 81156f2..ae914ec 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLess(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLess *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLess *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index 82141e5..f1b424b 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLessEqual(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLessEqual *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLessEqual *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index a12dce0..962ca2d 100644 (file)
@@ -25,9 +25,7 @@ std::unique_ptr<Kernel>
 build_kernel_CircleLocalResponseNormalization(const luci::CircleNode *circle_node,
                                               KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLocalResponseNormalization *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLocalResponseNormalization *>(circle_node);
   assert(node->arity() == 1);
   const Tensor *input = helper.getInputTensor(node->input());
   Tensor *output = helper.getOutputTensor(node);
index 6cf547a..4322041 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLogSoftmax(const luci::CircleNode *circle_node,
                                                       KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLogSoftmax *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLogSoftmax *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->logits());
index 2c9549f..bf3cb67 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLogicalAnd(const luci::CircleNode *circle_node,
                                                       KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLogicalAnd *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLogicalAnd *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index 3d327d6..fefcd9a 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLogicalNot(const luci::CircleNode *circle_node,
                                                       KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLogicalNot *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLogicalNot *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 50566bb..a416cb4 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLogicalOr(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLogicalOr *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLogicalOr *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index e4160ed..4a69dee 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleLogistic(const luci::CircleNode *circle_node,
                                                     KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleLogistic *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleLogistic *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 914f228..f66a206 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMaxPool2D(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMaxPool2D *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMaxPool2D *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->value());
index dc50d67..d0bff77 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMaximum(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMaximum *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMaximum *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index 97d9120..0dec63e 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMean(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMean *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMean *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index ff65952..1a49c10 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMinimum(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMinimum *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMinimum *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index ebf2945..b221b45 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMirrorPad(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMirrorPad *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMirrorPad *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 4f9da96..f998485 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleMul(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleMul *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleMul *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index 23c0053..9a9ecf9 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleNeg(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleNeg *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleNeg *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 8e5711f..3916a58 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleNotEqual(const luci::CircleNode *circle_node,
                                                     KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleNotEqual *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleNotEqual *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *x = helper.getInputTensor(node->x());
index e31601b..f3d700c 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CirclePRelu(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CirclePRelu *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CirclePRelu *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 6994720..efc5850 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CirclePack(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CirclePack *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CirclePack *>(circle_node);
   assert(node->arity() == node->values_count());
 
   std::vector<const Tensor *> inputs(node->values_count());
index 7705492..67ce997 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CirclePad(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CirclePad *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CirclePad *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 12deb15..e378a97 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CirclePadV2(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CirclePadV2 *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CirclePadV2 *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index b430bc9..d32fc3d 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CirclePow(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CirclePow *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CirclePow *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index fd98363..cb36fb6 100644 (file)
@@ -24,9 +24,8 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleQuantize(const luci::CircleNode *circle_node,
                                                     KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleQuantize *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleQuantize *>(circle_node);
+  assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->input());
   Tensor *output = helper.getOutputTensor(node);
index d53a66a..1d64c1c 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleRelu(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleRelu *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleRelu *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->features());
index f1b5d21..e50cd25 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleRelu6(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleRelu6 *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleRelu6 *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->features());
index 89e3ece..76ddd88 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleReshape(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleReshape *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleReshape *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->tensor());
index dca5658..dc2b88a 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleResizeBilinear(const luci::CircleNode *circle_node,
                                                           KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleResizeBilinear *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleResizeBilinear *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index d1ea19c..c7058ae 100644 (file)
@@ -25,9 +25,7 @@ std::unique_ptr<Kernel>
 build_kernel_CircleResizeNearestNeighbor(const luci::CircleNode *circle_node,
                                          KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleResizeNearestNeighbor *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleResizeNearestNeighbor *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->input());
index ea00f54..c1a7f53 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleReverseV2(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleReverseV2 *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleReverseV2 *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->tensor());
index ff87f43..0714a5d 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleRsqrt(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleRsqrt *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleRsqrt *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 89528d5..d172ef4 100644 (file)
@@ -24,9 +24,8 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSVDF(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSVDF *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSVDF *>(circle_node);
+  assert(node->arity() == 5);
 
   const Tensor *input = helper.getInputTensor(node->input());
   const Tensor *feature = helper.getInputTensor(node->weight_feature());
index 741cd08..60ac641 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSlice(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSlice *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSlice *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index b15e4b6..f41f63f 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSoftmax(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSoftmax *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSoftmax *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->logits());
index 91c237a..b6e6cf5 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSpaceToBatchND(const luci::CircleNode *circle_node,
                                                           KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSpaceToBatchND *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSpaceToBatchND *>(circle_node);
   assert(node->arity() == 3);
 
   const Tensor *input = helper.getInputTensor(node->input());
index 3cbbd97..63fdb95 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSpaceToDepth(const luci::CircleNode *circle_node,
                                                         KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSpaceToDepth *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSpaceToDepth *>(circle_node);
   assert(node->arity() == 1);
   const Tensor *input = helper.getInputTensor(node->input());
 
index 32553ad..3f6d4a7 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSplit(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSplit *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSplit *>(circle_node);
   auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
   assert(node->arity() == 2);
   assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
index d788164..0788822 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSplitV(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSplitV *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSplitV *>(circle_node);
   auto output_nodes = collectOutputNodes<luci::CircleSplitVOut>(node);
   assert(node->arity() == 3);
   assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
index 56dd986..b9843fe 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSqrt(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSqrt *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSqrt *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 43aadb9..0ad7c17 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSquare(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSquare *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSquare *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 6a2717a..e4c6fd8 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSquaredDifference(const luci::CircleNode *circle_node,
                                                              KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSquaredDifference *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSquaredDifference *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index 583ff93..6885f80 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSqueeze(const luci::CircleNode *circle_node,
                                                    KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSqueeze *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSqueeze *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->input());
index fe5fa77..359b4e3 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleStridedSlice(const luci::CircleNode *circle_node,
                                                         KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleStridedSlice *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleStridedSlice *>(circle_node);
   assert(node->arity() == 4);
 
   const Tensor *input = helper.getInputTensor(node->input());
index bad4fbb..a6252cb 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleSub(const luci::CircleNode *circle_node,
                                                KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleSub *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleSub *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input1 = helper.getInputTensor(node->x());
index f425529..a58ef60 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleTanh(const luci::CircleNode *circle_node,
                                                 KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleTanh *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleTanh *>(circle_node);
   assert(node->arity() == 1);
 
   const Tensor *input = helper.getInputTensor(node->x());
index 4e095fb..ea17d83 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleTranspose(const luci::CircleNode *circle_node,
                                                      KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleTranspose *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleTranspose *>(circle_node);
   assert(node->arity() == 2);
 
   const Tensor *input = helper.getInputTensor(node->a());
index 1b954c3..d773e30 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleTransposeConv(const luci::CircleNode *circle_node,
                                                          KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleTransposeConv *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleTransposeConv *>(circle_node);
   assert(node->arity() == 4);
 
   const Tensor *input_sizes = helper.getInputTensor(node->inputSizes());
index 978c738..a1c0d32 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleUnpack(const luci::CircleNode *circle_node,
                                                   KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleUnpack *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleUnpack *>(circle_node);
   auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
   assert(node->arity() == 1);
   assert(output_nodes.size() == static_cast<size_t>(node->num()));
index 284dc0c..8fde6ec 100644 (file)
@@ -24,9 +24,7 @@ namespace luci_interpreter
 std::unique_ptr<Kernel> build_kernel_CircleWhile(const luci::CircleNode *circle_node,
                                                  KernelBuilderHelper &helper)
 {
-  const auto *node = dynamic_cast<const luci::CircleWhile *>(circle_node);
-  if (node == nullptr)
-    throw std::runtime_error("wrong builder for operation");
+  const auto *node = loco::must_cast<const luci::CircleWhile *>(circle_node);
 
   auto output_nodes = collectOutputNodes<luci::CircleWhileOut>(node);
   assert(node->arity() == node->input_count());
index 1b2db23..bc0a00b 100644 (file)
@@ -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)
similarity index 54%
rename from compiler/tfldump/include/tflread/Model.h
rename to compiler/luci/import/include/luci/ImporterEx.h
index c6e4a94..852d4c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * 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.
  * limitations under the License.
  */
 
-#ifndef __TFLREAD_MODEL_H__
-#define __TFLREAD_MODEL_H__
+#ifndef __LUCI_IMPORTER_EX_H__
+#define __LUCI_IMPORTER_EX_H__
 
-#include <mio/tflite/schema_generated.h>
+#include "luci/IR/Module.h"
 
 #include <memory>
+#include <string>
 
-namespace tflread
+namespace luci
 {
 
-struct Model
+class ImporterEx final
 {
-  virtual ~Model() = default;
+public:
+  ImporterEx() = default;
 
-  virtual const ::tflite::Model *model(void) const = 0;
+public:
+  std::unique_ptr<Module> importVerifyModule(const std::string &input_path) const;
 };
 
-/**
- * @brief Load TensorFlow Lite model (as a raw Model) from a given path
- *
- * @note May return a nullptr
- */
-std::unique_ptr<Model> load_tflite(const std::string &path);
-
-} // namespace tflread
+} // namespace luci
 
-#endif // __TFLREAD_MODEL_H__
+#endif // __LUCI_IMPORTER_EX_H__
diff --git a/compiler/luci/import/src/ImporterEx.cpp b/compiler/luci/import/src/ImporterEx.cpp
new file mode 100644 (file)
index 0000000..db585fd
--- /dev/null
@@ -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 <foder/FileLoader.h>
+
+#include <memory>
+#include <iostream>
+
+namespace luci
+{
+
+std::unique_ptr<Module> ImporterEx::importVerifyModule(const std::string &input_path) const
+{
+  foder::FileLoader file_loader{input_path};
+  std::vector<char> 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<uint8_t *>(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
index c803898..9b4e8bd 100644 (file)
@@ -61,6 +61,7 @@ public:
       ShuffleWeightTo16x1Float32,
       RemoveRedundantTranspose,
       ReplaceMulAddWithDepthwiseConv,
+      ReplaceNonConstFCWithBatchMatMul,
       ReplaceSubWithAdd,
       SubstitutePackToReshape,
       SubstitutePadV2ToPad,
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * 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.
  * limitations under the License.
  */
 
-#ifndef __CIRCLEREAD_MODEL_H__
-#define __CIRCLEREAD_MODEL_H__
+#ifndef __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
+#define __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
 
-#include <mio/circle/schema_generated.h>
+#include <logo/Pass.h>
 
-#include <memory>
-
-namespace circleread
+namespace luci
 {
 
-struct Model
+/**
+ * @brief  Class to replace "FC with non-const weight" with Batched MatMul
+ */
+struct ReplaceNonConstFCWithBatchMatMulPass final : public logo::Pass
 {
-  virtual ~Model() = default;
+  const char *name(void) const final { return "luci::ReplaceNonConstFCWithBatchMatMulPass"; }
 
-  virtual const ::circle::Model *model(void) const = 0;
+  bool run(loco::Graph *g) final;
 };
 
-/**
- * @brief Load Circle model (as a raw Model) from a given path
- *
- * @note May return a nullptr
- */
-std::unique_ptr<Model> load_circle(const std::string &path);
-
-} // namespace circleread
+} // namespace luci
 
-#endif // __CIRCLEREAD_MODEL_H__
+#endif // __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
index 6dbb22d..8d6ab40 100644 (file)
@@ -46,6 +46,7 @@
 #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"
@@ -332,6 +333,10 @@ void CircleOptimizer::optimize(loco::Graph *g) const
   {
     phase.emplace_back(std::make_unique<luci::RemoveRedundantQuantizePass>());
   }
+  if (_options->query(Options::Algorithm::ReplaceNonConstFCWithBatchMatMul))
+  {
+    phase.emplace_back(std::make_unique<luci::ReplaceNonConstFCWithBatchMatMulPass>());
+  }
   if (_options->query(Options::Algorithm::ReplaceMulAddWithDepthwiseConv))
   {
     phase.emplace_back(std::make_unique<luci::ReplaceMulAddWithDepthwiseConvPass>());
index ce38a90..ac240a7 100644 (file)
@@ -252,8 +252,8 @@ void CircleQuantizer::quantize(loco::Graph *g) const
     static const std::vector<std::string> qwmm_supported_input_model_dtype{"float32"};
     static const std::vector<std::string> qwmm_supported_output_model_dtype{"uint8", "int16"};
     static const std::vector<std::string> qwmm_supported_granularity{"layer", "channel"};
-    static const std::vector<std::string> qwmm_supported_input_type{"uint8", "int16"};
-    static const std::vector<std::string> qwmm_supported_output_type{"uint8", "int16"};
+    static const std::vector<std::string> qwmm_supported_input_type{"uint8", "int16", "float32"};
+    static const std::vector<std::string> qwmm_supported_output_type{"uint8", "int16", "float32"};
 
     auto input_model_dtype =
       _options->param(Options::AlgorithmParameters::Quantize_input_model_dtype);
index 11970ff..c2f09d6 100644 (file)
@@ -186,6 +186,26 @@ struct FakeQuantize final : public luci::CircleNodeMutableVisitor<void>
   // 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); }
+  // TODO Move Conv2D here
+  void visit(luci::CircleDepthwiseConv2D *node) { fq_activation(node); }
+  void visit(luci::CircleFullyConnected *node) { fq_activation(node); }
+  void visit(luci::CircleInstanceNorm *node) { fq_activation(node); }
+  void visit(luci::CircleLogistic *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::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::CircleSoftmax *node) { fq_activation(node); }
+  void visit(luci::CircleTanh *node) { fq_activation(node); }
+  void visit(luci::CircleTransposeConv *node) { fq_activation(node); }
 };
 
 #undef RETURN_UNLESS
index f3ec6cd..10a651e 100644 (file)
@@ -325,6 +325,10 @@ public:
   }
 
 private:
+  bool condition_common_1_5(uint32_t ifm_channel_depth);
+  bool condition_common_3_4();
+
+private:
   template <enum PatternVersion> bool match();
 
 public:
@@ -368,21 +372,8 @@ private:
   if (not(condition))             \
     return false;
 
-template <> bool InstanceNormPattern::match<InstanceNormPattern::PatternVersion::Version_1>()
+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<luci::CircleNode *>(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<luci::CircleAdd *>(rsqrt->x());
   CHECK_OR_FALSE(add_as_variance);
 
@@ -408,6 +399,70 @@ template <> bool InstanceNormPattern::match<InstanceNormPattern::PatternVersion:
   CHECK_OR_FALSE(const_as_beta);
   CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
 
+  return true;
+}
+
+bool InstanceNormPattern::condition_common_3_4()
+{
+  // check left sub
+  ifm = sub->x();
+  CHECK_OR_FALSE(ifm);
+
+  luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
+  CHECK_OR_FALSE(ifm_node->rank() == 4);
+  CHECK_OR_FALSE(ifm_node->dim(3).known());
+
+  mean_of_ifm = dynamic_cast<luci::CircleMean *>(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<loco::DataType::FLOAT32>() == 1);
+
+  mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
+  CHECK_OR_FALSE(mean_as_variance);
+
+  square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
+  CHECK_OR_FALSE(square);
+
+  sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
+  CHECK_OR_FALSE(sub_2);
+  CHECK_OR_FALSE(ifm == sub_2->x());
+
+  mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(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<InstanceNormPattern::PatternVersion::Version_1>()
+{
+  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<luci::CircleNode *>(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::match<InstanceNormPattern::PatternVersion:
   CHECK_OR_FALSE(luci::fill(&div, &const_as_gamma).with_commutative_args_of(mul_gamma));
   CHECK_OR_FALSE(luci::fill(&sub, &add_as_variance).with_commutative_args_of(div));
 
-  // check left sub
-  ifm = sub->x();
-  CHECK_OR_FALSE(ifm);
-
-  luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
-  CHECK_OR_FALSE(ifm_node->rank() == 4);
-  CHECK_OR_FALSE(ifm_node->dim(3).known());
-
-  mean_of_ifm = dynamic_cast<luci::CircleMean *>(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<loco::DataType::FLOAT32>() == 1);
-
-  mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
-  CHECK_OR_FALSE(mean_as_variance);
-
-  square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
-  CHECK_OR_FALSE(square);
-
-  sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
-  CHECK_OR_FALSE(sub_2);
-  CHECK_OR_FALSE(ifm == sub_2->x());
-
-  mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(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::match<InstanceNormPattern::PatternVersion:
   CHECK_OR_FALSE(div);
   CHECK_OR_FALSE(luci::fill(&sub, &add_as_variance).with_commutative_args_of(div));
 
-  // check left sub
-  ifm = sub->x();
-  CHECK_OR_FALSE(ifm);
-
-  luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
-  CHECK_OR_FALSE(ifm_node->rank() == 4);
-  CHECK_OR_FALSE(ifm_node->dim(3).known());
-
-  mean_of_ifm = dynamic_cast<luci::CircleMean *>(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<loco::DataType::FLOAT32>() == 1);
-
-  mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
-  CHECK_OR_FALSE(mean_as_variance);
-
-  square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
-  CHECK_OR_FALSE(square);
-
-  sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
-  CHECK_OR_FALSE(sub_2);
-  CHECK_OR_FALSE(ifm == sub_2->x());
-
-  mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(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::match<InstanceNormPattern::PatternVersion:
   CHECK_OR_FALSE(ifm_circle->dim(3).known());
   uint32_t ifm_channel_depth = ifm_circle->dim(3).value();
 
-  add_as_variance = dynamic_cast<luci::CircleAdd *>(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<loco::DataType::FLOAT32>() == 1);
-
-  CHECK_OR_FALSE(is_instance_mean_v1(mean_as_variance));
-
-  sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(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<luci::CircleConst *>(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;
index d9a9d4d..3e2e533 100644 (file)
@@ -105,6 +105,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<luci::CircleDequantize>();
+  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
@@ -353,7 +370,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 +395,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 +426,28 @@ void QuantizeWithMinMaxPass::set_output_type(loco::Graph *g) const
     if (not from->quantparam())
       continue;
 
-    // Insert Quantize Op
+    // 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
+    {
+      // clang-format off
+    // 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));
+      // clang-format on
+    }
+
+    // Update dtype of output
+    output->dtype(_ctx->output_type);
 
     auto graph_output = outputs->at(output->index());
     graph_output->dtype(_ctx->output_type);
diff --git a/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.cpp
new file mode 100644 (file)
index 0000000..741b709
--- /dev/null
@@ -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 <luci/IR/CircleNodes.h>
+#include <luci/Profile/CircleNodeOrigin.h>
+#include <luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h>
+
+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<luci::CircleRelu>();
+      n->features(inp);
+      return n;
+    }
+    case luci::FusedActFunc::RELU6:
+    {
+      auto n = inp->graph()->nodes()->create<luci::CircleRelu6>();
+      n->features(inp);
+      return n;
+    }
+    case luci::FusedActFunc::RELU_N1_TO_1:
+    {
+      auto n = inp->graph()->nodes()->create<luci::CircleReluN1To1>();
+      n->features(inp);
+      return n;
+    }
+    case luci::FusedActFunc::TANH:
+    {
+      auto n = inp->graph()->nodes()->create<luci::CircleTanh>();
+      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<luci::CircleConst *>(fc->weights()))
+    return false; // NonConst
+
+  if ((ty = dynamic_cast<luci::CircleTranspose *>(fc->weights()))) // is y a transpose?
+  {
+    adj_y = false;
+    if (dynamic_cast<luci::CircleConst *>(ty->a()))
+      return false;
+    else
+      y = loco::must_cast<luci::CircleNode *>(ty->a());
+  }
+  else
+  { // y is not transpose and not const
+    y = loco::must_cast<luci::CircleNode *>(fc->weights());
+  }
+  if ((tx = dynamic_cast<luci::CircleTranspose *>(fc->input())))
+  {
+    adj_x = true;
+    x = loco::must_cast<luci::CircleNode *>(tx->a());
+  }
+  else
+  {
+    x = loco::must_cast<luci::CircleNode *>(fc->input());
+  }
+
+  b = loco::must_cast<luci::CircleNode *>(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<luci::CircleBatchMatMul>();
+  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<loco::DataType::FLOAT32>() && ac; i++)
+    {
+      ac &= c->at<loco::DataType::FLOAT32>(i) == 0.0f;
+    }
+    return ac;
+  };
+
+  auto bc = dynamic_cast<luci::CircleConst *>(b);
+  if ((nullptr != bc) && !all_zero(bc))
+  {
+    auto bias_add = fc->graph()->nodes()->create<luci::CircleAdd>();
+    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<luci::CircleFullyConnected *>(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 (file)
index 0000000..7606a61
--- /dev/null
@@ -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 <luci/test/TestIOGraph.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+using namespace luci::test;
+
+// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp
+template <typename T>
+luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype,
+                                     const std::vector<uint32_t> &shape,
+                                     const std::vector<T> &values)
+{
+  auto node = g->nodes()->create<luci::CircleConst>();
+  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<DT>(size);                        \
+    for (uint32_t i = 0; i < values.size(); ++i) \
+      node->at<DT>(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<luci::CircleTranspose>();
+    _tr_y->a(_y);
+    std::vector<int32_t> tr_val = {1, 0};
+    _tr_y->perm(create_const_node(g, loco::DataType::S32, {2}, tr_val));
+
+    _fc = g->nodes()->create<luci::CircleFullyConnected>();
+    _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<float> 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<luci::CircleBatchMatMul *>(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<luci::CircleAdd *>(g.output()->from());
+  EXPECT_NE(nullptr, mm);
+}
+
+TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, wrong_op_NEG)
+{
+  loco::Graph g;
+
+  auto inp = g.nodes()->create<luci::CircleInput>();
+  auto relu = g.nodes()->create<luci::CircleRelu>();
+  relu->features(inp);
+
+  luci::ReplaceNonConstFCWithBatchMatMulPass pass;
+  auto changed = pass.run(&g);
+
+  EXPECT_EQ(false, changed);
+}
index e896188..0a5e6a5 100644 (file)
@@ -10,4 +10,5 @@ require("oops")
 require("hermes")
 require("hermes-std")
 require("tflchef")
+require("circlechef")
 require("tflite2circle")
index 9d156f3..7e4421f 100644 (file)
@@ -258,49 +258,10 @@ loco::NodeShape infer_add_n(const luci::CircleAddN *node)
   return loco::NodeShape{shape};
 }
 
-loco::NodeShape infer_arg_max(const luci::CircleArgMax *node)
+template <class CIRCLENODE> loco::NodeShape infer_arg_maxmin(const CIRCLENODE *node)
 {
-  auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
-  auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>();
-
-  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<luci::CircleConst *>(node->dimension());
-    LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
-                "Only support int32 CircleConst for CircleArgMax");
-
-    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<loco::DataType::S32>();
-  }
-  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<uint32_t>(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);
-  }
-  return loco::NodeShape{shape_output};
-}
-
-loco::NodeShape infer_arg_min(const luci::CircleArgMin *node)
-{
-  auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
-  auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>();
+  auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
+  auto dimension_shape = luci::shape_get(node->dimension()).template as<loco::TensorShape>();
 
   int64_t select_axis = 0;
   {
@@ -310,13 +271,13 @@ loco::NodeShape infer_arg_min(const luci::CircleArgMin *node)
     // Support S32 for now.
     auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
     LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
-                "Only support int32 CircleConst for CircleArgMin");
+                "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<loco::DataType::S32>();
+    select_axis = const_shape_node->template scalar<loco::DataType::S32>();
   }
   assert(select_axis < input_shape.rank());
   assert(select_axis >= 0); // TODO support minus of this breaks
@@ -1180,45 +1141,17 @@ loco::NodeShape infer_reshape(const luci::CircleReshape *node)
   return loco::NodeShape{output_shape};
 }
 
-loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node)
+template <class CIRCLENODE> loco::NodeShape infer_resize_type(const CIRCLENODE *node)
 {
-  auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
-
-  if (input_shape.rank() != 4)
-    INTERNAL_EXN("Expected ResizeBilinear input to have rank 4");
-
-  auto *const_node = loco::must_cast<luci::CircleConst *>(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<loco::DataType::S32>(0);
-  output_shape.dim(2) = const_node->at<loco::DataType::S32>(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<loco::TensorShape>();
+  auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
 
   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<luci::CircleConst *>(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 +1162,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<loco::DataType::S32>(0);
-  output_shape.dim(2) = const_node->at<loco::DataType::S32>(1);
+  output_shape.dim(1) = const_node->template at<loco::DataType::S32>(0);
+  output_shape.dim(2) = const_node->template at<loco::DataType::S32>(1);
   output_shape.dim(3) = input_shape.dim(3);
 
   return loco::NodeShape{output_shape};
@@ -2080,9 +2013,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
   {
@@ -2348,12 +2281,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
index d3ffc23..7a1ba2b 100644 (file)
@@ -19,6 +19,8 @@
 
 #include <mio/circle/schema_generated.h>
 
+#include <vector>
+
 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 <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+  if (flat_array == nullptr)
+  {
+    throw std::runtime_error("flat array is nullptr");
+  }
+
+  std::vector<T> 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
 
similarity index 67%
rename from compiler/circledump/src/Read.h
rename to compiler/mio-circle04/include/mio_circle/Reader.h
index 05b0e50..c36bff9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * 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.
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef __CIRCLEREAD_READ_H__
-#define __CIRCLEREAD_READ_H__
+#ifndef __MIO_CIRCLE04_READER_H__
+#define __MIO_CIRCLE04_READER_H__
 
 #include <mio/circle/schema_generated.h>
 
 #include <string>
 #include <vector>
 
-namespace circleread
-{
+// 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.
 
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+namespace mio
+{
+namespace circle
 {
-  if (flat_array == nullptr)
-  {
-    throw std::runtime_error("flat array is nullptr");
-  }
-
-  std::vector<T> 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
@@ -47,36 +38,38 @@ template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T
 class Reader
 {
 private:
-  using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
-  using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
-  using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
-  using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
-  using CircleMetadata_t = flatbuffers::Vector<flatbuffers::Offset<circle::Metadata>>;
-  using CircleSignatureDef_t = flatbuffers::Vector<flatbuffers::Offset<circle::SignatureDef>>;
+  using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<::circle::SubGraph>>;
+  using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Buffer>>;
+  using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Tensor>>;
+  using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Operator>>;
+  using CircleMetadata_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Metadata>>;
+  using CircleSignatureDef_t = flatbuffers::Vector<flatbuffers::Offset<::circle::SignatureDef>>;
 
 public:
-  Reader(const circle::Model *model);
+  Reader(const ::circle::Model *model);
 
   Reader() = delete;
 
 public:
   uint32_t version() const { return _version; }
 
-  const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
+  const std::vector<const ::circle::OperatorCode *> &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<int32_t> &inputs() const { return _inputs; }
   const std::vector<int32_t> &outputs() const { return _outputs; }
-  const circle::DataFormat &data_format() const { return _data_format; }
+  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;
+  ::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);
@@ -95,12 +88,13 @@ private:
 
   uint32_t _subgraph_index = 0;
   std::string _subgraph_name;
-  std::vector<const circle::OperatorCode *> _op_codes;
+  std::vector<const ::circle::OperatorCode *> _op_codes;
   std::vector<int32_t> _inputs;
   std::vector<int32_t> _outputs;
-  circle::DataFormat _data_format = circle::DataFormat::DataFormat_CHANNELS_FIRST;
+  ::circle::DataFormat _data_format = ::circle::DataFormat::DataFormat_CHANNELS_FIRST;
 };
 
-} // namespace circleread
+} // namespace circle
+} // namespace mio
 
-#endif // __CIRCLEREAD_READ_H__
+#endif // __MIO_CIRCLE04_READER_H__
similarity index 67%
rename from compiler/circle-inspect/src/Reader.cpp
rename to compiler/mio-circle04/src/Reader.cpp
index 0e28652..f61877c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * 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.
  * limitations under the License.
  */
 
-#include "Reader.h"
-
-#include <mio_circle/Helper.h>
+#include "mio_circle/Reader.h"
+#include "mio_circle/Helper.h"
 
 #include <sstream>
 #include <string>
 
-namespace circleinspect
+namespace mio
+{
+namespace circle
 {
 
-Reader::Reader(const circle::Model *model)
+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)
@@ -64,20 +73,20 @@ size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
   return 0;
 }
 
-circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
+::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);
+  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
+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);
+  const ::circle::OperatorCode *opcode = _op_codes.at(index);
 
   if (!mio::circle::is_valid(opcode))
   {
@@ -89,18 +98,19 @@ std::string Reader::opcode_name(const circle::Operator *op) const
   return mio::circle::opcode_name(opcode);
 }
 
-std::string Reader::tensor_name(const circle::Tensor *tensor) const
+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
+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;
 
@@ -113,10 +123,14 @@ bool Reader::select_subgraph(uint32_t sgindex)
     return false;
   }
 
-  const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+  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());
@@ -124,4 +138,5 @@ bool Reader::select_subgraph(uint32_t sgindex)
   return true;
 }
 
-} // namespace circleinspect
+} // 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 (file)
index 0000000..104454a
--- /dev/null
@@ -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 <flatbuffers/flatbuffers.h>
+#include <gtest/gtest.h>
+
+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<circle::Model> _model;
+  std::vector<flatbuffers::Offset<circle::OperatorCode>> _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
index f2eff24..832aef7 100644 (file)
@@ -93,13 +93,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',
index ac920a4..e182edf 100644 (file)
@@ -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
 
index 51fd9a3..96dfc86 100644 (file)
@@ -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)
index 31b9061..6363614 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 unset(RECORD_MINMAX_CONVERSION_TEST)
 
 macro(addTest NAME)
index c9f1d0c..faa402f 100644 (file)
@@ -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.");
 
index 10a1451..6dbf98d 100644 (file)
@@ -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)
   {
index 27f2463..79a3987 100644 (file)
@@ -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
index 48b098e..83596fa 100644 (file)
@@ -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
index 789e585..427e575 100644 (file)
@@ -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
index 2fb8223..ad776a6 100644 (file)
@@ -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
index b75c507..ac9f14d 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nnas_include(TargetRequire)
 
 unset(REQUIRED_TARGETS)
index 87cf783..95a296e 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nnas_include(TargetRequire)
 
 unset(REQUIRED_TARGETS)
index 41974f7..a6c451e 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nnas_include(TargetRequire)
 
 unset(REQUIRED_TARGETS)
index 2e76e21..fde3e60 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nnas_include(TargetRequire)
 
 unset(REQUIRED_TARGETS)
index 0b47393..97aa07f 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nncc_find_resource(TensorFlowTests)
 
 #
index 9e1cb72..2c6e3a1 100644 (file)
@@ -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")
 
index 3e62e0f..8505ff4 100644 (file)
@@ -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
   {
index 2fba335..5bead5b 100644 (file)
@@ -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")
 
index 6d18976..6234549 100644 (file)
@@ -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
   {
index 948b1ce..6205ac6 100644 (file)
@@ -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)
index 3c3352b..d9a20a2 100644 (file)
@@ -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)
index cf96d2e..1ac99ad 100644 (file)
@@ -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<int32_t> &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
index 053a5c1..e96ae73 100644 (file)
@@ -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__
index 3e880a6..23d3606 100644 (file)
@@ -27,11 +27,7 @@ void TFliteOpAdd::filler(const tflite::Operator *op, TFliteImport *import,
 {
   // Add may have constant input
 
-  const std::vector<int32_t> &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,
index d52caf0..65e4c2c 100644 (file)
@@ -25,11 +25,7 @@ namespace tflchef
 void TFliteOpMaximum::filler(const tflite::Operator *op, TFliteImport *import,
                              tflchef::ModelRecipe *model_recipe) const
 {
-  const std::vector<int32_t> &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,
index 6440f1d..b4d255c 100644 (file)
@@ -25,11 +25,7 @@ namespace tflchef
 void TFliteOpMinimum::filler(const tflite::Operator *op, TFliteImport *import,
                              tflchef::ModelRecipe *model_recipe) const
 {
-  const std::vector<int32_t> &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,
index 9faa4ac..1145ff7 100644 (file)
@@ -27,11 +27,7 @@ void TFliteOpMul::filler(const tflite::Operator *op, TFliteImport *import,
 {
   // Mul may have constant input
 
-  const std::vector<int32_t> &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,
index ad99219..4f096ce 100644 (file)
@@ -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);
   }
 }
 
index db7f4c9..332cba0 100644 (file)
@@ -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);
   }
 }
 
index 0b1c9f3..a6b657f 100644 (file)
@@ -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<int32_t> &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,
index 548a09a..ec09a69 100644 (file)
@@ -25,9 +25,11 @@ namespace tflchef
 void TFliteOpScatterNd::filler(const tflite::Operator *op, TFliteImport *import,
                                tflchef::ModelRecipe *model_recipe) const
 {
+  const std::vector<int32_t> &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 *,
index a975ca4..bc45a94 100644 (file)
@@ -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<int32_t> &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,
index 0a08bbf..584be0a 100644 (file)
@@ -27,11 +27,7 @@ void TFliteOpSub::filler(const tflite::Operator *op, TFliteImport *import,
 {
   // Sub may have constant input
 
-  const std::vector<int32_t> &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,
index b38b35a..1b9d420 100644 (file)
 #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__
index d4605ce..f6c6789 100644 (file)
 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
   {
index 1451e8b..119bee6 100644 (file)
 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
   {
index fac0be6..4102326 100644 (file)
@@ -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)
index 38c9c06..a3e748b 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 #include <arser/arser.h>
-#include <tflread/Model.h>
+#include <foder/FileLoader.h>
 #include <tfldump/Dump.h>
 
 #include <iostream>
@@ -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<std::string>("tflite");
   // Load TF lite model from a tflite file
-  std::unique_ptr<tflread::Model> 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<char> 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;
index b1abf94..a11f6b2 100644 (file)
@@ -1,3 +1,4 @@
 require("arser")
+require("foder")
 require("mio-tflite280")
 require("safemain")
diff --git a/compiler/tfldump/src/Load.cpp b/compiler/tfldump/src/Load.cpp
deleted file mode 100644 (file)
index d2f6e06..0000000
+++ /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 <tflread/Model.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-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<Model> 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<tflread::Model>{new MemoryMappedModel(fd.release(), data, size)};
-}
-
-} // namespace tflread
index 83fe23a..2e67d48 100644 (file)
@@ -1,3 +1,7 @@
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
 nnas_include(TargetRequire)
 
 unset(REQUIRED_TARGETS)
index fb8c211..6afe1b0 100644 (file)
@@ -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/docs/release/1.20/index.rst b/docs/release/1.20/index.rst
new file mode 100644 (file)
index 0000000..082d867
--- /dev/null
@@ -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 (file)
index 0000000..2c75e06
--- /dev/null
@@ -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"
+
index 3058aee..5a7d894 100644 (file)
@@ -72,7 +72,7 @@ opencl_files=($(find ./runtime/onert/backend/gpu_cl/open_cl/ \( -name "*.cc" -o
 # Exclude *.test.cpp 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/*' '*_schema_generated.h' "${opencl_files[@]}"
 
 # Final coverage data
 cp -v ${EXCLUDED_COVERAGE_INFO_PATH} ${COVERAGE_INFO_PATH}
index 2763ac5..62c0aae 100644 (file)
@@ -1,3 +1,26 @@
+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 <mhs4670go@naver.com> Tue, 26 Apr 2022 12:00:00 +0900
+
 one (1.19.0) bionic; urgency=medium
 
   * `circle-quantizer` supports input/output type option
index 4cf0abc..b0acef8 100644 (file)
@@ -1,3 +1,10 @@
+one (1.20.0) bionic; urgency=low
+
+  * Introduce TRIX backend
+  * API supports new data type NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED
+
+ --  Chunseok Lee <chunseok.lee@samsung.com>  Wed, 26 Apr 2022 12:00:00 +0900
+
 one (1.19.0) bionic; urgency=low
 
   * Synch up version with ONE Compiler
index 421106c..3b2aa11 100644 (file)
@@ -20,21 +20,25 @@ 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+=("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"
index 60500b1..ec865be 100644 (file)
@@ -15,20 +15,25 @@ 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+=("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"
index 6a857d2..e5f2922 100644 (file)
@@ -8,7 +8,7 @@ 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"
@@ -25,3 +25,4 @@ 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"
index 26d8de4..8da7f45 100755 (executable)
@@ -32,9 +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' && \
-     export BUILD_TYPE=Release && \
+
+CMD="export BUILD_TYPE=Release && \
      cp -nv Makefile.template Makefile && \
      make all install build_test_suite"
 ./nnfw docker-run bash -c "$CMD"
index 697fed8..2510d9c 100755 (executable)
@@ -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/res/TensorFlowLiteRecipes/FullyConnected_007/test.recipe b/res/TensorFlowLiteRecipes/FullyConnected_007/test.recipe
new file mode 100644 (file)
index 0000000..572badf
--- /dev/null
@@ -0,0 +1,29 @@
+operand {
+  name: "x"
+  type: FLOAT32
+  shape { dim: 2 dim: 4 }
+}
+operand {
+  name: "y"
+  type: FLOAT32
+  shape { dim: 2 dim: 4 }
+}
+operand {
+  name: "out"
+  type: FLOAT32
+  shape { dim: 2 dim: 2 }
+}
+operation {
+  type: "FullyConnected"
+  fullyconnected_options {
+    activation: NONE
+    keep_num_dims: true
+  }
+  input: "x"
+  input: "y"
+  input: ""
+  output: "out"
+}
+input: "x"
+input: "y"
+output: "out"
diff --git a/res/TensorFlowLiteRecipes/FullyConnected_007/test.reverse b/res/TensorFlowLiteRecipes/FullyConnected_007/test.reverse
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/res/TensorFlowLiteRecipes/FullyConnected_007/test.rule b/res/TensorFlowLiteRecipes/FullyConnected_007/test.rule
new file mode 100644 (file)
index 0000000..01518e5
--- /dev/null
@@ -0,0 +1,7 @@
+# To check if FullyConnected with non-const weight is replaced by MatMul
+# with replace_non_const_fc_with_batch_matmul pass
+
+RULE    "VERIFY_FILE_FORMAT"      $(verify_file_format) '=' 1
+
+RULE    "BATCH_MATMUL_EXIST"      $(op_count BATCH_MATMUL) '=' 1
+RULE    "NO_FULLY_CONNECTED"      $(op_count FULLY_CONNECTED) '=' 0
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_000/test.recipe b/res/TensorFlowLiteRecipes/Quant_Conv_000/test.recipe
new file mode 100644 (file)
index 0000000..8a9328b
--- /dev/null
@@ -0,0 +1,44 @@
+operand {
+  name: "ifm"
+  type: FLOAT32
+  shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+  name: "filter"
+  type: FLOAT32
+  shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "bias"
+  type: FLOAT32
+  shape { dim: 64 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "ofm"
+  type: FLOAT32
+  shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+  type: "Conv2D"
+  conv2d_options {
+    padding: VALID
+    stride_w: 2
+    stride_h: 2
+  }
+  input: "ifm"
+  input: "filter"
+  input: "bias"
+  output: "ofm"
+}
+input: "ifm"
+output: "ofm"
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_000/test.rule b/res/TensorFlowLiteRecipes/Quant_Conv_000/test.rule
new file mode 100644 (file)
index 0000000..f7af083
--- /dev/null
@@ -0,0 +1,10 @@
+# To check float32 input.
+# Input is float32, Conv is uint8. Quantize Op is inserted at the beginning.
+
+RULE    "VERIFY_FILE_FORMAT"      $(verify_file_format) '=' 1
+
+RULE    "INPUT_FLOAT32"           $(tensor_dtype ifm) '=' FLOAT32
+RULE    "CONV_UINT8"              $(tensor_dtype ofm) '=' UINT8
+RULE    "WEIGHTS_UINT8"           $(tensor_dtype filter) '=' UINT8
+RULE    "BIAS_INT32"              $(tensor_dtype bias) '=' INT32
+RULE    "QUANTIZE_OP"             $(op_count QUANTIZE) '=' 1
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_001/test.recipe b/res/TensorFlowLiteRecipes/Quant_Conv_001/test.recipe
new file mode 100644 (file)
index 0000000..8a9328b
--- /dev/null
@@ -0,0 +1,44 @@
+operand {
+  name: "ifm"
+  type: FLOAT32
+  shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+  name: "filter"
+  type: FLOAT32
+  shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "bias"
+  type: FLOAT32
+  shape { dim: 64 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "ofm"
+  type: FLOAT32
+  shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+  type: "Conv2D"
+  conv2d_options {
+    padding: VALID
+    stride_w: 2
+    stride_h: 2
+  }
+  input: "ifm"
+  input: "filter"
+  input: "bias"
+  output: "ofm"
+}
+input: "ifm"
+output: "ofm"
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_001/test.rule b/res/TensorFlowLiteRecipes/Quant_Conv_001/test.rule
new file mode 100644 (file)
index 0000000..a3f52f2
--- /dev/null
@@ -0,0 +1,11 @@
+# To check float32 output.
+# Output is float32, Conv is uint8. Dequantize Op is inserted at the end.
+
+RULE    "VERIFY_FILE_FORMAT"      $(verify_file_format) '=' 1
+
+# Update tensor name (ofm_Dequantize) if 'create_dequantize' function is changed.
+RULE    "OUTPUT_FLOAT32"          $(tensor_dtype ofm_Dequantize) '=' FLOAT32
+RULE    "CONV_UINT8"              $(tensor_dtype ofm) '=' UINT8
+RULE    "WEIGHTS_UINT8"           $(tensor_dtype filter) '=' UINT8
+RULE    "BIAS_INT32"              $(tensor_dtype bias) '=' INT32
+RULE    "DEQUANTIZE_OP"           $(op_count DEQUANTIZE) '=' 1
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_002/test.recipe b/res/TensorFlowLiteRecipes/Quant_Conv_002/test.recipe
new file mode 100644 (file)
index 0000000..8a9328b
--- /dev/null
@@ -0,0 +1,44 @@
+operand {
+  name: "ifm"
+  type: FLOAT32
+  shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+  name: "filter"
+  type: FLOAT32
+  shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "bias"
+  type: FLOAT32
+  shape { dim: 64 }
+  filler {
+    tag: "gaussian"
+    arg: "0.0"
+    arg: "1.0"
+  }
+}
+operand {
+  name: "ofm"
+  type: FLOAT32
+  shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+  type: "Conv2D"
+  conv2d_options {
+    padding: VALID
+    stride_w: 2
+    stride_h: 2
+  }
+  input: "ifm"
+  input: "filter"
+  input: "bias"
+  output: "ofm"
+}
+input: "ifm"
+output: "ofm"
diff --git a/res/TensorFlowLiteRecipes/Quant_Conv_002/test.rule b/res/TensorFlowLiteRecipes/Quant_Conv_002/test.rule
new file mode 100644 (file)
index 0000000..2187895
--- /dev/null
@@ -0,0 +1,13 @@
+# To check float32 input/output.
+# Input/Output is float32, Conv is uint8.
+# Quantize Op is inserted at the beginning, Dequantize Op is inserted at the end.
+
+RULE    "VERIFY_FILE_FORMAT"      $(verify_file_format) '=' 1
+
+RULE    "INPUT_FLOAT32"           $(tensor_dtype ifm) '=' FLOAT32
+RULE    "OUTPUT_FLOAT32"          $(tensor_dtype ofm_Dequantize) '=' FLOAT32
+RULE    "CONV_UINT8"              $(tensor_dtype ofm) '=' UINT8
+RULE    "WEIGHTS_UINT8"           $(tensor_dtype filter) '=' UINT8
+RULE    "BIAS_INT32"              $(tensor_dtype bias) '=' INT32
+RULE    "QUANTIZE_OP"             $(op_count QUANTIZE) '=' 1
+RULE    "DEQUANTIZE_OP"           $(op_count DEQUANTIZE) '=' 1
index 557d403..8d211bb 100644 (file)
@@ -8,4 +8,6 @@ target_link_libraries(nnfw_lib_misc PRIVATE nnfw_common)
 target_link_libraries(nnfw_lib_misc PRIVATE nnfw_coverage)
 
 add_executable(nnfw_tensor_index_iterator "examples/tensor_index_iterator.cpp")
-target_link_libraries(nnfw_tensor_index_iterator nnfw_lib_misc)
+target_link_libraries(nnfw_tensor_index_iterator PRIVATE nnfw_lib_misc)
+
+add_subdirectory(test)
diff --git a/runtime/libs/misc/test/CMakeLists.txt b/runtime/libs/misc/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0aaa3fb
--- /dev/null
@@ -0,0 +1,15 @@
+if(NOT TARGET nnfw_lib_misc)
+    return()
+endif()
+
+if(NOT ENABLE_TEST)
+  return()
+endif(NOT ENABLE_TEST)
+
+add_executable(nnfw_index_enumerator_test IndexEnumerator.test.cpp)
+target_link_libraries(nnfw_index_enumerator_test PRIVATE nnfw_lib_misc)
+target_link_libraries(nnfw_index_enumerator_test PRIVATE nnfw_coverage)
+target_link_libraries(nnfw_index_enumerator_test PUBLIC gtest gtest_main ${LIB_PTHREAD})
+
+add_test(nnfw_index_enumerator_test nnfw_index_enumerator_test)
+install(TARGETS nnfw_index_enumerator_test DESTINATION unittest_standalone)
diff --git a/runtime/libs/misc/test/IndexEnumerator.test.cpp b/runtime/libs/misc/test/IndexEnumerator.test.cpp
new file mode 100644 (file)
index 0000000..4cff606
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 "misc/tensor/IndexEnumerator.h"
+
+#include <vector>
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+using nnfw::misc::tensor::Shape;
+using nnfw::misc::tensor::Index;
+using nnfw::misc::tensor::IndexEnumerator;
+
+TEST(MiscIndexEnumeratorTest, iterate_full_range)
+{
+  const uint32_t H = 3;
+  const uint32_t W = 4;
+
+  const Shape shape{H, W};
+
+  std::vector<uint32_t> count;
+
+  count.resize(H * W, 0);
+
+  for (IndexEnumerator e{shape}; e.valid(); e.advance())
+  {
+    const auto &ind = e.curr();
+
+    ASSERT_EQ(2, ind.rank());
+    count.at(ind.at(0) * W + ind.at(1)) += 1;
+  }
+
+  ASSERT_TRUE(std::all_of(count.begin(), count.end(), [](uint32_t n) { return n == 1; }));
+}
+
+TEST(MiscIndexEnumeratorTest, neg_zero_rank_shape)
+{
+  // Test abnormal case of empty shape
+  // It is expected not to throw any exception, do nothing
+  const Shape shape{};
+  IndexEnumerator e{shape};
+  ASSERT_NO_THROW(e.valid());
+  ASSERT_NO_THROW(e.advance());
+  SUCCEED();
+}
index be1ed65..64b3df1 100644 (file)
@@ -6,6 +6,9 @@ add_executable(ndarray_test ndarray_test.cpp)
 
 target_link_libraries(ndarray_test PRIVATE ndarray)
 
+# Requires linking nnfw_coverage: check header coverage
+target_link_libraries(ndarray_test PRIVATE nnfw_coverage)
+
 nnfw_find_package(GTest)
 if(NOT GTest_FOUND)
     message(STATUS "GTest not avaialble. Skipping NDArray test build")
index 4b5ad57..cecca98 100644 (file)
@@ -24,8 +24,14 @@ TEST(NDArray_tests, basic_data_test)
 {
 
   float raw_data[] = {1, 2, 3, 4};
+  int32_t raw_data_int[] = {1, 2, 3, 4};
+  uint32_t raw_data_uint[] = {1, 2, 3, 4};
+  int8_t raw_data_int8[] = {1, 2, 3, 4};
 
   Array<float> data22{raw_data, {2, 2}};
+  Array<int32_t> data22_int{raw_data_int, {2, 2}};
+  Array<uint32_t> data22_uint{raw_data_uint, {2, 2}};
+  Array<int8_t> data22_int8{raw_data_int8, {2, 2}};
 
   ASSERT_FLOAT_EQ(data22.at(0, 0), 1);
   ASSERT_FLOAT_EQ(data22.at(0, 1), 2);
@@ -44,9 +50,238 @@ TEST(NDArray_tests, basic_data_test)
   ASSERT_EQ(data14.shape().dim(0), 1);
   ASSERT_EQ(data14.shape().dim(1), 4);
 
-  ContiguousSpan<float> cs = data22.flat();
-  ASSERT_EQ(cs.size(), 4);
-  ASSERT_FLOAT_EQ(cs.at(3), 4);
+  // <float, false>
+  {
+    ContiguousSpan<float> cs = data22.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<float> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+    float sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<float> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<float>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_FLOAT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_FLOAT_EQ(cs4->at(3), 4);
+  }
+
+  // <float, true>
+  {
+    ContiguousSpan<float, true> cs = data22.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<float, true> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+    float sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_FLOAT_EQ(sum, 10);
+
+    std::vector<float> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<float, true>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_FLOAT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_FLOAT_EQ(cs4->at(3), 4);
+  }
+
+  // <int32_t, false>
+  {
+    ContiguousSpan<int32_t> cs = data22_int.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<int32_t> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_EQ(cs2.at(3), 4);
+
+    int32_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<int32_t> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<int32_t>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_EQ(cs4->at(3), 4);
+  }
+
+  // <int32_t, true>
+  {
+    ContiguousSpan<int32_t, true> cs = data22_int.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<int32_t, true> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_EQ(cs2.at(3), 4);
+
+    int32_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<int32_t> array_data{1, 2, 3, 4};
+    auto cs3 =
+      std::make_unique<ContiguousSpan<int32_t, true>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_EQ(cs4->at(3), 4);
+  }
+
+  // <uint32_t, false>
+  {
+    ContiguousSpan<uint32_t> cs = data22_uint.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<uint32_t> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_EQ(cs2.at(3), 4);
+
+    uint32_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<uint32_t> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<uint32_t>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+  }
+
+  // <uint32_t, true>
+  {
+    ContiguousSpan<uint32_t, true> cs = data22_uint.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<uint32_t, true> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_EQ(cs2.at(3), 4);
+
+    uint32_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<uint32_t> array_data{1, 2, 3, 4};
+    auto cs3 =
+      std::make_unique<ContiguousSpan<uint32_t, true>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_EQ(cs4->at(3), 4);
+  }
+
+  // <int8_t, false>
+  {
+    ContiguousSpan<int8_t> cs = data22_int8.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<int8_t> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+    int8_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<int8_t> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<int8_t>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_EQ(cs4->at(3), 4);
+
+    auto cs5 = ContiguousSpan<int8_t>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs5.size(), 4);
+    ASSERT_EQ(cs5.at(3), 4);
+  }
+
+  // <int8_t, true>
+  {
+    ContiguousSpan<int8_t, true> cs = data22_int8.flat();
+    ASSERT_EQ(cs.size(), 4);
+    ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+    ContiguousSpan<int8_t, true> cs2 = std::move(cs);
+    ASSERT_EQ(cs2.size(), 4);
+    ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+    int8_t sum = 0;
+    for (auto it = cs2.begin(); it < cs2.end(); it++)
+    {
+      sum += *it;
+    }
+    ASSERT_EQ(sum, 10);
+
+    std::vector<int8_t> array_data{1, 2, 3, 4};
+    auto cs3 = std::make_unique<ContiguousSpan<int8_t, true>>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs3->size(), 4);
+    ASSERT_EQ(cs3->at(3), 4);
+
+    auto cs4 = std::move(cs3);
+    ASSERT_EQ(cs3, nullptr);
+    ASSERT_EQ(cs4->size(), 4);
+    ASSERT_EQ(cs4->at(3), 4);
+
+    auto cs5 = ContiguousSpan<int8_t, true>(array_data.begin(), array_data.end());
+    ASSERT_EQ(cs5.size(), 4);
+    ASSERT_EQ(cs5.at(3), 4);
+  }
 
   Array<float> lv = std::move(data14);
   ASSERT_FLOAT_EQ(lv.at(0, 0), 1);
@@ -57,66 +292,339 @@ TEST(NDArray_tests, basic_data_test)
 
 TEST(NDArray_tests, slice_write_test)
 {
-  float raw_data[4] = {0};
+  // float
+  {
+    float raw_data[4] = {0};
 
-  Array<float> data22{raw_data, {2, 2}};
+    Array<float> data22{raw_data, {2, 2}};
+
+    data22.slice(1) = {1, 2};
+
+    ASSERT_FLOAT_EQ(data22.at(0, 0), 0);
+    ASSERT_FLOAT_EQ(data22.at(0, 1), 0);
+    ASSERT_FLOAT_EQ(data22.at(1, 0), 1);
+    ASSERT_FLOAT_EQ(data22.at(1, 1), 2);
+  }
+
+  // int32_t
+  {
+    int32_t raw_data[4] = {0};
+    Array<int32_t> data22{raw_data, {2, 2}};
+
+    data22.slice(1) = {1, 2};
 
-  data22.slice(1) = {1, 2};
+    ASSERT_EQ(data22.at(0, 0), 0);
+    ASSERT_EQ(data22.at(0, 1), 0);
+    ASSERT_EQ(data22.at(1, 0), 1);
+    ASSERT_EQ(data22.at(1, 1), 2);
+  }
 
-  ASSERT_FLOAT_EQ(data22.at(0, 0), 0);
-  ASSERT_FLOAT_EQ(data22.at(0, 1), 0);
-  ASSERT_FLOAT_EQ(data22.at(1, 0), 1);
-  ASSERT_FLOAT_EQ(data22.at(1, 1), 2);
+  // uint32_t
+  {
+    uint32_t raw_data[4] = {0};
+    Array<uint32_t> data22{raw_data, {2, 2}};
+
+    data22.slice(1) = {1, 2};
+
+    ASSERT_EQ(data22.at(0, 0), 0);
+    ASSERT_EQ(data22.at(0, 1), 0);
+    ASSERT_EQ(data22.at(1, 0), 1);
+    ASSERT_EQ(data22.at(1, 1), 2);
+  }
+
+  // int8_t
+  {
+    int8_t raw_data[4] = {0};
+    Array<int8_t> data22{raw_data, {2, 2}};
+
+    data22.slice(1) = {1, 2};
+
+    ASSERT_EQ(data22.at(0, 0), 0);
+    ASSERT_EQ(data22.at(0, 1), 0);
+    ASSERT_EQ(data22.at(1, 0), 1);
+    ASSERT_EQ(data22.at(1, 1), 2);
+  }
 }
 
 TEST(NDArray_tests, slice_read_test)
 {
-  float raw_data[4] = {1, 2, 3, 4};
+  // float
+  {
+    float raw_data[4] = {1, 2, 3, 4};
 
-  Array<float> data22{raw_data, {2, 2}};
+    Array<float> data22{raw_data, {2, 2}};
 
-  auto slice = data22.slice(1);
+    auto slice = data22.slice(1);
 
-  ASSERT_FLOAT_EQ(slice[0], 3);
-  ASSERT_FLOAT_EQ(slice[1], 4);
+    ASSERT_FLOAT_EQ(slice[0], 3);
+    ASSERT_FLOAT_EQ(slice[1], 4);
+  }
+
+  // int32_t
+  {
+    int32_t raw_data[4] = {1, 2, 3, 4};
+
+    Array<int32_t> data22{raw_data, {2, 2}};
+
+    auto slice = data22.slice(1);
+
+    ASSERT_EQ(slice[0], 3);
+    ASSERT_EQ(slice[1], 4);
+  }
+
+  // uint32_t
+  {
+    uint32_t raw_data[4] = {1, 2, 3, 4};
+
+    Array<uint32_t> data22{raw_data, {2, 2}};
+
+    auto slice = data22.slice(1);
+
+    ASSERT_EQ(slice[0], 3);
+    ASSERT_EQ(slice[1], 4);
+  }
+
+  // int8_t
+  {
+    int8_t raw_data[4] = {1, 2, 3, 4};
+
+    Array<int8_t> data22{raw_data, {2, 2}};
+
+    auto slice = data22.slice(1);
+
+    ASSERT_EQ(slice[0], 3);
+    ASSERT_EQ(slice[1], 4);
+  }
 }
 
 TEST(NDArray_tests, multidim_test)
 {
-  float raw_data[5] = {0, 1, 2, 3, 4};
+  // float
+  {
+    float raw_data[5] = {0, 1, 2, 3, 4};
+
+    Array<float> data22{raw_data, {1, 1, 1, 1, 5}};
 
-  Array<float> data22{raw_data, {1, 1, 1, 1, 5}};
+    ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+    ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+    ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+    ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+    ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+  }
 
-  ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 0), 0);
-  ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 1), 1);
-  ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 2), 2);
-  ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 3), 3);
-  ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+  // int32_t
+  {
+    int32_t raw_data[5] = {0, 1, 2, 3, 4};
+
+    Array<int32_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+  }
+
+  // uint32_t
+  {
+    uint32_t raw_data[5] = {0, 1, 2, 3, 4};
+
+    Array<uint32_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+  }
+
+  // int8_t
+  {
+    int8_t raw_data[5] = {0, 1, 2, 3, 4};
+
+    Array<int8_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+    ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+  }
 }
 
 TEST(NDArray_tests, slice_assign_test)
 {
-  std::vector<float> v1{1, 2, 3, 4, 5};
-  std::vector<float> v2(5);
+  // float
+  {
+    std::vector<float> v1{1, 2, 3, 4, 5};
+    std::vector<float> v2(5);
+
+    ContiguousSpan<float> span1(v1.begin(), v1.end());
+    ContiguousSpan<float> span2(v2.begin(), v2.end());
+
+    span2.assign(span1);
+
+    ASSERT_EQ(v1, v2);
+    ASSERT_EQ(span1.size(), 5);
+    ASSERT_EQ(span2.size(), 5);
+
+    ASSERT_EQ(span2.at(2), 3);
+    ASSERT_EQ(span2.at(4), 5);
+
+    ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+    ContiguousSpan<float> span3(span2.offset(1));
+    ASSERT_EQ(span3.size(), 4);
+    ASSERT_EQ(span3.at(0), 2);
+    ASSERT_EQ(span3.at(1), 3);
+    ASSERT_EQ(span3[2], 4);
+    ASSERT_EQ(span3[3], 5);
+
+    // const
+    ContiguousSpan<float, true> span4(v1.begin(), v1.end());
+    ASSERT_EQ(span4.size(), 5);
+    ASSERT_EQ(span4.at(0), 1);
+    ASSERT_EQ(span4.at(1), 2);
+    ASSERT_EQ(span4.at(2), 3);
+    ASSERT_EQ(span4[3], 4);
+    ASSERT_EQ(span4[4], 5);
+
+    ContiguousSpan<float, true> span5(span4.offset(1));
+    ASSERT_EQ(span5.size(), 4);
+    ASSERT_EQ(span5.at(0), 2);
+    ASSERT_EQ(span5.at(1), 3);
+    ASSERT_EQ(span5[2], 4);
+    ASSERT_EQ(span5[3], 5);
+
+    ASSERT_EQ(*(span5.data() + 2), *(span5.data() + 2));
+  }
+
+  // int32_t
+  {
+    std::vector<int32_t> v1{1, 2, 3, 4, 5};
+    std::vector<int32_t> v2(5);
+
+    ContiguousSpan<int32_t> span1(v1.begin(), v1.end());
+    ContiguousSpan<int32_t> span2(v2.begin(), v2.end());
+
+    span2.assign(span1);
+
+    ASSERT_EQ(v1, v2);
+    ASSERT_EQ(span1.size(), 5);
+    ASSERT_EQ(span2.size(), 5);
+
+    ASSERT_EQ(span2.at(2), 3);
+    ASSERT_EQ(span2.at(4), 5);
+
+    ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+    ContiguousSpan<int32_t> span3(span2.offset(1));
+    ASSERT_EQ(span3.size(), 4);
+    ASSERT_EQ(span3.at(0), 2);
+    ASSERT_EQ(span3.at(1), 3);
+    ASSERT_EQ(span3[2], 4);
+    ASSERT_EQ(span3[3], 5);
+
+    // const
+    ContiguousSpan<int32_t, true> span4(v1.begin(), v1.end());
+    ASSERT_EQ(span4.size(), 5);
+    ASSERT_EQ(span4.at(0), 1);
+    ASSERT_EQ(span4.at(1), 2);
+    ASSERT_EQ(span4.at(2), 3);
+    ASSERT_EQ(span4[3], 4);
+    ASSERT_EQ(span4[4], 5);
+
+    ContiguousSpan<int32_t, true> span5(span4.offset(1));
+    ASSERT_EQ(span5.size(), 4);
+    ASSERT_EQ(span5.at(0), 2);
+    ASSERT_EQ(span5.at(1), 3);
+    ASSERT_EQ(span5[2], 4);
+    ASSERT_EQ(span5[3], 5);
+  }
+
+  // uint32_t
+  {
+    std::vector<uint32_t> v1{1, 2, 3, 4, 5};
+    std::vector<uint32_t> v2(5);
+
+    ContiguousSpan<uint32_t> span1(v1.begin(), v1.end());
+    ContiguousSpan<uint32_t> span2(v2.begin(), v2.end());
+
+    span2.assign(span1);
+
+    ASSERT_EQ(v1, v2);
+    ASSERT_EQ(span1.size(), 5);
+    ASSERT_EQ(span2.size(), 5);
+
+    ASSERT_EQ(span2.at(2), 3);
+    ASSERT_EQ(span2.at(4), 5);
+
+    ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+    ContiguousSpan<uint32_t> span3(span2.offset(1));
+    ASSERT_EQ(span3.size(), 4);
+    ASSERT_EQ(span3.at(0), 2);
+    ASSERT_EQ(span3.at(1), 3);
+    ASSERT_EQ(span3[2], 4);
+    ASSERT_EQ(span3[3], 5);
+
+    // const
+    ContiguousSpan<uint32_t, true> span4(v1.begin(), v1.end());
+    ASSERT_EQ(span4.size(), 5);
+    ASSERT_EQ(span4.at(0), 1);
+    ASSERT_EQ(span4.at(1), 2);
+    ASSERT_EQ(span4.at(2), 3);
+    ASSERT_EQ(span4[3], 4);
+    ASSERT_EQ(span4[4], 5);
+
+    ContiguousSpan<uint32_t, true> span5(span4.offset(1));
+    ASSERT_EQ(span5.size(), 4);
+    ASSERT_EQ(span5.at(0), 2);
+    ASSERT_EQ(span5.at(1), 3);
+    ASSERT_EQ(span5[2], 4);
+    ASSERT_EQ(span5[3], 5);
+  }
+
+  // int8_t
+  {
+    std::vector<int8_t> v1{1, 2, 3, 4, 5};
+    std::vector<int8_t> v2(5);
+
+    ContiguousSpan<int8_t> span1(v1.begin(), v1.end());
+    ContiguousSpan<int8_t> span2(v2.begin(), v2.end());
+
+    span2.assign(span1);
 
-  ContiguousSpan<float> span1(v1.begin(), v1.end());
-  ContiguousSpan<float> span2(v2.begin(), v2.end());
+    ASSERT_EQ(v1, v2);
+    ASSERT_EQ(span1.size(), 5);
+    ASSERT_EQ(span2.size(), 5);
 
-  span2.assign(span1);
+    ASSERT_EQ(span2.at(2), 3);
+    ASSERT_EQ(span2.at(4), 5);
 
-  ASSERT_EQ(v1, v2);
-  ASSERT_EQ(span1.size(), 5);
-  ASSERT_EQ(span2.size(), 5);
+    ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
 
-  ASSERT_EQ(span2.at(2), 3);
-  ASSERT_EQ(span2.at(4), 5);
+    ContiguousSpan<int8_t> span3(span2.offset(1));
+    ASSERT_EQ(span3.size(), 4);
+    ASSERT_EQ(span3.at(0), 2);
+    ASSERT_EQ(span3.at(1), 3);
+    ASSERT_EQ(span3[2], 4);
+    ASSERT_EQ(span3[3], 5);
 
-  ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+    // const
+    ContiguousSpan<int8_t, true> span4(v1.begin(), v1.end());
+    ASSERT_EQ(span4.size(), 5);
+    ASSERT_EQ(span4.at(0), 1);
+    ASSERT_EQ(span4.at(1), 2);
+    ASSERT_EQ(span4.at(2), 3);
+    ASSERT_EQ(span4[3], 4);
+    ASSERT_EQ(span4[4], 5);
 
-  ContiguousSpan<float> span3(span2.offset(1));
-  ASSERT_EQ(span3.size(), 4);
-  ASSERT_EQ(span3.at(0), 2);
-  ASSERT_EQ(span3.at(1), 3);
-  ASSERT_EQ(span3.at(2), 4);
-  ASSERT_EQ(span3.at(3), 5);
+    ContiguousSpan<int8_t, true> span5(span4.offset(1));
+    ASSERT_EQ(span5.size(), 4);
+    ASSERT_EQ(span5.at(0), 2);
+    ASSERT_EQ(span5.at(1), 3);
+    ASSERT_EQ(span5[2], 4);
+    ASSERT_EQ(span5[3], 5);
+  }
 }
index 5455757..a94be24 100644 (file)
@@ -1,6 +1,6 @@
 set(LIB_ONERT_BACKEND_TRIX onert_backend_trix)
 
-nnfw_find_package(TRIXEngine EXACT 2.5.0 QUIET)
+nnfw_find_package(TRIXEngine QUIET 2.5.0)
 if(NOT TRIXEngine_FOUND)
   return()
 endif(NOT TRIXEngine_FOUND)
index 40d1d23..c98d050 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef __ONERT_EXEC_FEATURE_NHWC_VIEW_H__
 #define __ONERT_EXEC_FEATURE_NHWC_VIEW_H__
 
-#include "../Reader.h"
+#include "Reader.h"
 
 #include <cassert>
 #include <cstddef>
index 5649f28..41186a8 100644 (file)
@@ -290,6 +290,8 @@ ir::DataType BaseLoader<LoaderDomain>::BaseLoader::tensorTypeToDataType(const Te
     case TensorType::TensorType_INT8:
       return ir::DataType::QUANT_INT8_ASYMM;
     // case TensorType::TensorType_FLOAT64
+    case TensorType::TensorType_UINT32:
+      return ir::DataType::UINT32;
     default:
       throw std::runtime_error(
         std::string("Unsupported tensor type: ").append(EnumNameTensorType(type)));
index 7a0df4e..8d9063f 100644 (file)
@@ -2,7 +2,7 @@ if (NOT BUILD_TRIX_LOADER)
   return()
 endif ()
 
-nnfw_find_package(TRIXEngine EXACT 2.5.0 QUIET)
+nnfw_find_package(TRIXEngine QUIET 2.5.0)
 if(TRIXEngine_FOUND)
   list(APPEND SOURCES src/trix_loader.cc)
 else()
index 3889997..f0bd701 100644 (file)
@@ -10,6 +10,10 @@ target_link_libraries(${TEST_ONERT} onert_core)
 target_link_libraries(${TEST_ONERT} gtest)
 target_link_libraries(${TEST_ONERT} gtest_main)
 target_link_libraries(${TEST_ONERT} ${LIB_PTHREAD} dl)
+
+# Requires linking nnfw_coverage: check header coverage
+target_link_libraries(${TEST_ONERT} nnfw_coverage)
+
 add_test(${TEST_ONERT} ${TEST_ONERT})
 
 install(TARGETS ${TEST_ONERT} DESTINATION unittest_standalone)
diff --git a/runtime/onert/test/core/exec/feature/MockTensor.h b/runtime/onert/test/core/exec/feature/MockTensor.h
new file mode 100644 (file)
index 0000000..1d2d375
--- /dev/null
@@ -0,0 +1,66 @@
+
+/*
+ * 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 "backend/ITensor.h"
+
+template <typename T> class MockTensor : public onert::backend::ITensor
+{
+public:
+  MockTensor<T>(onert::ir::Shape &shape, T *buf, onert::ir::Layout layout)
+    : _buf(reinterpret_cast<uint8_t *>(buf)), _shape(shape), _layout(layout)
+  {
+  }
+
+public:
+  uint8_t *buffer() const override { return _buf; }
+
+  size_t calcOffset(const onert::ir::Coordinates &coords) const override
+  {
+    size_t rank = _shape.rank();
+    rank = rank == 0 ? 1 : rank;
+    size_t offset = 0;
+    for (size_t i = 0; i < rank; ++i)
+    {
+      auto dim = _shape.rank() == 0 ? 1 : _shape.dim(i);
+      offset = offset * dim + coords[i];
+    }
+    offset *= sizeof(T);
+
+    return offset;
+  }
+
+  onert::ir::Shape getShape() const override { return _shape; }
+
+public: // DUMMY methods
+  size_t total_size() const override { return 0; }
+  onert::ir::Layout layout() const override { return _layout; }
+  onert::ir::DataType data_type() const override { return onert::ir::DataType::UINT8; }
+  float data_scale() const override { return 0; }
+  int32_t data_zero_point() const override { return 0; }
+  const std::vector<float> &data_scales() const override { return _dummy_scales; }
+  const std::vector<int32_t> &data_zero_points() const override { return _dummy_zerops; }
+  bool has_padding() const override { return false; }
+  void access(const std::function<void(ITensor &tensor)> &fn) override {}
+  bool is_dynamic() const override { return false; }
+
+private:
+  uint8_t *_buf = nullptr;
+  onert::ir::Shape _shape;
+  onert::ir::Layout _layout = onert::ir::Layout::UNKNOWN;
+  std::vector<float> _dummy_scales;
+  std::vector<int32_t> _dummy_zerops;
+};
diff --git a/runtime/onert/test/core/exec/feature/nchw/Reader.cc b/runtime/onert/test/core/exec/feature/nchw/Reader.cc
new file mode 100644 (file)
index 0000000..61bd5f1
--- /dev/null
@@ -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 "../MockTensor.h"
+
+#include "exec/feature/nchw/Reader.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class Reader_nchw : public testing::Test
+{
+public:
+  void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+  void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    _shape = onert::ir::FeatureShape(batch, depth, height, width);
+  }
+
+  void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    auto elem_size = sizeof(T);
+    _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+                                      width * elem_size);
+  }
+
+  void createReader()
+  {
+    _reader =
+      std::make_shared<nchw::Reader<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+  }
+
+  void createUsingMockTensor()
+  {
+    onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+    _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NCHW);
+    _reader = std::make_shared<nchw::Reader<T>>(_tensor.get());
+  }
+
+  std::shared_ptr<Reader<T>> _reader = nullptr;
+
+private:
+  std::shared_ptr<std::vector<T>> _data = nullptr;
+  onert::ir::FeatureShape _shape;
+  onert::ir::FeatureShape _stride;
+  std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ReaderTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(Reader_nchw, ReaderTypes);
+
+TYPED_TEST(Reader_nchw, basic_reader)
+{
+  this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+  this->setShape(1, 2, 3, 2);
+  this->setStride(12, 6, 2, 1);
+  this->createReader();
+
+  // Data: NCHW
+  // Shape: NCHW
+  ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 8);
+  ASSERT_EQ(this->_reader->at(1, 1, 0), 8);
+
+  // Data: NCHW
+  // Shape: NCHW
+  this->createUsingMockTensor();
+
+  ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 6);
+  ASSERT_EQ(this->_reader->at(1, 1, 0), 6);
+}
diff --git a/runtime/onert/test/core/exec/feature/nchw/View.cc b/runtime/onert/test/core/exec/feature/nchw/View.cc
new file mode 100644 (file)
index 0000000..bf90676
--- /dev/null
@@ -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 "../MockTensor.h"
+
+#include "exec/feature/nchw/View.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class View_nchw : public testing::Test
+{
+public:
+  void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+  void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    _shape = onert::ir::FeatureShape(batch, depth, height, width);
+  }
+
+  void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    auto elem_size = sizeof(T);
+    _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+                                      width * elem_size);
+  }
+
+  void createView()
+  {
+    _view =
+      std::make_shared<nchw::View<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+  }
+
+  void createUsingMockTensor()
+  {
+    onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+    _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NCHW);
+    _view = std::make_shared<nchw::View<T>>(_tensor.get());
+  }
+
+  std::shared_ptr<nchw::View<T>> _view = nullptr;
+
+private:
+  std::shared_ptr<std::vector<T>> _data = nullptr;
+  onert::ir::FeatureShape _shape;
+  onert::ir::FeatureShape _stride;
+  std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ViewTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(View_nchw, ViewTypes);
+
+TYPED_TEST(View_nchw, basic_view)
+{
+  this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+  this->setShape(1, 2, 3, 2);
+  this->setStride(12, 6, 2, 1);
+  this->createView();
+
+  // Data: NCHW
+  // Shape: NCHW
+  ASSERT_EQ(this->_view->at(0, 1, 1, 0), 8);
+  ASSERT_EQ(this->_view->at(1, 1, 0), 8);
+
+  // Data: NCHW
+  // Shape: NCHW
+  this->createUsingMockTensor();
+
+  ASSERT_EQ(this->_view->at(0, 1, 1, 0), 6);
+  ASSERT_EQ(this->_view->at(1, 1, 0), 6);
+}
diff --git a/runtime/onert/test/core/exec/feature/nhwc/Reader.cc b/runtime/onert/test/core/exec/feature/nhwc/Reader.cc
new file mode 100644 (file)
index 0000000..6c31931
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 "../MockTensor.h"
+
+#include "exec/feature/nhwc/Reader.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class Reader_nhwc : public testing::Test
+{
+public:
+  void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+  void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    _shape = onert::ir::FeatureShape(batch, depth, height, width);
+  }
+
+  void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    auto elem_size = sizeof(T);
+    _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+                                      width * elem_size);
+  }
+
+  void createReader()
+  {
+    _reader =
+      std::make_shared<nhwc::Reader<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+  }
+
+  void createUsingMockTensor()
+  {
+    onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+    _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NHWC);
+    _reader = std::make_shared<nhwc::Reader<T>>(_tensor.get());
+  }
+
+  std::shared_ptr<nhwc::Reader<T>> _reader = nullptr;
+
+private:
+  std::shared_ptr<std::vector<T>> _data = nullptr;
+  onert::ir::FeatureShape _shape;
+  onert::ir::FeatureShape _stride;
+  std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ReaderTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(Reader_nhwc, ReaderTypes);
+TYPED_TEST_SUITE(MockTensorReader_nhwc, ReaderTypes);
+
+TYPED_TEST(Reader_nhwc, basic_reader)
+{
+  this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+  this->setShape(1, 2, 3, 2);
+  this->setStride(12, 1, 6, 2);
+  this->createReader();
+
+  // Data: NCHW
+  // Shape: NHWC
+  ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 8);
+  ASSERT_EQ(this->_reader->at(1, 1, 0), 8);
+
+  // Data: NHWC
+  // Shape: NHWC
+  this->createUsingMockTensor();
+
+  ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 6);
+  ASSERT_EQ(this->_reader->at(1, 1, 0), 6);
+}
diff --git a/runtime/onert/test/core/exec/feature/nhwc/View.cc b/runtime/onert/test/core/exec/feature/nhwc/View.cc
new file mode 100644 (file)
index 0000000..52a2b3d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 "../MockTensor.h"
+
+#include "exec/feature/nhwc/View.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class View_nhwc : public testing::Test
+{
+public:
+  void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+  void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    _shape = onert::ir::FeatureShape(batch, depth, height, width);
+  }
+
+  void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+  {
+    auto elem_size = sizeof(T);
+    _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+                                      width * elem_size);
+  }
+
+  void createView()
+  {
+    _view =
+      std::make_shared<nhwc::View<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+  }
+
+  void createUsingMockTensor()
+  {
+    onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+    _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NHWC);
+    _view = std::make_shared<nhwc::View<T>>(_tensor.get());
+  }
+
+  std::shared_ptr<nhwc::View<T>> _view = nullptr;
+
+private:
+  std::shared_ptr<std::vector<T>> _data = nullptr;
+  onert::ir::FeatureShape _shape;
+  onert::ir::FeatureShape _stride;
+  std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ViewTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(View_nhwc, ViewTypes);
+TYPED_TEST_SUITE(MockTensorView_nhwc, ViewTypes);
+
+TYPED_TEST(View_nhwc, basic_view)
+{
+  this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+  this->setShape(1, 2, 3, 2);
+  this->setStride(12, 1, 6, 2);
+  this->createView();
+
+  // Data: NCHW
+  // Shape: NHWC
+  ASSERT_EQ(this->_view->at(0, 1, 1, 0), 8);
+  ASSERT_EQ(this->_view->at(1, 1, 0), 8);
+
+  // Data: NHWC
+  // Shape: NHWC
+  this->createUsingMockTensor();
+
+  ASSERT_EQ(this->_view->at(0, 1, 1, 0), 6);
+  ASSERT_EQ(this->_view->at(1, 1, 0), 6);
+}
index e4e4ba1..4f1c7f9 100644 (file)
@@ -269,6 +269,20 @@ uint32_t CircleGen::addOperatorFloorDiv(const OperatorParams &params)
                                 circle::BuiltinOptions_NONE, 0);
 }
 
+uint32_t CircleGen::addOperatorGreater(const OperatorParams &params)
+{
+  auto options = circle::CreateLessOptions(_fbb).Union();
+  return addOperatorWithOptions(params, circle::BuiltinOperator_GREATER,
+                                circle::BuiltinOptions_GreaterOptions, options);
+}
+
+uint32_t CircleGen::addOperatorGreaterEqual(const OperatorParams &params)
+{
+  auto options = circle::CreateGreaterOptions(_fbb).Union();
+  return addOperatorWithOptions(params, circle::BuiltinOperator_GREATER_EQUAL,
+                                circle::BuiltinOptions_GreaterEqualOptions, options);
+}
+
 uint32_t CircleGen::addOperatorL2Normalization(const OperatorParams &params)
 {
   auto options = circle::CreateL2NormOptions(_fbb).Union();
@@ -283,6 +297,13 @@ uint32_t CircleGen::addOperatorLess(const OperatorParams &params)
                                 circle::BuiltinOptions_LessOptions, options);
 }
 
+uint32_t CircleGen::addOperatorLessEqual(const OperatorParams &params)
+{
+  auto options = circle::CreateLessOptions(_fbb).Union();
+  return addOperatorWithOptions(params, circle::BuiltinOperator_LESS_EQUAL,
+                                circle::BuiltinOptions_LessEqualOptions, options);
+}
+
 uint32_t CircleGen::addOperatorLeakyRelu(const OperatorParams &params, float alpha)
 {
   auto options = circle::CreateLeakyReluOptions(_fbb, alpha).Union();
@@ -319,6 +340,13 @@ uint32_t CircleGen::addOperatorNeg(const OperatorParams &params)
                                 circle::BuiltinOptions_NegOptions, options);
 }
 
+uint32_t CircleGen::addOperatorNotEqual(const OperatorParams &params)
+{
+  auto options = circle::CreateEqualOptions(_fbb).Union();
+  return addOperatorWithOptions(params, circle::BuiltinOperator_NOT_EQUAL,
+                                circle::BuiltinOptions_NotEqualOptions, options);
+}
+
 uint32_t CircleGen::addOperatorOneHot(const OperatorParams &params, int32_t axis)
 {
   auto options = circle::CreateOneHotOptions(_fbb, axis).Union();
index 062a8d3..d780eb1 100644 (file)
@@ -174,16 +174,20 @@ public:
   uint32_t addOperatorFullyConnected(const OperatorParams &params,
                                      circle::FullyConnectedOptionsWeightsFormat weights_format =
                                        circle::FullyConnectedOptionsWeightsFormat_DEFAULT);
+  uint32_t addOperatorGreater(const OperatorParams &params);
+  uint32_t addOperatorGreaterEqual(const OperatorParams &params);
   uint32_t addOperatorIf(const OperatorParams &params, uint32_t then_subg, uint32_t else_subg);
   uint32_t addOperatorInstanceNorm(const OperatorParams &params, float epsilon,
                                    circle::ActivationFunctionType actfn);
   uint32_t addOperatorL2Normalization(const OperatorParams &params);
   uint32_t addOperatorLeakyRelu(const OperatorParams &params, float alpha);
   uint32_t addOperatorLess(const OperatorParams &params);
+  uint32_t addOperatorLessEqual(const OperatorParams &params);
   uint32_t addOperatorLogSoftmax(const OperatorParams &params);
   uint32_t addOperatorMul(const OperatorParams &params, circle::ActivationFunctionType actfn);
   uint32_t addOperatorMean(const OperatorParams &params, bool keep_dims);
   uint32_t addOperatorNeg(const OperatorParams &params);
+  uint32_t addOperatorNotEqual(const OperatorParams &params);
   uint32_t addOperatorOneHot(const OperatorParams &params, int32_t axis);
   uint32_t addOperatorPad(const OperatorParams &params);
   uint32_t addOperatorPadV2(const OperatorParams &params);
index c210229..9fc0e86 100644 (file)
@@ -283,7 +283,7 @@ TEST_F(GenModelTest, neg_OneOp_Add_VarToVarSize0_InvalidShape)
   SUCCEED();
 }
 
-TEST_F(GenModelTest, net_OneOp_Add_VarToVarInt16)
+TEST_F(GenModelTest, neg_OneOp_Add_VarToVarInt16)
 {
   CircleGen cgen;
   int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT16}, 1., 2);
index 928df2d..b4cfa6f 100644 (file)
@@ -89,6 +89,34 @@ TEST_F(GenModelTest, OneOp_Cast_BoolToInt32)
   SUCCEED();
 }
 
+TEST_F(GenModelTest, OneOp_Cast_Uint8ToFloat32)
+{
+  CircleGen cgen = genSimpleCastModel(circle::TensorType_UINT8, circle::TensorType_FLOAT32);
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  // clang-format off
+  _context->addTestCase(
+    TestCaseData{}.addInput<uint8_t>({0, 100, 200, 255})
+                  .addOutput<float>({0., 100., 200., 255.}));
+  // clang-format on
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, OneOp_Cast_Int64ToFloat32)
+{
+  CircleGen cgen = genSimpleCastModel(circle::TensorType_INT64, circle::TensorType_FLOAT32);
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(TestCaseData{}
+                          .addInput<int64_t>({-12345, 3, 100, 2147483648})
+                          .addOutput<float>({-12345., 3., 100., 2147483648.}));
+  _context->setBackends({"cpu"});
+
+  SUCCEED();
+}
+
 TEST_F(GenModelTest, OneOp_Cast_AfterEqual)
 {
   CircleGen cgen;
diff --git a/tests/nnfw_api/src/one_op_tests/Greater.cc b/tests/nnfw_api/src/one_op_tests/Greater.cc
new file mode 100644 (file)
index 0000000..b63075c
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 "GenModelTest.h"
+
+struct GreaterVariationParam
+{
+  TestCaseData tcd;
+  circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+  const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class GreaterVariation : public GenModelTest,
+                         public ::testing::WithParamInterface<GreaterVariationParam>
+{
+};
+
+// Input shape:
+//   Base: {1, 2, 2, 1}
+//   Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, GreaterVariation,
+                         ::testing::Values(
+                           // Float type
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                   .addInput<float>({0.1, 0.2, 0.3, 0.4})
+                                                   .addOutput<bool>({false, true, false, true})},
+                           // Float type - broadcast
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                   .addInput<float>({0.3})
+                                                   .addOutput<bool>({false, false, false, true})},
+                           // Int32 type
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<int32_t>({1, 3, 2, 7})
+                                                   .addInput<int32_t>({1, 2, 3, 4})
+                                                   .addOutput<bool>({false, true, false, true}),
+                                                 circle::TensorType::TensorType_INT32},
+                           // Int32 type - broadcast
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<int32_t>({1, 3, 2, 7})
+                                                   .addInput<int32_t>({5})
+                                                   .addOutput<bool>({false, false, false, true}),
+                                                 circle::TensorType::TensorType_INT32},
+                           // Int64 type
+                           // NYI: acl backend
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<int64_t>({1, 3, -2, 7})
+                                                   .addInput<int64_t>({1, 2, 3, 4})
+                                                   .addOutput<bool>({false, true, false, true}),
+                                                 circle::TensorType::TensorType_INT64,
+                                                 {"cpu"}},
+                           // Int64 type - broadcast
+                           // NYI: acl backend
+                           GreaterVariationParam{TestCaseData{}
+                                                   .addInput<int64_t>({1, 3, -2, 7})
+                                                   .addInput<int64_t>({1})
+                                                   .addOutput<bool>({false, true, false, true}),
+                                                 circle::TensorType::TensorType_INT64,
+                                                 {"cpu"}}));
+
+TEST_P(GreaterVariation, Test)
+{
+  auto &param = GetParam();
+
+  auto lhs_data = param.tcd.inputs.at(0);
+  auto rhs_data = param.tcd.inputs.at(1);
+
+  bool broadcast_lhs = false;
+  bool broadcast_rhs = false;
+  if (lhs_data.size() != rhs_data.size())
+  {
+    if (lhs_data.size() < rhs_data.size())
+      broadcast_lhs = true;
+    else
+      broadcast_rhs = true;
+  }
+
+  CircleGen cgen;
+  const auto output_type = circle::TensorType::TensorType_BOOL;
+
+  int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+  cgen.addOperatorGreater({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(param.tcd);
+  _context->setBackends(param.backends);
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Greater_DifferentType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+  cgen.addOperatorGreater({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Greater_InvalidType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  cgen.addOperatorGreater({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
diff --git a/tests/nnfw_api/src/one_op_tests/GreaterEqual.cc b/tests/nnfw_api/src/one_op_tests/GreaterEqual.cc
new file mode 100644 (file)
index 0000000..f824030
--- /dev/null
@@ -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 "GenModelTest.h"
+
+struct GreaterEqualVariationParam
+{
+  TestCaseData tcd;
+  circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+  const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class GreaterEqualVariation : public GenModelTest,
+                              public ::testing::WithParamInterface<GreaterEqualVariationParam>
+{
+};
+
+// Input shape:
+//   Base: {1, 2, 2, 1}
+//   Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(
+  GenModelTest, GreaterEqualVariation,
+  ::testing::Values(
+    // Float type
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                 .addInput<float>({0.1, 0.2, 0.3, 0.4})
+                                 .addOutput<bool>({true, true, false, true})},
+    // Float type - broadcast
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                 .addInput<float>({0.3})
+                                 .addOutput<bool>({false, true, false, true})},
+    // Int32 type
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<int32_t>({1, 3, 2, 7})
+                                 .addInput<int32_t>({1, 2, 3, 4})
+                                 .addOutput<bool>({true, true, false, true}),
+                               circle::TensorType::TensorType_INT32},
+    // Int32 type - broadcast
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<int32_t>({1, 3, 2, 7})
+                                 .addInput<int32_t>({5})
+                                 .addOutput<bool>({false, false, false, true}),
+                               circle::TensorType::TensorType_INT32},
+    // Int64 type
+    // NYI: acl backend
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<int64_t>({1, 3, -2, 7})
+                                 .addInput<int64_t>({1, 2, 3, 4})
+                                 .addOutput<bool>({true, true, false, true}),
+                               circle::TensorType::TensorType_INT64,
+                               {"cpu"}},
+    // Int64 type - broadcast
+    // NYI: acl backend
+    GreaterEqualVariationParam{TestCaseData{}
+                                 .addInput<int64_t>({1, 3, -2, 7})
+                                 .addInput<int64_t>({1})
+                                 .addOutput<bool>({true, true, false, true}),
+                               circle::TensorType::TensorType_INT64,
+                               {"cpu"}}));
+
+TEST_P(GreaterEqualVariation, Test)
+{
+  auto &param = GetParam();
+
+  auto lhs_data = param.tcd.inputs.at(0);
+  auto rhs_data = param.tcd.inputs.at(1);
+
+  bool broadcast_lhs = false;
+  bool broadcast_rhs = false;
+  if (lhs_data.size() != rhs_data.size())
+  {
+    if (lhs_data.size() < rhs_data.size())
+      broadcast_lhs = true;
+    else
+      broadcast_rhs = true;
+  }
+
+  CircleGen cgen;
+  const auto output_type = circle::TensorType::TensorType_BOOL;
+
+  int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+  cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(param.tcd);
+  _context->setBackends(param.backends);
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_GreaterEqual_DifferentType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+  cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_GreaterEqual_InvalidType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
diff --git a/tests/nnfw_api/src/one_op_tests/Less.cc b/tests/nnfw_api/src/one_op_tests/Less.cc
new file mode 100644 (file)
index 0000000..6f76465
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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 "GenModelTest.h"
+
+struct LessVariationParam
+{
+  TestCaseData tcd;
+  circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+  const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class LessVariation : public GenModelTest, public ::testing::WithParamInterface<LessVariationParam>
+{
+};
+
+// Input shape:
+//   Base: {1, 2, 2, 1}
+//   Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, LessVariation,
+                         ::testing::Values(
+                           // Float type
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                .addInput<float>({0.1, 0.2, 0.3, 0.4})
+                                                .addOutput<bool>({false, false, true, false})},
+                           // Float type - broadcast
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                .addInput<float>({0.3})
+                                                .addOutput<bool>({true, false, true, false})},
+                           // Int32 type
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<int32_t>({1, 3, 2, 7})
+                                                .addInput<int32_t>({1, 2, 3, 4})
+                                                .addOutput<bool>({false, false, true, false}),
+                                              circle::TensorType::TensorType_INT32},
+                           // Int32 type - broadcast
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<int32_t>({1, 3, 2, 7})
+                                                .addInput<int32_t>({5})
+                                                .addOutput<bool>({true, true, true, false}),
+                                              circle::TensorType::TensorType_INT32},
+                           // Int64 type
+                           // NYI: acl backend
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<int64_t>({1, 3, -2, 7})
+                                                .addInput<int64_t>({1, 2, 3, 4})
+                                                .addOutput<bool>({false, false, true, false}),
+                                              circle::TensorType::TensorType_INT64,
+                                              {"cpu"}},
+                           // Int64 type - broadcast
+                           // NYI: acl backend
+                           LessVariationParam{TestCaseData{}
+                                                .addInput<int64_t>({1, 3, -2, 7})
+                                                .addInput<int64_t>({1})
+                                                .addOutput<bool>({false, false, true, false}),
+                                              circle::TensorType::TensorType_INT64,
+                                              {"cpu"}}));
+
+TEST_P(LessVariation, Test)
+{
+  auto &param = GetParam();
+
+  auto lhs_data = param.tcd.inputs.at(0);
+  auto rhs_data = param.tcd.inputs.at(1);
+
+  bool broadcast_lhs = false;
+  bool broadcast_rhs = false;
+  if (lhs_data.size() != rhs_data.size())
+  {
+    if (lhs_data.size() < rhs_data.size())
+      broadcast_lhs = true;
+    else
+      broadcast_rhs = true;
+  }
+
+  CircleGen cgen;
+  const auto output_type = circle::TensorType::TensorType_BOOL;
+
+  int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+  cgen.addOperatorLess({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(param.tcd);
+  _context->setBackends(param.backends);
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Less_DifferentType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+  cgen.addOperatorLess({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Less_InvalidType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  cgen.addOperatorLess({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
diff --git a/tests/nnfw_api/src/one_op_tests/LessEqual.cc b/tests/nnfw_api/src/one_op_tests/LessEqual.cc
new file mode 100644 (file)
index 0000000..e0e6d66
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 "GenModelTest.h"
+
+struct LessEqualVariationParam
+{
+  TestCaseData tcd;
+  circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+  const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class LessEqualVariation : public GenModelTest,
+                           public ::testing::WithParamInterface<LessEqualVariationParam>
+{
+};
+
+// Input shape:
+//   Base: {1, 2, 2, 1}
+//   Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, LessEqualVariation,
+                         ::testing::Values(
+                           // Float type
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                     .addInput<float>({0.1, 0.2, 0.3, 0.4})
+                                                     .addOutput<bool>({true, false, true, false})},
+                           // Float type - broadcast
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<float>({0.1, 0.3, 0.2, 0.7})
+                                                     .addInput<float>({0.3})
+                                                     .addOutput<bool>({true, true, true, false})},
+                           // Int32 type
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<int32_t>({1, 3, 2, 7})
+                                                     .addInput<int32_t>({1, 2, 3, 4})
+                                                     .addOutput<bool>({true, false, true, false}),
+                                                   circle::TensorType::TensorType_INT32},
+                           // Int32 type - broadcast
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<int32_t>({1, 3, 2, 7})
+                                                     .addInput<int32_t>({5})
+                                                     .addOutput<bool>({true, true, true, false}),
+                                                   circle::TensorType::TensorType_INT32},
+                           // Int64 type
+                           // NYI: acl backend
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<int64_t>({1, 3, -2, 7})
+                                                     .addInput<int64_t>({1, 2, 3, 4})
+                                                     .addOutput<bool>({true, false, true, false}),
+                                                   circle::TensorType::TensorType_INT64,
+                                                   {"cpu"}},
+                           // Int64 type - broadcast
+                           // NYI: acl backend
+                           LessEqualVariationParam{TestCaseData{}
+                                                     .addInput<int64_t>({1, 3, -2, 7})
+                                                     .addInput<int64_t>({1})
+                                                     .addOutput<bool>({true, false, true, false}),
+                                                   circle::TensorType::TensorType_INT64,
+                                                   {"cpu"}}));
+
+TEST_P(LessEqualVariation, Test)
+{
+  auto &param = GetParam();
+
+  auto lhs_data = param.tcd.inputs.at(0);
+  auto rhs_data = param.tcd.inputs.at(1);
+
+  bool broadcast_lhs = false;
+  bool broadcast_rhs = false;
+  if (lhs_data.size() != rhs_data.size())
+  {
+    if (lhs_data.size() < rhs_data.size())
+      broadcast_lhs = true;
+    else
+      broadcast_rhs = true;
+  }
+
+  CircleGen cgen;
+  const auto output_type = circle::TensorType::TensorType_BOOL;
+
+  int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+  cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(param.tcd);
+  _context->setBackends(param.backends);
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_LessEqual_DifferentType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+  cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_LessEqual_InvalidType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
diff --git a/tests/nnfw_api/src/one_op_tests/NotEqual.cc b/tests/nnfw_api/src/one_op_tests/NotEqual.cc
new file mode 100644 (file)
index 0000000..6a3fec1
--- /dev/null
@@ -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 "GenModelTest.h"
+
+struct NotEqualVariationParam
+{
+  TestCaseData tcd;
+  circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+  const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class NotEqualVariation : public GenModelTest,
+                          public ::testing::WithParamInterface<NotEqualVariationParam>
+{
+};
+
+// Input shape:
+//   Base: {1, 2, 2, 1}
+//   Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, NotEqualVariation,
+                         ::testing::Values(
+                           // Float type
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<float>({0.1, 0.3, 0.5, 0.7})
+                                                    .addInput<float>({0.1, 0.2, 0.3, 0.4})
+                                                    .addOutput<bool>({false, true, true, true})},
+                           // Float type - broadcast
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<float>({0.1, 0.3, 0.5, 0.7})
+                                                    .addInput<float>({0.3})
+                                                    .addOutput<bool>({true, false, true, true})},
+                           // Int32 type
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<int32_t>({1, 3, 5, 7})
+                                                    .addInput<int32_t>({1, 2, 3, 4})
+                                                    .addOutput<bool>({false, true, true, true}),
+                                                  circle::TensorType::TensorType_INT32},
+                           // Int32 type - broadcast
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<int32_t>({1, 3, 5, 7})
+                                                    .addInput<int32_t>({5})
+                                                    .addOutput<bool>({true, true, false, true}),
+                                                  circle::TensorType::TensorType_INT32},
+                           // Int64 type
+                           // NYI: acl backend
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<int64_t>({1, 3, 5, 7})
+                                                    .addInput<int64_t>({1, 2, 3, 4})
+                                                    .addOutput<bool>({false, true, true, true}),
+                                                  circle::TensorType::TensorType_INT64,
+                                                  {"cpu"}},
+                           // Int64 type - broadcast
+                           // NYI: acl backend
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<int64_t>({1, 3, 5, 7})
+                                                    .addInput<int64_t>({1})
+                                                    .addOutput<bool>({false, true, true, true}),
+                                                  circle::TensorType::TensorType_INT64,
+                                                  {"cpu"}},
+                           // Bool type
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<bool>({false, false, true, true})
+                                                    .addInput<bool>({false, true, false, true})
+                                                    .addOutput<bool>({false, true, true, false}),
+                                                  circle::TensorType::TensorType_BOOL},
+                           // Bool type - broadcast
+                           NotEqualVariationParam{TestCaseData{}
+                                                    .addInput<bool>({false, false, true, true})
+                                                    .addInput<bool>({false})
+                                                    .addOutput<bool>({false, false, true, true}),
+                                                  circle::TensorType::TensorType_BOOL}
+
+                           ));
+
+TEST_P(NotEqualVariation, Test)
+{
+  auto &param = GetParam();
+
+  auto lhs_data = param.tcd.inputs.at(0);
+  auto rhs_data = param.tcd.inputs.at(1);
+
+  bool broadcast_lhs = false;
+  bool broadcast_rhs = false;
+  if (lhs_data.size() != rhs_data.size())
+  {
+    if (lhs_data.size() < rhs_data.size())
+      broadcast_lhs = true;
+    else
+      broadcast_rhs = true;
+  }
+
+  CircleGen cgen;
+  const auto output_type = circle::TensorType::TensorType_BOOL;
+
+  int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+                          : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+  int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+  cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->addTestCase(param.tcd);
+  _context->setBackends(param.backends);
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_NotEqual_DifferentType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+  cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_NotEqual_InvalidType)
+{
+  CircleGen cgen;
+  int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+  int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+  cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+  cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+  _context = std::make_unique<GenModelTestContext>(cgen.finish());
+  _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+  _context->expectFailModelLoad();
+
+  SUCCEED();
+}
index f90018e..e4b9774 100644 (file)
@@ -29,14 +29,13 @@ void RawFormatter::loadInputs(const std::string &filename, std::vector<Allocatio
   uint32_t num_inputs;
   NNPR_ENSURE_STATUS(nnfw_input_size(session_, &num_inputs));
 
-  // TODO: Support multiple inputs
-  // Option 1. Get comman-separated input file list like --load:raw in.0,in.1,in.2
-  // Option 2. Get prefix --load:raw out
-  //           Internally access out.0, out.1, out.2, ... out.{N} where N is determined by api.
-  if (num_inputs != 1)
-  {
-    throw std::runtime_error("Only 1 input is supported for raw input");
-  }
+  // Support multiple inputs
+  // Option 1: Get comman-separated input file list like --load:raw a,b,c
+  // Option 2: Get prefix --load:raw in
+  //           Internally access in.0, in.1, in.2, ... in.{N-1} where N is determined by nnfw info
+  //           query api.
+  //
+  // Currently Option 2 is implemented.
   try
   {
     for (uint32_t i = 0; i < num_inputs; ++i)
@@ -48,11 +47,12 @@ void RawFormatter::loadInputs(const std::string &filename, std::vector<Allocatio
       auto bufsz = bufsize_for(&ti);
       inputs[i].alloc(bufsz);
 
-      std::ifstream file(filename, std::ios::ate | std::ios::binary);
+      std::ifstream file(filename + "." + std::to_string(i), std::ios::ate | std::ios::binary);
       auto filesz = file.tellg();
       if (bufsz != filesz)
       {
-        throw std::runtime_error("Input Size does not match: " + std::to_string(bufsz) +
+        throw std::runtime_error("Input " + std::to_string(i) +
+                                 " size does not match: " + std::to_string(bufsz) +
                                  " expected, but " + std::to_string(filesz) + " provided.");
       }
       file.seekg(0, std::ios::beg);
@@ -74,12 +74,6 @@ void RawFormatter::dumpOutputs(const std::string &filename, std::vector<Allocati
 {
   uint32_t num_outputs;
   NNPR_ENSURE_STATUS(nnfw_output_size(session_, &num_outputs));
-  // TODO: Support multiple outputs
-  // Available options are same.
-  if (num_outputs != 1)
-  {
-    throw std::runtime_error("Only 1 output is supported for raw input");
-  }
   try
   {
     for (uint32_t i = 0; i < num_outputs; i++)