summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/bcq-tools/CMakeLists.txt2
-rw-r--r--compiler/bcq-tools/README.md64
-rw-r--r--compiler/bcq-tools/generate_bcq_metadata219
-rw-r--r--compiler/bcq-tools/generate_bcq_output_arrays257
-rw-r--r--compiler/bcq-tools/preserve_bcq_info116
-rw-r--r--compiler/circle-tensordump/src/Dump.cpp4
-rw-r--r--compiler/circle2circle-dredd-recipe-test/test.lst4
-rw-r--r--compiler/circle2circle/src/Circle2Circle.cpp73
-rw-r--r--compiler/circledump/src/Dump.cpp126
-rw-r--r--compiler/circledump/src/OpPrinter.cpp22
-rw-r--r--compiler/common-artifacts/exclude.lst26
-rw-r--r--compiler/loco/src/IR/Nodes.test.cpp4
-rw-r--r--compiler/luci-interpreter/src/core/KernelParams.h22
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.cpp55
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.h1
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.test.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/ArgMax.test.cpp9
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.cpp37
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.h1
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp91
-rw-r--r--compiler/luci-interpreter/src/kernels/BinaryOpCommon.h73
-rw-r--r--compiler/luci-interpreter/src/kernels/CMakeLists.txt63
-rw-r--r--compiler/luci-interpreter/src/kernels/Concatenation.cpp10
-rw-r--r--compiler/luci-interpreter/src/kernels/Concatenation.test.cpp94
-rw-r--r--compiler/luci-interpreter/src/kernels/Conv2D.test.cpp61
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthToSpace.cpp24
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp45
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp14
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp234
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.cpp128
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.test.cpp150
-rw-r--r--compiler/luci-interpreter/src/kernels/Elu.cpp2
-rw-r--r--compiler/luci-interpreter/src/kernels/Elu.test.cpp21
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.test.cpp187
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.cpp57
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.test.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.cpp85
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp135
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.cpp78
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.h1
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp145
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.cpp116
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/If.cpp13
-rw-r--r--compiler/luci-interpreter/src/kernels/If.test.cpp34
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Normalize.cpp13
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp82
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Pool2D.cpp6
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp69
-rw-r--r--compiler/luci-interpreter/src/kernels/LeakyRelu.cpp2
-rw-r--r--compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp86
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp6
-rw-r--r--compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp41
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.cpp91
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp111
-rw-r--r--compiler/luci-interpreter/src/kernels/Logistic.cpp4
-rw-r--r--compiler/luci-interpreter/src/kernels/Logistic.test.cpp111
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.cpp38
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.h1
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp46
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.test.cpp67
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.cpp81
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.h1
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.test.cpp54
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.test.cpp67
-rw-r--r--compiler/luci-interpreter/src/kernels/Mul.test.cpp6
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.test.cpp187
-rw-r--r--compiler/luci-interpreter/src/kernels/Pad.test.cpp14
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.cpp78
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.test.cpp101
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.cpp153
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.h54
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.test.cpp246
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.cpp114
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.h51
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.test.cpp152
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.cpp88
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.h50
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.test.cpp135
-rw-r--r--compiler/luci-interpreter/src/kernels/Reshape.test.cpp6
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp75
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp231
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp76
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp211
-rw-r--r--compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp7
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.cpp34
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.h3
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.test.cpp74
-rw-r--r--compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp6
-rw-r--r--compiler/luci-interpreter/src/kernels/Split.test.cpp51
-rw-r--r--compiler/luci-interpreter/src/kernels/Sqrt.test.cpp7
-rw-r--r--compiler/luci-interpreter/src/kernels/Squeeze.test.cpp13
-rw-r--r--compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp34
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.cpp139
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.test.cpp180
-rw-r--r--compiler/luci-interpreter/src/kernels/Tanh.test.cpp14
-rw-r--r--compiler/luci-interpreter/src/kernels/TestUtils.cpp24
-rw-r--r--compiler/luci-interpreter/src/kernels/TestUtils.h75
-rw-r--r--compiler/luci-interpreter/src/kernels/Transpose.test.cpp33
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.cpp110
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.h2
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp90
-rw-r--r--compiler/luci-interpreter/src/kernels/Utils.cpp9
-rw-r--r--compiler/luci-interpreter/src/kernels/Utils.h14
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.cpp242
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.h19
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp344
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.cpp61
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.h4
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.cpp18
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.cpp39
-rw-r--r--compiler/luci/import/include/luci/Import/CircleReader.h1
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes.h2
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h37
-rw-r--r--compiler/luci/import/src/CircleReader.cpp77
-rw-r--r--compiler/luci/import/src/GraphBuilderRegistry.cpp4
-rw-r--r--compiler/luci/import/src/Nodes/CircleConst.cpp11
-rw-r--r--compiler/luci/import/src/Nodes/CircleDequantize.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp83
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h4
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeDecl.h8
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.h2
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.lst2
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h115
-rw-r--r--compiler/luci/lang/include/luci/IR/SparsityParam.h233
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp94
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp2
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSum.test.cpp2
-rw-r--r--compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp100
-rw-r--r--compiler/luci/logex/src/FormattedGraph.cpp67
-rw-r--r--compiler/luci/pass/include/luci/CircleOptimizer.h15
-rw-r--r--compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h38
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h69
-rw-r--r--compiler/luci/pass/src/CircleOptimizer.cpp67
-rw-r--r--compiler/luci/pass/src/FoldDequantizePass.cpp206
-rw-r--r--compiler/luci/pass/src/FuseAddWithTConvPass.cpp120
-rw-r--r--compiler/luci/pass/src/FuseBCQPass.cpp560
-rw-r--r--compiler/luci/pass/src/FuseBatchNormWithTConv.cpp20
-rw-r--r--compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp372
-rw-r--r--compiler/luci/pass/src/QuantizationUtils.cpp60
-rw-r--r--compiler/luci/pass/src/QuantizationUtils.h10
-rw-r--r--compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp30
-rw-r--r--compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp294
-rw-r--r--compiler/luci/pass/src/RequantizePass.cpp4
-rw-r--r--compiler/luci/pass/src/Sparsifier.cpp229
-rw-r--r--compiler/luci/pass/src/Sparsifier.h87
-rw-r--r--compiler/luci/pass/src/SparsifyTensorPass.cpp123
-rw-r--r--compiler/luci/service/src/CircleShapeInferenceRule.cpp27
-rw-r--r--compiler/luci/service/src/CircleTypeInferenceRule.cpp7
-rw-r--r--compiler/luci/service/src/Validate.cpp5
-rw-r--r--compiler/luci/tests/test.lst6
-rw-r--r--compiler/mio-tf/CMakeLists.txt2
-rw-r--r--compiler/one-cmds/CMakeLists.txt6
-rw-r--r--compiler/one-cmds/how-to-prepare-virtualenv.txt23
-rw-r--r--compiler/one-cmds/how-to-use-one-commands.txt4
-rw-r--r--compiler/one-cmds/one-import-bcq118
-rw-r--r--compiler/one-cmds/one-import-tf68
-rw-r--r--compiler/one-cmds/one-import-tflite24
-rw-r--r--compiler/one-cmds/one-optimize20
-rw-r--r--compiler/one-cmds/one-pack24
-rw-r--r--compiler/one-cmds/one-prepare-venv4
-rw-r--r--compiler/one-cmds/one-quantize95
-rw-r--r--compiler/one-cmds/tests/CMakeLists.txt49
-rw-r--r--compiler/one-cmds/tests/README.txt27
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_001.test45
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_001.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_002.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_003.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_004.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_005.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_006.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_007.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_008.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_009.test48
-rw-r--r--compiler/one-cmds/tests/one-import_001.test44
-rw-r--r--compiler/one-cmds/tests/one-import_neg_001.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_002.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_003.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_004.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_005.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_006.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_007.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_008.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_009.test48
-rw-r--r--compiler/one-cmds/tests/one-import_neg_010.test49
-rw-r--r--compiler/one-cmds/tests/one-optimize_001.test51
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_001.test47
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_002.test47
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_003.test51
-rw-r--r--compiler/one-cmds/tests/one-pack_001.test51
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_001.test43
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_002.test47
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_003.test45
-rw-r--r--compiler/one-cmds/tests/one-quantize_001.test54
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_001.test61
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_002.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_003.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_004.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_005.test50
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_006.test50
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_007.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_008.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_009.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_010.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_011.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_012.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_013.test60
-rw-r--r--compiler/one-cmds/tests/prepare_test_materials.sh64
-rw-r--r--compiler/oneco/CMakeLists.txt2
-rw-r--r--compiler/onnx-tools/CMakeLists.txt21
-rw-r--r--compiler/onnx-tools/README.md65
-rw-r--r--compiler/onnx-tools/onnx-dump.py146
-rw-r--r--compiler/onnx-tools/onnx-ops.py45
-rw-r--r--compiler/onnxkit/CMakeLists.txt2
-rw-r--r--compiler/pota-quantization-value-test/README.md14
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json28
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json28
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json6
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json61
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json34
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json8
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json53
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json76
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json8
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json95
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json13
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json13
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json3
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json58
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/test.lst20
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt1
-rwxr-xr-xcompiler/record-minmax-conversion-test/gen_h5_random_inputs.py5
-rw-r--r--compiler/record-minmax/src/HDF5Importer.h9
-rw-r--r--compiler/souschef/include/souschef/Dims.h1
-rwxr-xr-xcompiler/tf2tfliteV2/tf2tfliteV2.py14
-rw-r--r--compiler/tflchef/core/src/Convert.cpp73
-rw-r--r--compiler/tflchef/core/src/Convert.h5
-rw-r--r--compiler/tflchef/core/src/ModelChef.cpp49
-rw-r--r--compiler/tflchef/core/src/Op/Dequantize.cpp27
-rw-r--r--compiler/tflchef/core/src/Op/Dequantize.h46
-rw-r--r--compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp45
-rw-r--r--compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h53
-rw-r--r--compiler/tflchef/core/src/OpChef.def2
-rw-r--r--compiler/tflchef/core/src/OpChefs.h2
-rw-r--r--compiler/tflchef/proto/tflchef.proto55
-rw-r--r--compiler/tflchef/tflite/src/Convert.cpp37
-rw-r--r--compiler/tflchef/tflite/src/Convert.h2
-rw-r--r--compiler/tflchef/tflite/src/Op/Dequantize.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Dequantize.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/FullyConnected.cpp10
-rw-r--r--compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp66
-rw-r--r--compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h39
-rw-r--r--compiler/tflchef/tflite/src/RecipeChef.cpp84
-rw-r--r--compiler/tflchef/tflite/src/TFliteOpChefs.h2
-rw-r--r--compiler/tflchef/tflite/src/TFliteOpRegistry.h2
-rw-r--r--compiler/tfldump/src/Dump.cpp126
-rw-r--r--compiler/tfldump/src/OpPrinter.cpp22
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions.h1
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp41
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h32
-rw-r--r--compiler/tflite2circle/src/CircleModel.cpp71
-rw-r--r--compiler/tflite2circle/src/DataLookup.cpp15
-rw-r--r--compiler/tflite2circle/src/DataLookup.h3
-rw-r--r--compiler/tflite2circle/src/TFLBuiltinOptions.lst2
-rw-r--r--compiler/vconone/CMakeLists.txt2
479 files changed, 17366 insertions, 1254 deletions
diff --git a/compiler/bcq-tools/CMakeLists.txt b/compiler/bcq-tools/CMakeLists.txt
index fcf01de7d..59b61a538 100644
--- a/compiler/bcq-tools/CMakeLists.txt
+++ b/compiler/bcq-tools/CMakeLists.txt
@@ -1,6 +1,6 @@
set(BCQ_TOOLS_FILES
+ generate_bcq_metadata
generate_bcq_output_arrays
- preserve_bcq_info
)
foreach(BCQ_TOOLS IN ITEMS ${BCQ_TOOLS_FILES})
diff --git a/compiler/bcq-tools/README.md b/compiler/bcq-tools/README.md
index 18b0f4826..0acd0ba00 100644
--- a/compiler/bcq-tools/README.md
+++ b/compiler/bcq-tools/README.md
@@ -2,77 +2,69 @@
This directory includes some tools related with BCQ.
-## preserve_bcq_info
+## generate_bcq_output_arrays
### Purpose
-`preserve_bcq_info` is for preserving constant nodes which include BCQ information.
-When `.pb` file is converted to `.tflite` file by TFlite converter, constant nodes whose values are exactly same are removed and then linked to only one representative node.
-This makes us impossible to know what constant node should be linked to a node which we want to apply BCQ.
-One of the solutions is making all the same constant nodes different by inserting unique values and ignore the newly generated unique values when BCQ fusing is applied.
-`preserve_bcq_info` will generate and insert unique dummy values to the constant nodes whose values are same not to be removed by Tensorflow Lite converter.
-As a result, BCQ information will be preserved.
+To apply BCQ, BCQ information nodes should be designated as model output so that they are alive even after TFLite conversion is finished.
+However, there are so many nodes to designate and sometimes we cannot copy and paste all of them because the string size is too big.
+`generate_bcq_output_arrays` is for generating output_arrays, which include BCQ information nodes.
### How to use
```bash
-preserve_bcq_info \
+generate_bcq_output_arrays \
--input_path /path/to/original_model.pb \
---output_path /path/to/preserved_model.pb
+--output_path /path/to/output_arrays.txt
```
### How it works
-If we add unique dummy value at the end of each constant nodes, all the constant nodes would be different. Following is an example.
-
```
-[Original Constant Nodes]
-const(value=[1, 2, 3], name='const1')
-const(value=[1, 2, 3], name='const2')
-const(value=[1, 2, 3], name='const3')
-
-[After BCQ information preserved]
+[Original BCQ information nodes]
const(value=[1, 2, 3, -1], name='const1')
const(value=[1, 2, 3, -2], name='const2')
const(value=[1, 2, 3, -3], name='const3')
-```
-For dummy values, negative values are used instead of positive values.
-This is because positive valus may be confused with original constant node values.
-For your information, unique dummy value starts from -1 and moves to -2, -3, ..., -N, where N is the number of preserved constant nodes.
+[Generated output_arrays]
+,const1,const2,const3
+```
### Caution
-- Newly generated dummy values should be ignored when the constant nodes are used.
+- Generated output_arrays will be start with comma.
-## generate_bcq_output_arrays
+## generate_bcq_metadata
### Purpose
-To apply BCQ, BCQ information nodes should be designated as model output so that they are alive even after TFLite conversion is finished.
-However, there are so many nodes to designate and sometimes we cannot copy and paste all of them because the string size is too big.
-`generate_bcq_output_arrays` is for generating output_arrays, which include BCQ information nodes.
+`generate_bcq_metadata` is for appending metadata as output of a model which includes BCQ information.
+The appended metadata is used for connecting BCQ related operations and constant nodes.
### How to use
```bash
-generate_bcq_output_arrays \
+generate_bcq_metadata \
--input_path /path/to/original_model.pb \
---output_path /path/to/output_arrays.txt
+--output_path /path/to/metadata_inserted_model.pb
+--output_arrays output1,output2,...,outputN
```
### How it works
+Metadata will be generated as following description.
```
-[Original BCQ information nodes]
-const(value=[1, 2, 3, -1], name='const1')
-const(value=[1, 2, 3, -2], name='const2')
-const(value=[1, 2, 3, -3], name='const3')
-
-[Generated output_arrays]
-,const1,const2,const3
+< Generated Metadata in BCQ version 1 >
+[0] Starting magic number = {-2e9 + 27}
+[1] Version of BCQ = {1}
+[2] The number of original model outputs = {N | N > 0}
+[3] Bundle size = {7, 8}
+[4] Ending magic number = {2e9 - 27}
```
+- BCQ version 1
+ - Two magic numbers, starting and ending magic number, are used for indicating that the model includes BCQ metadata. To decrease value duplication probability, prime number is used and the value is inserted not only at the beginning but also at the end.
+ - The word **bundle** means that a set of BCQ information and BCQ applicable operation. If six BCQ information nodes are used for one operation, the six information nodes and the other one operation are packaged as **bundle**. Then, in this case, the bundle size will be 6 + 1 = 7.
### Caution
-- Generated output_arrays will be start with comma.
+- If there is no BCQ information in original model, any changes will be applied.
diff --git a/compiler/bcq-tools/generate_bcq_metadata b/compiler/bcq-tools/generate_bcq_metadata
new file mode 100644
index 000000000..8405556aa
--- /dev/null
+++ b/compiler/bcq-tools/generate_bcq_metadata
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import numpy as np
+import tensorflow as tf
+
+import argparse
+import sys
+
+ONE_START_MAGICNUM = int(-2e9 + 27)
+ONE_END_MAGICNUM = int(2e9 - 27)
+
+
+def _get_parser():
+ """
+ Returns an ArgumentParser for generating BCQ metadata.
+ """
+ parser = argparse.ArgumentParser(
+ description=("Command line tool to generate metadata of BCQ nodes"))
+
+ # Input and output path.
+ parser.add_argument(
+ "-i",
+ "--input_path",
+ type=str,
+ help="Full filepath of the input file.",
+ required=True)
+ parser.add_argument(
+ "-o",
+ "--output_path",
+ type=str,
+ help="Full filepath of the output file.",
+ required=True)
+ parser.add_argument(
+ "-O",
+ "--output_arrays",
+ type=str,
+ help="Original model output arrays",
+ required=True)
+
+ return parser
+
+
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
+ graph_def.ParseFromString(f.read())
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
+ return graph
+
+
+def generate_metadata_header(original_graph, bcq_version, output_arrays):
+ # Generating metadata starts
+ metadata_values = np.array([ONE_START_MAGICNUM])
+
+ # Append BCQ version
+ metadata_values = np.append(metadata_values, bcq_version)
+
+ # Append original output count
+ output_cnt = output_arrays.count(',') + 1
+ metadata_values = np.append(metadata_values, output_cnt)
+
+ return metadata_values
+
+
+def generate_bcq_metadata_v1(flags):
+ """
+ BCQv1 contains following metadata.
+ - The number of each BCQ information set
+ """
+
+ is_valid = True
+ allowed_info_names = [
+ "bcqinfo_do_w_x", "bcqinfo_alpha", "bcqinfo_packed_binary_code",
+ "bcqinfo_number_of_clusters", "bcqinfo_size_of_clusters",
+ "bcqinfo_qbits_of_clusters", "bcqinfo_dequant_weight"
+ ]
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ prefix_infonames_dict = {}
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ prefix_index = node.name.index("/bcqinfo_")
+ prefix = node.name[:prefix_index]
+ infoname = node.name[prefix_index + 1:]
+
+ if infoname not in allowed_info_names:
+ is_valid = False
+ break
+
+ if prefix not in prefix_infonames_dict:
+ prefix_infonames_dict[prefix] = set()
+
+ prefix_infonames_dict[prefix].add(infoname)
+
+ # All the number of BCQ information should be same
+ num_of_bcqinfo = -1
+ for key in prefix_infonames_dict:
+ infonames = prefix_infonames_dict[key]
+ if num_of_bcqinfo == -1:
+ num_of_bcqinfo = len(infonames)
+ elif num_of_bcqinfo != len(infonames):
+ is_valid = False
+
+ # The number of BCQv1 information should be 6 or 7
+ if num_of_bcqinfo != 6 and num_of_bcqinfo != 7:
+ is_valid = False
+
+ # If BCQ information is invalid, return original model
+ if is_valid == False:
+ return original_graph_def
+
+ new_graph_def = tf.compat.v1.GraphDef()
+ for node in original_graph_def.node:
+ new_node = new_graph_def.node.add()
+ new_node.CopyFrom(node)
+
+ # Generate metadata header
+ metadata_values = generate_metadata_header(original_graph, 1, flags.output_arrays)
+
+ # Append metadata of BCQv1
+ metadata_values = np.append(metadata_values, num_of_bcqinfo + 1)
+
+ # Finish generating metadata
+ metadata_values = np.append(metadata_values, ONE_END_MAGICNUM)
+
+ # Generate metadata tensor
+ metadata_tensor = tf.make_tensor_proto(metadata_values, tf.int32)
+
+ new_node = new_graph_def.node.add()
+ new_node.op = "Const"
+ new_node.name = "one_compiler/bcqinfo_one_metadata"
+ new_node.attr["dtype"].CopyFrom(
+ tf.core.framework.attr_value_pb2.AttrValue(type=tf.int32.as_datatype_enum))
+ new_node.attr["value"].tensor.CopyFrom(metadata_tensor)
+ return new_graph_def
+
+
+def determine_bcq_version(flags):
+ """
+ CAUTION : For now, BCQ has only one version and thus always returns 1 when BCQ
+ information nodes are included. If new BCQ version is introduced,
+ this function must be updated accordingly.
+
+ When BCQ information does not exist, -1 is returned.
+ """
+ bcq_version = -1
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ bcq_version = 1
+ break
+
+ return bcq_version
+
+
+def generate_bcq_metadata(flags):
+ """
+ Basic format of metadata is as following.
+ - Magic number indicating start
+ - Version of BCQ Format
+ - The number of original outputs
+ - Metadata based on each BCQ format
+ - Magic number indicating end
+ """
+ program_version = 1
+ model_version = determine_bcq_version(flags)
+
+ if model_version == 1:
+ result_graph_def = generate_bcq_metadata_v1(flags)
+ elif model_version == -1:
+ # When there is no BCQ information, do nothing
+ result_graph_def = load_graph(flags.input_path)
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
+
+ tf.io.write_graph(result_graph_def, '.', flags.output_path, False)
+
+
+def main():
+ # Parse argument.
+ parser = _get_parser()
+ flags = parser.parse_known_args(args=sys.argv[1:])
+
+ # Generate a new pb file, which BCQ metadata is included.
+ generate_bcq_metadata(flags[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/bcq-tools/generate_bcq_output_arrays b/compiler/bcq-tools/generate_bcq_output_arrays
index 48e8a9373..b71a37410 100644
--- a/compiler/bcq-tools/generate_bcq_output_arrays
+++ b/compiler/bcq-tools/generate_bcq_output_arrays
@@ -1,5 +1,20 @@
#!/usr/bin/env python3
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
import tensorflow as tf
import argparse
@@ -21,61 +36,223 @@ def _get_parser():
help="Full filepath of the input file.",
required=True)
parser.add_argument(
- "-o",
- "--output_path",
+ "-m",
+ "--metadata_path",
+ type=str,
+ help="Full filepath for the file that provides metadata.",
+ required=True)
+ parser.add_argument(
+ "-A",
+ "--output_arrays_path",
type=str,
- help="Full filepath of the output file.",
+ help="Full filepath for the file that provides output arrays",
required=True)
return parser
-def load_graph(frozen_graph_filename):
- """
- Load graph from frozen pb file
- """
- with tf.compat.v1.gfile.GFile(frozen_graph_filename, "rb") as f:
- graph_def = tf.compat.v1.GraphDef()
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
graph_def.ParseFromString(f.read())
- with tf.Graph().as_default() as graph:
- tf.import_graph_def(graph_def, name='')
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
return graph
-def dtype2str(dtype):
- if dtype == "int32":
- return "TF_INT32"
- elif dtype == "int64":
- return "TF_INT64"
- elif dtype == "float32":
- return "TF_FLOAT"
- elif dtype == "bool":
- return "TF_BOOL"
- else:
- raise Exception("Not supported dtype")
+def find_bcq_version(flags):
+ """
+ If BCQ metadata exists, BCQ version is in the second element.
+ Return -1 when the metadata is not found.
+ """
+ graph = load_graph(flags.input_path)
+ graph_def = graph.as_graph_def()
+ for node in graph_def.node:
+ if node.op == "Const" and "one_compiler/bcqinfo_one_metadata" in node.name:
+ metadata_tensor = tf.make_ndarray(node.attr["value"].tensor)
+ return metadata_tensor[1]
+ return -1
+
+
+def print_bcqinfo_output_arrays_v1(flags):
+ """
+ This function generates a file which includes output arrays of BCQ v1
+ information bundles. Each bundle is consisted with one of candidate
+ operations (BCQ may be applied) and BCQ constant nodes related with
+ the operation.
+ """
+ graph = load_graph(flags.input_path)
+ graph_def = graph.as_graph_def()
+ ops = graph.get_operations()
+ # If there is a constant node named PREFIX_1/bcqinfo_alpha,
+ # it is used for applying BCQ to constant node named PREFIX_1.
+ # Collected prefixes will be used for connecting
+ # bcqinfo nodes and user operations of prefix nodes.
+ prefix_set = set()
+ has_dequant_weight = False
+ for op in ops:
+ if op.type == "Const" and "/bcqinfo_" in op.outputs[0].name:
+ # Metadata do not have prefix
+ if "one_compiler/bcqinfo_one_metadata" in op.outputs[0].name:
+ continue
-def print_output_arrays(flags):
- graph_model = load_graph(flags.input_path)
- graph_model_def = graph_model.as_graph_def()
- ops = graph_model.get_operations()
+ prefix_index = op.outputs[0].name.index("/bcqinfo_")
+ prefix = op.outputs[0].name[:prefix_index]
+ prefix_set.add(prefix)
- output_names = [op.outputs[0].name for op in ops
- if op.type == "Const" and "bcqinfo_" in op.outputs[0].name]
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ infoname = op.outputs[0].name[prefix_index + 1:-2]
+ if infoname == "bcqinfo_dequant_weight":
+ has_dequant_weight = True
- output_arrays = ""
- for output_name in output_names:
- output_arrays += ","
+ # Ideal situation is that the user nodes of BCQ applicable constant nodes
+ # are BCQ applicable operations such as MatMul, GatherV2, etc.
+ # However, operations which do not change original values such as
+ # Ideneity or Transpose can exist between them. In view of TensorFlow Lite,
+ # real user nodes of BCQ applicable constant nodes must be found first.
+ # This work is done by BFS search with queue.
- colon_index = output_name.find(":")
- if colon_index == -1:
- output_arrays += output_name
- else:
- output_arrays += output_name[:colon_index]
+ prefix_node_dict = {} # key : prefix / value : list of candidates
+ matmul_node_prefix_dict = {} # key : Name of MatMul node / value : prefix
- f = open(flags.output_path, 'w')
- f.write(output_arrays)
- f.close()
+ queue_prefix = list(prefix_set)
+ queue_nodename = [queue_prefix[idx] + ":0" for idx in range(len(queue_prefix))]
+
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+ if prefix not in prefix_node_dict.keys():
+ prefix_node_dict[prefix] = []
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ if op.type == "MatMul" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ matmul_node_prefix_dict[op.outputs[0].name[:-2]] = prefix
+ elif op.type == "Einsum" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif op.type == "GatherV2" and op.inputs[0].name == nodename:
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif len(op.outputs) == 1:
+ for i in range(len(op.inputs)):
+ if op.inputs[i].name == nodename:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ break
+
+ # When TensorFlow model is converted to TensorFlow Lite model,
+ # more than one operation can be fused as one.
+ # For example, MatMul + BiasAdd + ReLU in TensorFlow can be fused as
+ # one FullyConnected in TensorFlow Lite.
+ # It means that even real user nodes of BCQ applicable constant nodes
+ # in TensorFlow are found, they may be real user nodes in TensorFlow Lite.
+ # Therefore additional candidates of real user nodes should be found either.
+ # Finding additional candidates is done by BFS search with queue.
+
+ fuseop_prefix_dict = {} # key : Candidate operation / Value : prefix
+
+ # These ops can be candidate. However other candidates may exists after these ops.
+ mark_type = ["Add", "AddV2", "BiasAdd", "Reshape", "Transpose"]
+
+ # These ops can be candidate. And no more candidates will be found after these ops.
+ mark_and_stop_type = ["Relu", "Relu6", "Tanh"]
+
+ # These ops cannot be candidates but other candidates may exists after these ops.
+ # NOTE : Some of following ops may be removed from the list but not sure for now.
+ pass_type = [
+ "BatchToSpaceND", "Cast", "DepthToSpace", "ExpandDims", "ResizeBilinear",
+ "ResizeNearestNeighbor", "ScatterNd", "SpaceToBatchND", "SpaceToDepth", "Squeeze",
+ "Identity", "Pack", "Unpack", "Stack"
+ ]
+
+ queue_prefix = list(matmul_node_prefix_dict.values())
+ queue_nodename = [matmul + ":0" for matmul in matmul_node_prefix_dict.keys()]
+
+ visited_nodes = set(queue_nodename)
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ for i in range(len(op.inputs)):
+ if nodename == op.inputs[i].name:
+ if op.type in mark_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ if op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+ elif op.type in mark_and_stop_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ elif op.type in pass_type and op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+
+ # Write the name of metadata node
+ with open(flags.metadata_path, 'w') as f_metadata:
+ f_metadata.write("one_compiler/bcqinfo_one_metadata,")
+
+ # Write all pairs of candidate operations and related BCQ information nodes.
+ with open(flags.output_arrays_path, 'w') as f_arrays:
+ for prefix in prefix_set:
+ for fusable_op in prefix_node_dict[prefix]:
+ f_arrays.write("," + prefix + "/bcqinfo_do_w_x")
+ f_arrays.write("," + prefix + "/bcqinfo_alpha")
+ f_arrays.write("," + prefix + "/bcqinfo_packed_binary_code")
+ f_arrays.write("," + prefix + "/bcqinfo_number_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_size_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_qbits_of_clusters")
+ f_arrays.write("," + fusable_op)
+ if has_dequant_weight:
+ f_arrays.write("," + prefix + "/bcqinfo_dequant_weight")
+ for fuseop in fuseop_prefix_dict.keys():
+ if len(fuseop_prefix_dict[fuseop]) == 1:
+ prefix = fuseop_prefix_dict[fuseop].pop()
+ f_arrays.write("," + prefix + "/bcqinfo_do_w_x")
+ f_arrays.write("," + prefix + "/bcqinfo_alpha")
+ f_arrays.write("," + prefix + "/bcqinfo_packed_binary_code")
+ f_arrays.write("," + prefix + "/bcqinfo_number_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_size_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_qbits_of_clusters")
+ f_arrays.write("," + fuseop)
+ if has_dequant_weight:
+ f_arrays.write("," + prefix + "/bcqinfo_dequant_weight")
+
+
+def print_bcq_output_arrays(flags):
+ program_version = 1
+ model_version = find_bcq_version(flags)
+
+ if model_version == 1:
+ print_bcqinfo_output_arrays_v1(flags)
+ elif model_version == -1:
+ # When BCQ information not found, print nothing.
+ f_metadata = open(flags.metadata_path, 'w')
+ f_arrays = open(flags.output_arrays_path, 'w')
+ f_metadata.close()
+ f_arrays.close()
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
def main():
@@ -83,7 +260,7 @@ def main():
parser = _get_parser()
flags = parser.parse_known_args(args=sys.argv[1:])
- print_output_arrays(flags[0])
+ print_bcq_output_arrays(flags[0])
if __name__ == "__main__":
diff --git a/compiler/bcq-tools/preserve_bcq_info b/compiler/bcq-tools/preserve_bcq_info
deleted file mode 100644
index 2ede8d4d0..000000000
--- a/compiler/bcq-tools/preserve_bcq_info
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env python3
-
-import tensorflow as tf
-import numpy as np
-
-import argparse
-import sys
-
-
-def _get_parser():
- """
- Returns an ArgumentParser for preserving BCQ information.
- """
- parser = argparse.ArgumentParser(
- description=("Command line tool to preserve BCQ information"))
-
- # Input and output path.
- parser.add_argument(
- "-i",
- "--input_path",
- type=str,
- help="Full filepath of the input file.",
- required=True)
- parser.add_argument(
- "-o",
- "--output_path",
- type=str,
- help="Full filepath of the output file.",
- required=True)
-
- return parser
-
-
-def load_graph(frozen_graph_filename):
- """
- Load graph from frozen pb file
- """
- with tf.compat.v1.gfile.GFile(frozen_graph_filename, "rb") as f:
- graph_def = tf.compat.v1.GraphDef()
- graph_def.ParseFromString(f.read())
- with tf.Graph().as_default() as graph:
- tf.import_graph_def(graph_def, name='')
- return graph
-
-
-def preserve_bcq_info(flags):
- """
- Generate unique dummy value from -1 to -N.
-
- We use negative values to preserve BCQ information because
- positive values may cause some confusion with real BCQ information values.
- """
-
- class UniqueValueGen:
- def __init__(self):
- self.unique_value = -1
-
- def gen(self):
- val = self.unique_value
- self.unique_value = val - 1
- return val
-
- unique_value = UniqueValueGen()
-
- original_graph_model = load_graph(flags.input_path)
- original_graph_model_def = original_graph_model.as_graph_def()
-
- new_graph = tf.compat.v1.GraphDef()
- substitution_dict = {}
-
- DT_INT32 = None # Just for copying DT_INT32 attribute value
-
- for node in original_graph_model_def.node:
- if node.op == "Const":
- # Because bcqinfo_do_w_x is BOOL type, we cannot add dummy value at the end.
- # Therefore we should convert the type to INT32 type.
- if "/bcqinfo_do_w_x" in node.name:
- original_tensor = tf.make_ndarray(node.attr["value"].tensor)
- substitution_dict[node.name] = tf.make_tensor_proto(
- [int(original_tensor[0]), unique_value.gen()], tf.int32)
-
- preserved_bcqinfo_list = ["/bcqinfo_number_of_clusters", "/bcqinfo_size_of_clusters",
- "/bcqinfo_qbits_of_clusters"]
-
- if any(name in node.name for name in preserved_bcqinfo_list):
- original_tensor = tf.make_ndarray(
- node.attr["value"].tensor) # variable name change
- substitution_dict[node.name] = tf.make_tensor_proto(
- np.append(original_tensor, unique_value.gen()), tf.int32)
- DT_INT32 = node.attr["dtype"]
-
- for node in original_graph_model_def.node:
- if node.name in substitution_dict:
- new_node = new_graph.node.add()
- new_node.op = "Const"
- new_node.name = node.name
- new_node.attr["dtype"].CopyFrom(DT_INT32)
- new_node.attr["value"].tensor.CopyFrom(substitution_dict[node.name])
- else:
- new_node = new_graph.node.add()
- new_node.CopyFrom(node)
-
- tf.io.write_graph(new_graph, '.', flags.output_path, False)
-
-
-def main():
- # Parse argument.
- parser = _get_parser()
- flags = parser.parse_known_args(args=sys.argv[1:])
-
- # Generate a new pb file, which BCQ information is preserved.
- preserve_bcq_info(flags[0])
-
-
-if __name__ == "__main__":
- main()
diff --git a/compiler/circle-tensordump/src/Dump.cpp b/compiler/circle-tensordump/src/Dump.cpp
index a8d32564f..dee2f3620 100644
--- a/compiler/circle-tensordump/src/Dump.cpp
+++ b/compiler/circle-tensordump/src/Dump.cpp
@@ -182,6 +182,10 @@ H5::PredType hdf5_dtype_cast(const circle::TensorType &circle_type)
{
return H5::PredType::NATIVE_UINT8;
}
+ case circle::TensorType_INT16:
+ {
+ return H5::PredType::NATIVE_INT16;
+ }
case circle::TensorType_INT32:
{
return H5::PredType::NATIVE_INT32;
diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst
index 302c3a796..08fc4ec00 100644
--- a/compiler/circle2circle-dredd-recipe-test/test.lst
+++ b/compiler/circle2circle-dredd-recipe-test/test.lst
@@ -10,7 +10,11 @@
## TFLITE RECIPE
+Add(Net_TConv_Add_000 PASS fuse_add_with_tconv)
+Add(Net_TConv_Add_001 PASS fuse_add_with_tconv)
+Add(Net_TConv_Add_002 PASS fuse_add_with_tconv)
Add(Net_TConv_BN_000 PASS fuse_batchnorm_with_tconv)
+Add(Net_TConv_BN_001 PASS fuse_batchnorm_with_tconv)
Add(Net_InstanceNorm_001 PASS fuse_instnorm)
Add(Net_InstanceNorm_002 PASS fuse_instnorm)
Add(BatchMatMulV2_000 PASS resolve_customop_batchmatmul)
diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp
index 39ceade3a..940d01e36 100644
--- a/compiler/circle2circle/src/Circle2Circle.cpp
+++ b/compiler/circle2circle/src/Circle2Circle.cpp
@@ -60,6 +60,18 @@ int entry(int argc, char **argv)
arser.add_argument("--all").nargs(0).required(false).default_value(false).help(
"Enable all optimize options");
+ arser.add_argument("--fold_dequantize")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fold dequantize op");
+
+ 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_tconv")
.nargs(0)
.required(false)
@@ -111,6 +123,41 @@ int entry(int argc, char **argv)
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");
+ // 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_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_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");
+
try
{
arser.parse(argc, argv);
@@ -130,6 +177,10 @@ int entry(int argc, char **argv)
options->enable(Algorithms::ResolveCustomOpBatchMatMul);
options->enable(Algorithms::ResolveCustomOpMatMul);
}
+ if (arser.get<bool>("--fold_dequantize"))
+ options->enable(Algorithms::FoldDequantize);
+ if (arser.get<bool>("--fuse_add_with_tconv"))
+ options->enable(Algorithms::FuseAddWithTConv);
if (arser.get<bool>("--fuse_batchnorm_with_tconv"))
options->enable(Algorithms::FuseBatchNormWithTConv);
if (arser.get<bool>("--fuse_bcq"))
@@ -151,6 +202,27 @@ int entry(int argc, char **argv)
std::string input_path = arser.get<std::string>("input");
std::string output_path = arser.get<std::string>("output");
+ if (arser["--sparsify_tensor"])
+ {
+ options->enable(Algorithms::SparsifyTensorPass);
+ options->param(AlgorithmParameters::Sparsify_tensor_name,
+ arser.get<std::string>("--sparsify_tensor"));
+ options->param(AlgorithmParameters::Sparsify_traversal_order,
+ arser.get<std::string>("--sparsify_traversal_order"));
+ options->param(AlgorithmParameters::Sparsify_format,
+ arser.get<std::string>("--sparsify_format"));
+ if (arser["--sparsify_block_size"])
+ options->param(AlgorithmParameters::Sparsify_block_size,
+ arser.get<std::string>("--sparsify_block_size"));
+ else
+ {
+ std::cerr << "ERROR: Block size not provided" << std::endl;
+ return 255;
+ }
+ options->param(AlgorithmParameters::Sparsify_block_map,
+ arser.get<std::string>("--sparsify_block_map"));
+ }
+
// Load model from the file
foder::FileLoader file_loader{input_path};
std::vector<char> model_data;
@@ -189,6 +261,7 @@ int entry(int argc, char **argv)
// call luci optimizations
optimizer.optimize(graph);
+ optimizer.sparsify(graph);
if (!luci::validate(graph))
{
diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp
index c695b0721..f8e2d61f3 100644
--- a/compiler/circledump/src/Dump.cpp
+++ b/compiler/circledump/src/Dump.cpp
@@ -73,10 +73,34 @@ std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
return os;
}
-template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
+template <typename T>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect, uint32_t size)
+{
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << fbvect->Get(q);
+ }
+}
+
+template <>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<uint8_t> *fbvect, uint32_t size)
+{
+ assert(fbvect);
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << static_cast<uint32_t>(fbvect->Get(q));
+ }
+}
+
+template <typename T>
+std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
{
if (fbvect == nullptr)
- return;
+ return os;
bool ellipsis = (fbvect->size() > 4);
auto limit_size = ellipsis ? 4 : fbvect->size();
@@ -85,22 +109,14 @@ template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vect
{
os << "(" << fbvect->size() << ") ";
}
- for (uint32_t q = 0; q < limit_size; q++)
- {
- if (q)
- os << ", ";
- os << fbvect->Get(q);
- }
+
+ dump_fbvect(os, fbvect, limit_size);
+
if (ellipsis)
{
os << " ... ";
}
-}
-template <typename T>
-std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
-{
- dump_fbvect(os, fbvect);
return os;
}
@@ -182,8 +198,90 @@ void dump_sub_graph(std::ostream &os, circleread::Reader &reader)
os << std::endl;
}
}
+
+ if (const auto &s_params = tensor->sparsity())
+ {
+ std::string strsparsity = " Sparsity: ";
+ std::string strsindent(strsparsity.size(), ' ');
+ os << strsparsity;
+
+ if (s_params->traversal_order())
+ {
+ os << "traversal_order(" << s_params->traversal_order() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (s_params->block_map())
+ {
+ os << "block_map(" << s_params->block_map() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (const auto &dim_metadata = s_params->dim_metadata())
+ {
+ uint32_t idx = 0;
+ for (const auto &dm : *dim_metadata)
+ {
+ std::string strdm = "dim_metadata[" + std::to_string(idx++) + "]: ";
+ std::string strdm_indent = strsindent + std::string(strdm.size(), ' ');
+ os << strdm;
+
+ os << "format(" << circle::EnumNameDimensionType(dm->format()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "dense_size(" << dm->dense_size() << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments_type("
+ << circle::EnumNameSparseIndexVector(dm->array_segments_type()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments(";
+ switch (dm->array_segments_type())
+ {
+ case circle::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case circle::SparseIndexVector_Int32Vector:
+ os << dm->array_segments_as_Int32Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint16Vector:
+ os << dm->array_segments_as_Uint16Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint8Vector:
+ os << dm->array_segments_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_segments");
+ }
+ os << ")" << std::endl << strdm_indent;
+
+ os << "array_indices_type(" << circle::EnumNameSparseIndexVector(dm->array_indices_type())
+ << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_indices(";
+ switch (dm->array_indices_type())
+ {
+ case circle::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case circle::SparseIndexVector_Int32Vector:
+ os << dm->array_indices_as_Int32Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint16Vector:
+ os << dm->array_indices_as_Uint16Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint8Vector:
+ os << dm->array_indices_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_indices");
+ }
+ os << ")" << std::endl << strsindent;
+ }
+ }
+ }
+ os << std::endl;
}
- os << std::endl;
// dump operators
os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl;
diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp
index a0a063e79..ef22baaee 100644
--- a/compiler/circledump/src/OpPrinter.cpp
+++ b/compiler/circledump/src/OpPrinter.cpp
@@ -593,6 +593,25 @@ public:
}
};
+class UnidirectionalSequenceLSTMPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << "cell_clip(" << params->cell_clip() << ") ";
+ os << "proj_clip(" << params->proj_clip() << ") ";
+ os << "time_major(" << params->time_major() << ") ";
+ os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
class UniquePrinter : public OpPrinter
{
public:
@@ -707,6 +726,7 @@ OpPrinterRegistry::OpPrinterRegistry()
_op_map[circle::BuiltinOperator_CONV_2D] = make_unique<Conv2DPrinter>();
_op_map[circle::BuiltinOperator_DEPTH_TO_SPACE] = make_unique<DepthToSpacePrinter>();
_op_map[circle::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>();
+ // There is no Option for DEQUANTIZE
_op_map[circle::BuiltinOperator_DIV] = make_unique<DivPrinter>();
// There is no Option for FLOOR
// There is no Option for FLOOR_MOD
@@ -761,6 +781,8 @@ OpPrinterRegistry::OpPrinterRegistry()
_op_map[circle::BuiltinOperator_SUM] = make_unique<ReducerPrinter>();
_op_map[circle::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>();
// There is no Option for TOPK_V2
+ _op_map[circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] =
+ make_unique<UnidirectionalSequenceLSTMPrinter>();
_op_map[circle::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>();
_op_map[circle::BuiltinOperator_WHILE] = make_unique<WhilePrinter>();
_op_map[circle::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>();
diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst
index 886f607cf..e64859f06 100644
--- a/compiler/common-artifacts/exclude.lst
+++ b/compiler/common-artifacts/exclude.lst
@@ -5,12 +5,7 @@
#[[ optimize : Exclude from circle optimization(circle2circle) ]]
## TensorFlowLiteRecipes
-optimize(Unique_000)
-optimize(Unique_001)
-optimize(Unique_002)
-optimize(Unique_003)
-optimize(Unique_U8_000)
-optimize(Unique_U8_001)
+optimize(UnidirectionalSequenceLSTM_001) # This recipe contains is_variable Tensor
## CircleRecipes
@@ -45,13 +40,12 @@ tcgenerate(Conv2D_003) # runtime doesn't support dilation
tcgenerate(Conv2D_U8_000)
tcgenerate(Conv2D_U8_001)
tcgenerate(Cos_000)
-tcgenerate(DepthToSpace_000)
tcgenerate(DepthwiseConv2D_001) # runtime doesn't support dilation
tcgenerate(DepthwiseConv2D_003) # runtime doesn't support dilation
tcgenerate(DepthwiseConv2D_U8_000)
tcgenerate(DepthwiseConv2D_U8_001) # luci-interpreter doesn't support channel-wise quantization yet
+tcgenerate(Dequantize_000) # runtime and luci-interpreter doesn't support Dequantize op yet
tcgenerate(Div_000)
-tcgenerate(ELU_000)
tcgenerate(Equal_000)
tcgenerate(Exp_000)
tcgenerate(ExpandDims_000)
@@ -74,13 +68,9 @@ tcgenerate(Greater_000)
tcgenerate(GreaterEqual_000)
tcgenerate(If_000)
tcgenerate(If_001)
-tcgenerate(L2Normalize_000) # runtime doesn't support
-tcgenerate(L2Pool2D_000) # runtime doesn't support
tcgenerate(L2Pool2D_U8_000)
-tcgenerate(LeakyRelu_000) # runtime doesn't support
tcgenerate(Less_000)
tcgenerate(LessEqual_000)
-tcgenerate(LocalResponseNormalization_000) # runtime doesn't support
tcgenerate(Log_000)
tcgenerate(LogicalAnd_000)
tcgenerate(LogicalNot_000)
@@ -115,7 +105,6 @@ tcgenerate(Pack_U8_000)
tcgenerate(Pad_U8_000)
tcgenerate(PadV2_000)
tcgenerate(Pow_000)
-tcgenerate(PRelu_000)
tcgenerate(Range_000)
tcgenerate(Rank_000)
tcgenerate(ReduceAny_000)
@@ -139,7 +128,6 @@ tcgenerate(ResizeNearestNeighbor_000)
tcgenerate(ReverseSequence_000)
tcgenerate(ReverseV2_000)
tcgenerate(Round_000)
-tcgenerate(Rsqrt_000)
tcgenerate(ScatterNd_000)
tcgenerate(SegmentSum_000)
tcgenerate(Select_000)
@@ -150,32 +138,26 @@ tcgenerate(SelectV2_001)
tcgenerate(SelectV2_002)
tcgenerate(Shape_000)
tcgenerate(Sin_000)
-tcgenerate(Slice_000)
tcgenerate(Softmax_U8_000)
tcgenerate(SpaceToBatchND_000)
tcgenerate(SpaceToBatchND_001)
tcgenerate(SpaceToBatchND_002)
tcgenerate(SpaceToBatchND_003)
-tcgenerate(SpaceToDepth_000)
tcgenerate(SparseToDense_000)
tcgenerate(SplitV_000)
-tcgenerate(Sqrt_000)
tcgenerate(Square_000)
tcgenerate(SquaredDifference_000)
-tcgenerate(Squeeze_000)
-tcgenerate(StridedSlice_000)
-tcgenerate(StridedSlice_001)
-tcgenerate(StridedSlice_002)
tcgenerate(Sub_000)
tcgenerate(Sub_001)
tcgenerate(Sub_U8_000)
tcgenerate(Sum_000)
tcgenerate(Sum_001)
-tcgenerate(Tanh_000)
tcgenerate(Tile_000)
tcgenerate(Tile_U8_000)
tcgenerate(TopKV2_000)
tcgenerate(TopKV2_001)
+tcgenerate(UnidirectionalSequenceLSTM_000) # runtime and luci-interpreter doesn't support UnidirectionalSequenceLSTM op yet
+tcgenerate(UnidirectionalSequenceLSTM_001) # runtime and luci-interpreter doesn't support UnidirectionalSequenceLSTM op yet
tcgenerate(Unique_000)
tcgenerate(Unique_001)
tcgenerate(Unique_002)
diff --git a/compiler/loco/src/IR/Nodes.test.cpp b/compiler/loco/src/IR/Nodes.test.cpp
index 0b2210357..bd1c74253 100644
--- a/compiler/loco/src/IR/Nodes.test.cpp
+++ b/compiler/loco/src/IR/Nodes.test.cpp
@@ -523,11 +523,11 @@ TEST(TensorBroadcastTest, mapping)
{
loco::TensorBroadcast tensor_broadcast_node;
- ASSERT_EQ(false, tensor_broadcast_node.mapping()->defined(0));
+ ASSERT_FALSE(tensor_broadcast_node.mapping()->defined(0));
tensor_broadcast_node.mapping()->dim(0) = 3;
- ASSERT_EQ(true, tensor_broadcast_node.mapping()->defined(0));
+ ASSERT_TRUE(tensor_broadcast_node.mapping()->defined(0));
ASSERT_EQ(3, tensor_broadcast_node.mapping()->dim(0));
}
diff --git a/compiler/luci-interpreter/src/core/KernelParams.h b/compiler/luci-interpreter/src/core/KernelParams.h
index 65d119761..06926cdc1 100644
--- a/compiler/luci-interpreter/src/core/KernelParams.h
+++ b/compiler/luci-interpreter/src/core/KernelParams.h
@@ -72,6 +72,11 @@ struct DepthwiseConv2DParams
Activation activation;
};
+struct DivParams
+{
+ Activation activation;
+};
+
struct FullyConnectedParams
{
Activation activation;
@@ -115,6 +120,23 @@ struct ReducerParams
bool keep_dims;
};
+struct ResizeBilinearParams
+{
+ bool align_corners;
+ bool half_pixel_centers;
+};
+
+struct ResizeNearestNeighborParams
+{
+ bool align_corners;
+ bool half_pixel_centers;
+};
+
+struct SubParams
+{
+ Activation activation;
+};
+
struct SpaceToDepthParams
{
int block_size;
diff --git a/compiler/luci-interpreter/src/kernels/Add.cpp b/compiler/luci-interpreter/src/kernels/Add.cpp
index 9ed155e94..8d119d516 100644
--- a/compiler/luci-interpreter/src/kernels/Add.cpp
+++ b/compiler/luci-interpreter/src/kernels/Add.cpp
@@ -17,6 +17,7 @@
#include "kernels/Add.h"
+#include "kernels/BinaryOpCommon.h"
#include "kernels/Utils.h"
#include <tensorflow/lite/kernels/internal/reference/add.h>
@@ -36,10 +37,13 @@ Add::Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddPa
void Add::configure()
{
- if (input1()->element_type() != input2()->element_type())
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ if (input1()->element_type() == DataType::S16)
{
- throw std::runtime_error("Input Tensor Data Type Mismatch.");
+ LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 &&
+ output()->zero_point() == 0);
}
+
output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
}
@@ -53,6 +57,9 @@ void Add::execute() const
case DataType::U8:
evalQuantized();
break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
default:
throw std::runtime_error("Unsupported type.");
}
@@ -140,5 +147,49 @@ void Add::evalQuantized() const
}
}
+void Add::evalQuantizedS16() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ constexpr int left_shift = 12;
+ const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale);
+ const double real_input1_multiplier = input1_scale / twice_max_input_scale;
+ const double real_input2_multiplier = input2_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{};
+ int input1_shift{}, input2_shift{}, output_shift{};
+ quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift);
+ quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift);
+ quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ auto fn = [input1_multiplier, input1_shift, //
+ input2_multiplier, input2_shift, //
+ output_multiplier, output_shift, //
+ activation_min, activation_max](int16_t input1_val, int16_t input2_val) {
+ const int32_t shifted_input1_val = static_cast<int32_t>(input1_val) << left_shift;
+ const int32_t shifted_input2_val = static_cast<int32_t>(input2_val) << left_shift;
+ const int32_t scaled_input1_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ shifted_input1_val, input1_multiplier, input1_shift);
+ const int32_t scaled_input2_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ shifted_input2_val, input2_multiplier, input2_shift);
+ const int32_t raw_sum = scaled_input1_val + scaled_input2_val;
+ const int32_t raw_output = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ raw_sum, output_multiplier, output_shift);
+ const int32_t clamped_output = std::min(activation_max, std::max(activation_min, raw_output));
+ return static_cast<int16_t>(clamped_output);
+ };
+
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<int16_t>(input1()),
+ getTensorShape(input2()), getTensorData<int16_t>(input2()),
+ getTensorShape(output()), getTensorData<int16_t>(output()), fn);
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Add.h b/compiler/luci-interpreter/src/kernels/Add.h
index a1f7e0406..79518845d 100644
--- a/compiler/luci-interpreter/src/kernels/Add.h
+++ b/compiler/luci-interpreter/src/kernels/Add.h
@@ -40,6 +40,7 @@ public:
private:
void evalFloat() const;
void evalQuantized() const;
+ void evalQuantizedS16() const;
};
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/Add.test.cpp b/compiler/luci-interpreter/src/kernels/Add.test.cpp
index 705b648c8..de8a3bbb0 100644
--- a/compiler/luci-interpreter/src/kernels/Add.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Add.test.cpp
@@ -57,18 +57,10 @@ TEST(AddTest, Uint8)
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f);
for (int i = 0; i < output_data.size(); i++)
{
- Tensor input1_tensor{
- getElementType<uint8_t>(), base_shape, {{quant_param.first}, {quant_param.second}}, ""};
- Tensor input2_tensor{
- getElementType<uint8_t>(), test_shapes[i], {{quant_param.first}, {quant_param.second}}, ""};
- std::vector<uint8_t> quantized_input1_value =
- quantize<uint8_t>(base_data, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantized_input2_value =
- quantize<uint8_t>(test_data, quant_param.first, quant_param.second);
- input1_tensor.writeData(quantized_input1_value.data(),
- quantized_input1_value.size() * sizeof(uint8_t));
- input2_tensor.writeData(quantized_input2_value.data(),
- quantized_input2_value.size() * sizeof(uint8_t));
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
Tensor output_tensor =
makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
@@ -79,26 +71,17 @@ TEST(AddTest, Uint8)
kernel.configure();
kernel.execute();
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor),
- output_tensor.scale(), output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(output_data[i], kQuantizedTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
}
// Re-run with exchanged inputs.
for (int i = 0; i < output_data.size(); i++)
{
- Tensor input1_tensor{
- getElementType<uint8_t>(), test_shapes[i], {{quant_param.first}, {quant_param.second}}, ""};
- Tensor input2_tensor{
- getElementType<uint8_t>(), base_shape, {{quant_param.first}, {quant_param.second}}, ""};
- std::vector<uint8_t> quantized_input1_value =
- quantize<uint8_t>(test_data, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantized_input2_value =
- quantize<uint8_t>(base_data, quant_param.first, quant_param.second);
- input1_tensor.writeData(quantized_input1_value.data(),
- quantized_input1_value.size() * sizeof(uint8_t));
- input2_tensor.writeData(quantized_input2_value.data(),
- quantized_input2_value.size() * sizeof(uint8_t));
+ Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
Tensor output_tensor =
makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
@@ -109,9 +92,8 @@ TEST(AddTest, Uint8)
kernel.configure();
kernel.execute();
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor),
- output_tensor.scale(), output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(output_data[i], kQuantizedTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
}
}
@@ -145,8 +127,7 @@ TEST(AddTest, Float)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(test_outputs[i], 0.0001f)))
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
<< "With shape number " << i;
}
// Re-run with exchanged inputs.
@@ -163,8 +144,72 @@ TEST(AddTest, Float)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(test_outputs[i], 0.0001f)))
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+ }
+}
+
+TEST(AddTest, SInt16)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::vector<std::vector<int32_t>> ref_output_shapes{
+ {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+
+ std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::vector<std::vector<float>> ref_outputs = {
+ {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f,
+ 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f,
+ 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f},
+ {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f},
+ {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f,
+ 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f,
+ 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f},
+ {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}};
+
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0);
+ const float tolerance = output_tensor.scale();
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor),
+ ::testing::ElementsAreArray(ref_output_shapes[i]))
+ << "With shape number " << i;
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
+ << "With shape number " << i;
+ }
+ // Re-run with exchanged inputs and different scales.
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 5.0 / 32767, 0);
+ const float tolerance = output_tensor.scale();
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor),
+ ::testing::ElementsAreArray(ref_output_shapes[i]))
+ << "With shape number " << i;
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
<< "With shape number " << i;
}
}
diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp
index 2ab7ff0da..c6734a114 100644
--- a/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp
@@ -32,12 +32,9 @@ void Check(std::initializer_list<int32_t> input_shape,
std::initializer_list<int32_t> output_shape, std::initializer_list<T1> input_data,
std::initializer_list<int32_t> dimension_data, std::initializer_list<T2> output_data)
{
-
- Tensor input_tensor{getElementType<T1>(), input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(T1));
- Tensor dimension_tensor{DataType::S32, dimension_shape, {}, ""};
- dimension_tensor.writeData(dimension_data.begin(), dimension_data.size() * sizeof(int32_t));
-
+ constexpr DataType element_type = getElementType<T1>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor dimension_tensor = makeInputTensor<DataType::S32>(dimension_shape, dimension_data);
Tensor output_tensor = makeOutputTensor(getElementType<T2>());
ArgMaxParams params{};
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp
index cdd81d7d6..df54f9786 100644
--- a/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp
@@ -18,6 +18,7 @@
#include "kernels/Utils.h"
+#include <tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h>
#include <tensorflow/lite/kernels/internal/reference/pooling.h>
#include <stdexcept>
@@ -61,11 +62,13 @@ void AveragePool2D::configure()
computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width);
if (input()->element_type() == DataType::U8)
{
- if (input()->scale() != output()->scale() || input()->zero_point() != output()->zero_point())
- {
- throw std::runtime_error(
- "Quantization param for Input and output must be same(scale or zero-point)");
- }
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point());
+ }
+ else if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
}
output()->resize({batches, output_height, output_width, depth});
}
@@ -80,6 +83,9 @@ void AveragePool2D::execute() const
case DataType::U8:
evalQuantized();
break;
+ case DataType::S16:
+ evalSInt16();
+ break;
default:
throw std::runtime_error("Unsupported type.");
}
@@ -126,5 +132,26 @@ void AveragePool2D::evalQuantized() const
getTensorData<uint8_t>(output()));
}
+void AveragePool2D::evalSInt16() const
+{
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_integer_ops::AveragePool(
+ params, getTensorShape(input()), getTensorData<int16_t>(input()), //
+ getTensorShape(output()), getTensorData<int16_t>(output()));
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.h b/compiler/luci-interpreter/src/kernels/AveragePool2D.h
index 91f212b3a..282a58797 100644
--- a/compiler/luci-interpreter/src/kernels/AveragePool2D.h
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.h
@@ -39,6 +39,7 @@ public:
private:
void evalFloat() const;
void evalQuantized() const;
+ void evalSInt16() const;
private:
int32_t _padding_height{};
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp
index cc80e5e90..83e48c89d 100644
--- a/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp
@@ -53,25 +53,21 @@ TEST(AveragePool2DTest, Float)
0, 1.5, //
4.5, 6, //
};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1}));
}
TEST(AveragePool2DTest, Uint8_0)
{
+ std::vector<float> input_data{
+ 0, -6, 12, 4, //
+ -3, -2, 10, 7, //
+ };
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
- Tensor input_tensor{DataType::U8, {1, 2, 4, 1}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quant_input = quantize<uint8_t>(
- {
- 0, -6, 12, 4, //
- -3, -2, 10, 7, //
- },
- quant_param.first, quant_param.second);
- input_tensor.writeData(quant_input.data(), quant_input.size() * sizeof(uint8_t));
-
Pool2DParams params{};
params.padding = Padding::VALID;
params.filter_height = 2;
@@ -84,26 +80,22 @@ TEST(AveragePool2DTest, Uint8_0)
kernel.configure();
kernel.execute();
- EXPECT_THAT(dequantize(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear({0.0, 6.0})));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0.0, 6.0}));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1}));
}
TEST(AveragePool2DTest, Uint8_1)
{
+ std::vector<float> input_data{
+ 0, 6, 12, 4, //
+ 3, 2, 10, 7, //
+ };
+
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
- Tensor input_tensor{DataType::U8, {1, 2, 4, 1}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quant_input = quantize<uint8_t>(
- {
- 0, 6, 12, 4, //
- 3, 2, 10, 7, //
- },
- quant_param.first, quant_param.second);
- input_tensor.writeData(quant_input.data(), quant_input.size() * sizeof(uint8_t));
-
Pool2DParams params{};
params.padding = Padding::VALID;
params.filter_height = 2;
@@ -116,12 +108,42 @@ TEST(AveragePool2DTest, Uint8_1)
kernel.configure();
kernel.execute();
- EXPECT_THAT(dequantize(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear({2.75, 6.0})));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({2.75, 6.0}));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1}));
}
+TEST(AveragePool2DTest, SInt16)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 1};
+ std::vector<float> input_data{
+ -4, -3, -2, -1, 0, //
+ 1, 2, 3, 4, 5, //
+ 6, 7, 8, 9, 10, //
+ };
+ std::vector<float> ref_output_data{
+ 0, 1.5, //
+ 4.5, 6, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.5, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.filter_height = 2;
+ params.filter_width = 3;
+ params.stride_height = 1;
+ params.stride_width = 2;
+ params.activation = Activation::RELU6;
+
+ AveragePool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
TEST(AveragePool2DTest, Invalid_Input_Shape_NEG)
{
Shape input_shape{1, 3, 5};
@@ -170,20 +192,17 @@ TEST(AveragePool2DTest, In_Out_Type_NEG)
TEST(AveragePool2DTest, Quant_Param_NEG)
{
+ std::vector<float> input_data{
+ 0, -6, 12, 4, //
+ -3, -2, 10, 7, //
+ };
+
std::pair<float, int32_t> quant_param1 = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
std::pair<float, int32_t> quant_param2 = quantizationParams<uint8_t>(-7.875f, 7.875f);
- Tensor input_tensor{
- DataType::U8, {1, 2, 4, 1}, {{quant_param1.first}, {quant_param1.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param1.first,
+ quant_param1.second, input_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param2.first, quant_param2.second);
- std::vector<uint8_t> quant_input = quantize<uint8_t>(
- {
- 0, -6, 12, 4, //
- -3, -2, 10, 7, //
- },
- quant_param1.first, quant_param1.second);
- input_tensor.writeData(quant_input.data(), quant_input.size() * sizeof(uint8_t));
-
Pool2DParams params{};
params.padding = Padding::VALID;
params.filter_height = 2;
diff --git a/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h
new file mode 100644
index 000000000..62bd4158e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
+#define LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
+
+#include "tensorflow/lite/kernels/internal/common.h"
+#include "tensorflow/lite/kernels/internal/types.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+// Derived from tensorflow/lite/kernels/internal/reference/maximum_minimum.h (v2.3.0).
+template <typename T, typename Op, int N = 5>
+void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape,
+ const T *input1_data,
+ const tflite::RuntimeShape &unextended_input2_shape,
+ const T *input2_data,
+ const tflite::RuntimeShape &unextended_output_shape, T *output_data,
+ Op op)
+{
+ if (unextended_input1_shape == unextended_input2_shape)
+ {
+ const int flat_size = tflite::MatchingElementsSize(
+ unextended_input1_shape, unextended_input2_shape, unextended_output_shape);
+ for (int i = 0; i < flat_size; ++i)
+ {
+ output_data[i] = op(input1_data[i], input2_data[i]);
+ }
+ }
+ else
+ {
+ assert(unextended_input1_shape.DimensionsCount() <= N);
+ assert(unextended_input2_shape.DimensionsCount() <= N);
+ assert(unextended_output_shape.DimensionsCount() <= N);
+
+ tflite::NdArrayDesc<N> desc1{};
+ tflite::NdArrayDesc<N> desc2{};
+ tflite::NdArrayDesc<N> output_desc{};
+ tflite::NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape,
+ &desc1, &desc2);
+ tflite::CopyDimsToDesc(tflite::RuntimeShape::ExtendedShape(N, unextended_output_shape),
+ &output_desc);
+
+ auto fn = [&](int indexes[N]) {
+ output_data[SubscriptToIndex(output_desc, indexes)] =
+ op(input1_data[SubscriptToIndex(desc1, indexes)],
+ input2_data[SubscriptToIndex(desc2, indexes)]);
+ };
+ tflite::NDOpsHelper<N>(output_desc, fn);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
diff --git a/compiler/luci-interpreter/src/kernels/CMakeLists.txt b/compiler/luci-interpreter/src/kernels/CMakeLists.txt
index 040ac5911..b460321bd 100644
--- a/compiler/luci-interpreter/src/kernels/CMakeLists.txt
+++ b/compiler/luci-interpreter/src/kernels/CMakeLists.txt
@@ -16,10 +16,22 @@ set(SOURCES
DepthToSpace.cpp
DepthwiseConv2D.h
DepthwiseConv2D.cpp
+ Div.h
+ Div.cpp
Elu.h
Elu.cpp
+ Floor.h
+ Floor.cpp
+ FloorDiv.h
+ FloorDiv.cpp
+ Equal.h
+ Equal.cpp
FullyConnected.h
FullyConnected.cpp
+ Greater.h
+ Greater.cpp
+ GreaterEqual.h
+ GreaterEqual.cpp
If.h
If.cpp
L2Normalize.h
@@ -28,20 +40,44 @@ set(SOURCES
L2Pool2D.cpp
LeakyRelu.h
LeakyRelu.cpp
+ Less.h
+ Less.cpp
+ LessEqual.h
+ LessEqual.cpp
LocalResponseNormalization.h
LocalResponseNormalization.cpp
Logistic.h
Logistic.cpp
+ LogSoftmax.h
+ LogSoftmax.cpp
+ Maximum.h
+ Maximum.cpp
MaxPool2D.h
MaxPool2D.cpp
Mean.h
Mean.cpp
+ Minimum.h
+ Minimum.cpp
Mul.h
Mul.cpp
+ NotEqual.h
+ NotEqual.cpp
Pad.h
Pad.cpp
+ Pow.h
+ Pow.cpp
+ Prelu.h
+ Prelu.cpp
+ Relu.h
+ Relu.cpp
+ Relu6.h
+ Relu6.cpp
Reshape.h
Reshape.cpp
+ ResizeBilinear.h
+ ResizeBilinear.cpp
+ ResizeNearestNeighbor.h
+ ResizeNearestNeighbor.cpp
Reverse.h
Reverse.cpp
Rsqrt.h
@@ -60,6 +96,8 @@ set(SOURCES
Sqrt.cpp
Squeeze.h
Squeeze.cpp
+ Sub.h
+ Sub.cpp
Tanh.h
Tanh.cpp
Transpose.h
@@ -69,7 +107,11 @@ set(SOURCES
Unpack.h
Unpack.cpp)
-list(APPEND SOURCES Utils.h Utils.cpp ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc)
+list(APPEND SOURCES
+ BinaryOpCommon.h
+ Utils.h
+ Utils.cpp
+ ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc)
add_library(luci_interpreter_kernels STATIC ${SOURCES})
set_target_properties(luci_interpreter_kernels PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -92,19 +134,37 @@ set(TEST_SOURCES
Conv2D.test.cpp
DepthToSpace.test.cpp
DepthwiseConv2D.test.cpp
+ Div.test.cpp
Elu.test.cpp
+ Floor.test.cpp
+ FloorDiv.test.cpp
+ Equal.test.cpp
FullyConnected.test.cpp
+ Greater.test.cpp
+ GreaterEqual.test.cpp
If.test.cpp
L2Normalize.test.cpp
L2Pool2D.test.cpp
LeakyRelu.test.cpp
+ Less.test.cpp
+ LessEqual.test.cpp
LocalResponseNormalization.test.cpp
Logistic.test.cpp
+ LogSoftmax.test.cpp
+ Maximum.test.cpp
MaxPool2D.test.cpp
Mean.test.cpp
+ Minimum.test.cpp
Mul.test.cpp
+ NotEqual.test.cpp
Pad.test.cpp
+ Pow.test.cpp
+ Prelu.test.cpp
+ Relu.test.cpp
+ Relu6.test.cpp
Reshape.test.cpp
+ ResizeBilinear.test.cpp
+ ResizeNearestNeighbor.test.cpp
Reverse.test.cpp
Rsqrt.test.cpp
Slice.test.cpp
@@ -114,6 +174,7 @@ set(TEST_SOURCES
StridedSlice.test.cpp
Sqrt.test.cpp
Squeeze.test.cpp
+ Sub.test.cpp
Tanh.test.cpp
Transpose.test.cpp
TransposeConv.test.cpp
diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.cpp
index 812ab7609..6f8820446 100644
--- a/compiler/luci-interpreter/src/kernels/Concatenation.cpp
+++ b/compiler/luci-interpreter/src/kernels/Concatenation.cpp
@@ -36,20 +36,20 @@ Concatenation::Concatenation(std::vector<const Tensor *> inputs, Tensor *output,
void Concatenation::configure()
{
const int num_inputs = _inputs.size();
- assert(num_inputs > 0);
+ LUCI_INTERPRETER_CHECK(num_inputs > 0);
const Tensor *t0 = _inputs[0];
int axis = _params.axis;
if (axis < 0)
axis += t0->shape().num_dims();
- assert(axis >= 0 && axis < t0->shape().num_dims());
+ LUCI_INTERPRETER_CHECK(axis >= 0 && axis < t0->shape().num_dims());
int32_t sum_axis = t0->shape().dim(axis);
for (int i = 1; i < num_inputs; ++i)
{
const Tensor *tensor = _inputs[i];
- assert(tensor->element_type() == t0->element_type());
- assert(tensor->shape().num_dims() == t0->shape().num_dims());
+ LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type());
+ LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims());
for (int d = 0; d < t0->shape().num_dims(); ++d)
{
if (d == axis)
@@ -58,7 +58,7 @@ void Concatenation::configure()
}
else
{
- assert(tensor->shape().dim(d) == t0->shape().dim(d));
+ LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d));
}
}
}
diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp
index d9a7097d0..91707a256 100644
--- a/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp
@@ -44,7 +44,7 @@ TEST(ConcatenationTest, Float)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})));
+ FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}));
}
{
params.axis = -2; // Same as '0'.
@@ -54,7 +54,7 @@ TEST(ConcatenationTest, Float)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})));
+ FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}));
}
{
params.axis = 1;
@@ -64,7 +64,7 @@ TEST(ConcatenationTest, Float)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})));
+ FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12}));
}
{
params.axis = -1; // Same as '1'.
@@ -74,10 +74,96 @@ TEST(ConcatenationTest, Float)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})));
+ FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12}));
}
}
+TEST(ConcatenationTest, Input_Number_Check_NEG)
+{
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Invalid_Axis_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -3;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Type_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<uint8_t> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Dimension_Num_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({1, 2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Dimension_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12, 13, 14, 15};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Unsupported_Configure_Type_NEG)
+{
+ std::vector<int8_t> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<int8_t> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::S8>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S8>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S8);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp
index 0446d9760..be8364528 100644
--- a/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp
@@ -66,8 +66,7 @@ TEST(Conv2DTest, Float)
0, 40, 0, 44, // row = 1
};
std::vector<int32_t> ref_output_shape{1, 2, 2, 2};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -114,46 +113,38 @@ TEST(Conv2DTest, FloatCheck)
37, 4, 3, // second batch, right
};
std::vector<int32_t> ref_output_shape{2, 1, 2, 3};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
TEST(Conv2DTest, Uint8)
{
+ std::vector<float> input_data{
+ // First batch
+ 1, 1, 1, 1, // row = 1
+ 2, 2, 2, 2, // row = 2
+ // Second batch
+ 1, 2, 3, 4, // row = 1
+ 1, 2, 3, 4, // row = 2
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, // first 2x2 filter
+ -1, 1, -1, 1, // second 2x2 filter
+ -1, -1, 1, 1, // third 2x2 filter
+ };
+ std::vector<float> bias_data{1, 2, 3};
+
std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
- Shape bias_shape = {3};
- Tensor input_tensor{
- DataType::U8, {2, 2, 4, 1}, {{input_quant_param.first}, {input_quant_param.second}}, ""};
- Tensor filter_tensor{
- DataType::U8, {3, 2, 2, 1}, {{input_quant_param.first}, {input_quant_param.second}}, ""};
- Tensor bias_tensor{
- DataType::S32, bias_shape, {{input_quant_param.first * input_quant_param.first}, {0}}, ""};
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2, 4, 1}, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({3, 2, 2, 1}, input_quant_param.first,
+ input_quant_param.second, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ {3}, input_quant_param.first * input_quant_param.first, 0, bias_data);
Tensor output_tensor =
makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
- std::vector<uint8_t> quantized_input = quantize<uint8_t>(
- {
- // First batch
- 1, 1, 1, 1, // row = 1
- 2, 2, 2, 2, // row = 2
- // Second batch
- 1, 2, 3, 4, // row = 1
- 1, 2, 3, 4, // row = 2
- },
- input_quant_param.first, input_quant_param.second);
- std::vector<uint8_t> quantized_filter = quantize<uint8_t>(
- {
- 1, 2, 3, 4, // first 2x2 filter
- -1, 1, -1, 1, // second 2x2 filter
- -1, -1, 1, 1, // third 2x2 filter
- },
- input_quant_param.first, input_quant_param.second);
- std::vector<int32_t> bias_data =
- quantize<int32_t>({1, 2, 3}, input_quant_param.first * input_quant_param.first, 0);
- input_tensor.writeData(quantized_input.data(), quantized_input.size() * sizeof(uint8_t));
- filter_tensor.writeData(quantized_filter.data(), quantized_filter.size() * sizeof(uint8_t));
- bias_tensor.writeData(bias_data.data(), bias_data.size() * sizeof(int32_t));
Conv2DParams params{};
params.padding = Padding::VALID;
@@ -174,9 +165,7 @@ TEST(Conv2DTest, Uint8)
37, 4, 3, // second batch, right
};
std::vector<int32_t> ref_output_shape{2, 1, 2, 3};
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor),
- output_quant_param.first, output_quant_param.second),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp
index cab63e26d..57238313c 100644
--- a/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp
+++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp
@@ -30,20 +30,10 @@ DepthToSpace::DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpa
void DepthToSpace::configure()
{
- if (input()->shape().num_dims() != 4)
- {
- throw std::runtime_error("Invalid input num_dims.");
- }
- if (output()->element_type() != DataType::FLOAT32 && output()->element_type() != DataType::U8 &&
- output()->element_type() != DataType::S8 && output()->element_type() != DataType::S32 &&
- output()->element_type() != DataType::S64)
- {
- throw std::runtime_error("Invalid output type");
- }
- if (input()->element_type() != output()->element_type())
- {
- throw std::runtime_error("Type mismatch on input and output.");
- }
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 ||
+ output()->element_type() == DataType::U8)
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type())
const int block_size = params().block_size;
const int32_t input_height = input()->shape().dim(1);
const int32_t input_width = input()->shape().dim(2);
@@ -52,9 +42,9 @@ void DepthToSpace::configure()
int32_t output_width = input_width * block_size;
int32_t output_channels = input_channels / block_size / block_size;
- assert(input_height == output_height / block_size);
- assert(input_width == output_width / block_size);
- assert(input_channels == output_channels * block_size * block_size);
+ LUCI_INTERPRETER_CHECK(input_height == output_height / block_size);
+ LUCI_INTERPRETER_CHECK(input_width == output_width / block_size);
+ LUCI_INTERPRETER_CHECK(input_channels == output_channels * block_size * block_size);
Shape output_shape(4);
output_shape.dim(0) = input()->shape().dim(0);
diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp b/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp
index 1b805702d..3dee4ad36 100644
--- a/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp
@@ -55,6 +55,51 @@ TYPED_TEST(DepthToSpaceTest, SimpleCase)
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
+TEST(DepthToSpaceTest, InvalidInputShape_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthToSpaceParams params{};
+ params.block_size = 2;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthToSpaceTest, InOutTypeMismatch_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ DepthToSpaceParams params{};
+ params.block_size = 2;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthToSpaceTest, InvalidBlockSize_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthToSpaceParams params{};
+ params.block_size = 3;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp
index b01a5e086..99d52715b 100644
--- a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp
+++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp
@@ -48,33 +48,33 @@ void DepthwiseConv2D::configure()
// We only support (1) and (3) for now.
if (input()->element_type() == DataType::FLOAT32 && filter()->element_type() == DataType::FLOAT32)
{
- assert(bias() == nullptr || bias()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::FLOAT32);
}
else if (input()->element_type() == DataType::U8 && filter()->element_type() == DataType::U8)
{
- assert(bias() == nullptr || bias()->element_type() == DataType::S32);
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32);
}
else
{
throw std::runtime_error("Unsupported type.");
}
- assert(output()->element_type() == input()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == input()->element_type());
const Shape &input_shape = input()->shape();
const Shape &filter_shape = filter()->shape();
- assert(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4);
const int32_t batches = input_shape.dim(0);
const int32_t input_height = input_shape.dim(1);
const int32_t input_width = input_shape.dim(2);
// Filter format: [1, H, W, O].
- assert(filter_shape.dim(0) == 1);
+ LUCI_INTERPRETER_CHECK(filter_shape.dim(0) == 1);
const int32_t filter_height = filter_shape.dim(1);
const int32_t filter_width = filter_shape.dim(2);
const int32_t channels_out = filter_shape.dim(3);
- assert(bias() == nullptr ||
- (bias()->shape().num_dims() == 1 && bias()->shape().dim(0) == channels_out));
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || (bias()->shape().num_dims() == 1 &&
+ bias()->shape().dim(0) == channels_out));
const int32_t output_height =
computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height,
diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp
index a9b43d864..a5128289f 100644
--- a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp
@@ -66,47 +66,37 @@ TEST(DepthwiseConv2DTest, Float)
71, 0, 99, 0, //
167, 0, 227, 28, //
};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4}));
}
TEST(DepthwiseConv2DTest, Uint8)
{
+ std::vector<float> input_data{
+ 1, 2, 7, 8, // column 1
+ 3, 4, 9, 10, // column 2
+ 5, 6, 11, 12, // column 3
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+
std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
- Tensor input_tensor{
- DataType::U8, {1, 3, 2, 2}, {{input_quant_param.first}, {input_quant_param.second}}, ""};
- Tensor filter_tensor{
- DataType::U8, {1, 2, 2, 4}, {{input_quant_param.first}, {input_quant_param.second}}, ""};
- Tensor bias_tensor{
- DataType::S32, {4}, {{input_quant_param.first * input_quant_param.first}, {0}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 3, 2, 2}, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 4}, input_quant_param.first,
+ input_quant_param.second, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ {4}, input_quant_param.first * input_quant_param.first, 0, bias_data);
Tensor output_tensor =
makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
- std::vector<uint8_t> quant_input = quantize<uint8_t>(
- {
- 1, 2, 7, 8, // column 1
- 3, 4, 9, 10, // column 2
- 5, 6, 11, 12, // column 3
- },
- input_quant_param.first, input_quant_param.second);
- std::vector<uint8_t> quant_filter = quantize<uint8_t>(
- {
- 1, 2, 3, 4, //
- -9, 10, -11, 12, //
- 5, 6, 7, 8, //
- 13, -14, 15, -16, //
- },
- input_quant_param.first, input_quant_param.second);
- std::vector<int32_t> quant_bias =
- quantize<int32_t>({1, 2, 3, 4}, input_quant_param.first * input_quant_param.first, 0);
-
- input_tensor.writeData(quant_input.data(), quant_input.size() * sizeof(uint8_t));
- filter_tensor.writeData(quant_filter.data(), quant_filter.size() * sizeof(uint8_t));
- bias_tensor.writeData(quant_bias.data(), quant_bias.size() * sizeof(int32_t));
-
DepthwiseConv2DParams params{};
params.padding = Padding::VALID;
params.depth_multiplier = 2;
@@ -124,12 +114,190 @@ TEST(DepthwiseConv2DTest, Uint8)
71, -34, 99, -20, //
91, -26, 127, -4, //
};
- EXPECT_THAT(dequantize(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4}));
}
+TEST(DepthwiseConv2DTest, InvalidBiasType_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<int32_t> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InOutTypeMismatch_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidInputShape_NEG)
+{
+ Shape input_shape{4, 2, 2};
+ Shape filter_shape{2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidFilterShape_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{2, 1, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidBiasDim_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 4, 2};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Div.cpp b/compiler/luci-interpreter/src/kernels/Div.cpp
new file mode 100644
index 000000000..e75876b3a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Div.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Div::Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams &params)
+ : KernelWithParams<DivParams>({input1, input2}, {output}, params)
+{
+}
+
+void Div::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Div::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Div::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastDivSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Div::evalQuantized() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const double real_output_multiplier = input1_scale / (input2_scale * output_scale);
+
+ int32_t output_multiplier{};
+ int output_shift{};
+
+ quantizeMultiplier(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+
+ params.input1_offset = -input1()->zero_point(); // Note the '-'.
+ params.input2_offset = -input2()->zero_point(); // Note the '-'.
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastDivSlow(
+ params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Div.h b/compiler/luci-interpreter/src/kernels/Div.h
new file mode 100644
index 000000000..6040cdd02
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_DIV_H
+#define LUCI_INTERPRETER_KERNELS_DIV_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Div : public KernelWithParams<DivParams>
+{
+public:
+ Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_DIV_H
diff --git a/compiler/luci-interpreter/src/kernels/Div.test.cpp b/compiler/luci-interpreter/src/kernels/Div.test.cpp
new file mode 100644
index 000000000..77eb2e9c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.test.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Div.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+float GetTolerance(float min, float max)
+{
+ const float kQuantizedStep = (max - min) / 255.0f;
+ const float kQuantizedTolerance = 2.0f * kQuantizedStep + kQuantizedStep * kQuantizedStep;
+ return kQuantizedTolerance;
+}
+
+TEST(DivTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 1};
+
+ std::vector<int32_t> output_shape = {2, 3, 1, 1};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f};
+ std::vector<float> input2_data{0.2f, 1.6f, 0.5f, 0.4f, 1.6f, 0.4f};
+ std::vector<float> test_outputs{1.5f, 1.4375f, 1.8f, 1.25f, 0.5f, 2.75f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input2_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(DivTest, FloatBroadcast)
+{
+ Shape input1_shape = {1, 3};
+ Shape input2_shape = {3, 1};
+
+ std::vector<float> input1_data{-0.3f, 2.3f, 0.9f};
+ std::vector<float> input2_data{0.2f, 1.6f, 0.5f};
+ std::vector<float> test_outputs{0.f, 11.5f, 4.5f, 0.f, 1.4375f, 0.5625f, 0.f, 4.6f, 1.8f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(input1_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(input2_shape, input2_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+}
+
+TEST(DivTest, Uint8)
+{
+ Shape base_shape = {1, 2, 2, 1};
+
+ std::vector<int32_t> output_shape = {1, 2, 2, 1};
+
+ std::vector<float> input1_data = {-0.8f, -0.2f, 0.3f, 0.7f};
+ std::vector<float> input2_data = {-0.8f, 0.4f, 0.8f, 1.0f};
+ std::vector<float> test_outputs{1.0f, 0.f, 0.375f, 0.7f};
+
+ const float kQuantizedTolerance = GetTolerance(-1.0, 1.0);
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.f, 1.f);
+
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input1_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input2_data);
+
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(test_outputs, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(DivTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DivTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Elu.cpp b/compiler/luci-interpreter/src/kernels/Elu.cpp
index 5de4a1f3b..456396055 100644
--- a/compiler/luci-interpreter/src/kernels/Elu.cpp
+++ b/compiler/luci-interpreter/src/kernels/Elu.cpp
@@ -31,7 +31,7 @@ Elu::Elu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
void Elu::configure()
{
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
output()->resize(input()->shape());
}
diff --git a/compiler/luci-interpreter/src/kernels/Elu.test.cpp b/compiler/luci-interpreter/src/kernels/Elu.test.cpp
index 52444cbea..0235d6552 100644
--- a/compiler/luci-interpreter/src/kernels/Elu.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Elu.test.cpp
@@ -29,9 +29,7 @@ using namespace testing;
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- Tensor input_tensor{DataType::FLOAT32, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(float));
-
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
Elu kernel(&input_tensor, &output_tensor);
@@ -39,8 +37,7 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int
kernel.execute();
(void)output_shape;
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
}
TEST(EluTest, SimpleElu)
@@ -59,6 +56,20 @@ TEST(EluTest, SimpleElu)
});
}
+TEST(EluTest, InOutTypeMismatch_NEG)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, -6, 2, -4, //
+ 3, -2, 10, -0.1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Elu kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Equal.cpp b/compiler/luci-interpreter/src/kernels/Equal.cpp
new file mode 100644
index 000000000..f58de1250
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Equal.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Equal::Equal(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Equal::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Equal::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Equal::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Equal(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void Equal::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::EqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Equal.h b/compiler/luci-interpreter/src/kernels/Equal.h
new file mode 100644
index 000000000..69b3be774
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Equal : public Kernel
+{
+public:
+ Equal(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/Equal.test.cpp b/compiler/luci-interpreter/src/kernels/Equal.test.cpp
new file mode 100644
index 000000000..fb0de8bbf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Equal.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(EqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, // Row 1
+ false, true, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(EqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ 0.9, 0.7, 0.5, // Row 4
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, // Row 1
+ false, false, false, // Row 2
+ false, false, false, // Row 3
+ true, true, true, // Row 4
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(EqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.55, 0.5, // Row 1
+ -1, 0, 0.05, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, false, // Row 1
+ false, true, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(EqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ -1, 0.05, 0, 1, // Row 4
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, false, false, // Row 1
+ false, false, true, false, // Row 2
+ false, false, false, false, // Row 3
+ true, true, true, true, // Row 4
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(EqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(EqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Floor.cpp b/compiler/luci-interpreter/src/kernels/Floor.cpp
new file mode 100644
index 000000000..e3c4246cc
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Floor.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/floor.h>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Floor::Floor(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Floor::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void Floor::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Floor::evalFloat() const
+{
+ tflite::reference_ops::Floor(getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Floor.h b/compiler/luci-interpreter/src/kernels/Floor.h
new file mode 100644
index 000000000..ca3ad5997
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_H
+#define LUCI_INTERPRETER_KERNELS_FLOOR_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Floor : public Kernel
+{
+public:
+ Floor(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_FLOOR_H
diff --git a/compiler/luci-interpreter/src/kernels/Floor.test.cpp b/compiler/luci-interpreter/src/kernels/Floor.test.cpp
new file mode 100644
index 000000000..3e1ab6f3a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Floor.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(FloorTest, SimpleFloat)
+{
+ std::initializer_list<int32_t> input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0.2, 8.6, 2.4, 4.3, // Row 1
+ 3, 7.1, 10.5, -0.9, // Row 2
+ };
+
+ std::initializer_list<int32_t> ref_output_shape{1, 2, 4, 1};
+ std::vector<float> ref_output_data{
+ 0, 8, 2, 4, // Row 1
+ 3, 7, 10, -1, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Floor kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Floor kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp
new file mode 100644
index 000000000..b6f36cea3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FloorDiv.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/binary_function.h>
+#include <cmath>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+FloorDiv::FloorDiv(const Tensor *input, const Tensor *alpha, Tensor *output)
+ : Kernel({input, alpha}, {output})
+{
+}
+
+void FloorDiv::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(y()->element_type() == output()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void FloorDiv::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void FloorDiv::evalFloat() const
+{
+ auto FloorDivFunc = [](float x, float y) -> float {
+ return std::floor(static_cast<double>(x) / static_cast<double>(y));
+ };
+
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+
+ // Check the denominator
+ for (int i = 0; i < getTensorShape(y()).FlatSize(); ++i)
+ {
+ LUCI_INTERPRETER_CHECK(y_data[i] != 0);
+ }
+
+ if (x()->shape() != y()->shape())
+ {
+ tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>(
+ getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()),
+ getTensorData<float>(output()), FloorDivFunc);
+ }
+ else
+ {
+ tflite::reference_ops::BinaryFunction<float, float, float>(
+ getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()),
+ getTensorData<float>(output()), FloorDivFunc);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.h b/compiler/luci-interpreter/src/kernels/FloorDiv.h
new file mode 100644
index 000000000..e9c47d81a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
+#define LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class FloorDiv : public Kernel
+{
+public:
+ FloorDiv(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp
new file mode 100644
index 000000000..a5bc700f7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FloorDiv.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(FloorDivTest, FloatSimple)
+{
+ Shape x_shape{2, 3};
+ std::vector<float> x_data{
+ 0.5, 2.4, 3.1, // Row 1
+ 1.9, -1.9, -2.8, // Row 2
+ };
+
+ Shape y_shape = x_shape;
+ std::vector<float> y_data{
+ 2.0, 0.5, 3.0, // Row 1
+ 1.0, -1.0, -2.0, // Row 2
+ };
+
+ std::vector<int32_t> ref_output_shape{2, 3};
+ std::vector<float> ref_output_data{
+ 0, 4, 1, // Row 1
+ 1, 1, 1, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(y_shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorDivTest, FloatBroadcast)
+{
+ Shape x_shape{1, 3};
+ std::vector<float> x_data{
+ 0.5, 2.4, -3.1, // Row 1
+ };
+
+ Shape y_shape{3, 3};
+ std::vector<float> y_data{
+ 1.0, 1.0, 1.0, // Row 1
+ 2.0, -0.5, -2.0, // Row 2
+ 0.3, 0.7, 0.9, // Row 3
+ };
+
+ std::vector<int32_t> ref_output_shape{3, 3};
+ std::vector<float> ref_output_data{
+ 0, 2, -4, // Row 1
+ 0, -5, 1, // Row 2
+ 1, 3, -4, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(y_shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorDivTest, DivByZero_NEG)
+{
+ Shape shape{3};
+ std::vector<float> x_data{1, 0, -1};
+ std::vector<float> y_data{0, 0, 0};
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+TEST(FloorDivTest, Input_Output_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(FloorDivTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp
index 6529c5e77..7fa76d5e7 100644
--- a/compiler/luci-interpreter/src/kernels/FullyConnected.cpp
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp
@@ -36,27 +36,54 @@ FullyConnected::FullyConnected(const Tensor *input, const Tensor *weights, const
void FullyConnected::configure()
{
- if (weights()->element_type() != DataType::FLOAT32)
+ if (weights()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::S32)
+ }
+ else if (weights()->element_type() == DataType::FLOAT32)
+ {
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::FLOAT32)
+ }
+ else
+ {
throw std::runtime_error("Unsupported type.");
-
- assert(input()->element_type() == DataType::FLOAT32);
- assert(weights()->element_type() == DataType::FLOAT32);
- assert(bias() == nullptr || bias()->element_type() == DataType::FLOAT32);
+ }
const Shape &input_shape = input()->shape();
const Shape &weights_shape = weights()->shape();
- assert(weights_shape.num_dims() == 2);
- assert(bias() == nullptr || bias()->shape().num_elements() == weights_shape.dim(0));
+ LUCI_INTERPRETER_CHECK(weights_shape.num_dims() == 2);
+ LUCI_INTERPRETER_CHECK(bias() == nullptr ||
+ bias()->shape().num_elements() == weights_shape.dim(0));
- assert(input_shape.num_elements() % weights_shape.dim(1) == 0);
+ LUCI_INTERPRETER_CHECK(input_shape.num_elements() % weights_shape.dim(1) == 0);
const int32_t batch_size = input_shape.num_elements() / weights_shape.dim(1);
const int32_t num_units = weights_shape.dim(0);
+ if (bias())
+ LUCI_INTERPRETER_CHECK(bias()->shape().num_elements() == weights()->shape().dim(0));
+
output()->resize({batch_size, num_units});
}
-void FullyConnected::execute() const { evalFloat(); }
+void FullyConnected::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
void FullyConnected::evalFloat() const
{
@@ -75,5 +102,38 @@ void FullyConnected::evalFloat() const
getTensorShape(output()), getTensorData<float>(output()));
}
+void FullyConnected::evalQuantized() const
+{
+ double real_multiplier = 0.0;
+ int output_shift;
+ int32_t output_activation_min;
+ int32_t output_activation_max;
+ int32_t output_multiplier;
+ real_multiplier =
+ getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale());
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+ calculateActivationRangeQuantized(params().activation, output(), &output_activation_min,
+ &output_activation_max);
+
+ int32_t input_offset = -input()->zero_point();
+ int32_t filter_offset = -weights()->zero_point();
+ int32_t output_offset = output()->zero_point();
+
+ tflite::FullyConnectedParams op_params{};
+ op_params.input_offset = input_offset;
+ op_params.weights_offset = filter_offset;
+ op_params.output_offset = output_offset;
+ op_params.output_multiplier = output_multiplier;
+ op_params.output_shift = output_shift;
+ op_params.quantized_activation_min = output_activation_min;
+ op_params.quantized_activation_max = output_activation_max;
+ op_params.lhs_cacheable = false;
+ op_params.rhs_cacheable = false;
+ tflite::reference_ops::FullyConnected(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(weights()), getTensorData<uint8_t>(weights()), getTensorShape(bias()),
+ getTensorData<int32_t>(bias()), getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.h b/compiler/luci-interpreter/src/kernels/FullyConnected.h
index 2e3174c74..204f11ebb 100644
--- a/compiler/luci-interpreter/src/kernels/FullyConnected.h
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.h
@@ -41,6 +41,7 @@ public:
private:
void evalFloat() const;
+ void evalQuantized() const;
};
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp
index 8077fcb5c..d194ce1a0 100644
--- a/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp
@@ -26,7 +26,85 @@ namespace
using namespace testing;
-TEST(FullyConnectedTest, Float)
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape,
+ std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> weights_data,
+ std::initializer_list<float> bias_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(
+ std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape,
+ std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> weights_data,
+ std::initializer_list<float> bias_data, std::initializer_list<float> output_data)
+{
+ const float quantized_tolerance = getTolerance(-127, 128, 255);
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
+ std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::U8>(weights_shape, input_quant_param.first,
+ input_quant_param.second, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ bias_shape, input_quant_param.first * input_quant_param.first, 0, bias_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, quantized_tolerance));
+}
+
+template <typename T> class FullyConnectedTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(FullyConnectedTest, DataTypes);
+
+TYPED_TEST(FullyConnectedTest, Simple)
+{
+ Check<TypeParam>({3, 2, 2, 1}, {3, 6}, {3}, {2, 3},
+ {
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ },
+ {
+ -3, -7, 4, -4, -6, 4, // unit = 0
+ 3, 5, 2, 3, -3, -8, // unit = 1
+ -3, 7, 4, 9, 0, -5, // unit = 2
+ },
+ {-1, -5, -8}, {
+ 0, 0, 32, // batch = 0
+ 22, 11, 47, // batch = 1
+ });
+}
+
+TEST(FullyConnectedTest, InvalidBiasType_NEG)
{
Shape input_shape{3, 2, 2, 1};
std::vector<float> input_data{
@@ -40,6 +118,34 @@ TEST(FullyConnectedTest, Float)
-3, 7, 4, 9, 0, -5, // unit = 2
};
Shape bias_shape{3};
+ std::vector<int32_t> bias_data{-1, -5, -8};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(FullyConnectedTest, InvalidWeightShapeDim_NEG)
+{
+ Shape input_shape{3, 2, 2, 1};
+ std::vector<float> input_data{
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ };
+ Shape weights_shape{1, 3, 6};
+ std::vector<float> weights_data{
+ -3, -7, 4, -4, -6, 4, // unit = 0
+ 3, 5, 2, 3, -3, -8, // unit = 1
+ -3, 7, 4, 9, 0, -5, // unit = 2
+ };
+ Shape bias_shape{3};
std::vector<float> bias_data{-1, -5, -8};
Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
@@ -51,15 +157,38 @@ TEST(FullyConnectedTest, Float)
params.activation = Activation::RELU;
FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
- kernel.configure();
- kernel.execute();
+ EXPECT_ANY_THROW(kernel.configure());
+}
- std::vector<float> ref_output_data{
- 0, 0, 32, // batch = 0
- 22, 11, 47, // batch = 1
+TEST(FullyConnectedTest, BiasElementNumWeightDimMismatch_NEG)
+{
+ Shape input_shape{3, 2, 2, 1};
+ std::vector<float> input_data{
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ Shape weights_shape{6, 3};
+ std::vector<float> weights_data{
+ -3, -7, 4, // unit = 0
+ -4, -6, 4, // unit = 1
+ 3, 5, 2, // unit = 2
+ 3, -3, -8, // unit = 3
+ -3, 7, 4, // unit = 4
+ 9, 0, -5, // unit = 5
+ };
+ Shape bias_shape{3};
+ std::vector<float> bias_data{-1, -5, -8};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/Greater.cpp b/compiler/luci-interpreter/src/kernels/Greater.cpp
new file mode 100644
index 000000000..f0dd2db36
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Greater.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Greater::Greater(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Greater::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Greater::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Greater::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreater(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Greater(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void Greater::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Greater.h b/compiler/luci-interpreter/src/kernels/Greater.h
new file mode 100644
index 000000000..a65d29f5c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_GREATER_H
+#define LUCI_INTERPRETER_KERNELS_GREATER_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Greater : public Kernel
+{
+public:
+ Greater(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_GREATER_H
diff --git a/compiler/luci-interpreter/src/kernels/Greater.test.cpp b/compiler/luci-interpreter/src/kernels/Greater.test.cpp
new file mode 100644
index 000000000..3122fa840
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Greater.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(GreaterTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, // Row 1
+ true, false, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(GreaterTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, // Row 1
+ true, false, false, // Row 2
+ false, false, true, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(GreaterTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 3);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, false, // Row 1
+ true, true, false, false, // Row 2
+ true, false, true, false, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(GreaterTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp
new file mode 100644
index 000000000..68135e27c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/GreaterEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+GreaterEqual::GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output)
+ : Kernel({x, y}, {output})
+{
+}
+
+void GreaterEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void GreaterEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void GreaterEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void GreaterEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.h b/compiler/luci-interpreter/src/kernels/GreaterEqual.h
new file mode 100644
index 000000000..e948d698f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class GreaterEqual : public Kernel
+{
+public:
+ GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp
new file mode 100644
index 000000000..11e62644c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/GreaterEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(GreaterEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, // Row 1
+ true, true, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(GreaterEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, // Row 1
+ true, false, false, // Row 2
+ false, false, true, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(GreaterEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, false, // Row 1
+ true, true, true, false, // Row 2
+ true, false, true, false, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(GreaterEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp
index e6bdee338..ca982d591 100644
--- a/compiler/luci-interpreter/src/kernels/If.cpp
+++ b/compiler/luci-interpreter/src/kernels/If.cpp
@@ -15,6 +15,7 @@
*/
#include "kernels/If.h"
+#include "kernels/Utils.h"
#include <cstring>
@@ -40,14 +41,14 @@ If::If(const Tensor *cond, const std::vector<const Tensor *> &inputs, std::vecto
void If::configure()
{
- assert(cond()->element_type() == DataType::BOOL);
- assert(cond()->shape().num_elements() == 1);
+ LUCI_INTERPRETER_CHECK(cond()->element_type() == DataType::BOOL);
+ LUCI_INTERPRETER_CHECK(cond()->shape().num_elements() == 1);
for (RuntimeGraph *graph : {_then_graph, _else_graph})
{
(void)graph;
- assert(graph->getInputTensors().size() == getInputTensors().size() - 1);
- assert(graph->getOutputTensors().size() == getOutputTensors().size());
+ LUCI_INTERPRETER_CHECK(graph->getInputTensors().size() == getInputTensors().size() - 1);
+ LUCI_INTERPRETER_CHECK(graph->getOutputTensors().size() == getOutputTensors().size());
}
}
@@ -62,7 +63,7 @@ void If::execute() const
// Copy kernel inputs to active graph inputs.
for (size_t i = 0; i < getInputTensors().size() - 1; ++i)
{
- assert(graph_inputs[i]->element_type() == input(i)->element_type());
+ LUCI_INTERPRETER_CHECK(graph_inputs[i]->element_type() == input(i)->element_type());
graph_inputs[i]->resize(input(i)->shape());
const int32_t num_elements = input(i)->shape().num_elements();
@@ -75,7 +76,7 @@ void If::execute() const
// Copy graph outputs to kernel outputs.
for (size_t i = 0; i < getOutputTensors().size(); ++i)
{
- assert(graph_outputs[i]->element_type() == output(i)->element_type());
+ LUCI_INTERPRETER_CHECK(graph_outputs[i]->element_type() == output(i)->element_type());
output(i)->resize(graph_outputs[i]->shape());
const int32_t num_elements = output(i)->shape().num_elements();
diff --git a/compiler/luci-interpreter/src/kernels/If.test.cpp b/compiler/luci-interpreter/src/kernels/If.test.cpp
index 9b3857ce3..6967407fb 100644
--- a/compiler/luci-interpreter/src/kernels/If.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/If.test.cpp
@@ -85,7 +85,7 @@ TEST(IfTest, CondTrue)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output), ElementsAreArray(ArrayFloatNear({6, 9})));
+ EXPECT_THAT(extractTensorData<float>(output), FloatArrayNear({6, 9}));
}
TEST(IfTest, CondFalse)
@@ -103,7 +103,37 @@ TEST(IfTest, CondFalse)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output), ElementsAreArray(ArrayFloatNear({5, 14})));
+ EXPECT_THAT(extractTensorData<float>(output), FloatArrayNear({5, 14}));
+}
+
+TEST(IfTest, InvalidCondType_NEG)
+{
+ Tensor cond = makeInputTensor<DataType::FLOAT32>({1}, {1});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(IfTest, InvalidCondElementNum_NEG)
+{
+ Tensor cond = makeInputTensor<DataType::BOOL>({2}, {false, true});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ EXPECT_ANY_THROW(kernel.configure());
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp
index cfa535075..0bf133d9c 100644
--- a/compiler/luci-interpreter/src/kernels/L2Normalize.cpp
+++ b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp
@@ -34,15 +34,16 @@ L2Normalize::L2Normalize(const Tensor *input, Tensor *output, const L2NormParams
void L2Normalize::configure()
{
- assert(input()->shape().num_dims() <= 4);
- assert(output()->element_type() == DataType::FLOAT32 || output()->element_type() == DataType::U8);
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 ||
+ output()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
if (output()->element_type() == DataType::U8)
{
- assert(output()->scale() == (1. / 128.));
- assert(output()->zero_point() == 128);
+ LUCI_INTERPRETER_CHECK(output()->scale() == (1. / 128.));
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 128);
}
- assert(params().activation == Activation::NONE);
+ LUCI_INTERPRETER_CHECK(params().activation == Activation::NONE);
output()->resize(input()->shape());
}
diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp
index f53eaca94..8f9431182 100644
--- a/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp
@@ -26,11 +26,11 @@ namespace
using namespace testing;
-TEST(L2NormalizeTest, Float)
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- std::vector<float> input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1};
-
- Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, input_data);
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
L2NormParams params{};
@@ -40,14 +40,76 @@ TEST(L2NormalizeTest, Float)
kernel.configure();
kernel.execute();
- std::vector<float> ref_output_data{-0.55, 0.3, 0.35, 0.6, -0.35, 0.05};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> quant_param =
+ quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f,
+ std::max(input_data) > 0 ? std::max(input_data) : 0.f);
+
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 128., 128);
+
+ L2NormParams params{};
+ params.activation = Activation::NONE;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <typename T> class L2NormalizeTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(L2NormalizeTest, DataTypes);
+
+TYPED_TEST(L2NormalizeTest, Simple)
+{
+ Check<TypeParam>({1, 1, 1, 6}, {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1},
+ {-0.55, 0.3, 0.35, 0.6, -0.35, 0.05});
}
-// TODO Uint8Quantized
-// Implement GetDequantizedOutput Function.
-// Create Test for Uint8 Case
+TEST(L2NormalizeTest, ActivationType_NEG)
+{
+ std::vector<float> input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ L2NormParams params{};
+ params.activation = Activation::RELU6;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(L2NormalizeTest, InvalidOutputQuantParam_NEG)
+{
+ std::vector<float> input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1};
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 1, 1, 6}, 1. / 64., 127, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 64., 127);
+
+ L2NormParams params{};
+ params.activation = Activation::NONE;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
} // namespace
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp
index 37a6ddedc..979364a7f 100644
--- a/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp
+++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp
@@ -36,8 +36,8 @@ L2Pool2D::L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams &para
void L2Pool2D::configure()
{
- assert(input()->shape().num_dims() == 4);
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
int batches = input()->shape().dim(0);
int height = input()->shape().dim(1);
@@ -55,7 +55,7 @@ void L2Pool2D::configure()
_padding_height =
computePadding(params().stride_height, 1, height, params().filter_height, out_height);
- assert(input()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32);
output()->resize({batches, out_height, out_width, channels_out});
}
diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp
index 06bb9388f..5f834e3c1 100644
--- a/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp
@@ -50,8 +50,7 @@ TEST(L2Pool2DTest, FloatNone)
kernel.execute();
std::vector<float> ref_output_data{3.5, 6.5};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
@@ -78,8 +77,7 @@ TEST(L2Pool2DTest, FloatRelu)
kernel.execute();
std::vector<float> ref_output_data{3.53553, 6.5};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
@@ -106,8 +104,7 @@ TEST(L2Pool2DTest, FloatRelu1)
kernel.execute();
std::vector<float> ref_output_data{0.353553, 1.0};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
@@ -134,8 +131,7 @@ TEST(L2Pool2DTest, FloatRelu6)
kernel.execute();
std::vector<float> ref_output_data{0.353553, 6.0};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
@@ -162,12 +158,11 @@ TEST(L2Pool2DTest, FloatPaddingSame)
kernel.execute();
std::vector<float> ref_output_data{3.5, 6.5};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
-TEST(L2Pool2DTest, FloatPaddingSameSlide1)
+TEST(L2Pool2DTest, FloatPaddingSameStride)
{
Shape input_shape{1, 2, 4, 1};
std::vector<float> input_data{
@@ -190,12 +185,11 @@ TEST(L2Pool2DTest, FloatPaddingSameSlide1)
kernel.execute();
std::vector<float> ref_output_data{3.5, 6.0, 6.5, 5.70088, 2.54951, 7.2111, 8.63134, 7.0};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
-TEST(L2Pool2DTest, FloatPaddingValidSlide1)
+TEST(L2Pool2DTest, FloatPaddingValidStride)
{
Shape input_shape{1, 2, 4, 1};
std::vector<float> input_data{
@@ -218,11 +212,54 @@ TEST(L2Pool2DTest, FloatPaddingValidSlide1)
kernel.execute();
std::vector<float> ref_output_data{3.5, 6.0, 6.5};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
// TODO make a Shape checking of output_tensor.
}
+TEST(L2Pool2DTest, InvalidInputShape_NEG)
+{
+ Shape input_shape{1, 2, 4};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(L2Pool2DTest, InvalidInputOutputType_NEG)
+{
+ Shape input_shape{1, 2, 4};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp
index 1a26debe0..919b12792 100644
--- a/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp
+++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp
@@ -36,7 +36,7 @@ LeakyRelu::LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams
void LeakyRelu::configure()
{
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
if (input()->element_type() == DataType::U8)
{
double alpha_multiplier = input()->scale() * params().alpha / output()->scale();
diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp
index c79d3d6bc..2778549ed 100644
--- a/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp
@@ -28,12 +28,11 @@ using namespace testing;
template <typename T>
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
- std::initializer_list<T> input_data, std::initializer_list<T> output_data, float alpha,
- DataType element_type)
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data,
+ float alpha)
{
- Tensor input_tensor{element_type, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(T));
-
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(element_type);
LeakyReluParams params{};
@@ -44,30 +43,75 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int
kernel.configure();
kernel.execute();
- (void)output_shape;
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
}
-TEST(LeakReluTest, FloatSimple)
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data, float alpha)
{
- Check<float>(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, /*input_data=*/
- {
- 0.0f, 1.0f, 3.0f, // Row 1
- 1.0f, -1.0f, -2.0f, // Row 2
- },
- /*output_data=*/
- {
- 0.0f, 1.0f, 3.0f, // Row 1
- 1.0f, -0.5f, -1.0f, // Row 2
- },
- /*alpha=*/0.5f, getElementType<float>());
+ const float quantized_tolerance = getTolerance(-8, 127.f / 16.f, 255);
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-8, 127.f / 16.f);
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ LeakyReluParams params{};
+ params.alpha = alpha;
+
+ LeakyRelu kernel(&input_tensor, &output_tensor, params);
+
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, quantized_tolerance));
+}
+
+template <typename T> class LeakReluTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(LeakReluTest, DataTypes);
+
+TYPED_TEST(LeakReluTest, Simple)
+{
+ Check<TypeParam>(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ },
+ /*output_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -0.5f, -1.0f, // Row 2
+ },
+ /*alpha=*/0.5f);
SUCCEED();
}
-// TODO Uint8Simple
-// Implement GetDequantizedOutput Function.
-// Create Test for Uint8 Case
+TEST(LeakReluTest, IvalidInputOutputType_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ });
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ LeakyReluParams params{};
+ params.alpha = 0.5f;
+
+ LeakyRelu kernel(&input_tensor, &output_tensor, params);
+
+ EXPECT_ANY_THROW(kernel.configure());
+}
} // namespace
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/Less.cpp b/compiler/luci-interpreter/src/kernels/Less.cpp
new file mode 100644
index 000000000..041444926
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Less.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Less::Less(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Less::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Less::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Less::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLess(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Less(op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+void Less::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Less.h b/compiler/luci-interpreter/src/kernels/Less.h
new file mode 100644
index 000000000..fe03e10b1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LESS_H
+#define LUCI_INTERPRETER_KERNELS_LESS_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Less : public Kernel
+{
+public:
+ Less(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LESS_H
diff --git a/compiler/luci-interpreter/src/kernels/Less.test.cpp b/compiler/luci-interpreter/src/kernels/Less.test.cpp
new file mode 100644
index 000000000..73aa30b36
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Less.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LessTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, // Row 1
+ false, false, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(LessTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, // Row 1
+ false, true, true, // Row 2
+ true, true, false, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(LessTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, true, // Row 1
+ false, false, false, true, // Row 2
+ false, true, false, true, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LessTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.cpp
new file mode 100644
index 000000000..b8aaba178
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LessEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+LessEqual::LessEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void LessEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void LessEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void LessEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void LessEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.h b/compiler/luci-interpreter/src/kernels/LessEqual.h
new file mode 100644
index 000000000..ed4b0f1ea
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LessEqual : public Kernel
+{
+public:
+ LessEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp
new file mode 100644
index 000000000..9184c061f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LessEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LessEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, // Row 1
+ false, true, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(LessEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, // Row 1
+ false, true, true, // Row 2
+ true, true, false, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(LessEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, true, // Row 1
+ false, false, true, true, // Row 2
+ false, true, false, true, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LessEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp
index 08efa1d6a..b78e27128 100644
--- a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp
+++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp
@@ -36,9 +36,9 @@ LocalResponseNormalization::LocalResponseNormalization(
void LocalResponseNormalization::configure()
{
- assert(input()->shape().num_dims() == 4);
- assert(output()->element_type() == DataType::FLOAT32);
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
output()->resize(input()->shape());
}
diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp
index 4191bdb29..d98305c1a 100644
--- a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp
@@ -44,7 +44,7 @@ TEST(LocalResponseNormalizationTest, SameAsL2Norm)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({-0.55, 0.3, 0.35, 0.6, -0.35, 0.05})));
+ FloatArrayNear({-0.55, 0.3, 0.35, 0.6, -0.35, 0.05}));
}
TEST(LocalResponseNormalizationTest, WithAlpha)
@@ -64,7 +64,7 @@ TEST(LocalResponseNormalizationTest, WithAlpha)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({-0.275, 0.15, 0.175, 0.3, -0.175, 0.025})));
+ FloatArrayNear({-0.275, 0.15, 0.175, 0.3, -0.175, 0.025}));
}
TEST(LocalResponseNormalizationTest, WithBias)
@@ -84,7 +84,7 @@ TEST(LocalResponseNormalizationTest, WithBias)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear({-0.22, 0.12, 0.14, 0.24, -0.14, 0.02})));
+ FloatArrayNear({-0.22, 0.12, 0.14, 0.24, -0.14, 0.02}));
}
TEST(LocalResponseNormalizationTest, SmallRadius)
@@ -104,8 +104,39 @@ TEST(LocalResponseNormalizationTest, SmallRadius)
kernel.execute();
EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(
- ArrayFloatNear({-0.264926, 0.125109, 0.140112, 0.267261, -0.161788, 0.0244266})));
+ FloatArrayNear({-0.264926, 0.125109, 0.140112, 0.267261, -0.161788, 0.0244266}));
+}
+
+TEST(LocalResponseNormalizationTest, InvalidInputDimension_NEG)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 1.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LocalResponseNormalizationTest, InvalidInputOutputType_NEG)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 1.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp b/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp
new file mode 100644
index 000000000..03d13e4ce
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogSoftmax.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+LogSoftmax::LogSoftmax(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void LogSoftmax::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->scale() == 16. / 256);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 255);
+
+ tflite::SoftmaxParams params{};
+
+ params.table = _table;
+ params.beta = 1.0;
+
+ tflite::optimized_ops::PopulateSoftmaxLookupTable(&params, input()->scale(), params.beta);
+ }
+ output()->resize(input()->shape());
+}
+
+void LogSoftmax::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void LogSoftmax::evalFloat() const
+{
+ tflite::SoftmaxParams params{};
+ tflite::reference_ops::LogSoftmax(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void LogSoftmax::evalQuantized() const
+{
+ const auto input_shape = getTensorShape(input());
+ const auto output_shape = getTensorShape(output());
+ const auto input_scale = input()->scale();
+ uint8_t *output_data = getTensorData<uint8_t>(output());
+ const uint8_t *input_data = getTensorData<uint8_t>(input());
+
+ tflite::SoftmaxParams params{};
+
+ params.table = const_cast<float *>(_table);
+ params.zero_point = output()->zero_point();
+ params.scale = output()->scale();
+
+ tflite::optimized_ops::LogSoftmax(params, input_scale, input_shape, input_data, output_shape,
+ output_data);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.h b/compiler/luci-interpreter/src/kernels/LogSoftmax.h
new file mode 100644
index 000000000..18477fbe3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
+#define LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LogSoftmax : public Kernel
+{
+public:
+ LogSoftmax(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+ float _table[256];
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp
new file mode 100644
index 000000000..d3b331dfe
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogSoftmax.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LogSoftmaxTest, Float)
+{
+ Shape input_shape{2, 4};
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ -4.14297, -10.14297, -2.14297, -.142971, //
+ -7.00104, -12.00104, -.00104087, -9.00104, //
+ };
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(LogSoftmaxTest, Uint8)
+{
+ float kMin = -10;
+ float kMax = 10;
+ float kLogSoftmaxQuantizedTolerance = 16. / 256;
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax);
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ -4.14297, -10.14297, -2.14297, -.142971, //
+ -7.00104, -12.00104, -.00104087, -9.00104, //
+ };
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kLogSoftmaxQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({189, 93, 221, 253, 142, 63, 255, 111}));
+}
+
+TEST(LogSoftmaxTest, InvalidInputOutputType_NEG)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 4}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogSoftmaxTest, InvalidOutputQuantParam_NEG)
+{
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-10, 10);
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 20. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Logistic.cpp b/compiler/luci-interpreter/src/kernels/Logistic.cpp
index c7d45615c..97d7bf13d 100644
--- a/compiler/luci-interpreter/src/kernels/Logistic.cpp
+++ b/compiler/luci-interpreter/src/kernels/Logistic.cpp
@@ -29,10 +29,10 @@ Logistic::Logistic(const Tensor *input, Tensor *output) : Kernel({input}, {outpu
void Logistic::configure()
{
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
if (input()->element_type() == DataType::U8)
{
- assert(output()->scale() == 1. / 256);
+ LUCI_INTERPRETER_CHECK(output()->scale() == 1. / 256);
populateLookupTable();
}
output()->resize(input()->shape());
diff --git a/compiler/luci-interpreter/src/kernels/Logistic.test.cpp b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp
index 00feddf3d..d3bbb330d 100644
--- a/compiler/luci-interpreter/src/kernels/Logistic.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp
@@ -26,31 +26,108 @@ namespace
using namespace testing;
-TEST(LogisticTest, Float)
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- Shape input_shape{1, 2, 4, 1};
- std::vector<float> input_data{
- 0, -6, 2, 4, //
- 3, -2, 10, 1, //
- };
- Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
- Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ Tensor input_tensor = makeInputTensor<getElementType<T>()>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(getElementType<T>());
Logistic kernel(&input_tensor, &output_tensor);
kernel.configure();
kernel.execute();
- std::vector<float> ref_output_data{
- 0.5, 0.002473, 0.880797, 0.982014, //
- 0.952574, 0.119203, 0.999955, 0.731059, //
- };
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
- // TODO make a Shape checking of output_tensor.
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
-// TODO Uint8
-// Need to Implement GetDequantizedOutput Function.
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> input_quant_param =
+ quantizationParams<uint8_t>(std::min(input_data), std::max(input_data));
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale() * 2));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <typename T> class LogisticTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(LogisticTest, DataTypes);
+
+TYPED_TEST(LogisticTest, Simple)
+{
+ Check<TypeParam>(
+ {89}, {89},
+ {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636,
+ -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000,
+ -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364,
+ -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727,
+ -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091,
+ -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455,
+ -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818,
+ -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818,
+ 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455,
+ 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091,
+ 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727,
+ 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364,
+ 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000,
+ 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636,
+ 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000},
+ {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198,
+ 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786,
+ 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065,
+ 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576,
+ 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562,
+ 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805,
+ 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241,
+ 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759,
+ 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195,
+ 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438,
+ 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424,
+ 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935,
+ 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214,
+ 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802,
+ 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021});
+}
+
+TEST(LogisticTest, IvalidInputOutputType_NEG)
+{
+ Shape input_shape = {1};
+ std::vector<float> input_data{10};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogisticTest, IvalidQuantParam_NEG)
+{
+ Shape input_shape = {2};
+ std::vector<float> input_data{-10, 10};
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-10, 10);
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 255, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
} // namespace
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp
index afecf9058..123e6e1a2 100644
--- a/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp
@@ -18,6 +18,7 @@
#include "kernels/Utils.h"
+#include <tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h>
#include <tensorflow/lite/kernels/internal/reference/pooling.h>
#include <stdexcept>
@@ -35,7 +36,7 @@ MaxPool2D::MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams &pa
void MaxPool2D::configure()
{
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
assert(input()->shape().num_dims() == 4);
const Shape &input_shape = input()->shape();
const int32_t batches = input_shape.dim(0);
@@ -54,10 +55,15 @@ void MaxPool2D::configure()
computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width);
output()->resize({batches, output_height, output_width, depth});
- if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S8)
+ if (input()->element_type() == DataType::U8)
{
- assert(input()->scale() == output()->scale());
- assert(input()->zero_point() == output()->zero_point());
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point());
+ }
+ else if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
}
}
@@ -71,6 +77,9 @@ void MaxPool2D::execute() const
case DataType::U8:
evalQuantized();
break;
+ case DataType::S16:
+ evalSInt16();
+ break;
default:
throw std::runtime_error("Unsupported type.");
}
@@ -116,5 +125,26 @@ void MaxPool2D::evalQuantized() const
getTensorShape(output()), getTensorData<uint8_t>(output()));
}
+void MaxPool2D::evalSInt16() const
+{
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_integer_ops::MaxPool(
+ params, getTensorShape(input()), getTensorData<int16_t>(input()), //
+ getTensorShape(output()), getTensorData<int16_t>(output()));
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.h b/compiler/luci-interpreter/src/kernels/MaxPool2D.h
index 7a59ff022..bb7666305 100644
--- a/compiler/luci-interpreter/src/kernels/MaxPool2D.h
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.h
@@ -39,6 +39,7 @@ public:
private:
void evalFloat() const;
void evalQuantized() const;
+ void evalSInt16() const;
private:
int32_t _padding_height{};
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp
index 390255d89..1d7fe06c4 100644
--- a/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp
@@ -54,8 +54,7 @@ TEST(MaxPool2DTest, Float)
5, 6, //
};
std::initializer_list<int32_t> ref_output_shape{1, 2, 2, 1};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -66,11 +65,9 @@ TEST(MaxPool2DTest, Uint8)
0, -6, 12, 4, //
-3, -2, 10, 7, //
};
- Tensor input_tensor{DataType::U8, {1, 2, 4, 1}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantize_input =
- quantize<uint8_t>(input_data, quant_param.first, quant_param.second);
- input_tensor.writeData(quantize_input.data(), quantize_input.size() * sizeof(uint8_t));
Pool2DParams params{};
params.padding = Padding::VALID;
@@ -86,12 +83,43 @@ TEST(MaxPool2DTest, Uint8)
std::vector<float> ref_output_data{0.0, 6.0};
std::initializer_list<int32_t> ref_output_shape{1, 1, 2, 1};
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
+TEST(MaxPool2DTest, SInt16)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 1};
+ std::vector<float> input_data{
+ 1, -1, 0, -2, 2, //
+ -7, -6, -5, -4, -3, //
+ 5, 4, 3, 6, 7, //
+ };
+ std::vector<float> ref_output_data{
+ 1, 2, //
+ 5, 6, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.2, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.filter_height = 2;
+ params.filter_width = 3;
+ params.stride_height = 1;
+ params.stride_width = 2;
+ params.activation = Activation::RELU6;
+
+ MaxPool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.cpp b/compiler/luci-interpreter/src/kernels/Maximum.cpp
new file mode 100644
index 000000000..c522b0706
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Maximum.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Maximum::Maximum(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Maximum::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type())
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type())
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Maximum::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalMaximum<float>();
+ break;
+ case DataType::U8:
+ evalMaximum<uint8_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> inline void Maximum::evalMaximum() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()),
+ [](T x, T y) { return std::max(x, y); });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.h b/compiler/luci-interpreter/src/kernels/Maximum.h
new file mode 100644
index 000000000..3c99e69c7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MAXIMUM_H
+#define LUCI_INTERPRETER_KERNELS_MAXIMUM_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Maximum : public Kernel
+{
+public:
+ Maximum(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> inline void evalMaximum() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MAXIMUM_H
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.test.cpp b/compiler/luci-interpreter/src/kernels/Maximum.test.cpp
new file mode 100644
index 000000000..2ddaeaf04
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Maximum.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MaximumTest, Float)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44};
+ std::vector<float> input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43};
+ Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{1.0, 0.0, 1.0, 12.0, -2.0, -1.43};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(MaximumTest, Uint8)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<uint8_t> input_data1{1, 0, 2, 11, 2, 23};
+ std::vector<uint8_t> input_data2{0, 0, 1, 12, 255, 1};
+ Tensor input_tensor1 = makeInputTensor<DataType::U8>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::U8>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({1, 0, 2, 12, 255, 23}));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mean.cpp b/compiler/luci-interpreter/src/kernels/Mean.cpp
index 2394e2c0e..7d022eaf8 100644
--- a/compiler/luci-interpreter/src/kernels/Mean.cpp
+++ b/compiler/luci-interpreter/src/kernels/Mean.cpp
@@ -130,8 +130,13 @@ Mean::Mean(const Tensor *input, const Tensor *axes, Tensor *output, const Reduce
void Mean::configure()
{
- assert(input()->element_type() == output()->element_type());
- assert(axes()->element_type() == DataType::S32);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32);
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+
const Shape &input_shape = input()->shape();
int input_num_dims = input_shape.num_dims();
@@ -169,6 +174,9 @@ void Mean::execute() const
case DataType::U8:
evalQuantized();
break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
default:
throw std::runtime_error("Unsupported type.");
}
@@ -245,5 +253,74 @@ void Mean::evalQuantized() const
}
}
+void Mean::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &output_shape = output()->shape();
+
+ const auto *axes_data = getTensorData<int32_t>(axes());
+ const int num_axes = axes()->shape().num_elements();
+
+ constexpr int32_t output_min = -std::numeric_limits<int16_t>::max();
+ constexpr int32_t output_max = std::numeric_limits<int16_t>::max();
+
+ // Defer to specialized implementation for 4D Mean across axes 1 & 2.
+ if (_params.keep_dims && input_shape.num_dims() == 4 && num_axes == 2 &&
+ ((axes_data[0] == 1 && axes_data[1] == 2) || (axes_data[0] == 2 && axes_data[1] == 1)))
+ {
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t depth = input_shape.dim(3);
+ assert(output_shape.num_dims() == 4);
+ assert(output_shape.dim(0) == batches);
+ assert(output_shape.dim(1) == 1);
+ assert(output_shape.dim(2) == 1);
+ assert(output_shape.dim(3) == depth);
+
+ const double real_multiplier =
+ static_cast<double>(input()->scale()) / static_cast<double>(output()->scale());
+
+ int32_t output_multiplier{};
+ int output_shift{};
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const int32_t num_elements_in_axes = input_height * input_width;
+
+ for (int32_t batch = 0; batch < batches; ++batch)
+ {
+ for (int32_t c = 0; c < depth; ++c)
+ {
+ int32_t acc = 0;
+ for (int32_t in_y = 0; in_y < input_height; ++in_y)
+ {
+ for (int32_t in_x = 0; in_x < input_width; ++in_x)
+ {
+ acc += input_data[calcOffset(input_shape, batch, in_y, in_x, c)];
+ }
+ }
+ int32_t scaled_acc =
+ tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ // Divide by the number of elements rounding to the nearest integer.
+ scaled_acc = scaled_acc > 0
+ ? (scaled_acc + num_elements_in_axes / 2) / num_elements_in_axes
+ : (scaled_acc - num_elements_in_axes / 2) / num_elements_in_axes;
+
+ scaled_acc = std::max(scaled_acc, output_min);
+ scaled_acc = std::min(scaled_acc, output_max);
+
+ output_data[calcOffset(output_shape, batch, 0, 0, c)] = scaled_acc;
+ }
+ }
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported configuration.");
+ }
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mean.h b/compiler/luci-interpreter/src/kernels/Mean.h
index 9cc793c72..1cc046894 100644
--- a/compiler/luci-interpreter/src/kernels/Mean.h
+++ b/compiler/luci-interpreter/src/kernels/Mean.h
@@ -42,6 +42,7 @@ public:
private:
void evalFloat() const;
void evalQuantized() const;
+ void evalQuantizedS16() const;
private:
std::unique_ptr<Tensor> _temp_index;
diff --git a/compiler/luci-interpreter/src/kernels/Mean.test.cpp b/compiler/luci-interpreter/src/kernels/Mean.test.cpp
index f4e411ca4..e81d2ad5f 100644
--- a/compiler/luci-interpreter/src/kernels/Mean.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Mean.test.cpp
@@ -47,8 +47,7 @@ TEST(MeanTest, FloatKeepDims)
std::vector<float> ref_output_data{10.5, 12.5, 14.5};
std::initializer_list<int32_t> ref_output_shape{1, 3, 1};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -72,8 +71,7 @@ TEST(MeanTest, FloatKeepDims4DMean)
std::vector<float> ref_output_data{6, 7, 18, 19};
std::initializer_list<int32_t> ref_output_shape{2, 1, 1, 2};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -97,8 +95,7 @@ TEST(MeanTest, FloatNotKeepDims)
std::vector<float> ref_output_data{12, 13};
std::initializer_list<int32_t> ref_output_shape{2};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -109,12 +106,10 @@ TEST(MeanTest, Uint8KeepDims)
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
std::vector<int32_t> axis_data{1};
- Tensor input_tensor{DataType::U8, {3, 2}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({3, 2}, quant_param.first, quant_param.second, input_data);
Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantize_input =
- quantize<uint8_t>(input_data, quant_param.first, quant_param.second);
- input_tensor.writeData(quantize_input.data(), quantize_input.size() * sizeof(uint8_t));
ReducerParams params{};
params.keep_dims = true;
@@ -125,9 +120,8 @@ TEST(MeanTest, Uint8KeepDims)
std::vector<float> ref_output_data{0.3, 0.35, 0.55};
std::initializer_list<int32_t> ref_output_shape{3, 1};
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data, kQuantizedTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
@@ -138,12 +132,10 @@ TEST(MeanTest, Uint8NotKeepDims)
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
std::vector<int32_t> axis_data{1};
- Tensor input_tensor{DataType::U8, {1, 3, 2}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 2}, quant_param.first, quant_param.second, input_data);
Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantize_input =
- quantize<uint8_t>(input_data, quant_param.first, quant_param.second);
- input_tensor.writeData(quantize_input.data(), quantize_input.size() * sizeof(uint8_t));
ReducerParams params{};
params.keep_dims = false;
@@ -154,12 +146,34 @@ TEST(MeanTest, Uint8NotKeepDims)
std::vector<float> ref_output_data{0.4, 0.4};
std::initializer_list<int32_t> ref_output_shape{1, 2};
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data, kQuantizedTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
+TEST(MeanTest, SInt16KeepDims4D)
+{
+ std::vector<float> input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0};
+ std::vector<int32_t> axes_data{1, 2};
+ std::vector<float> ref_output_data{6, 7, 18, 19};
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({2, 2, 3, 2}, 0.25, 0, input_data);
+ Tensor axes_tensor = makeInputTensor<DataType::S32>({2}, axes_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0);
+
+ ReducerParams params{};
+ params.keep_dims = true;
+
+ Mean kernel(&input_tensor, &axes_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 1, 1, 2}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
} // namespace
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.cpp b/compiler/luci-interpreter/src/kernels/Minimum.cpp
new file mode 100644
index 000000000..5eb13455e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Minimum.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Minimum::Minimum(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Minimum::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type())
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type())
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Minimum::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalMinimum<float>();
+ break;
+ case DataType::U8:
+ evalMinimum<uint8_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> inline void Minimum::evalMinimum() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()),
+ [](T x, T y) { return std::min(x, y); });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.h b/compiler/luci-interpreter/src/kernels/Minimum.h
new file mode 100644
index 000000000..5ff4035b4
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MINIMUM_H
+#define LUCI_INTERPRETER_KERNELS_MINIMUM_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Minimum : public Kernel
+{
+public:
+ Minimum(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> inline void evalMinimum() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MINIMUM_H
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.test.cpp b/compiler/luci-interpreter/src/kernels/Minimum.test.cpp
new file mode 100644
index 000000000..b6420dd9b
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Minimum.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MinimumTest, Float)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44};
+ std::vector<float> input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43};
+ Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{-1.0, 0.0, -1.0, 11.0, -3.0, -1.44};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(MinimumTest, Uint8)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<uint8_t> input_data1{1, 0, 2, 11, 2, 23};
+ std::vector<uint8_t> input_data2{0, 0, 1, 12, 255, 1};
+ Tensor input_tensor1 = makeInputTensor<DataType::U8>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::U8>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 1, 11, 2, 1}));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mul.test.cpp b/compiler/luci-interpreter/src/kernels/Mul.test.cpp
index f2255ac3f..fbda3bece 100644
--- a/compiler/luci-interpreter/src/kernels/Mul.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Mul.test.cpp
@@ -56,8 +56,7 @@ TEST(MulTest, Float)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(test_outputs[i], 0.0001f)))
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
<< "With shape number " << i;
}
// Re-run with exchanged inputs.
@@ -74,8 +73,7 @@ TEST(MulTest, Float)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(test_outputs[i], 0.0001f)))
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
<< "With shape number " << i;
}
}
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.cpp
new file mode 100644
index 000000000..cd2f6c2c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/NotEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+NotEqual::NotEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void NotEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void NotEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void NotEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowNotEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::NotEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void NotEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowNotEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::NotEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.h b/compiler/luci-interpreter/src/kernels/NotEqual.h
new file mode 100644
index 000000000..d729c6c14
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class NotEqual : public Kernel
+{
+public:
+ NotEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp
new file mode 100644
index 000000000..8c8712371
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/NotEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(NotEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, // Row 1
+ true, false, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(NotEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ 0.9, 0.7, 0.5, // Row 4
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, // Row 1
+ true, true, true, // Row 2
+ true, true, true, // Row 3
+ false, false, false, // Row 4
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(NotEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.55, 0.5, // Row 1
+ -1, 0, 0.05, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, true, // Row 1
+ true, false, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(NotEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ -1, 0.05, 0, 1, // Row 4
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, true, true, // Row 1
+ true, true, false, true, // Row 2
+ true, true, true, true, // Row 3
+ false, false, false, false, // Row 4
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(NotEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(NotEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pad.test.cpp b/compiler/luci-interpreter/src/kernels/Pad.test.cpp
index 15fcd0da3..4bee07629 100644
--- a/compiler/luci-interpreter/src/kernels/Pad.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Pad.test.cpp
@@ -34,12 +34,10 @@ TEST(Pad, Uint8)
std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
std::vector<float> input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3};
std::vector<int32_t> paddings_data{0, 0, 0, 2, 1, 3, 0, 0};
- Tensor input_tensor{DataType::U8, {1, 2, 3, 1}, {{quant_param.first}, {quant_param.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, input_data);
Tensor paddings_tensor = makeInputTensor<DataType::S32>({4, 2}, paddings_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
- std::vector<uint8_t> quantize_input =
- quantize<uint8_t>(input_data, quant_param.first, quant_param.second);
- input_tensor.writeData(quantize_input.data(), quantize_input.size() * sizeof(uint8_t));
Pad kernel(&input_tensor, &paddings_tensor, &output_tensor);
kernel.configure();
@@ -47,9 +45,8 @@ TEST(Pad, Uint8)
std::vector<float> ref_output_data{0, -0.8, 0.2, 0.9, 0, 0, 0, 0, 0.7, 0.1, -0.3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- EXPECT_THAT(dequantize(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data, kQuantizedTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1}));
}
@@ -69,8 +66,7 @@ TEST(Pad, Float)
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 4, 5,
6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
std::initializer_list<int32_t> ref_output_shape{2, 4, 6, 1};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/Pow.cpp b/compiler/luci-interpreter/src/kernels/Pow.cpp
new file mode 100644
index 000000000..afc10b80e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pow.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Pow::Pow(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Pow::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Pow::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ eval<float>();
+ break;
+ case DataType::S32:
+ eval<int32_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> void Pow::eval() const
+{
+ tflite::ArithmeticParams params{};
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastPow4DSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Pow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pow.h b/compiler/luci-interpreter/src/kernels/Pow.h
new file mode 100644
index 000000000..8ff865e40
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_POW_H
+#define LUCI_INTERPRETER_KERNELS_POW_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Pow : public Kernel
+{
+public:
+ Pow(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> void eval() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_POW_H
diff --git a/compiler/luci-interpreter/src/kernels/Pow.test.cpp b/compiler/luci-interpreter/src/kernels/Pow.test.cpp
new file mode 100644
index 000000000..69d8946c8
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pow.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(PowTest, SimplePow)
+{
+ std::initializer_list<int32_t> base_shape = {1, 1, 3, 2};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::vector<float> test_outputs{0.786f, 1.2838f, 1.043f, 0.7071f, 0.8f, 1.08956f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape));
+}
+
+TEST(PowTest, FloatBroadcastPow)
+{
+ std::initializer_list<int32_t> input1_shape = {1, 3};
+ std::initializer_list<int32_t> input2_shape = {3, 1};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f};
+ std::vector<float> input2_data{0.2f, 0.3f, 0.4f};
+ std::vector<float> test_outputs{0.786f, 1.18126f, 0.9791f, 0.6968f, 1.28386f,
+ 0.96888f, 0.6178f, 1.3953f, 0.9587f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(input1_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(input2_shape, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+}
+
+TEST(PowTest, IntPow)
+{
+ std::initializer_list<int32_t> base_shape = {1, 3};
+
+ std::vector<int32_t> input_data{2, 3, 4};
+ std::vector<int32_t> test_outputs{4, 27, 256};
+
+ Tensor input1_tensor = makeInputTensor<DataType::S32>(base_shape, input_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S32>(base_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<int32_t>(output_tensor), ::testing::ElementsAreArray(test_outputs));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape));
+}
+
+TEST(PowTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.0f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {4});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.cpp b/compiler/luci-interpreter/src/kernels/Prelu.cpp
new file mode 100644
index 000000000..e658d87b5
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Prelu.h"
+
+#include "kernels/BinaryOpCommon.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Prelu::Prelu(const Tensor *input, const Tensor *alpha, Tensor *output)
+ : Kernel({input, alpha}, {output})
+{
+}
+
+void Prelu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(alpha()->element_type() == output()->element_type());
+
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16)
+ {
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && alpha()->zero_point() == 0 &&
+ output()->zero_point() == 0);
+ }
+ double alpha_multiplier = input()->scale() * alpha()->scale() / output()->scale();
+ quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha);
+ double identity_multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity);
+ }
+ output()->resize(calculateShapeForBroadcast(input()->shape(), alpha()->shape()));
+}
+
+void Prelu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Prelu::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto alpha_data = getTensorData<float>(alpha());
+ const auto size = getTensorShape(input()).FlatSize();
+ auto output_data = getTensorData<float>(output());
+
+ auto PreluFunc = [](float input, float alpha) { return input >= 0.0 ? input : input * alpha; };
+
+ if (input()->shape() != alpha()->shape())
+ {
+ tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>(
+ getTensorShape(input()), getTensorData<float>(input()), getTensorShape(alpha()),
+ getTensorData<float>(alpha()), getTensorShape(output()), getTensorData<float>(output()),
+ PreluFunc);
+ }
+ else
+ {
+ for (auto i = decltype(size){0}; i < size; ++i)
+ {
+ if (input_data[i] >= 0)
+ output_data[i] = input_data[i];
+ else
+ output_data[i] = input_data[i] * alpha_data[i];
+ }
+ }
+}
+
+void Prelu::evalQuantized() const
+{
+ tflite::PreluParams op_params{};
+
+ op_params.input_offset = -input()->zero_point(); // Note the '-'.
+ op_params.alpha_offset = -alpha()->zero_point(); // Note the '-'.
+ op_params.output_offset = output()->zero_point();
+ op_params.output_shift_1 = _output_shift_identity;
+ op_params.output_multiplier_1 = _output_multiplier_identity;
+ op_params.output_shift_2 = _output_shift_alpha;
+ op_params.output_multiplier_2 = _output_multiplier_alpha;
+
+ if (input()->shape() != alpha()->shape())
+ {
+ tflite::reference_ops::BroadcastPrelu4DSlow(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(alpha()), getTensorData<uint8_t>(alpha()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Prelu<uint8_t>(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(alpha()),
+ getTensorData<uint8_t>(alpha()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+}
+
+void Prelu::evalQuantizedS16() const
+{
+ constexpr int32_t quantized_min = std::numeric_limits<int16_t>::min();
+ constexpr int32_t quantized_max = std::numeric_limits<int16_t>::max();
+
+ auto fn = [this, quantized_min, quantized_max](int16_t input_val, int16_t alpha_val) {
+ const int32_t output_val =
+ input_val >= 0
+ ? tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier_identity,
+ _output_shift_identity)
+ : tflite::MultiplyByQuantizedMultiplier(input_val * alpha_val, _output_multiplier_alpha,
+ _output_shift_alpha);
+ const int32_t clamped_output = std::min(quantized_max, std::max(quantized_min, output_val));
+ return static_cast<int16_t>(clamped_output);
+ };
+
+ BinaryOpBroadcastSlow(getTensorShape(input()), getTensorData<int16_t>(input()),
+ getTensorShape(alpha()), getTensorData<int16_t>(alpha()),
+ getTensorShape(output()), getTensorData<int16_t>(output()), fn);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.h b/compiler/luci-interpreter/src/kernels/Prelu.h
new file mode 100644
index 000000000..c7911a63f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_PRELU_H
+#define LUCI_INTERPRETER_KERNELS_PRELU_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Prelu : public Kernel
+{
+public:
+ Prelu(const Tensor *input, const Tensor *alpha, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *alpha() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ int32_t _output_multiplier_alpha = 0;
+ int32_t _output_shift_alpha = 0;
+ int32_t _output_multiplier_identity = 0;
+ int32_t _output_shift_identity = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_PRELU_H
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.test.cpp b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp
new file mode 100644
index 000000000..30702c826
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Prelu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> alpha_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
+ std::initializer_list<T> alpha_data, std::initializer_list<T> output_data)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor alpha_tensor = makeInputTensor<element_type>(alpha_shape, alpha_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(PreluTest, FloatSimple)
+{
+ Check<float>(/*input_shape=*/{2, 3}, /*alpha_shape=*/{2, 3},
+ /*output_shape=*/{2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ },
+ /*alpha_data=*/
+ {
+ 0.0f, 0.5f, 0.1f, // Row 1
+ 0.0f, 0.5f, 0.1f, // Row 2
+ },
+ /*output_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -0.5f, -0.2f, // Row 2
+ });
+
+ SUCCEED();
+}
+
+TEST(PreluTest, FloatBroadcast)
+{
+ Check<float>(/*input_shape=*/{1, 2, 2, 3}, /*alpha_shape=*/{1, 1, 3},
+ /*output_shape=*/{1, 2, 2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 1.0f, 1.0f, 1.0f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -2.0f, -2.0f, -2.0f, // Row 2, Column 2
+ },
+ /*alpha_data=*/
+ {0.0f, 1.0f, 2.0f},
+ /*output_data=*/
+ {
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 1.0f, 1.0f, 1.0f, // Row 1, Column 2
+ 0.0f, -1.0f, -2.0f, // Row 2, Column 1
+ 0.0f, -2.0f, -4.0f, // Row 2, Column 2
+ });
+
+ SUCCEED();
+}
+
+float GetTolerance(float min, float max) { return (max - min) / 255.0; }
+
+TEST(PreluTest, Uint8Simple)
+{
+ std::vector<float> input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f};
+ std::vector<float> alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f};
+ std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f};
+
+ float kQuantizedTolerance = GetTolerance(-1.0, 1.0);
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1}));
+
+ SUCCEED();
+}
+
+TEST(PreluTest, Uint8Broadcast)
+{
+ std::vector<float> input_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -0.25f, -0.25f, -0.25f, // Row 2, Column 2
+ };
+ std::vector<float> alpha_data{0.0f, 0.5f, -0.5f};
+ std::vector<float> ref_output_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ 0.0f, -0.5f, 0.5f, // Row 2, Column 1
+ 0.0f, -0.125f, 0.125f // Row 2, Column 2
+ };
+ std::vector<float> ref_quant_output_data{
+ 128, 128, 128, // Row 1, Column 1
+ 192, 192, 192, // Row 1, Column 2
+ 128, 64, 192, // Row 2, Column 1
+ 128, 112, 144 // Row 2, Column 2
+ };
+ float kQuantizedTolerance = 2 * (1. / 256);
+ const float kMin = -1;
+ const float kMax = 127.f / 128.f;
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 3}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor alpha_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 3}, quant_param.first, quant_param.second, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray(ref_quant_output_data));
+}
+
+TEST(PreluTest, SInt16Simple)
+{
+ std::vector<float> input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f};
+ std::vector<float> alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f};
+ std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f};
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(PreluTest, SInt16Broadcast)
+{
+ std::vector<float> input_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -0.25f, -0.25f, -0.25f, // Row 2, Column 2
+ };
+ std::vector<float> alpha_data{0.0f, 0.5f, -0.5f};
+ std::vector<float> ref_output_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ 0.0f, -0.5f, 0.5f, // Row 2, Column 1
+ 0.0f, -0.125f, 0.125f // Row 2, Column 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 2, 3}, 0.01, 0, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 1, 3}, 0.1, 0, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.001, 0);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(PreluTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor alpha_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PreluTest, Input_Alpha_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor alpha_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PreluTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor alpha_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu.cpp b/compiler/luci-interpreter/src/kernels/Relu.cpp
new file mode 100644
index 000000000..a2e02d708
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Relu::Relu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Relu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16)
+ {
+ double multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift);
+ }
+ output()->resize(input()->shape());
+}
+
+void Relu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Relu::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto input_shape = getTensorShape(input());
+ auto output_data = getTensorData<float>(output());
+ auto output_shape = getTensorShape(output());
+
+ tflite::optimized_ops::Relu(input_shape, input_data, output_shape, output_data);
+}
+
+void Relu::evalQuantized() const
+{
+ tflite::ReluParams params;
+ params.input_offset = input()->zero_point();
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = _output_multiplier;
+ params.output_shift = _output_shift;
+
+ params.quantized_activation_min =
+ std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset);
+ params.quantized_activation_max = static_cast<int32_t>(std::numeric_limits<uint8_t>::max());
+
+ tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+void Relu::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ constexpr int32_t output_min = 0;
+ constexpr int32_t output_max = std::numeric_limits<int16_t>::max();
+
+ const int32_t num_elements = input()->shape().num_elements();
+
+ for (int32_t i = 0; i < num_elements; ++i)
+ {
+ const int32_t input_val = input_data[i];
+ int32_t output_val =
+ tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift);
+ output_val = std::max(output_val, output_min);
+ output_val = std::min(output_val, output_max);
+ output_data[i] = static_cast<int16_t>(output_val);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu.h b/compiler/luci-interpreter/src/kernels/Relu.h
new file mode 100644
index 000000000..b813f0cdf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RELU_H
+#define LUCI_INTERPRETER_KERNELS_RELU_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Relu : public Kernel
+{
+public:
+ Relu(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ int32_t _output_multiplier{0};
+ int32_t _output_shift{0};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RELU_H
diff --git a/compiler/luci-interpreter/src/kernels/Relu.test.cpp b/compiler/luci-interpreter/src/kernels/Relu.test.cpp
new file mode 100644
index 000000000..cabefa733
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(ReluTest, FloatSimple)
+{
+ std::vector<float> input_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ };
+
+ std::vector<float> ref_output_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, 0.0f, 0.0f, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(ReluTest, Uint8Quantized)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float f_min = (-128.0 / 128.0) * 8;
+ const float f_max = (127.0 / 128.0) * 8;
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({128, 128, 160, 192, 176, 128, 240, 144}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1}));
+}
+
+TEST(ReluTest, Uint8Requantized)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float in_min = (-128.0 / 128.0) * 8;
+ const float in_max = (127.0 / 128.0) * 8;
+ const float out_min = (0.0 / 256.0) * 8;
+ const float out_max = (255.0 / 256.0) * 8;
+
+ std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first,
+ quant_input.second, input_data);
+
+ std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 64, 128, 96, 0, 224, 32}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1}));
+}
+
+TEST(ReluTest, SInt16)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+ std::vector<float> ref_output_data{
+ 0, 0, 2, 4, //
+ 3, 0, 7, 1, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 4, 1}, 0.5, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.25, 0);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(ReluTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ReluTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.cpp b/compiler/luci-interpreter/src/kernels/Relu6.cpp
new file mode 100644
index 000000000..1046ef27b
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu6.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Relu6::Relu6(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Relu6::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+
+ if (input()->element_type() == DataType::U8)
+ {
+ double multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift);
+ }
+ output()->resize(input()->shape());
+}
+
+void Relu6::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Relu6::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto input_shape = getTensorShape(input());
+ auto output_data = getTensorData<float>(output());
+ auto output_shape = getTensorShape(output());
+
+ tflite::optimized_ops::Relu6(input_shape, input_data, output_shape, output_data);
+}
+
+void Relu6::evalQuantized() const
+{
+ tflite::ReluParams params;
+ params.input_offset = input()->zero_point();
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = _output_multiplier;
+ params.output_shift = _output_shift;
+
+ params.quantized_activation_min =
+ std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset);
+ params.quantized_activation_max =
+ std::min(static_cast<int32_t>(std::numeric_limits<uint8_t>::max()),
+ params.output_offset + static_cast<int32>(roundf(6.f / output()->scale())));
+
+ tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.h b/compiler/luci-interpreter/src/kernels/Relu6.h
new file mode 100644
index 000000000..f5030b588
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RELU6_H
+#define LUCI_INTERPRETER_KERNELS_RELU6_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Relu6 : public Kernel
+{
+public:
+ Relu6(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _output_multiplier{0};
+ int32_t _output_shift{0};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RELU6_H
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.test.cpp b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp
new file mode 100644
index 000000000..a7f104d85
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu6.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(Relu6Test, FloatSimple)
+{
+ std::vector<float> input_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 7.0f, -1.0f, -2.0f, // Row 2
+ };
+
+ std::vector<float> ref_output_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 6.0f, 0.0f, 0.0f, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(Relu6Test, Uint8Quantized)
+{
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float f_min = (-128.0 / 128.0) * 10;
+ const float f_max = (127.0 / 128.0) * 10;
+ const float tolerance = (f_max - f_min) / 255.0;
+
+ std::vector<float> input_data{
+ 0, -6, 2, 8, //
+ -2, 3, 7, 1, //
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({128, 128, 154, 205, 128, 166, 205, 141}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance));
+}
+
+TEST(Relu6Test, Uint8Requantized)
+{
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float in_min = (-128.0 / 128.0) * 10;
+ const float in_max = (127.0 / 128.0) * 10;
+ const float out_min = (0.0 / 256.0) * 0;
+ const float out_max = (255.0 / 256.0) * 6;
+ const float tolerance = (in_max - in_min) / 255.0;
+
+ std::vector<float> input_data{
+ 0, -6, 2, 8, //
+ -2, 3, 7, 1, //
+ };
+
+ std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first,
+ quant_input.second, input_data);
+
+ std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 87, 255, 0, 127, 255, 43}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance));
+}
+
+TEST(Relu6Test, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(Relu6Test, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Reshape.test.cpp b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp
index 7255b8132..38159380f 100644
--- a/compiler/luci-interpreter/src/kernels/Reshape.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp
@@ -42,8 +42,7 @@ TEST(ReshapeTest, Regular)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(input_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(input_data));
}
TEST(ReshapeTest, UnknownDimension)
@@ -60,8 +59,7 @@ TEST(ReshapeTest, UnknownDimension)
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(input_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(input_data));
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp
new file mode 100644
index 000000000..9385855cf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeBilinear.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+ResizeBilinear::ResizeBilinear(const Tensor *input, const Tensor *size, Tensor *output,
+ const ResizeBilinearParams &params)
+ : KernelWithParams<ResizeBilinearParams>({input, size}, {output}, params)
+{
+}
+
+void ResizeBilinear::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1);
+ LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32);
+ if (params().half_pixel_centers && params().align_corners)
+ throw std::runtime_error("If half_pixel_centers is True, align_corners must be False.");
+ LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2);
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = getTensorData<int32_t>(size())[0];
+ output_shape.dim(2) = getTensorData<int32_t>(size())[1];
+ output_shape.dim(3) = input()->shape().dim(3);
+ output()->resize(output_shape);
+}
+
+void ResizeBilinear::execute() const
+{
+ tflite::ResizeBilinearParams op_params{};
+ op_params.align_corners = params().align_corners;
+ op_params.half_pixel_centers = params().half_pixel_centers;
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::ResizeBilinear(
+ op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(size()),
+ getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::ResizeBilinear(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.h b/compiler/luci-interpreter/src/kernels/ResizeBilinear.h
new file mode 100644
index 000000000..b7bdc2ab7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
+#define LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ResizeBilinear : public KernelWithParams<ResizeBilinearParams>
+{
+public:
+ ResizeBilinear(const Tensor *input, const Tensor *shape, Tensor *output,
+ const ResizeBilinearParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *size() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp
new file mode 100644
index 000000000..51c1359da
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeBilinear.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data, std::initializer_list<float> output_data,
+ bool align_corners, bool half_pixel_centers)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data,
+ std::initializer_list<float> output_data, bool align_corners,
+ bool half_pixel_centers)
+{
+ // On TFlite example use Uint8 value it self, so this means quant param scale 1.0f and zero
+ // point 0.
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, 1.0, 0, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0, 0);
+
+ ResizeBilinearParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class ResizeBilinearTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ResizeBilinearTest, DataTypes);
+
+TYPED_TEST(ResizeBilinearTest, SimpleTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 5, 6, //
+ 7, 9, 10, //
+ 9, 11, 12, //
+ 4, 8, 10, //
+ 8, 12, 14, //
+ 10, 14, 16, //
+ },
+ false, false);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, HalfPixelCenterFloatTest)
+{
+ Check<float>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 1, 2, //
+ 3, 4, //
+ 1, 2, //
+ 3, 4 //
+ },
+ {3, 3},
+ {
+ 1, 1.5, 2, //
+ 2, 2.5, 3, //
+ 3, 3.5, 4, //
+ 1, 1.5, 2, //
+ 2, 2.5, 3, //
+ 3, 3.5, 4, //
+ },
+ false, true);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, HalfPixelCenterUint8Test)
+{
+ Check<uint8_t>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 12, 16 //
+ },
+ {3, 3},
+ {
+ 2, 4, 6, //
+ 6, 7, 9, //
+ 9, 10, 12, //
+ 4, 7, 10, //
+ 8, 10, 13, //
+ 12, 14, 16, //
+ },
+ false, true);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, InputShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, SizeShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, SizeDimInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, InvalidParams_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = true;
+ params.half_pixel_centers = true;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp
new file mode 100644
index 000000000..e4ad8f742
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeNearestNeighbor.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+ResizeNearestNeighbor::ResizeNearestNeighbor(const Tensor *input, const Tensor *size,
+ Tensor *output,
+ const ResizeNearestNeighborParams &params)
+ : KernelWithParams<ResizeNearestNeighborParams>({input, size}, {output}, params)
+{
+}
+
+void ResizeNearestNeighbor::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1);
+ LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32);
+ LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2);
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = getTensorData<int32_t>(size())[0];
+ output_shape.dim(2) = getTensorData<int32_t>(size())[1];
+ output_shape.dim(3) = input()->shape().dim(3);
+ output()->resize(output_shape);
+}
+
+void ResizeNearestNeighbor::execute() const
+{
+ tflite::ResizeNearestNeighborParams op_params{};
+ op_params.align_corners = params().align_corners;
+ op_params.half_pixel_centers = params().half_pixel_centers;
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::reference_ops::ResizeNearestNeighbor(
+ op_params, getTensorShape(input()), getTensorData<int32_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<int32_t>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::ResizeNearestNeighbor(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h
new file mode 100644
index 000000000..137d031cf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
+#define LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ResizeNearestNeighbor : public KernelWithParams<ResizeNearestNeighborParams>
+{
+public:
+ ResizeNearestNeighbor(const Tensor *input, const Tensor *shape, Tensor *output,
+ const ResizeNearestNeighborParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *size() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp
new file mode 100644
index 000000000..9a804cca7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeNearestNeighbor.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data, std::initializer_list<float> output_data,
+ bool align_corners, bool half_pixel_centers)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data,
+ std::initializer_list<float> output_data, bool align_corners,
+ bool half_pixel_centers)
+{
+ std::pair<float, int32_t> quant_param =
+ quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f,
+ std::max(input_data) > 0 ? std::max(input_data) : 0.f);
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.first);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class ResizeNearestNeighborTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ResizeNearestNeighborTest, DataTypes);
+
+TYPED_TEST(ResizeNearestNeighborTest, SimpleTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 3, 6, //
+ 3, 3, 6, //
+ 9, 9, 12, //
+ 4, 4, 10, //
+ 4, 4, 10, //
+ 10, 10, 16, //
+ },
+ false, false);
+}
+
+TYPED_TEST(ResizeNearestNeighborTest, AlignCenterTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 6, 6, //
+ 9, 12, 12, //
+ 9, 12, 12, //
+ 4, 10, 10, //
+ 10, 16, 16, //
+ 10, 16, 16, //
+ },
+ true, false);
+}
+
+TYPED_TEST(ResizeNearestNeighborTest, HalfPixelCenterTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 6, 6, //
+ 9, 12, 12, //
+ 9, 12, 12, //
+ 4, 10, 10, //
+ 10, 16, 16, //
+ 10, 16, 16, //
+ },
+ false, true);
+}
+
+TEST(ResizeNearestNeighborTest, InputShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeNearestNeighborTest, SizeShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeNearestNeighborTest, SizeDimInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp
index 69b55d2f2..d33b800be 100644
--- a/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp
@@ -29,17 +29,14 @@ using namespace testing;
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- Tensor input_tensor{DataType::FLOAT32, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(float));
-
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
Rsqrt kernel(&input_tensor, &output_tensor);
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.cpp b/compiler/luci-interpreter/src/kernels/Softmax.cpp
index 2fb7f3f2c..642c0ad75 100644
--- a/compiler/luci-interpreter/src/kernels/Softmax.cpp
+++ b/compiler/luci-interpreter/src/kernels/Softmax.cpp
@@ -19,6 +19,7 @@
#include "kernels/Utils.h"
#include <tensorflow/lite/kernels/internal/reference/softmax.h>
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
#include <stdexcept>
@@ -35,7 +36,15 @@ Softmax::Softmax(const Tensor *input, Tensor *output, const SoftmaxParams &param
void Softmax::configure()
{
- assert(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= 1);
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 0);
+ tflite::SoftmaxParams op_params{};
+ op_params.table = _table;
+ tflite::optimized_ops::PopulateSoftmaxLookupTable(&op_params, input()->scale(), params().beta);
+ }
output()->resize(input()->shape());
}
@@ -46,6 +55,12 @@ void Softmax::execute() const
case DataType::FLOAT32:
evalFloat();
break;
+ case DataType::S8:
+ evalQuantized<int8_t>();
+ break;
+ case DataType::U8:
+ evalQuantized<uint8_t>();
+ break;
default:
throw std::runtime_error("Unsupported type.");
}
@@ -53,12 +68,23 @@ void Softmax::execute() const
void Softmax::evalFloat() const
{
- tflite::SoftmaxParams params{};
- params.beta = _params.beta;
+ tflite::SoftmaxParams op_params{};
+ op_params.beta = params().beta;
- tflite::reference_ops::Softmax(params, getTensorShape(input()), getTensorData<float>(input()),
+ tflite::reference_ops::Softmax(op_params, getTensorShape(input()), getTensorData<float>(input()),
getTensorShape(output()), getTensorData<float>(output()));
}
+template <typename T> void Softmax::evalQuantized() const
+{
+ tflite::SoftmaxParams op_params{};
+ op_params.table = const_cast<float *>(_table);
+ op_params.zero_point = output()->zero_point();
+ op_params.scale = output()->scale();
+
+ tflite::optimized_ops::Softmax(op_params, getTensorShape(input()), getTensorData<T>(input()),
+ getTensorShape(output()), getTensorData<T>(output()));
+}
+
} // namespace kernels
} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.h b/compiler/luci-interpreter/src/kernels/Softmax.h
index 2e4eda492..1f281df1c 100644
--- a/compiler/luci-interpreter/src/kernels/Softmax.h
+++ b/compiler/luci-interpreter/src/kernels/Softmax.h
@@ -38,6 +38,9 @@ public:
private:
void evalFloat() const;
+ template <typename T> void evalQuantized() const;
+
+ float _table[256];
};
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.test.cpp b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp
index 2193c3e83..d3d8209a5 100644
--- a/compiler/luci-interpreter/src/kernels/Softmax.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp
@@ -26,15 +26,10 @@ namespace
using namespace testing;
-TEST(SoftmaxTest, Float)
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- Shape input_shape{2, 1, 2, 3};
- std::vector<float> input_data{
- 5, -9, 8, //
- -7, 2, -4, //
- 1, -2, 9, //
- 3, -6, -1, //
- };
Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
@@ -45,14 +40,61 @@ TEST(SoftmaxTest, Float)
kernel.configure();
kernel.execute();
- std::vector<float> ref_output_data{
- 0.38514, 0.09497, 0.51989, //
- 0.20792, 0.51141, 0.28067, //
- 0.25212, 0.18678, 0.56110, //
- 0.48149, 0.19576, 0.32275, //
- };
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), output_shape);
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> input_quant_param =
+ quantizationParams<uint8_t>(std::min<float>(std::min<float>(input_data), 0.f),
+ std::max<float>(std::max<float>(input_data), 0.f));
+ std::pair<float, int32_t> output_quant_param =
+ quantizationParams<uint8_t>(std::min<float>(std::min<float>(output_data), 0.f),
+ std::max<float>(std::max<float>(output_data), 0.f));
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ SoftmaxParams params{};
+ params.beta = 0.1;
+
+ Softmax kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class SoftmaxTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SoftmaxTest, DataTypes);
+
+TYPED_TEST(SoftmaxTest, Simple)
+{
+ Check<TypeParam>({2, 1, 2, 3}, {2, 1, 2, 3},
+ {
+ 5, -9, 8, //
+ -7, 2, -4, //
+ 1, -2, 9, //
+ 3, -6, -1, //
+ },
+ {
+ 0.38514, 0.09497, 0.51989, //
+ 0.20792, 0.51141, 0.28067, //
+ 0.25212, 0.18678, 0.56110, //
+ 0.48149, 0.19576, 0.32275, //
+ });
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp b/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp
index e4a0fd642..77b6655dc 100644
--- a/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp
@@ -35,13 +35,13 @@ TYPED_TEST_CASE(SpaceToDepthTest, DataTypes);
TYPED_TEST(SpaceToDepthTest, SimpleCase)
{
+ constexpr DataType element_type = getElementType<TypeParam>();
std::vector<TypeParam> input_data{1, 5, 6, 7, 2, 3, 4, 8};
Shape input_shape{1, 2, 2, 2};
- Tensor input_tensor{getElementType<TypeParam>(), input_shape, {{}, {}}, ""};
- input_tensor.writeData(input_data.data(), input_data.size() * sizeof(TypeParam));
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
std::vector<TypeParam> output_data{1, 5, 6, 7, 2, 3, 4, 8};
std::vector<int32_t> output_shape{1, 1, 1, 8};
- Tensor output_tensor = makeOutputTensor(getElementType<TypeParam>());
+ Tensor output_tensor = makeOutputTensor(element_type);
SpaceToDepthParams params{};
params.block_size = 2;
diff --git a/compiler/luci-interpreter/src/kernels/Split.test.cpp b/compiler/luci-interpreter/src/kernels/Split.test.cpp
index 11d0b1ea9..2147d15c1 100644
--- a/compiler/luci-interpreter/src/kernels/Split.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Split.test.cpp
@@ -30,11 +30,11 @@ using namespace testing;
template <typename T>
void Check(int axis, int num_splits, std::initializer_list<int32_t> input_shape,
std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
- std::vector<std::vector<T>> output_data, DataType element_type)
+ std::vector<std::vector<T>> output_data)
{
+ constexpr DataType element_type = getElementType<T>();
Tensor axis_tensor = makeInputTensor<DataType::S32>({}, {axis});
- Tensor input_tensor{element_type, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(T));
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
std::vector<Tensor> output_tensors;
output_tensors.reserve(num_splits);
@@ -74,51 +74,42 @@ TYPED_TEST(SplitTest, FourDimensional)
{
{1, 2, 3, 4, 5, 6, 7, 8}, //
{9, 10, 11, 12, 13, 14, 15, 16}, //
- },
- getElementType<TypeParam>());
+ });
Check<TypeParam>(
/*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2},
- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
- {
- {1, 2, 3, 4, 9, 10, 11, 12}, //
- {5, 6, 7, 8, 13, 14, 15, 16}, //
- },
- getElementType<TypeParam>());
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 3, 4, 9, 10, 11, 12}, //
+ {5, 6, 7, 8, 13, 14, 15, 16}, //
+ });
Check<TypeParam>(
/*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2},
- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
- {
- {1, 2, 5, 6, 9, 10, 13, 14}, //
- {3, 4, 7, 8, 11, 12, 15, 16}, //
- },
- getElementType<TypeParam>());
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 5, 6, 9, 10, 13, 14}, //
+ {3, 4, 7, 8, 11, 12, 15, 16}, //
+ });
Check<TypeParam>(
/*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1},
- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
- {
- {1, 3, 5, 7, 9, 11, 13, 15}, //
- {2, 4, 6, 8, 10, 12, 14, 16}, //
- },
- getElementType<TypeParam>());
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 3, 5, 7, 9, 11, 13, 15}, //
+ {2, 4, 6, 8, 10, 12, 14, 16}, //
+ });
}
TYPED_TEST(SplitTest, OneDimensional)
{
Check<TypeParam>(
/*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8},
- {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}, getElementType<TypeParam>());
+ {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}});
}
TYPED_TEST(SplitTest, NegativeAxis)
{
Check<TypeParam>(
/*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2},
- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
- {
- {1, 2, 3, 4, 5, 6, 7, 8}, //
- {9, 10, 11, 12, 13, 14, 15, 16},
- },
- getElementType<TypeParam>());
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 3, 4, 5, 6, 7, 8}, //
+ {9, 10, 11, 12, 13, 14, 15, 16},
+ });
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp
index cdd208280..504db4493 100644
--- a/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp
@@ -29,17 +29,14 @@ using namespace testing;
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
std::initializer_list<float> input_data, std::initializer_list<float> output_data)
{
- Tensor input_tensor{DataType::FLOAT32, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(float));
-
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
Sqrt kernel(&input_tensor, &output_tensor);
kernel.configure();
kernel.execute();
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ::testing::ElementsAreArray(ArrayFloatNear(output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp
index 3a34284dd..ff9fb09d2 100644
--- a/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp
@@ -29,17 +29,14 @@ using namespace testing;
template <typename T>
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
std::initializer_list<T> input_data, std::initializer_list<T> output_data,
- DataType element_type, std::vector<int32_t> squeeze_dims)
+ std::initializer_list<int32_t> squeeze_dims)
{
- Tensor input_tensor{element_type, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(T));
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(element_type);
SqueezeParams params{};
- for (size_t i = 0; i < squeeze_dims.size(); i++)
- {
- params.squeeze_dims.push_back(squeeze_dims.at(i));
- }
+ params.squeeze_dims = squeeze_dims;
Squeeze kernel(&input_tensor, &output_tensor, params);
kernel.configure();
@@ -64,7 +61,7 @@ TYPED_TEST(SqueezeTest, TotalTest)
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
/*output_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
- getElementType<TypeParam>(), {-1, 0});
+ {-1, 0});
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp b/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp
index 5ab06e2ec..66dffcaf2 100644
--- a/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp
@@ -36,17 +36,12 @@ TEST(StridedSliceTest, Float)
std::vector<int32_t> end_data{1, 3, 2};
Shape strides_shape{3};
std::vector<int32_t> strides_data{1, 1, 1};
- Tensor input_tensor{DataType::FLOAT32, input_shape, {}, ""};
- Tensor begin_tensor{DataType::S32, begin_shape, {}, ""};
- Tensor end_tensor{DataType::S32, end_shape, {}, ""};
- Tensor strides_tensor{DataType::S32, strides_shape, {}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor begin_tensor = makeInputTensor<DataType::S32>(begin_shape, begin_data);
+ Tensor end_tensor = makeInputTensor<DataType::S32>(end_shape, end_data);
+ Tensor strides_tensor = makeInputTensor<DataType::S32>(strides_shape, strides_data);
Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
- input_tensor.writeData(input_data.data(), input_data.size() * sizeof(float));
- begin_tensor.writeData(begin_data.data(), begin_data.size() * sizeof(int32_t));
- end_tensor.writeData(end_data.data(), end_data.size() * sizeof(int32_t));
- strides_tensor.writeData(strides_data.data(), strides_data.size() * sizeof(int32_t));
-
StridedSliceParams params{};
params.begin_mask = 0;
params.end_mask = 0;
@@ -61,8 +56,7 @@ TEST(StridedSliceTest, Float)
std::vector<int32_t> output_shape{3, 2};
std::vector<float> output_data{1, 2, 3, 4, 5, 6};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
@@ -70,24 +64,18 @@ TEST(StridedSliceTest, Uint8)
{
Shape input_shape{2, 3, 2};
std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
- std::vector<uint8_t> quant_input_data = quantize<uint8_t>(input_data, 1.0f, 0);
Shape begin_shape{3};
std::vector<int32_t> begin_data{0, 0, 0};
Shape end_shape{3};
std::vector<int32_t> end_data{1, 3, 2};
Shape strides_shape{3};
std::vector<int32_t> strides_data{1, 1, 1};
- Tensor input_tensor{DataType::U8, input_shape, {{1.0f}, {0}}, ""};
- Tensor begin_tensor{DataType::S32, begin_shape, {}, ""};
- Tensor end_tensor{DataType::S32, end_shape, {}, ""};
- Tensor strides_tensor{DataType::S32, strides_shape, {}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, 1.0f, 0, input_data);
+ Tensor begin_tensor = makeInputTensor<DataType::S32>(begin_shape, begin_data);
+ Tensor end_tensor = makeInputTensor<DataType::S32>(end_shape, end_data);
+ Tensor strides_tensor = makeInputTensor<DataType::S32>(strides_shape, strides_data);
Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0f, 0);
- input_tensor.writeData(quant_input_data.data(), quant_input_data.size() * sizeof(uint8_t));
- begin_tensor.writeData(begin_data.data(), begin_data.size() * sizeof(int32_t));
- end_tensor.writeData(end_data.data(), end_data.size() * sizeof(int32_t));
- strides_tensor.writeData(strides_data.data(), strides_data.size() * sizeof(int32_t));
-
StridedSliceParams params{};
params.begin_mask = 0;
params.end_mask = 0;
@@ -102,9 +90,7 @@ TEST(StridedSliceTest, Uint8)
std::vector<int32_t> output_shape{3, 2};
std::vector<float> output_data{1, 2, 3, 4, 5, 6};
- EXPECT_THAT(dequantize(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(output_data)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(output_data));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/Sub.cpp b/compiler/luci-interpreter/src/kernels/Sub.cpp
new file mode 100644
index 000000000..dd9c1102f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sub.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/legacy_optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Sub::Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams &params)
+ : KernelWithParams<SubParams>({input1, input2}, {output}, params)
+{
+}
+
+void Sub::configure()
+{
+ LUCI_INTERPRETER_CHECK(!(input1()->element_type() != input2()->element_type()))
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Sub::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Sub::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastSubSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::optimized_ops::Sub(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Sub::evalQuantized() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const int left_shift = 20;
+ const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale);
+ const double real_input1_multiplier = input1_scale / twice_max_input_scale;
+ const double real_input2_multiplier = input2_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{};
+ int input1_shift{}, input2_shift{}, output_shift{};
+ quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift);
+ quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift);
+ quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.left_shift = left_shift;
+ // The kernel expects inputs' zero points to be negated.
+ params.input1_offset = -input1()->zero_point(); // Note the '-'.
+ params.input1_multiplier = input1_multiplier;
+ params.input1_shift = input1_shift;
+ params.input2_offset = -input2()->zero_point(); // Note the '-'.
+ params.input2_multiplier = input2_multiplier;
+ params.input2_shift = input2_shift;
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastSubSlow(
+ params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Sub.h b/compiler/luci-interpreter/src/kernels/Sub.h
new file mode 100644
index 000000000..d7940b5c6
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SUB_H
+#define LUCI_INTERPRETER_KERNELS_SUB_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Sub : public KernelWithParams<SubParams>
+{
+public:
+ Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SUB_H
diff --git a/compiler/luci-interpreter/src/kernels/Sub.test.cpp b/compiler/luci-interpreter/src/kernels/Sub.test.cpp
new file mode 100644
index 000000000..9f77fe7e0
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.test.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sub.h"
+#include "kernels/TestUtils.h"
+
+#include <algorithm>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+using std::pair;
+using std::vector;
+using std::transform;
+using std::initializer_list;
+
+// for quantized Add, the error shouldn't exceed step
+float GetTolerance(float min, float max)
+{
+ float kQuantizedStep = (max - min) / 255.0;
+ return kQuantizedStep;
+}
+
+TEST(SubTest, Uint8)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ vector<float> base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ vector<Shape> test_shapes = {{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ vector<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ vector<vector<int32_t>> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+ vector<vector<float>> output_data = {
+ {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f,
+ 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
+ -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f},
+ {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f},
+ {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
+ 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
+ -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f},
+ {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}};
+
+ float kQuantizedTolerance = GetTolerance(-3.f, 3.f);
+ pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f);
+ for (size_t i = 0; i < output_data.size(); ++i)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ SubParams params{};
+ params.activation = Activation::NONE;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+
+ // Inversion step for output_data, because subtract is not commutative operation
+ auto multiply = [](auto &i) {
+ transform(i.begin(), i.end(), i.begin(), [](auto &value) { return value * -1.0f; });
+ };
+ for_each(output_data.begin(), output_data.end(), multiply);
+
+ // Re-run with exchanged inputs.
+ for (size_t i = 0; i < output_data.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ SubParams params{};
+ params.activation = Activation::NONE;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+}
+
+TEST(SubTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ vector<vector<int32_t>> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+ vector<vector<float>> test_outputs = {
+ {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f,
+ 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
+ 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}};
+
+ vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+}
+
+TEST(SubTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(SubTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Tanh.test.cpp b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp
index 392b8672d..f91ffa1db 100644
--- a/compiler/luci-interpreter/src/kernels/Tanh.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp
@@ -45,8 +45,7 @@ TEST(TanhTest, Float)
0, -0.9999877, 0.9640275, 0.999329, //
0.99505475, -0.9640275, 1, 0.7615941, //
};
- EXPECT_THAT(extractTensorData<float>(output_tensor),
- ElementsAreArray(ArrayFloatNear(ref_output_data)));
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
}
TEST(TanhTest, Uint8)
@@ -70,13 +69,10 @@ TEST(TanhTest, Uint8)
0, -6, 2, 4, //
-4, -2, 8, 1, //
};
- Tensor input_tensor{
- DataType::U8, {2, 6, 4, 1}, {{input_quant_param.first}, {input_quant_param.second}}, ""};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({2, 6, 4, 1}, input_quant_param.first,
+ input_quant_param.second, input_data);
Tensor output_tensor =
makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
- std::vector<uint8_t> quantize_input =
- quantize<uint8_t>(input_data, input_quant_param.first, input_quant_param.second);
- input_tensor.writeData(quantize_input.data(), quantize_input.size() * sizeof(uint8_t));
Tanh kernel(&input_tensor, &output_tensor);
kernel.configure();
@@ -97,9 +93,7 @@ TEST(TanhTest, Uint8)
-0.999329, -0.96402, 0.99999, 0.76159, //
};
std::vector<int32_t> ref_output_shape{2, 6, 4, 1};
- EXPECT_THAT(dequantize<uint8_t>(extractTensorData<uint8_t>(output_tensor), output_tensor.scale(),
- output_tensor.zero_point()),
- ElementsAreArray(ArrayFloatNear(ref_output_data, kTanhTolerance)));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data, kTanhTolerance));
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
}
diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.cpp b/compiler/luci-interpreter/src/kernels/TestUtils.cpp
index 2c8a6ae78..4c19c8810 100644
--- a/compiler/luci-interpreter/src/kernels/TestUtils.cpp
+++ b/compiler/luci-interpreter/src/kernels/TestUtils.cpp
@@ -17,6 +17,8 @@
#include "kernels/TestUtils.h"
+#include <stdexcept>
+
namespace luci_interpreter
{
namespace kernels
@@ -34,7 +36,25 @@ Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point)
return Tensor(element_type, {}, {{scale}, {zero_point}}, "");
}
-std::vector<Matcher<float>> ArrayFloatNear(const std::vector<float> &values, float max_abs_error)
+std::vector<float> dequantizeTensorData(const Tensor &tensor)
+{
+ if (tensor.element_type() == DataType::U8)
+ {
+ return dequantize(extractTensorData<uint8_t>(tensor), tensor.scale(), tensor.zero_point());
+ }
+ else if (tensor.element_type() == DataType::S16)
+ {
+ // S16 quantization is symmetric, so zero point should be zero.
+ assert(tensor.zero_point() == 0);
+ return dequantize(extractTensorData<int16_t>(tensor), tensor.scale(), 0);
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+Matcher<std::vector<float>> FloatArrayNear(const std::vector<float> &values, float max_abs_error)
{
std::vector<Matcher<float>> matchers;
matchers.reserve(values.size());
@@ -42,7 +62,7 @@ std::vector<Matcher<float>> ArrayFloatNear(const std::vector<float> &values, flo
{
matchers.emplace_back(FloatNear(v, max_abs_error));
}
- return matchers;
+ return ElementsAreArray(matchers);
}
std::vector<int32_t> extractTensorShape(const Tensor &tensor)
diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.h b/compiler/luci-interpreter/src/kernels/TestUtils.h
index 5311a1949..e5bd6a2db 100644
--- a/compiler/luci-interpreter/src/kernels/TestUtils.h
+++ b/compiler/luci-interpreter/src/kernels/TestUtils.h
@@ -32,6 +32,9 @@ namespace kernels
namespace testing
{
+template <typename T>
+std::vector<T> quantize(const std::vector<float> &data, float scale, int32_t zero_point);
+
template <DataType DT>
Tensor makeInputTensor(const Shape &shape, const std::vector<typename DataTypeImpl<DT>::Type> &data)
{
@@ -40,6 +43,17 @@ Tensor makeInputTensor(const Shape &shape, const std::vector<typename DataTypeIm
return tensor;
}
+template <DataType DT>
+Tensor makeInputTensor(const Shape &shape, float scale, int32_t zero_point,
+ const std::vector<float> &data)
+{
+ using NativeT = typename DataTypeImpl<DT>::Type;
+ Tensor tensor(DT, shape, {{scale}, {zero_point}}, "");
+ std::vector<NativeT> quantized_data = quantize<NativeT>(data, scale, zero_point);
+ tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT));
+ return tensor;
+}
+
Tensor makeOutputTensor(DataType element_type);
Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point);
@@ -65,27 +79,44 @@ template <typename T> std::vector<T> extractTensorData(const Tensor &tensor)
return std::vector<T>(data_ptr, data_ptr + tensor.shape().num_elements());
}
-std::vector<::testing::Matcher<float>> ArrayFloatNear(const std::vector<float> &values,
+std::vector<float> dequantizeTensorData(const Tensor &tensor);
+
+// Array version of `::testing::FloatNear` matcher.
+::testing::Matcher<std::vector<float>> FloatArrayNear(const std::vector<float> &values,
float max_abs_error = 1.0e-5f);
template <typename T>
-inline std::vector<T> quantize(const std::vector<float> &data, float scale, int32_t zero_point)
+std::vector<T> quantize(const std::vector<float> &data, float scale, int32_t zero_point)
{
- assert(!std::is_floating_point<T>::value);
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
+
+ float q_min{}, q_max{};
+ if (std::is_signed<T>::value)
+ {
+ // For now, assume that signed type implies signed symmetric quantization.
+ assert(zero_point == 0);
+ q_min = -std::numeric_limits<T>::max();
+ q_max = std::numeric_limits<T>::max();
+ }
+ else
+ {
+ q_min = 0;
+ q_max = std::numeric_limits<T>::max();
+ }
+
std::vector<T> q;
for (const auto &f : data)
{
- q.push_back(static_cast<T>(std::max<float>(
- std::numeric_limits<T>::lowest(),
- std::min<float>(std::numeric_limits<T>::max(), std::round(zero_point + (f / scale))))));
+ q.push_back(static_cast<T>(
+ std::max<float>(q_min, std::min<float>(q_max, std::round(zero_point + (f / scale))))));
}
return q;
}
template <typename T>
-inline std::vector<float> dequantize(const std::vector<T> &data, float scale, int32_t zero_point)
+std::vector<float> dequantize(const std::vector<T> &data, float scale, int32_t zero_point)
{
- assert(!std::is_floating_point<T>::value);
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
std::vector<float> f;
for (const T &q : data)
{
@@ -94,18 +125,16 @@ inline std::vector<float> dequantize(const std::vector<T> &data, float scale, in
return f;
}
+// NOTE Returns scale and zero point for _asymmetric_ range (both signed and unsigned).
template <typename T> std::pair<float, int32_t> quantizationParams(float f_min, float f_max)
{
- if (std::is_floating_point<T>::value)
- {
- return {1.0f, 0};
- }
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
int32_t zero_point = 0;
- double scale = 0;
+ float scale = 0;
const T qmin = std::numeric_limits<T>::lowest();
const T qmax = std::numeric_limits<T>::max();
- const double qmin_double = qmin;
- const double qmax_double = qmax;
+ const float qmin_double = qmin;
+ const float qmax_double = qmax;
// 0 should always be a representable value. Let's assume that the initial
// min,max range contains 0.
assert(f_max >= 0);
@@ -131,16 +160,16 @@ template <typename T> std::pair<float, int32_t> quantizationParams(float f_min,
// The arithmetic error on the zero point computed from either pair
// will be roughly machine_epsilon * (sum of absolute values of terms)
// so we want to use the variant that adds the smaller terms.
- const double zero_point_from_min = qmin_double - f_min / scale;
- const double zero_point_from_max = qmax_double - f_max / scale;
+ const float zero_point_from_min = qmin_double - f_min / scale;
+ const float zero_point_from_max = qmax_double - f_max / scale;
- const double zero_point_from_min_error = std::abs(qmin_double) + std::abs(f_min / scale);
+ const float zero_point_from_min_error = std::abs(qmin_double) + std::abs(f_min / scale);
- const double zero_point_from_max_error = std::abs(qmax_double) + std::abs(f_max / scale);
+ const float zero_point_from_max_error = std::abs(qmax_double) + std::abs(f_max / scale);
- const double zero_point_double = zero_point_from_min_error < zero_point_from_max_error
- ? zero_point_from_min
- : zero_point_from_max;
+ const float zero_point_double = zero_point_from_min_error < zero_point_from_max_error
+ ? zero_point_from_min
+ : zero_point_from_max;
// Now we need to nudge the zero point to be an integer
// (our zero points are integer, and this is motivated by the requirement
@@ -168,7 +197,7 @@ template <typename T> std::pair<float, int32_t> quantizationParams(float f_min,
assert(qmin <= nudged_zero_point);
zero_point = nudged_zero_point;
// finally, return the values
- return {static_cast<float>(scale), zero_point};
+ return {scale, zero_point};
}
inline float getTolerance(float min, float max, int quantize_steps)
diff --git a/compiler/luci-interpreter/src/kernels/Transpose.test.cpp b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp
index 87e6e2a00..1c99223a8 100644
--- a/compiler/luci-interpreter/src/kernels/Transpose.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp
@@ -29,14 +29,11 @@ using namespace testing;
template <typename T>
void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> perm_shape,
std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
- std::initializer_list<int32_t> perm_data, std::initializer_list<T> output_data,
- DataType element_type)
+ std::initializer_list<int32_t> perm_data, std::initializer_list<T> output_data)
{
- Tensor input_tensor{element_type, input_shape, {}, ""};
- input_tensor.writeData(input_data.begin(), input_data.size() * sizeof(T));
-
- Tensor perm_tensor{DataType::S32, perm_shape, {}, ""};
- perm_tensor.writeData(perm_data.begin(), perm_data.size() * sizeof(int32_t));
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor perm_tensor = makeInputTensor<DataType::S32>(perm_shape, perm_data);
Tensor output_tensor = makeOutputTensor(element_type);
Transpose kernel(&input_tensor, &perm_tensor, &output_tensor);
@@ -60,8 +57,7 @@ TYPED_TEST(TransposeTest, Small3D)
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23},
/*perm_data=*/{2, 0, 1},
/*output_data=*/{0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21,
- 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23},
- getElementType<TypeParam>());
+ 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23});
}
TYPED_TEST(TransposeTest, Large4D)
@@ -84,8 +80,7 @@ TYPED_TEST(TransposeTest, Large4D)
10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54,
70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114,
15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59,
- 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119},
- getElementType<TypeParam>());
+ 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119});
}
TYPED_TEST(TransposeTest, Large2D)
@@ -101,15 +96,13 @@ TYPED_TEST(TransposeTest, Large2D)
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119},
/*perm_data=*/{1, 0},
- /*output_data=*/{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49,
- 61, 73, 85, 97, 109, 2, 14, 26, 38, 50, 62, 74, 86, 98, 110,
- 3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 4, 16, 28, 40, 52,
- 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113,
- 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55,
- 67, 79, 91, 103, 115, 8, 20, 32, 44, 56, 68, 80, 92, 104, 116,
- 9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 10, 22, 34, 46, 58,
- 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119},
- getElementType<TypeParam>());
+ /*output_data=*/{
+ 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, 61, 73, 85, 97, 109,
+ 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 3, 15, 27, 39, 51, 63, 75, 87, 99, 111,
+ 4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113,
+ 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, 67, 79, 91, 103, 115,
+ 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 9, 21, 33, 45, 57, 69, 81, 93, 105, 117,
+ 10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119});
}
} // namespace
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp
index 898bae3da..07d92f07f 100644
--- a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp
@@ -43,18 +43,6 @@ void TransposeConv::configure()
assert(input()->element_type() == DataType::FLOAT32 || input()->element_type() == DataType::U8);
assert(input()->element_type() == output()->element_type());
assert(input()->shape().dim(3) == filter()->shape().dim(3));
- if (input()->element_type() == DataType::U8)
- {
- _scratch_tensor =
- std::make_unique<Tensor>(DataType::S32, output()->shape(), AffineQuantization{}, "");
- double real_multiplier = 0.0;
- const double input_product_scale = input()->scale() * filter()->scale();
- assert(input_product_scale >= 0);
- real_multiplier = input_product_scale / output()->scale();
- int exponent;
- quantizeMultiplier(real_multiplier, &_output_multiplier, &exponent);
- _output_shift = -exponent;
- }
const int num_dims = output_shape()->shape().dim(0);
Shape out_shape(num_dims);
@@ -62,6 +50,31 @@ void TransposeConv::configure()
for (int i = 0; i < num_dims; i++)
out_shape.dim(i) = shape_data[i];
output()->resize(out_shape);
+
+ const int32_t filter_height = filter()->shape().dim(1);
+ const int32_t filter_width = filter()->shape().dim(2);
+ const int32_t output_height = out_shape.dim(1);
+ const int32_t output_width = out_shape.dim(2);
+
+ const int32_t unused_output_height =
+ computeOutputSize(params().padding, output_height, filter_height, params().stride_height, 1);
+ const int32_t unused_output_width =
+ computeOutputSize(params().padding, output_width, filter_width, params().stride_width, 1);
+
+ _padding_height =
+ computePadding(params().stride_height, 1, output_height, filter_height, unused_output_height);
+ _padding_width =
+ computePadding(params().stride_width, 1, output_width, filter_width, unused_output_width);
+
+ if (input()->element_type() == DataType::U8)
+ {
+ _scratch_tensor =
+ std::make_unique<Tensor>(DataType::S32, output()->shape(), AffineQuantization{}, "");
+ const double input_product_scale = input()->scale() * filter()->scale();
+ assert(input_product_scale >= 0);
+ const double real_multiplier = input_product_scale / output()->scale();
+ quantizeMultiplier(real_multiplier, &_output_multiplier, &_output_shift);
+ }
}
void TransposeConv::execute() const
@@ -81,74 +94,45 @@ void TransposeConv::execute() const
void TransposeConv::evalFloat() const
{
- const int width = output()->shape().dim(2);
- const int height = output()->shape().dim(1);
-
- const int filter_width = filter()->shape().dim(2);
- const int filter_height = filter()->shape().dim(1);
-
- int unused_output_height, unused_output_width;
- unused_output_width =
- computeOutputSize(params().padding, width, filter_width, params().stride_width, 1);
- unused_output_height =
- computeOutputSize(params().padding, height, filter_height, params().stride_height, 1);
- int32_t offset = 0;
tflite::ConvParams op_params{};
op_params.padding_type = tflite::PaddingType::kSame;
- op_params.padding_values.height = computePaddingWithOffset(
- params().stride_height, 1, height, filter_height, unused_output_height, &offset);
- op_params.padding_values.height_offset = offset;
- op_params.padding_values.width = computePaddingWithOffset(
- params().stride_width, 1, width, filter_width, unused_output_width, &offset);
- op_params.padding_values.width_offset = offset;
+ op_params.padding_values.height = _padding_height;
+ op_params.padding_values.width = _padding_width;
op_params.stride_height = params().stride_height;
op_params.stride_width = params().stride_width;
op_params.output_multiplier = _output_multiplier;
- tflite::reference_ops::TransposeConv(
- op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()),
- getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()),
- getTensorShape(output()), getTensorData<float>(output()), tflite::RuntimeShape(),
- (float *)nullptr);
+ tflite::reference_ops::TransposeConv(op_params, //
+ getTensorShape(input()), getTensorData<float>(input()), //
+ getTensorShape(filter()), getTensorData<float>(filter()), //
+ getTensorShape(bias()), getTensorData<float>(bias()), //
+ getTensorShape(output()), getTensorData<float>(output()), //
+ tflite::RuntimeShape(), nullptr);
}
void TransposeConv::evalQuantized() const
{
- int32_t input_offset = -input()->zero_point();
- int32_t filter_offset = -filter()->zero_point();
- int32_t output_offset = filter()->zero_point();
- const int width = output()->shape().dim(2);
- const int height = output()->shape().dim(1);
-
- const int filter_width = filter()->shape().dim(2);
- const int filter_height = filter()->shape().dim(1);
-
- int unused_output_height, unused_output_width;
- unused_output_width =
- computeOutputSize(params().padding, width, filter_width, params().stride_width, 1);
- unused_output_height =
- computeOutputSize(params().padding, height, filter_height, params().stride_height, 1);
- int32_t offset = 0;
tflite::ConvParams op_params{};
op_params.padding_type = tflite::PaddingType::kSame;
- op_params.padding_values.height = computePaddingWithOffset(
- params().stride_height, 1, height, filter_height, unused_output_height, &offset);
- op_params.padding_values.width = computePaddingWithOffset(
- params().stride_width, 1, width, filter_width, unused_output_width, &offset);
+ op_params.padding_values.height = _padding_height;
+ op_params.padding_values.width = _padding_width;
op_params.stride_height = params().stride_height;
op_params.stride_width = params().stride_width;
- op_params.input_offset = input_offset;
- op_params.output_offset = output_offset;
- op_params.weights_offset = filter_offset;
+ // The kernel expects input and filter zero points to be negated.
+ op_params.input_offset = -input()->zero_point(); // Note the '-'.
+ op_params.weights_offset = -filter()->zero_point(); // Note the '-'.
+ op_params.output_offset = output()->zero_point();
op_params.output_multiplier = _output_multiplier;
- op_params.output_shift = -_output_shift;
+ op_params.output_shift = _output_shift;
op_params.quantized_activation_min = std::numeric_limits<uint8_t>::min();
op_params.quantized_activation_max = std::numeric_limits<uint8_t>::max();
- tflite::reference_ops::TransposeConv(
- op_params, getTensorShape(input()), getTensorData<uint8>(input()), getTensorShape(filter()),
- getTensorData<uint8>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()),
- getTensorShape(output()), getTensorData<uint8>(output()), tflite::RuntimeShape(),
- (uint8 *)nullptr, getTensorData<int32_t>(_scratch_tensor.get()));
+ tflite::reference_ops::TransposeConv(op_params, //
+ getTensorShape(input()), getTensorData<uint8>(input()), //
+ getTensorShape(filter()), getTensorData<uint8>(filter()), //
+ getTensorShape(bias()), getTensorData<int32_t>(bias()), //
+ getTensorShape(output()), getTensorData<uint8>(output()), //
+ tflite::RuntimeShape(), nullptr, //
+ getTensorData<int32_t>(_scratch_tensor.get()));
}
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.h b/compiler/luci-interpreter/src/kernels/TransposeConv.h
index 3a0eae761..444439c65 100644
--- a/compiler/luci-interpreter/src/kernels/TransposeConv.h
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.h
@@ -47,6 +47,8 @@ private:
private:
std::unique_ptr<Tensor> _scratch_tensor;
+ int32_t _padding_height{};
+ int32_t _padding_width{};
// The scaling factor from input to output (aka the 'real multiplier') can
// be represented as a fixed point multiplier plus a left shift.
int32_t _output_multiplier = 0;
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp
index 0fbe9328b..5a69e7798 100644
--- a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp
@@ -28,21 +28,18 @@ using namespace testing;
template <typename T, typename B>
void Check(std::initializer_list<int32_t> output_shape_shape,
- std::initializer_list<int32_t> weight_shape,
- std::initializer_list<int32_t> input_data_shape,
+ std::initializer_list<int32_t> weight_shape, std::initializer_list<int32_t> input_shape,
std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
std::initializer_list<int32_t> output_shape_data, std::initializer_list<T> weight_data,
- std::initializer_list<T> input_data_data, std::initializer_list<B> bias_data,
+ std::initializer_list<T> input_data, std::initializer_list<B> bias_data,
std::initializer_list<T> output_data, luci::Padding padding, int32_t stride_height,
- int32_t stride_width, DataType element_type)
+ int32_t stride_width)
{
- Tensor output_shape_tensor{element_type, output_shape_shape, {}, ""};
- output_shape_tensor.writeData(output_shape_data.begin(), output_shape_data.size() * sizeof(T));
- Tensor weight_tensor{element_type, weight_shape, {}, ""};
- weight_tensor.writeData(weight_data.begin(), weight_data.size() * sizeof(T));
- Tensor input_data_tensor{element_type, input_data_shape, {}, ""};
- input_data_tensor.writeData(input_data_data.begin(), input_data_data.size() * sizeof(T));
-
+ constexpr DataType element_type = getElementType<T>();
+ Tensor output_shape_tensor =
+ makeInputTensor<DataType::S32>(output_shape_shape, output_shape_data);
+ Tensor weight_tensor = makeInputTensor<element_type>(weight_shape, weight_data);
+ Tensor input_data_tensor = makeInputTensor<element_type>(input_shape, input_data);
Tensor output_tensor = makeOutputTensor(element_type);
TransposeConvParams params{};
@@ -71,14 +68,13 @@ void Check(std::initializer_list<int32_t> output_shape_shape,
TEST(TransposeConvTest, FloatSimple)
{
Check<float, float>(
- /*outputShape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1},
- /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*outputShape_data=*/{1, 4, 4, 1},
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1},
+ /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1},
/*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9},
/*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
/*bias_data=*/{},
/*output_data=*/{29, 62, 83, 75, 99, 192, 237, 198, 207, 372, 417, 330, 263, 446, 485, 365},
- /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1,
- getElementType<float>());
+ /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1);
SUCCEED();
}
@@ -86,16 +82,15 @@ TEST(TransposeConvTest, FloatSimple)
TEST(TransposeConvTest, FloatTwoFiltersTest)
{
Check<float, float>(
- /*outputShape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2},
- /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*outputShape_data=*/{1, 4, 4, 1},
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2},
+ /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1},
/*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
/*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
/*bias_data=*/{},
- /*output_data=*/{184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968,
- 3352, 3652, 2760},
- /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1,
- getElementType<float>());
+ /*output_data=*/
+ {184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760},
+ /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1);
SUCCEED();
}
@@ -103,28 +98,61 @@ TEST(TransposeConvTest, FloatTwoFiltersTest)
TEST(TransposeConvTest, SimpleBiasTest)
{
Check<float, float>(
- /*outputShape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1},
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1},
/*input_shape=*/{1, 2, 2, 1},
- /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*outputShape_data=*/{1, 5, 5, 2},
+ /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 5, 5, 2},
/*weight_data=*/{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18},
/*input_data=*/{1, 2, 3, 4},
/*bias_data=*/{3, 4},
/*output_data=*/{4, 6, 6, 8, 10, 14, 9, 12, 13, 16, 10, 12, 12, 14, 28, 32, 21,
24, 25, 28, 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, 24, 28, 30, 34,
64, 72, 39, 44, 47, 52, 42, 46, 48, 52, 106, 114, 63, 68, 71, 76},
- /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2,
- getElementType<float>());
+ /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2);
SUCCEED();
}
-// TODO Uint8Simple
-// Implement GetDequantizedOutput Function.
-// Create Test for Uint8 Case
+TEST(TransposeConvTest, UInt8)
+{
+ std::vector<float> input_data{1, 2, 3, 4};
+ std::vector<float> filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18};
+ std::vector<float> bias_data{3, 4};
+ std::vector<int32_t> output_shape_data{1, 5, 5, 2};
+ std::vector<float> ref_output_data{
+ 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, //
+ 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, //
+ 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, //
+ 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, //
+ 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, //
+ };
+
+ // Choose quantization parameters carefully.
+ auto input_quant = quantizationParams<uint8_t>(-8.0, 7.9375); // s = 1 / 16, zp = 128
+ auto filter_quant = quantizationParams<uint8_t>(-24.0, 39.75); // s = 1 / 4, zp = 96
+ auto output_quant = quantizationParams<uint8_t>(-64.0, 191.0); // s = 1, zp = 64
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first,
+ input_quant.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({2, 3, 3, 1}, filter_quant.first,
+ filter_quant.second, filter_data);
+ Tensor bias_tensor =
+ makeInputTensor<DataType::S32>({2}, input_quant.first * filter_quant.first, 0, bias_data);
+ Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second);
+
+ TransposeConvParams params{};
+ params.padding = Padding::VALID;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
-// TODO Uint8FiltersTest
-// Implement GetDequantizedOutput Function.
-// Create Test for Uint8 Case
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
} // namespace
} // namespace kernels
diff --git a/compiler/luci-interpreter/src/kernels/Utils.cpp b/compiler/luci-interpreter/src/kernels/Utils.cpp
index b9e7738a9..52e76a81c 100644
--- a/compiler/luci-interpreter/src/kernels/Utils.cpp
+++ b/compiler/luci-interpreter/src/kernels/Utils.cpp
@@ -89,20 +89,23 @@ static void calculateActivationRangeQuantizedImpl(Activation activation, int32_t
void calculateActivationRangeQuantized(Activation activation, const Tensor *output,
int32_t *activation_min, int32_t *activation_max)
{
+ // For now, assume that signed type implies signed symmetric quantization.
int32_t qmin{};
int32_t qmax{};
switch (output->element_type())
{
case DataType::U8:
- qmin = std::numeric_limits<uint8_t>::min();
+ qmin = 0;
qmax = std::numeric_limits<uint8_t>::max();
break;
case DataType::S8:
- qmin = std::numeric_limits<int8_t>::min();
+ assert(output->zero_point() == 0);
+ qmin = -std::numeric_limits<int8_t>::max();
qmax = std::numeric_limits<int8_t>::max();
break;
case DataType::S16:
- qmin = std::numeric_limits<int16_t>::min();
+ assert(output->zero_point() == 0);
+ qmin = -std::numeric_limits<int16_t>::max();
qmax = std::numeric_limits<int16_t>::max();
break;
default:
diff --git a/compiler/luci-interpreter/src/kernels/Utils.h b/compiler/luci-interpreter/src/kernels/Utils.h
index 7927151c6..67bb7581a 100644
--- a/compiler/luci-interpreter/src/kernels/Utils.h
+++ b/compiler/luci-interpreter/src/kernels/Utils.h
@@ -25,6 +25,7 @@
#include <cassert>
#include <cstdint>
+#include <stdexcept>
namespace luci_interpreter
{
@@ -70,6 +71,11 @@ inline int32_t computeOutputSize(Padding padding, int32_t image_size, int32_t fi
}
}
+inline int32_t calcOffset(const Shape &shape, int32_t d0, int32_t d1, int32_t d2, int32_t d3)
+{
+ return ((d0 * shape.dim(1) + d1) * shape.dim(2) + d2) * shape.dim(3) + d3;
+}
+
void calculateActivationRange(Activation activation, float *activation_min, float *activation_max);
void calculateActivationRangeQuantized(Activation activation, const Tensor *output,
@@ -94,6 +100,14 @@ void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quan
Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape);
+inline double getQuantizedConvolutionMultipler(float input_scale, float filter_scale,
+ float output_scale)
+{
+ const double input_product_scale = static_cast<double>(input_scale * filter_scale);
+ LUCI_INTERPRETER_CHECK(input_product_scale >= 0);
+ return input_product_scale / static_cast<double>(output_scale);
+}
+
inline tflite::RuntimeShape getTensorShape(const Tensor *tensor)
{
if (tensor == nullptr)
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp
index 126a1cb5b..66aa38ff0 100644
--- a/compiler/luci-interpreter/src/loader/KernelBuilder.cpp
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp
@@ -23,19 +23,37 @@
#include "kernels/Conv2D.h"
#include "kernels/DepthToSpace.h"
#include "kernels/DepthwiseConv2D.h"
+#include "kernels/Div.h"
#include "kernels/Elu.h"
+#include "kernels/Floor.h"
+#include "kernels/FloorDiv.h"
+#include "kernels/Equal.h"
#include "kernels/FullyConnected.h"
+#include "kernels/Greater.h"
+#include "kernels/GreaterEqual.h"
#include "kernels/If.h"
#include "kernels/L2Normalize.h"
#include "kernels/L2Pool2D.h"
#include "kernels/LeakyRelu.h"
+#include "kernels/Less.h"
+#include "kernels/LessEqual.h"
#include "kernels/LocalResponseNormalization.h"
#include "kernels/Logistic.h"
+#include "kernels/LogSoftmax.h"
+#include "kernels/Maximum.h"
#include "kernels/MaxPool2D.h"
#include "kernels/Mean.h"
+#include "kernels/Minimum.h"
#include "kernels/Mul.h"
+#include "kernels/NotEqual.h"
#include "kernels/Pad.h"
+#include "kernels/Pow.h"
+#include "kernels/Prelu.h"
+#include "kernels/Relu.h"
+#include "kernels/Relu6.h"
#include "kernels/Reshape.h"
+#include "kernels/ResizeBilinear.h"
+#include "kernels/ResizeNearestNeighbor.h"
#include "kernels/Reverse.h"
#include "kernels/Rsqrt.h"
#include "kernels/Slice.h"
@@ -44,6 +62,7 @@
#include "kernels/Split.h"
#include "kernels/StridedSlice.h"
#include "kernels/Sqrt.h"
+#include "kernels/Sub.h"
#include "kernels/Squeeze.h"
#include "kernels/Tanh.h"
#include "kernels/Unpack.h"
@@ -229,6 +248,19 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthwiseConv2D *
return std::make_unique<kernels::DepthwiseConv2D>(input, filter, bias, output, params);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDiv *node)
+{
+ assert(node->arity() == 2);
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ DivParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Div>(input1, input2, output, params);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
{
assert(node->arity() == 1);
@@ -239,6 +271,38 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
return std::make_unique<kernels::Elu>(input, output);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFloor *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Floor>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFloorDiv *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::FloorDiv>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Equal>(x, y, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *node)
{
assert(node->arity() == 3);
@@ -254,6 +318,28 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *n
return std::make_unique<kernels::FullyConnected>(input, weights, bias, output, params);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleGreater *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Greater>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleGreaterEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::GreaterEqual>(x, y, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleIf *node)
{
auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
@@ -323,6 +409,28 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLeakyRelu *node)
return std::make_unique<kernels::LeakyRelu>(input, output, params);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLess *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Less>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLessEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LessEqual>(x, y, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLocalResponseNormalization *node)
{
assert(node->arity() == 1);
@@ -348,6 +456,27 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogistic *node)
return std::make_unique<kernels::Logistic>(input, output);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogSoftmax *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->logits());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LogSoftmax>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaximum *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Maximum>(input1, input2, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaxPool2D *node)
{
assert(node->arity() == 1);
@@ -380,6 +509,17 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMean *node)
return std::make_unique<kernels::Mean>(input, axes, output, params);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMinimum *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Minimum>(input1, input2, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
{
assert(node->arity() == 2);
@@ -394,6 +534,17 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
return std::make_unique<kernels::Mul>(input1, input2, output, params);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleNotEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::NotEqual>(x, y, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *)
{
throw std::runtime_error("Output node cannot be executed.");
@@ -410,6 +561,49 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node)
return std::make_unique<kernels::Pad>(input, paddings, output);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePow *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Pow>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePRelu *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *alpha = getInputTensor(node->alpha());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Prelu>(input, alpha, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRelu *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Relu>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRelu6 *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Relu6>(input, output);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
{
assert(node->arity() == 2);
@@ -422,6 +616,40 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
return std::make_unique<kernels::Reshape>(input, shape, output);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleResizeBilinear *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *size = getInputTensor(node->size());
+ Tensor *output = getOutputTensor(node);
+
+ ResizeBilinearParams params{};
+ params.align_corners = node->align_corners();
+ params.half_pixel_centers = node->half_pixel_centers();
+
+ return std::make_unique<kernels::ResizeBilinear>(input, size, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleResizeNearestNeighbor *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *size = getInputTensor(node->size());
+ Tensor *output = getOutputTensor(node);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = node->align_corners();
+ // TODO update half_pixel_centers after CircleResizeNearestNeighbor updated
+ // Current CircleResizeNearestNeighbor don't have half_pixel_centers.
+ // default value on current is false.
+ // it need to be updated when CircleResizeNearestNeighbor updated.
+ params.half_pixel_centers = false;
+
+ return std::make_unique<kernels::ResizeNearestNeighbor>(input, size, output, params);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReverseV2 *node)
{
assert(node->arity() == 2);
@@ -443,6 +671,20 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRsqrt *node)
return std::make_unique<kernels::Rsqrt>(input, output);
}
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSub *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ SubParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Sub>(input1, input2, output, params);
+}
+
std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSlice *node)
{
assert(node->arity() == 3);
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.h b/compiler/luci-interpreter/src/loader/KernelBuilder.h
index 31cb9d8fc..663104700 100644
--- a/compiler/luci-interpreter/src/loader/KernelBuilder.h
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.h
@@ -47,23 +47,42 @@ public:
std::unique_ptr<Kernel> visit(const luci::CircleConst *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleDepthToSpace *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleDepthwiseConv2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleDiv *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleElu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleFloor *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleFloorDiv *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleEqual *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleFullyConnected *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleGreater *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleGreaterEqual *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleIf *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleL2Normalize *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleL2Pool2D *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleLeakyRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLess *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLessEqual *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleLocalResponseNormalization *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleLogistic *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogSoftmax *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleInput *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMaximum *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleMaxPool2D *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleMean *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMinimum *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleMul *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleNotEqual *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleOutput *node) override;
std::unique_ptr<Kernel> visit(const luci::CirclePad *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CirclePow *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CirclePRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleRelu6 *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleReshape *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleResizeBilinear *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleResizeNearestNeighbor *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleReverseV2 *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleRsqrt *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSub *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleSlice *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleSoftmax *node) override;
std::unique_ptr<Kernel> visit(const luci::CircleSpaceToDepth *node) override;
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp
index 4e2bc3d0b..ea055542d 100644
--- a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp
@@ -24,18 +24,36 @@
#include <kernels/Conv2D.h>
#include <kernels/DepthToSpace.h>
#include <kernels/DepthwiseConv2D.h>
+#include <kernels/Div.h>
#include <kernels/Elu.h>
+#include <kernels/Floor.h>
+#include <kernels/FloorDiv.h>
+#include <kernels/Equal.h>
#include <kernels/FullyConnected.h>
+#include <kernels/Greater.h>
+#include <kernels/GreaterEqual.h>
#include <kernels/L2Normalize.h>
#include <kernels/L2Pool2D.h>
#include <kernels/LeakyRelu.h>
+#include <kernels/Less.h>
+#include <kernels/LessEqual.h>
#include <kernels/LocalResponseNormalization.h>
#include <kernels/Logistic.h>
+#include <kernels/LogSoftmax.h>
+#include <kernels/Maximum.h>
#include <kernels/MaxPool2D.h>
#include <kernels/Mean.h>
+#include <kernels/Minimum.h>
#include <kernels/Mul.h>
+#include <kernels/NotEqual.h>
#include <kernels/Pad.h>
+#include <kernels/Pow.h>
+#include <kernels/Prelu.h>
+#include <kernels/Relu.h>
+#include <kernels/Relu6.h>
#include <kernels/Reshape.h>
+#include <kernels/ResizeBilinear.h>
+#include <kernels/ResizeNearestNeighbor.h>
#include <kernels/Reverse.h>
#include <kernels/Rsqrt.h>
#include <kernels/Slice.h>
@@ -43,6 +61,7 @@
#include <kernels/SpaceToDepth.h>
#include <kernels/Split.h>
#include <kernels/Sqrt.h>
+#include <kernels/Sub.h>
#include <kernels/Squeeze.h>
#include <kernels/StridedSlice.h>
#include <kernels/Tanh.h>
@@ -279,6 +298,26 @@ TEST_F(KernelBuilderTest, DepthwiseConv2D)
EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
}
+TEST_F(KernelBuilderTest, Div)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleDiv>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Div>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
TEST_F(KernelBuilderTest, Elu)
{
auto *input = createInputNode();
@@ -293,6 +332,54 @@ TEST_F(KernelBuilderTest, Elu)
checkTensor(kernel->output(), op);
}
+TEST_F(KernelBuilderTest, Floor)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleFloor>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Floor>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, FloorDiv)
+{
+ auto *x = createInputNode();
+ auto *y = createInputNode();
+
+ auto *op = createNode<luci::CircleFloorDiv>();
+ op->x(x);
+ op->y(y);
+
+ auto kernel = buildKernel<kernels::FloorDiv>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x);
+ checkTensor(kernel->y(), y);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Equal)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Equal>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, FullyConnected)
{
auto *input = createInputNode();
@@ -316,6 +403,40 @@ TEST_F(KernelBuilderTest, FullyConnected)
EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
}
+TEST_F(KernelBuilderTest, Greater)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleGreater>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Greater>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, GreaterEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleGreaterEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::GreaterEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, L2Normalize)
{
auto *input = createInputNode();
@@ -377,6 +498,40 @@ TEST_F(KernelBuilderTest, LeakyRelu)
EXPECT_THAT(kernel->params().alpha, Eq(op->alpha()));
}
+TEST_F(KernelBuilderTest, Less)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleLess>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Less>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LessEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleLessEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::LessEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, LocalResponseNormalization)
{
auto *input = createInputNode();
@@ -414,6 +569,37 @@ TEST_F(KernelBuilderTest, Logistic)
checkTensor(kernel->output(), op);
}
+TEST_F(KernelBuilderTest, LogSoftmax)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLogSoftmax>();
+ op->logits(input);
+
+ auto kernel = buildKernel<kernels::LogSoftmax>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Maximum)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleMaximum>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Maximum>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, MaxPool2D)
{
auto *input = createInputNode();
@@ -461,6 +647,23 @@ TEST_F(KernelBuilderTest, Mean)
EXPECT_THAT(kernel->params().keep_dims, Eq(op->keep_dims()));
}
+TEST_F(KernelBuilderTest, Minimum)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleMinimum>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Minimum>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, Mul)
{
auto *input1 = createInputNode();
@@ -481,6 +684,23 @@ TEST_F(KernelBuilderTest, Mul)
EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
}
+TEST_F(KernelBuilderTest, NotEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleNotEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::NotEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, Pad)
{
auto *input = createInputNode();
@@ -498,6 +718,68 @@ TEST_F(KernelBuilderTest, Pad)
checkTensor(kernel->output(), op);
}
+TEST_F(KernelBuilderTest, Pow)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CirclePow>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Pow>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Prelu)
+{
+ auto *input = createInputNode();
+ auto *alpha = createInputNode();
+
+ auto *op = createNode<luci::CirclePRelu>();
+ op->input(input);
+ op->alpha(alpha);
+
+ auto kernel = buildKernel<kernels::Prelu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->alpha(), alpha);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Relu)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleRelu>();
+ op->features(input);
+
+ auto kernel = buildKernel<kernels::Relu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Relu6)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleRelu6>();
+ op->features(input);
+
+ auto kernel = buildKernel<kernels::Relu6>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
TEST_F(KernelBuilderTest, Reshape)
{
auto *input = createInputNode();
@@ -515,6 +797,48 @@ TEST_F(KernelBuilderTest, Reshape)
checkTensor(kernel->output(), op);
}
+TEST_F(KernelBuilderTest, ResizeBilinear)
+{
+ auto *input = createInputNode();
+ auto *size = createInputNode();
+
+ auto *op = createNode<luci::CircleResizeBilinear>();
+ op->input(input);
+ op->size(size);
+ op->align_corners(true);
+ op->half_pixel_centers(true);
+
+ auto kernel = buildKernel<kernels::ResizeBilinear>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->size(), size);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners()));
+ EXPECT_THAT(kernel->params().half_pixel_centers, Eq(op->half_pixel_centers()));
+}
+
+TEST_F(KernelBuilderTest, ResizeNearestNeighbor)
+{
+ auto *input = createInputNode();
+ auto *size = createInputNode();
+
+ auto *op = createNode<luci::CircleResizeNearestNeighbor>();
+ op->input(input);
+ op->size(size);
+ op->align_corners(true);
+
+ auto kernel = buildKernel<kernels::ResizeNearestNeighbor>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->size(), size);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners()));
+ // TODO currently half_pixel_centers are not implemented on CircleResizeNearestNeighbor
+ // after adding, need to be updated.
+}
+
TEST_F(KernelBuilderTest, ReverseV2)
{
auto *input = createInputNode();
@@ -636,6 +960,26 @@ TEST_F(KernelBuilderTest, Sqrt)
checkTensor(kernel->output(), op);
}
+TEST_F(KernelBuilderTest, Sub)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleSub>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Sub>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
TEST_F(KernelBuilderTest, Squeeze)
{
auto *input = createInputNode();
diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp
index f097e71c5..1fdb40e51 100644
--- a/compiler/luci/export/src/CircleExporterUtils.cpp
+++ b/compiler/luci/export/src/CircleExporterUtils.cpp
@@ -36,6 +36,10 @@ circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func)
return circle::ActivationFunctionType_RELU_N1_TO_1;
case luci::FusedActFunc::RELU6:
return circle::ActivationFunctionType_RELU6;
+ case luci::FusedActFunc::TANH:
+ return circle::ActivationFunctionType_TANH;
+ case luci::FusedActFunc::SIGN_BIT:
+ return circle::ActivationFunctionType_SIGN_BIT;
default:
INTERNAL_EXN_V("trying to convert unsupported luci::FusedActFunc", oops::to_uint32(func));
}
@@ -83,6 +87,63 @@ circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode)
}
}
+circle::DimensionType to_circle_dimensiontype(luci::DimensionType type)
+{
+ switch (type)
+ {
+ case luci::DimensionType::DENSE:
+ return circle::DimensionType_DENSE;
+ case luci::DimensionType::SPARSE_CSR:
+ return circle::DimensionType_SPARSE_CSR;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::DimensionType", oops::to_uint32(type));
+ }
+}
+
+flatbuffers::Offset<void> to_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
+ const SparseIndexVector &sparse_idx_vec)
+{
+ auto type = sparse_idx_vec.type();
+ switch (type)
+ {
+ case luci::SparseIndexVectorType::NONE:
+ return flatbuffers::Offset<void>();
+ case luci::SparseIndexVectorType::I32:
+ {
+ return circle::CreateInt32VectorDirect(fb, sparse_idx_vec.as_int32_vector()).Union();
+ }
+ case luci::SparseIndexVectorType::U16:
+ {
+ return circle::CreateUint16VectorDirect(fb, sparse_idx_vec.as_uint16_vector()).Union();
+ }
+ case luci::SparseIndexVectorType::U8:
+ {
+ return circle::CreateUint8VectorDirect(fb, sparse_idx_vec.as_uint8_vector()).Union();
+ }
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::SparseIndexVectorType",
+ oops::to_uint32(type));
+ }
+}
+
+circle::SparseIndexVector to_circle_sparse_index_vector_type(luci::SparseIndexVectorType type)
+{
+ switch (type)
+ {
+ case luci::SparseIndexVectorType::NONE:
+ return circle::SparseIndexVector_NONE;
+ case luci::SparseIndexVectorType::I32:
+ return circle::SparseIndexVector_Int32Vector;
+ case luci::SparseIndexVectorType::U16:
+ return circle::SparseIndexVector_Uint16Vector;
+ case luci::SparseIndexVectorType::U8:
+ return circle::SparseIndexVector_Uint8Vector;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::SparseIndexVectorType",
+ oops::to_uint32(type));
+ }
+}
+
} // namespace luci
namespace luci
diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h
index f9ce6d2bf..7857213b2 100644
--- a/compiler/luci/export/src/CircleExporterUtils.h
+++ b/compiler/luci/export/src/CircleExporterUtils.h
@@ -32,6 +32,10 @@ namespace luci
circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func);
circle::TensorType to_circle_tensortype(loco::DataType type);
circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode);
+circle::DimensionType to_circle_dimensiontype(luci::DimensionType type);
+flatbuffers::Offset<void> to_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
+ const SparseIndexVector &sparse_idx_vec);
+circle::SparseIndexVector to_circle_sparse_index_vector_type(luci::SparseIndexVectorType type);
} // namespace luci
diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp
index 36d61f6c9..c937109cd 100644
--- a/compiler/luci/export/src/CircleOperationExporter.cpp
+++ b/compiler/luci/export/src/CircleOperationExporter.cpp
@@ -632,6 +632,7 @@ public:
void visit(luci::CircleCustom *) final;
void visit(luci::CircleDepthToSpace *) final;
void visit(luci::CircleDepthwiseConv2D *) final;
+ void visit(luci::CircleDequantize *) final;
void visit(luci::CircleDiv *) final;
void visit(luci::CircleElu *) final;
void visit(luci::CircleEqual *) final;
@@ -718,6 +719,7 @@ public:
void visit(luci::CircleTopKV2 *) final;
void visit(luci::CircleTranspose *) final;
void visit(luci::CircleTransposeConv *) final;
+ void visit(luci::CircleUnidirectionalSequenceLSTM *) final;
void visit(luci::CircleUnique *) final;
void visit(luci::CircleUnpack *) final;
void visit(luci::CircleWhere *) final;
@@ -866,6 +868,11 @@ void OperationExporter::visit(luci::CircleDepthwiseConv2D *node)
.Union());
}
+void OperationExporter::visit(luci::CircleDequantize *node)
+{
+ export_simple(node, circle::BuiltinOperator_DEQUANTIZE);
+}
+
void OperationExporter::visit(luci::CircleDiv *node)
{
export_simple(
@@ -1371,6 +1378,17 @@ void OperationExporter::visit(luci::CircleTransposeConv *node)
.Union());
}
+void OperationExporter::visit(luci::CircleUnidirectionalSequenceLSTM *node)
+{
+ export_simple(node, circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM,
+ circle::BuiltinOptions_UnidirectionalSequenceLSTMOptions,
+ CreateUnidirectionalSequenceLSTMOptions(
+ _ctx.builder, to_circle_actfunc(node->fusedActivationFunction()),
+ node->cell_clip(), node->proj_clip(), node->time_major(),
+ node->asymmetric_quantize_inputs())
+ .Union());
+}
+
void OperationExporter::visit(luci::CircleUnique *node) { export_node(_ctx, node); }
void OperationExporter::visit(luci::CircleUnpack *node) { export_node(_ctx, node); }
diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp
index dc8c2fbc9..86e324698 100644
--- a/compiler/luci/export/src/CircleTensorExporter.cpp
+++ b/compiler/luci/export/src/CircleTensorExporter.cpp
@@ -63,6 +63,9 @@ public:
luci::CircleQuantParam *quantparam(void) const { return _quantparam; }
void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; }
+ luci::SparsityParam *sparsityparam(void) const { return _sparsityparam; }
+ void sparsityparam(luci::SparsityParam *sp) { _sparsityparam = sp; }
+
private:
std::string _name;
@@ -72,6 +75,7 @@ private:
luci::CircleConst *_content = nullptr;
luci::CircleQuantParam *_quantparam = nullptr;
+ luci::SparsityParam *_sparsityparam = nullptr;
};
using CircleTensorContext = std::vector<CircleTensoInfo>;
@@ -109,6 +113,7 @@ void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx)
tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
tensor_info.quantparam(node->quantparam());
+ tensor_info.sparsityparam(node->sparsityparam());
set_tensor_index(node, tensor_index);
@@ -265,6 +270,8 @@ flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, l
{
case loco::DataType::FLOAT32:
return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ case loco::DataType::S8:
+ return encodeOpBufferByDType<loco::DataType::S8>(builder, c);
case loco::DataType::S16:
return encodeOpBufferByDType<loco::DataType::S16>(builder, c);
case loco::DataType::S32:
@@ -308,6 +315,34 @@ encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam
0, quantparam->quantized_dimension);
}
+flatbuffers::Offset<circle::SparsityParameters>
+encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsityparam)
+{
+ if (sparsityparam == nullptr)
+ return 0;
+
+ std::vector<flatbuffers::Offset<circle::DimensionMetadata>> dim_metadata_vec;
+ auto luci_dim_metadata = sparsityparam->dim_metadata;
+ for (auto it : luci_dim_metadata)
+ {
+ // array_segments
+ auto circle_array_segments = to_circle_sparse_index_vector(builder, it.array_segments());
+ auto circle_array_segments_type =
+ to_circle_sparse_index_vector_type(it.array_segments().type());
+
+ // array_indices
+ auto circle_array_indices = to_circle_sparse_index_vector(builder, it.array_indices());
+ auto circle_array_indices_type = to_circle_sparse_index_vector_type(it.array_indices().type());
+ auto dim_metadata = circle::CreateDimensionMetadata(
+ builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type,
+ circle_array_segments, circle_array_indices_type, circle_array_indices);
+ dim_metadata_vec.emplace_back(dim_metadata);
+ }
+
+ return circle::CreateSparsityParametersDirect(builder, &sparsityparam->traversal_order,
+ &sparsityparam->block_map, &dim_metadata_vec);
+}
+
void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder,
SerializedModelData &md, SerializedGraphData &gd)
{
@@ -322,12 +357,14 @@ void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &build
auto quantparam = encodeQuantizationParameters(builder, info.quantparam());
+ auto sparsityparam = encodeSparsityParameters(builder, info.sparsityparam());
+
auto buffer_id = static_cast<uint32_t>(md._buffers.size());
md._buffers.push_back(buffer);
auto name_offset = builder.CreateString(info.name());
auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
- quantparam, /*is_variable*/ false);
+ quantparam, /*is_variable*/ false, sparsityparam);
gd._tensors.push_back(tensor_offset);
}
diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h
index 3d85b9e35..388942490 100644
--- a/compiler/luci/import/include/luci/Import/CircleReader.h
+++ b/compiler/luci/import/include/luci/Import/CircleReader.h
@@ -23,6 +23,7 @@
#include <luci/IR/AttrPadding.h>
#include <luci/IR/CircleNode.h>
#include <luci/IR/CircleQuantParam.h>
+#include <luci/IR/SparsityParam.h>
#include <loco.h>
diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h
index 0b21d380f..28741064e 100644
--- a/compiler/luci/import/include/luci/Import/Nodes.h
+++ b/compiler/luci/import/include/luci/Import/Nodes.h
@@ -36,6 +36,7 @@
#include "Nodes/CircleCustom.h"
#include "Nodes/CircleDepthToSpace.h"
#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDequantize.h"
#include "Nodes/CircleDiv.h"
#include "Nodes/CircleElu.h"
#include "Nodes/CircleEqual.h"
@@ -123,6 +124,7 @@
#include "Nodes/CircleTopKV2.h"
#include "Nodes/CircleTranspose.h"
#include "Nodes/CircleTransposeConv.h"
+#include "Nodes/CircleUnidirectionalSequenceLSTM.h"
#include "Nodes/CircleUnique.h"
#include "Nodes/CircleUnpack.h"
#include "Nodes/CircleWhere.h"
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h
new file mode 100644
index 000000000..e25b80b0e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDequantizeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..4cc3320dc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleUnidirectionalSequenceLSTMGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp
index bc7f39762..9ddc37d0a 100644
--- a/compiler/luci/import/src/CircleReader.cpp
+++ b/compiler/luci/import/src/CircleReader.cpp
@@ -115,7 +115,9 @@ FusedActFunc luci_actfunc(const circle::ActivationFunctionType type)
case circle::ActivationFunctionType::ActivationFunctionType_RELU6:
return luci::FusedActFunc::RELU6;
case circle::ActivationFunctionType::ActivationFunctionType_TANH:
- break;
+ return luci::FusedActFunc::TANH;
+ case circle::ActivationFunctionType::ActivationFunctionType_SIGN_BIT:
+ return luci::FusedActFunc::SIGN_BIT;
default:
break;
}
@@ -149,6 +151,49 @@ MirrorPadMode luci_mirrorpad_mode(const circle::MirrorPadMode mode)
return MirrorPadMode::UNDEFINED;
}
+DimensionType luci_dim_type(const circle::DimensionType dim_type)
+{
+ switch (dim_type)
+ {
+ case circle::DimensionType_DENSE:
+ return DimensionType::DENSE;
+ case circle::DimensionType_SPARSE_CSR:
+ return DimensionType::SPARSE_CSR;
+ default:
+ throw std::runtime_error("Invalid DimensionType");
+ }
+}
+
+SparseIndexVector
+luci_sparse_index_vector(const circle::SparseIndexVectorUnion &sparse_index_vector)
+{
+ switch (sparse_index_vector.type)
+ {
+ case circle::SparseIndexVector_NONE:
+ return SparseIndexVector{SparseIndexVectorType::NONE, nullptr};
+ case circle::SparseIndexVector_Int32Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsInt32Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::I32, const_vec_ptr};
+ }
+ case circle::SparseIndexVector_Uint16Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsUint16Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::U16, const_vec_ptr};
+ }
+ case circle::SparseIndexVector_Uint8Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsUint8Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::U8, const_vec_ptr};
+ }
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type");
+ }
+}
+
std::unique_ptr<CircleQuantParam>
luci_quantparam(const circle::QuantizationParametersT *quantization)
{
@@ -174,6 +219,28 @@ luci_quantparam(const circle::QuantizationParametersT *quantization)
return nullptr;
}
+std::unique_ptr<SparsityParam> luci_sparsityparam(const circle::SparsityParametersT *sparsity)
+{
+ assert(sparsity);
+ const auto &traversal_order = sparsity->traversal_order;
+ const auto &block_map = sparsity->block_map;
+ const auto &dim_metadata = sparsity->dim_metadata;
+
+ // TODO find a condition that should return nullptr
+ auto sparsityparam = std::make_unique<SparsityParam>();
+
+ sparsityparam->traversal_order = traversal_order;
+ sparsityparam->block_map = block_map;
+ for (const auto &dm : dim_metadata)
+ {
+ sparsityparam->dim_metadata.emplace_back(luci_dim_type(dm->format), dm->dense_size,
+ luci_sparse_index_vector(dm->array_segments),
+ luci_sparse_index_vector(dm->array_indices));
+ }
+
+ return sparsityparam;
+}
+
void copy_tensor_attributes(const circle::TensorT &tensor, CircleNode *node)
{
node->name(tensor_name(tensor));
@@ -193,6 +260,14 @@ void copy_tensor_attributes(const circle::TensorT &tensor, CircleNode *node)
if (quantparam)
node->quantparam(std::move(quantparam));
}
+
+ const auto *sparsity = tensor.sparsity.get();
+ if (sparsity != nullptr)
+ {
+ auto sparsityparam = luci_sparsityparam(sparsity);
+ if (sparsityparam)
+ node->sparsityparam(std::move(sparsityparam));
+ }
}
circle::BuiltinOperator CircleReader::builtin_code(const circle::OperatorT &op) const
diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp
index c6bcacb54..d598d30f4 100644
--- a/compiler/luci/import/src/GraphBuilderRegistry.cpp
+++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp
@@ -45,6 +45,7 @@ GraphBuilderRegistry::GraphBuilderRegistry()
CIRCLE_NODE(COS, CircleCosGraphBuilder); // 108
CIRCLE_NODE(DEPTH_TO_SPACE, CircleDepthToSpaceGraphBuilder); // 5
CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DGraphBuilder); // 4
+ CIRCLE_NODE(DEQUANTIZE, CircleDequantizeGraphBuilder); // 6
CIRCLE_NODE(DIV, CircleDivGraphBuilder); // 42
CIRCLE_NODE(ELU, CircleEluGraphBuilder); // 111
CIRCLE_NODE(EQUAL, CircleEqualGraphBuilder); // 71
@@ -132,6 +133,7 @@ GraphBuilderRegistry::GraphBuilderRegistry()
CIRCLE_NODE(TOPK_V2, CircleTopKV2GraphBuilder); // 48
CIRCLE_NODE(TRANSPOSE, CircleTransposeGraphBuilder); // 39
CIRCLE_NODE(TRANSPOSE_CONV, CircleTransposeConvGraphBuilder); // 67
+ CIRCLE_NODE(UNIDIRECTIONAL_SEQUENCE_LSTM, CircleUnidirectionalSequenceLSTMGraphBuilder); // 44
CIRCLE_NODE(UNIQUE, CircleUniqueGraphBuilder); // 103
CIRCLE_NODE(UNPACK, CircleUnpackGraphBuilder); // 88
CIRCLE_NODE(WHERE, CircleWhereGraphBuilder); // 109
@@ -140,7 +142,6 @@ GraphBuilderRegistry::GraphBuilderRegistry()
#undef CIRCLE_NODE
- // BuiltinOperator_DEQUANTIZE = 6,
// BuiltinOperator_EMBEDDING_LOOKUP = 7,
// BuiltinOperator_HASHTABLE_LOOKUP = 10,
// BuiltinOperator_LSH_PROJECTION = 15,
@@ -152,7 +153,6 @@ GraphBuilderRegistry::GraphBuilderRegistry()
// BuiltinOperator_CALL = 31,
// BuiltinOperator_EMBEDDING_LOOKUP_SPARSE = 33,
// BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35,
- // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
// BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46,
// BuiltinOperator_DELEGATE = 51,
// BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52,
diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp
index fad7a0757..f69448dfe 100644
--- a/compiler/luci/import/src/Nodes/CircleConst.cpp
+++ b/compiler/luci/import/src/Nodes/CircleConst.cpp
@@ -51,6 +51,12 @@ static void copy_data(const std::vector<uint8_t> &raw_data, uint32_t num_element
{
using T = typename loco::DataTypeImpl<DT>::Type;
+ // TODO calculate the exact buffer size of sparse tensor
+ if (const_node->sparsityparam())
+ {
+ num_elements = raw_data.size() / sizeof(T);
+ }
+
assert(raw_data.size() == num_elements * sizeof(T));
const auto *data = reinterpret_cast<const T *>(raw_data.data());
@@ -61,9 +67,6 @@ static void copy_data(const std::vector<uint8_t> &raw_data, uint32_t num_element
}
}
-//
-// circleconst_from_tensor() ?
-//
CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index)
{
LOGGER(l);
@@ -77,7 +80,7 @@ CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_ind
std::vector<int32_t> const_dims = const_tensor.shape; // in NHWC
if (const_dims.size() == 0 && buffer.empty())
{
- // unknown shape tensor
+ // unknown shape tensor and scalar tensor
return nullptr;
}
diff --git a/compiler/luci/import/src/Nodes/CircleDequantize.cpp b/compiler/luci/import/src/Nodes/CircleDequantize.cpp
new file mode 100644
index 000000000..1936da97c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDequantize.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleDequantize.h"
+
+#include <luci/IR/Nodes/CircleDequantize.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleDequantizeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDequantizeGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleDequantize>();
+ node->input(inputs.at(0));
+
+ // No options for Dequantize
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..c41cf4def
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h"
+
+#include <luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleUnidirectionalSequenceLSTMGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 24)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleUnidirectionalSequenceLSTMGraphBuilder::build_node(
+ const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleUnidirectionalSequenceLSTM>();
+ node->input(inputs.at(0));
+ node->input_to_input_weights(inputs.at(1)); // Optional
+ node->input_to_cell_weights(inputs.at(2));
+ node->input_to_forget_weights(inputs.at(3));
+ node->input_to_output_weights(inputs.at(4));
+ node->recurrent_to_input_weights(inputs.at(5)); // Optional
+ node->recurrent_to_cell_weights(inputs.at(6));
+ node->recurrent_to_forget_weights(inputs.at(7));
+ node->recurrent_to_output_weights(inputs.at(8));
+ node->cell_to_input_weights(inputs.at(9)); // Optional
+ node->cell_to_forget_weights(inputs.at(10)); // Optional
+ node->cell_to_output_weights(inputs.at(11)); // Optional
+ node->input_gate_bias(inputs.at(12)); // Optional
+ node->forget_gate_bias(inputs.at(13));
+ node->cell_gate_bias(inputs.at(14));
+ node->output_gate_bias(inputs.at(15));
+ node->projection_weights(inputs.at(16)); // Optional
+ node->projection_bias(inputs.at(17)); // Optional
+ node->activation_state(inputs.at(18));
+ node->cell_state(inputs.at(19));
+ node->input_layer_norm_coefficients(inputs.at(20)); // Optional
+ node->forget_layer_norm_coefficients(inputs.at(21)); // Optional
+ node->cell_layer_norm_coefficients(inputs.at(22)); // Optional
+ node->output_layer_norm_coefficients(inputs.at(23)); // Optional
+ const std::vector<int32_t> optionals = {1, 5, 9, 10, 11, 12, 16, 17, 20, 21, 22, 23};
+ for (auto optional : optionals)
+ {
+ if (auto inp = dynamic_cast<luci::CircleOutputExclude *>(node->arg(optional)))
+ {
+ // CircleOutputExclude doesn't need a type, but since all nodes must have a type, a dummy type
+ // is inserted.
+ inp->dtype(loco::DataType::FLOAT32);
+ }
+ }
+
+ const auto *options = op.builtin_options.AsUnidirectionalSequenceLSTMOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ node->cell_clip(options->cell_clip);
+ node->proj_clip(options->proj_clip);
+ node->time_major(options->time_major);
+ node->asymmetric_quantize_inputs(options->asymmetric_quantize_inputs);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
index 2abae604b..3f21d5858 100644
--- a/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
+++ b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
@@ -28,7 +28,9 @@ enum class FusedActFunc
NONE,
RELU,
RELU_N1_TO_1,
- RELU6
+ RELU6,
+ TANH,
+ SIGN_BIT
};
} // namespace luci
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
index 967103e3c..edec9d18b 100644
--- a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
@@ -25,6 +25,7 @@
#include "CircleOpcode.h"
#include "CircleNodeVisitor.forward.h"
#include "CircleQuantParam.h"
+#include "SparsityParam.h"
#include <memory>
@@ -54,6 +55,12 @@ struct CircleNode : public loco::Node,
_quantparam = std::move(quantparam);
}
+ SparsityParam *sparsityparam(void) const { return _sparsityparam.get(); }
+ void sparsityparam(std::unique_ptr<SparsityParam> &&sparsityparam)
+ {
+ _sparsityparam = std::move(sparsityparam);
+ }
+
ShapeStatus shape_status(void) const { return _shape_status; }
void shape_status(ShapeStatus ss) { _shape_status = ss; }
@@ -63,6 +70,7 @@ struct CircleNode : public loco::Node,
private:
NodeName _name;
std::unique_ptr<CircleQuantParam> _quantparam;
+ std::unique_ptr<SparsityParam> _sparsityparam;
ShapeStatus _shape_status{ShapeStatus::UNDEFINED};
int32_t _op_version = 1;
};
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h
index 25b86d2e9..fde0b612b 100644
--- a/compiler/luci/lang/include/luci/IR/CircleNodes.h
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h
@@ -34,6 +34,7 @@
#include "Nodes/CircleCustom.h"
#include "Nodes/CircleDepthToSpace.h"
#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDequantize.h"
#include "Nodes/CircleDiv.h"
#include "Nodes/CircleElu.h"
#include "Nodes/CircleEqual.h"
@@ -120,6 +121,7 @@
#include "Nodes/CircleTopKV2.h"
#include "Nodes/CircleTranspose.h"
#include "Nodes/CircleTransposeConv.h"
+#include "Nodes/CircleUnidirectionalSequenceLSTM.h"
#include "Nodes/CircleUnique.h"
#include "Nodes/CircleUnpack.h"
#include "Nodes/CircleWhere.h"
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
index 9f0a1b16e..b9d545893 100644
--- a/compiler/luci/lang/include/luci/IR/CircleNodes.lst
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
@@ -27,6 +27,7 @@ CIRCLE_NODE(COS, luci::CircleCos)
CIRCLE_NODE(CUSTOM, luci::CircleCustom)
CIRCLE_NODE(DEPTH_TO_SPACE, luci::CircleDepthToSpace)
CIRCLE_NODE(DEPTHWISE_CONV_2D, luci::CircleDepthwiseConv2D)
+CIRCLE_NODE(DEQUANTIZE, luci::CircleDequantize)
CIRCLE_NODE(DIV, luci::CircleDiv)
CIRCLE_NODE(ELU, luci::CircleElu)
CIRCLE_NODE(EQUAL, luci::CircleEqual)
@@ -113,6 +114,7 @@ CIRCLE_NODE(TILE, luci::CircleTile)
CIRCLE_NODE(TOPK_V2, luci::CircleTopKV2)
CIRCLE_NODE(TRANSPOSE, luci::CircleTranspose)
CIRCLE_NODE(TRANSPOSE_CONV, luci::CircleTransposeConv)
+CIRCLE_NODE(UNIDIRECTIONAL_SEQUENCE_LSTM, luci::CircleUnidirectionalSequenceLSTM)
CIRCLE_NODE(UNIQUE, luci::CircleUnique)
CIRCLE_NODE(UNPACK, luci::CircleUnpack)
CIRCLE_NODE(WHERE, luci::CircleWhere)
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h
new file mode 100644
index 000000000..847c5dfc5
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELEDEQUANTIZE_H__
+#define __LUCI_IR_CIRCELEDEQUANTIZE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DEQUANTIZE in Circle
+ */
+class CircleDequantize final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::DEQUANTIZE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELEDEQUANTIZE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..4352b045b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
+#define __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief UNIDIRECTIONAL_SEQUENCE_LSTM in Circle
+ */
+class CircleUnidirectionalSequenceLSTM final
+ : public FixedArityNode<24, CircleNodeImpl<CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *input_to_input_weights(void) const { return at(1)->node(); }
+ void input_to_input_weights(loco::Node *node) { at(1)->node(node); }
+ loco::Node *input_to_forget_weights(void) const { return at(2)->node(); }
+ void input_to_forget_weights(loco::Node *node) { at(2)->node(node); }
+ loco::Node *input_to_cell_weights(void) const { return at(3)->node(); }
+ void input_to_cell_weights(loco::Node *node) { at(3)->node(node); }
+ loco::Node *input_to_output_weights(void) const { return at(4)->node(); }
+ void input_to_output_weights(loco::Node *node) { at(4)->node(node); }
+
+ loco::Node *recurrent_to_input_weights(void) const { return at(5)->node(); }
+ void recurrent_to_input_weights(loco::Node *node) { at(5)->node(node); }
+ loco::Node *recurrent_to_forget_weights(void) const { return at(6)->node(); }
+ void recurrent_to_forget_weights(loco::Node *node) { at(6)->node(node); }
+ loco::Node *recurrent_to_cell_weights(void) const { return at(7)->node(); }
+ void recurrent_to_cell_weights(loco::Node *node) { at(7)->node(node); }
+ loco::Node *recurrent_to_output_weights(void) const { return at(8)->node(); }
+ void recurrent_to_output_weights(loco::Node *node) { at(8)->node(node); }
+
+ loco::Node *cell_to_input_weights(void) const { return at(9)->node(); }
+ void cell_to_input_weights(loco::Node *node) { at(9)->node(node); }
+ loco::Node *cell_to_forget_weights(void) const { return at(10)->node(); }
+ void cell_to_forget_weights(loco::Node *node) { at(10)->node(node); }
+ loco::Node *cell_to_output_weights(void) const { return at(11)->node(); }
+ void cell_to_output_weights(loco::Node *node) { at(11)->node(node); }
+
+ loco::Node *input_gate_bias(void) const { return at(12)->node(); }
+ void input_gate_bias(loco::Node *node) { at(12)->node(node); }
+ loco::Node *forget_gate_bias(void) const { return at(13)->node(); }
+ void forget_gate_bias(loco::Node *node) { at(13)->node(node); }
+ loco::Node *cell_gate_bias(void) const { return at(14)->node(); }
+ void cell_gate_bias(loco::Node *node) { at(14)->node(node); }
+ loco::Node *output_gate_bias(void) const { return at(15)->node(); }
+ void output_gate_bias(loco::Node *node) { at(15)->node(node); }
+
+ loco::Node *projection_weights(void) const { return at(16)->node(); }
+ void projection_weights(loco::Node *node) { at(16)->node(node); }
+ loco::Node *projection_bias(void) const { return at(17)->node(); }
+ void projection_bias(loco::Node *node) { at(17)->node(node); }
+
+ loco::Node *activation_state(void) const { return at(18)->node(); }
+ void activation_state(loco::Node *node) { at(18)->node(node); }
+ loco::Node *cell_state(void) const { return at(19)->node(); }
+ void cell_state(loco::Node *node) { at(19)->node(node); }
+
+ loco::Node *input_layer_norm_coefficients(void) const { return at(20)->node(); }
+ void input_layer_norm_coefficients(loco::Node *node) { at(20)->node(node); }
+ loco::Node *forget_layer_norm_coefficients(void) const { return at(21)->node(); }
+ void forget_layer_norm_coefficients(loco::Node *node) { at(21)->node(node); }
+ loco::Node *cell_layer_norm_coefficients(void) const { return at(22)->node(); }
+ void cell_layer_norm_coefficients(loco::Node *node) { at(22)->node(node); }
+ loco::Node *output_layer_norm_coefficients(void) const { return at(23)->node(); }
+ void output_layer_norm_coefficients(loco::Node *node) { at(23)->node(node); }
+
+public:
+ float cell_clip(void) const { return _cell_clip; }
+ void cell_clip(float cell_clip) { _cell_clip = cell_clip; }
+ float proj_clip(void) const { return _proj_clip; }
+ void proj_clip(float proj_clip) { _proj_clip = proj_clip; }
+ bool time_major(void) const { return _time_major; }
+ void time_major(bool time_major) { _time_major = time_major; }
+ bool asymmetric_quantize_inputs(void) const { return _asymmetric_quantize_inputs; }
+ void asymmetric_quantize_inputs(bool asymmetric_quantize_inputs)
+ {
+ _asymmetric_quantize_inputs = asymmetric_quantize_inputs;
+ }
+
+private:
+ float _cell_clip = 0.0f;
+ float _proj_clip = 0.0f;
+ bool _time_major = false;
+ bool _asymmetric_quantize_inputs = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/luci/lang/include/luci/IR/SparsityParam.h b/compiler/luci/lang/include/luci/IR/SparsityParam.h
new file mode 100644
index 000000000..f471e5ef9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/SparsityParam.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_SPARSITYPARAM_H__
+#define __LUCI_IR_SPARSITYPARAM_H__
+
+#include <cstdint>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+namespace luci
+{
+
+enum DimensionType
+{
+ DENSE,
+ SPARSE_CSR,
+};
+
+enum SparseIndexVectorType
+{
+ NONE,
+ I32,
+ U16,
+ U8,
+};
+
+class SparseIndexVector
+{
+public:
+ SparseIndexVector() = default;
+ SparseIndexVector(const SparseIndexVectorType &type, const std::vector<int32_t> &sparse_index_vec)
+ : _type{type}
+ {
+ switch (type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ _vec_ptr = static_cast<void *>(
+ new std::vector<int32_t>(sparse_index_vec.begin(), sparse_index_vec.end()));
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ auto new_vec = new std::vector<uint16_t>(sparse_index_vec.size());
+ for (uint32_t idx = 0; idx < sparse_index_vec.size(); idx++)
+ {
+ new_vec->at(idx) = static_cast<uint16_t>(sparse_index_vec.at(idx));
+ }
+ _vec_ptr = static_cast<void *>(new_vec);
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ auto new_vec = new std::vector<uint8_t>(sparse_index_vec.size());
+ for (uint32_t idx = 0; idx < sparse_index_vec.size(); idx++)
+ {
+ new_vec->at(idx) = static_cast<uint8_t>(sparse_index_vec.at(idx));
+ }
+ _vec_ptr = static_cast<void *>(new_vec);
+ break;
+ }
+ default:
+ std::runtime_error("Invalid SparseIndexVectorType");
+ }
+ }
+
+ SparseIndexVector(SparseIndexVectorType type, const void *sparse_index_vec) : _type{type}
+ {
+ switch (type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ const std::vector<int32_t> *vec =
+ static_cast<const std::vector<int32_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<int32_t>(vec->begin(), vec->end()));
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ const std::vector<uint16_t> *vec =
+ static_cast<const std::vector<uint16_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<uint16_t>(vec->begin(), vec->end()));
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ const std::vector<uint8_t> *vec =
+ static_cast<const std::vector<uint8_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<uint8_t>(vec->begin(), vec->end()));
+ break;
+ }
+ default:
+ std::runtime_error("Invalid SparseIndexVectorType");
+ }
+ }
+
+ SparseIndexVector(const SparseIndexVector &sparse_index_vec)
+ : SparseIndexVector(sparse_index_vec._type, sparse_index_vec._vec_ptr)
+ {
+ }
+
+ SparseIndexVector(SparseIndexVector &&sparse_index_vec)
+ : _type{sparse_index_vec._type}, _vec_ptr{std::exchange(sparse_index_vec._vec_ptr, nullptr)}
+ {
+ }
+
+ SparseIndexVector &operator=(const SparseIndexVector &sparse_index_vec)
+ {
+ return *this = SparseIndexVector(sparse_index_vec);
+ }
+
+ SparseIndexVector &operator=(SparseIndexVector &&sparse_index_vector)
+ {
+ std::swap(_vec_ptr, sparse_index_vector._vec_ptr);
+ std::swap(_type, sparse_index_vector._type);
+ return *this;
+ }
+
+ ~SparseIndexVector()
+ {
+ switch (_type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ auto vec_ptr = static_cast<std::vector<int32_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ auto vec_ptr = static_cast<std::vector<uint16_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ auto vec_ptr = static_cast<std::vector<uint8_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ default:
+ break;
+ }
+ _vec_ptr = nullptr;
+ _type = SparseIndexVectorType::NONE;
+ }
+
+public:
+ SparseIndexVectorType type(void) const { return _type; }
+
+public:
+ const std::vector<int32_t> *as_int32_vector(void) const
+ {
+ return _type == SparseIndexVectorType::I32 ? static_cast<const std::vector<int32_t> *>(_vec_ptr)
+ : nullptr;
+ }
+ const std::vector<uint16_t> *as_uint16_vector(void) const
+ {
+ return _type == SparseIndexVectorType::U16
+ ? static_cast<const std::vector<uint16_t> *>(_vec_ptr)
+ : nullptr;
+ }
+ const std::vector<uint8_t> *as_uint8_vector(void) const
+ {
+ return _type == SparseIndexVectorType::U8 ? static_cast<const std::vector<uint8_t> *>(_vec_ptr)
+ : nullptr;
+ }
+
+private:
+ SparseIndexVectorType _type{SparseIndexVectorType::NONE};
+ void *_vec_ptr{nullptr};
+};
+
+class DimMetaData
+{
+public:
+ DimMetaData() = delete;
+ DimMetaData(DimensionType format, int32_t dense_size) : _format{format}, _dense_size{dense_size}
+ {
+ // DO NOTHING
+ }
+ DimMetaData(DimensionType format, int32_t dense_size, const SparseIndexVector &array_segments,
+ const SparseIndexVector &array_indices)
+ : _format{format}, _dense_size{dense_size}, _array_segments{array_segments},
+ _array_indices{array_indices}
+ {
+ // DO NOTHING
+ }
+
+public:
+ DimensionType format(void) const { return _format; }
+ int32_t dense_size(void) const { return _dense_size; }
+ const SparseIndexVector &array_segments(void) const { return _array_segments; }
+ const SparseIndexVector &array_indices(void) const { return _array_indices; }
+
+private:
+ DimensionType _format{DimensionType::DENSE};
+ int32_t _dense_size{0};
+ SparseIndexVector _array_segments;
+ SparseIndexVector _array_indices;
+};
+
+struct SparsityParam
+{
+ std::vector<int32_t> traversal_order;
+ std::vector<int32_t> block_map;
+ std::vector<DimMetaData> dim_metadata;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_SPARSITYPARAM_H__
diff --git a/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp b/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp
new file mode 100644
index 000000000..c3a132c60
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleDequantize.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+TEST(CircleDequantizeTest, constructor)
+{
+ luci::CircleDequantize dequant_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), dequant_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::DEQUANTIZE, dequant_node.opcode());
+
+ ASSERT_EQ(nullptr, dequant_node.input());
+}
+
+TEST(CircleDequantizeTest, common_NEG)
+{
+ luci::CircleDequantize dequant_node;
+
+ dequant_node.name("name");
+ ASSERT_EQ("name", dequant_node.name());
+
+ auto q = std::make_unique<luci::CircleQuantParam>();
+ dequant_node.quantparam(std::move(q));
+ ASSERT_NE(nullptr, dequant_node.quantparam());
+
+ ASSERT_EQ(luci::ShapeStatus::UNDEFINED, dequant_node.shape_status());
+ dequant_node.shape_status(luci::ShapeStatus::NOSHAPE);
+ ASSERT_NE(luci::ShapeStatus::UNDEFINED, dequant_node.shape_status());
+}
+
+TEST(CircleDequantizeTest, input_NEG)
+{
+ luci::CircleDequantize dequant_node;
+ luci::CircleDequantize node;
+
+ dequant_node.input(&node);
+ ASSERT_NE(nullptr, dequant_node.input());
+
+ dequant_node.input(nullptr);
+ ASSERT_EQ(nullptr, dequant_node.input());
+}
+
+TEST(CircleDequantizeTest, arity_NEG)
+{
+ luci::CircleDequantize dequant_node;
+
+ ASSERT_NO_THROW(dequant_node.arg(0));
+ ASSERT_THROW(dequant_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleDequantizeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleDequantize dequant_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dequant_node.accept(&tv), std::exception);
+}
+
+TEST(CircleDequantizeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleDequantize dequant_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dequant_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp b/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp
index 03f612ba7..073be6bcb 100644
--- a/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp
+++ b/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp
@@ -33,7 +33,7 @@ TEST(CircleSparseToDenseTest, constructor)
ASSERT_EQ(nullptr, stb_node.values());
ASSERT_EQ(nullptr, stb_node.default_value());
- ASSERT_EQ(false, stb_node.validate_indices());
+ ASSERT_FALSE(stb_node.validate_indices());
}
TEST(CircleSparseToDenseTest, input_NEG)
diff --git a/compiler/luci/lang/src/Nodes/CircleSum.test.cpp b/compiler/luci/lang/src/Nodes/CircleSum.test.cpp
index 84b51d671..f9d07b200 100644
--- a/compiler/luci/lang/src/Nodes/CircleSum.test.cpp
+++ b/compiler/luci/lang/src/Nodes/CircleSum.test.cpp
@@ -30,7 +30,7 @@ TEST(CircleSumTest, constructor_P)
ASSERT_EQ(nullptr, sum_node.input());
ASSERT_EQ(nullptr, sum_node.reduction_indices());
- ASSERT_EQ(false, sum_node.keep_dims());
+ ASSERT_FALSE(sum_node.keep_dims());
}
TEST(CircleSumTest, input_NEG)
diff --git a/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp b/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp
new file mode 100644
index 000000000..6b00d6f4c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleUnidirectionalSequenceLSTMTest, constructor_P)
+{
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), trc_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM, trc_node.opcode());
+
+ ASSERT_EQ(nullptr, trc_node.input());
+
+ ASSERT_EQ(nullptr, trc_node.input_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_cell_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_cell_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.cell_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.cell_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.cell_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.input_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.forget_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.cell_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.output_gate_bias());
+
+ ASSERT_EQ(nullptr, trc_node.projection_weights());
+ ASSERT_EQ(nullptr, trc_node.projection_bias());
+
+ ASSERT_EQ(nullptr, trc_node.activation_state());
+ ASSERT_EQ(nullptr, trc_node.cell_state());
+
+ ASSERT_EQ(nullptr, trc_node.input_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.forget_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.cell_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.output_layer_norm_coefficients());
+
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, trc_node.fusedActivationFunction());
+ ASSERT_EQ(0.f, trc_node.cell_clip());
+ ASSERT_EQ(0.f, trc_node.proj_clip());
+ ASSERT_EQ(false, trc_node.time_major());
+ ASSERT_EQ(false, trc_node.asymmetric_quantize_inputs());
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, arity_NEG)
+{
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ ASSERT_NO_THROW(trc_node.arg(20));
+ ASSERT_THROW(trc_node.arg(24), std::out_of_range);
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/logex/src/FormattedGraph.cpp b/compiler/luci/logex/src/FormattedGraph.cpp
index bb7c73d5f..5b543e769 100644
--- a/compiler/luci/logex/src/FormattedGraph.cpp
+++ b/compiler/luci/logex/src/FormattedGraph.cpp
@@ -92,6 +92,10 @@ const char *to_str(luci::FusedActFunc fused)
return "RELU_N1_TO_1";
case luci::FusedActFunc::RELU6:
return "RELU6";
+ case luci::FusedActFunc::TANH:
+ return "TANH";
+ case luci::FusedActFunc::SIGN_BIT:
+ return "SIGN_BIT";
default:
return "Error";
}
@@ -210,6 +214,7 @@ private:
IMPLEMENT(luci::CircleCustom)
IMPLEMENT(luci::CircleDepthToSpace)
IMPLEMENT(luci::CircleDepthwiseConv2D)
+ IMPLEMENT(luci::CircleDequantize)
IMPLEMENT(luci::CircleDiv)
IMPLEMENT(luci::CircleElu)
IMPLEMENT(luci::CircleExp)
@@ -294,6 +299,7 @@ private:
IMPLEMENT(luci::CircleTopKV2)
IMPLEMENT(luci::CircleTranspose)
IMPLEMENT(luci::CircleTransposeConv)
+ IMPLEMENT(luci::CircleUnidirectionalSequenceLSTM)
IMPLEMENT(luci::CircleUnique)
IMPLEMENT(luci::CircleUnpack)
IMPLEMENT(luci::CircleWhere)
@@ -980,12 +986,61 @@ bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTransposeConv
s.args().append("inputSizes", tbl->lookup(node->inputSizes()));
s.args().append("filter", tbl->lookup(node->filter()));
s.args().append("outBackprop", tbl->lookup(node->outBackprop()));
+ s.args().append("bias", tbl->lookup(node->bias()));
s.args().append("stride(h,w)", to_str(node->stride()));
s.args().append("padding", to_str(node->padding()));
s.state(locop::NodeSummary::State::Complete);
return true;
}
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnidirectionalSequenceLSTM *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+
+ s.args().append("input_to_input_weights", tbl->lookup(node->input_to_input_weights()));
+ s.args().append("input_to_forget_weights", tbl->lookup(node->input_to_forget_weights()));
+ s.args().append("input_to_cell_weights", tbl->lookup(node->input_to_cell_weights()));
+ s.args().append("input_to_output_weights", tbl->lookup(node->input_to_output_weights()));
+
+ s.args().append("recurrent_to_input_weights", tbl->lookup(node->recurrent_to_input_weights()));
+ s.args().append("recurrent_to_forget_weights", tbl->lookup(node->recurrent_to_forget_weights()));
+ s.args().append("recurrent_to_cell_weights", tbl->lookup(node->recurrent_to_cell_weights()));
+ s.args().append("recurrent_to_output_weights", tbl->lookup(node->recurrent_to_output_weights()));
+
+ s.args().append("cell_to_input_weights", tbl->lookup(node->cell_to_input_weights()));
+ s.args().append("cell_to_forget_weights", tbl->lookup(node->cell_to_forget_weights()));
+ s.args().append("cell_to_output_weights", tbl->lookup(node->cell_to_output_weights()));
+
+ s.args().append("input_gate_bias", tbl->lookup(node->input_gate_bias()));
+ s.args().append("forget_gate_bias", tbl->lookup(node->forget_gate_bias()));
+ s.args().append("cell_gate_bias", tbl->lookup(node->cell_gate_bias()));
+ s.args().append("output_gate_bias", tbl->lookup(node->output_gate_bias()));
+
+ s.args().append("projection_weights", tbl->lookup(node->projection_weights()));
+ s.args().append("projection_bias", tbl->lookup(node->projection_bias()));
+
+ s.args().append("activation_state", tbl->lookup(node->activation_state()));
+ s.args().append("cell_state", tbl->lookup(node->cell_state()));
+
+ s.args().append("input_layer_norm_coefficients",
+ tbl->lookup(node->input_layer_norm_coefficients()));
+ s.args().append("forget_layer_norm_coefficients",
+ tbl->lookup(node->forget_layer_norm_coefficients()));
+ s.args().append("cell_layer_norm_coefficients",
+ tbl->lookup(node->cell_layer_norm_coefficients()));
+ s.args().append("output_layer_norm_coefficients",
+ tbl->lookup(node->output_layer_norm_coefficients()));
+
+ s.args().append("cell_clip", to_str(node->cell_clip()));
+ s.args().append("proj_clip", to_str(node->proj_clip()));
+ s.args().append("time_major", to_str(node->time_major()));
+ s.args().append("asymmetric_quantize_inputs", to_str(node->asymmetric_quantize_inputs()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnique *node,
locop::NodeSummary &s)
{
@@ -1225,6 +1280,12 @@ bool CircleNodeSummaryBuilder::summary(const luci::CircleDepthwiseConv2D *node,
return summary_node(tbl(), node, s);
}
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDequantize *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
bool CircleNodeSummaryBuilder::summary(const luci::CircleDiv *node, locop::NodeSummary &s) const
{
return use_xy(tbl(), node, s);
@@ -1686,6 +1747,12 @@ bool CircleNodeSummaryBuilder::summary(const luci::CircleTransposeConv *node,
return summary_node(tbl(), node, s);
}
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUnidirectionalSequenceLSTM *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
bool CircleNodeSummaryBuilder::summary(const luci::CircleUnique *node, locop::NodeSummary &s) const
{
return summary_node(tbl(), node, s);
diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h
index a832844f8..32ab85ef5 100644
--- a/compiler/luci/pass/include/luci/CircleOptimizer.h
+++ b/compiler/luci/pass/include/luci/CircleOptimizer.h
@@ -32,6 +32,7 @@ public:
{
enum Algorithm
{
+ FuseAddWithTConv,
FuseBatchNormWithTConv,
FuseBCQ,
FuseInstanceNorm,
@@ -41,13 +42,23 @@ public:
QuantizeDequantizeWeights,
QuantizeWithMinMax,
Requantize,
+ FoldDequantize,
+ SparsifyTensorPass,
};
enum AlgorithmParameters
{
+ // quantize
Quantize_input_dtype,
Quantize_output_dtype,
- Quantize_granularity // layer-wise or channel-wise
+ Quantize_granularity, // layer-wise or channel-wise
+
+ // sparsify
+ Sparsify_tensor_name,
+ Sparsify_traversal_order,
+ Sparsify_format,
+ Sparsify_block_size,
+ Sparsify_block_map,
};
virtual ~Options() = default;
@@ -67,6 +78,8 @@ public:
void quantize(loco::Graph *) const;
+ void sparsify(loco::Graph *) const;
+
private:
std::unique_ptr<Options> _options;
};
diff --git a/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h b/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h
new file mode 100644
index 000000000..07610d3e1
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FOLD_DEQUANTIZE_PASS_H__
+#define __LUCI_FOLD_DEQUANTIZE_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fold Dequantize, which can be folded by constant inputs
+ *
+ */
+struct FoldDequantizePass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FOLD_DEQUANTIZE"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FOLD_DEQUANTIZE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h b/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h
new file mode 100644
index 000000000..89b120397
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
+#define __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse Add into CircleTransposeConv
+ */
+struct FuseAddWithTConvPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseAddWithTConvPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h
new file mode 100644
index 000000000..41f43bf88
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SPARSIFY_TENSOR_PASS_H__
+#define __LUCI_SPARSIFY_TENSOR_PASS_H__
+
+#include <logo/Pass.h>
+
+#include <luci/IR/SparsityParam.h>
+
+namespace luci
+{
+
+class CircleConst;
+
+/**
+ * @brief Pass to sparsify tensor
+ */
+struct SparsifyTensorPass final : public logo::Pass
+{
+public:
+ SparsifyTensorPass(const std::string &tensor_name, const std::vector<int32_t> &traversal_order,
+ const std::vector<DimensionType> &format,
+ const std::vector<int32_t> &block_size, const std::vector<int32_t> &block_map)
+ : _tensor_name{tensor_name}, _traversal_order{traversal_order}, _format{format},
+ _block_size{block_size}, _block_map{block_map}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const char *name(void) const final { return "luci::SparsifyTensorPass"; }
+
+ bool run(loco::Graph *g) final;
+
+ template <loco::DataType DT> void sparsify_tensor(luci::CircleConst *cop);
+
+private:
+ // Tensor name that the pass will sparsify
+ std::string _tensor_name;
+ std::vector<int32_t> _traversal_order;
+ std::vector<DimensionType> _format;
+ std::vector<int32_t> _block_size;
+ std::vector<int32_t> _block_map;
+};
+
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::S32>(luci::CircleConst *cop);
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::S8>(luci::CircleConst *cop);
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::FLOAT32>(luci::CircleConst *cop);
+
+} // namespace luci
+
+#endif // __LUCI_SPARSIFY_TENSOR_PASS_H__
diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp
index 2ee759b4e..0e6056ffc 100644
--- a/compiler/luci/pass/src/CircleOptimizer.cpp
+++ b/compiler/luci/pass/src/CircleOptimizer.cpp
@@ -16,6 +16,8 @@
#include "luci/CircleOptimizer.h"
+#include "luci/Pass/FoldDequantizePass.h"
+#include "luci/Pass/FuseAddWithTConvPass.h"
#include "luci/Pass/FuseBatchNormWithTConv.h"
#include "luci/Pass/FuseBCQPass.h"
#include "luci/Pass/FuseInstanceNormPass.h"
@@ -25,6 +27,7 @@
#include "luci/Pass/RequantizePass.h"
#include "luci/Pass/QuantizeWithMinMaxPass.h"
#include "luci/Pass/QuantizeDequantizeWeightsPass.h"
+#include "luci/Pass/SparsifyTensorPass.h"
// TODO add more passes
#include "luci/Pass/ShapeInferencePass.h"
@@ -40,10 +43,25 @@
#include <logo/Phase.h>
#include <memory>
+#include <sstream>
namespace
{
+std::vector<int> parseIntFromCommadelimitedStr(std::string str)
+{
+ std::vector<int> ret;
+ std::istringstream is(str);
+ for (uint32_t i; is >> i;)
+ {
+ assert(i != ',');
+ ret.push_back(i);
+ if (is.peek() == ',')
+ is.ignore();
+ }
+ return ret;
+}
+
using namespace luci;
class OptimizeOptionsImpl final : public luci::CircleOptimizer::Options
@@ -132,6 +150,14 @@ void CircleOptimizer::optimize(loco::Graph *g) const
{
phase.emplace_back(std::make_unique<FuseBatchNormWithTConvPass>());
}
+ if (_options->query(Options::Algorithm::FuseAddWithTConv))
+ {
+ phase.emplace_back(std::make_unique<FuseAddWithTConvPass>());
+ }
+ if (_options->query(Options::Algorithm::FoldDequantize))
+ {
+ phase.emplace_back(std::make_unique<luci::FoldDequantizePass>());
+ }
// Shape inference is needed for added nodes doing above transformations
phase.emplace_back(std::make_unique<luci::ShapeInferencePass>());
@@ -151,7 +177,7 @@ void CircleOptimizer::quantize(loco::Graph *g) const
if (_options->query(Options::Algorithm::QuantizeDequantizeWeights))
{
static const std::vector<std::string> fakeq_supported_input_dtype{"float32"};
- static const std::vector<std::string> fakeq_supported_output_dtype{"uint8"};
+ static const std::vector<std::string> fakeq_supported_output_dtype{"uint8", "int16"};
static const std::vector<std::string> fakeq_supported_granularity{"layer", "channel"};
auto input_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_dtype);
@@ -187,7 +213,7 @@ void CircleOptimizer::quantize(loco::Graph *g) const
if (_options->query(Options::Algorithm::QuantizeWithMinMax))
{
static const std::vector<std::string> qwmm_supported_input_dtype{"float32"};
- static const std::vector<std::string> qwmm_supported_output_dtype{"uint8"};
+ static const std::vector<std::string> qwmm_supported_output_dtype{"uint8", "int16"};
static const std::vector<std::string> qwmm_supported_granularity{"layer", "channel"};
auto input_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_dtype);
@@ -244,4 +270,41 @@ void CircleOptimizer::quantize(loco::Graph *g) const
phase_runner.run(phase);
}
+void CircleOptimizer::sparsify(loco::Graph *g) const
+{
+ if (_options->query(Options::Algorithm::SparsifyTensorPass))
+ {
+ std::string tensor_name = _options->param(Options::AlgorithmParameters::Sparsify_tensor_name);
+ std::string str_tarversal_order =
+ _options->param(Options::AlgorithmParameters::Sparsify_traversal_order);
+ std::string str_format = _options->param(Options::AlgorithmParameters::Sparsify_format);
+ std::string str_block_size = _options->param(Options::AlgorithmParameters::Sparsify_block_size);
+ std::string str_block_map = _options->param(Options::AlgorithmParameters::Sparsify_block_map);
+
+ // traversal order
+ std::vector<int32_t> traversal_order = parseIntFromCommadelimitedStr(str_tarversal_order);
+ // format
+ std::vector<DimensionType> format;
+ std::istringstream is(str_format);
+ for (char c; is >> c;)
+ {
+ assert(c != ',');
+ if (c == 'd')
+ format.push_back(DimensionType::DENSE);
+ else if (c == 's')
+ format.push_back(DimensionType::SPARSE_CSR);
+ if (is.peek() == ',')
+ is.ignore();
+ }
+ // block size
+ std::vector<int32_t> block_size = parseIntFromCommadelimitedStr(str_block_size);
+ // block map
+ std::vector<int32_t> block_map = parseIntFromCommadelimitedStr(str_block_map);
+
+ luci::SparsifyTensorPass sparsifier{tensor_name, traversal_order, format, block_size,
+ block_map};
+ sparsifier.run(g);
+ }
+}
+
} // namespace luci
diff --git a/compiler/luci/pass/src/FoldDequantizePass.cpp b/compiler/luci/pass/src/FoldDequantizePass.cpp
new file mode 100644
index 000000000..01c04f478
--- /dev/null
+++ b/compiler/luci/pass/src/FoldDequantizePass.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FoldDequantizePass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco/Service/TypeInference.h>
+
+namespace
+{
+
+bool is_hybrid_kernel_supported(loco::Node *node)
+{
+ if (dynamic_cast<luci::CircleFullyConnected *>(node) != nullptr)
+ return true;
+
+ return false;
+}
+
+bool is_foldable_const(luci::CircleConst *node)
+{
+ if (node->quantparam() == nullptr)
+ return false;
+
+ if (node->dtype() == loco::DataType::S8)
+ return true;
+ if (node->dtype() == loco::DataType::U8)
+ return true;
+
+ return false;
+}
+
+luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node)
+{
+ if (const_node->quantparam() == nullptr)
+ {
+ throw std::runtime_error("Given constant node has no quantization parameter");
+ }
+
+ auto g = const_node->graph();
+ auto new_const_node = g->nodes()->create<luci::CircleConst>();
+
+ new_const_node->dtype(loco::DataType::FLOAT32);
+ new_const_node->rank(const_node->rank());
+ uint32_t dim_size = 1;
+ for (uint32_t i = 0; i < new_const_node->rank(); ++i)
+ {
+ new_const_node->dim(i) = const_node->dim(i);
+ dim_size *= const_node->dim(i).value();
+ }
+ new_const_node->size<loco::DataType::FLOAT32>(dim_size);
+ new_const_node->shape_status(luci::ShapeStatus::VALID);
+
+ const int32_t q_dim = const_node->quantparam()->quantized_dimension;
+ const int32_t q_dim_value = const_node->dim(q_dim).value();
+
+ int32_t right_count = q_dim_value;
+ for (uint32_t i = q_dim + 1; i < const_node->rank(); ++i)
+ right_count *= const_node->dim(i).value();
+
+ if (const_node->dtype() == loco::DataType::S8)
+ {
+ for (uint32_t i = 0; i < const_node->size<loco::DataType::S8>(); ++i)
+ {
+ uint32_t qd = (i % right_count) / (right_count / q_dim_value);
+ if (qd >= const_node->quantparam()->zerop.size())
+ qd = 0;
+
+ new_const_node->at<loco::DataType::FLOAT32>(i) =
+ (float)(const_node->at<loco::DataType::S8>(i) - const_node->quantparam()->zerop.at(qd)) *
+ const_node->quantparam()->scale.at(qd);
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < const_node->size<loco::DataType::U8>(); ++i)
+ {
+ uint32_t qd = (i % right_count) / (right_count / q_dim_value);
+ if (qd >= const_node->quantparam()->zerop.size())
+ qd = 0;
+
+ new_const_node->at<loco::DataType::FLOAT32>(i) =
+ (float)((int)const_node->at<loco::DataType::U8>(i) -
+ const_node->quantparam()->zerop.at(qd)) *
+ const_node->quantparam()->scale.at(qd);
+ }
+ }
+
+ return new_const_node;
+}
+
+bool replace_const_node(loco::Node *node, luci::CircleConst *const_node)
+{
+ if (auto gather = dynamic_cast<luci::CircleGather *>(node))
+ {
+ gather->params(dequantized_const_node(const_node));
+ gather->dtype(loco::DataType::FLOAT32);
+ return true;
+ }
+ else
+ {
+ // TODO Support more ops
+ return false;
+ }
+}
+
+} // namespace
+
+namespace luci
+{
+
+/**
+ *
+ * Folding pattern 1 - When input of Dequantize is foldable constant
+ *
+ * [Before]
+ * quantized_const_input ---------- Dequantize ---------- Op ---
+ * +-- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ * [After]
+ * dequantized_const_input -------------------------------- Op ---
+ *
+ * quantized_const_input ----- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ *
+ * Folding pattern 2 - When input of Dequantize uses quantized output value
+ *
+ * [Before]
+ * quantized_const_input ----- Gather ----- Dequantize --- Op ---
+ * +-- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ * [After]
+ * dequantized_const_input ------Gather -------------------- Op ---
+ *
+ * quantized_const_input ----- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ *
+ */
+bool FoldDequantizePass::run(loco::Graph *g)
+{
+ bool changed = false;
+
+ for (auto node : loco::all_nodes(g))
+ {
+ if (auto circle_dequant = dynamic_cast<luci::CircleDequantize *>(node))
+ {
+ if (auto const_input = dynamic_cast<luci::CircleConst *>(circle_dequant->input()))
+ {
+ // Pattern 1 - When input of Dequantize is foldable constant
+ if (is_foldable_const(const_input))
+ {
+ loco::replace(circle_dequant).with(dequantized_const_node(const_input));
+ changed = true;
+ }
+ }
+ }
+ else if (auto const_node = dynamic_cast<luci::CircleConst *>(node))
+ {
+ if (is_foldable_const(const_node))
+ {
+ for (auto const_node_user : loco::succs(const_node))
+ {
+ // If user is hybrid kernel supported operation, do not dequantize
+ if (is_hybrid_kernel_supported(const_node_user))
+ continue;
+
+ auto users = loco::succs(const_node_user);
+ if (users.size() > 1)
+ continue;
+
+ // Pattern 2 - When input of Dequantize uses quantized output value
+ if (auto dequant = dynamic_cast<luci::CircleDequantize *>(*users.begin()))
+ {
+ if (replace_const_node(const_node_user, const_node))
+ {
+ loco::replace(dequant).with(const_node_user);
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp
new file mode 100644
index 000000000..bd7805f6a
--- /dev/null
+++ b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseAddWithTConvPass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace
+{
+/**
+ * Fuse add to TCONV if possible
+ *
+ * BEFORE
+ *
+ * [CircleTransposeConv]
+ * |
+ * [add]
+ * AFTER
+ *
+ * [CircleTransposeConv]
+ */
+bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv)
+{
+ // check whether it has bias or not. This optimization works only if it doesn't.
+ auto bias = dynamic_cast<luci::CircleOutputExclude *>(tconv->bias());
+ if (not bias)
+ return false;
+
+ // get weight of tconv
+ auto filter = dynamic_cast<luci::CircleConst *>(tconv->filter());
+ if (not filter)
+ return false;
+ if (filter->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // get add node
+ auto tconv_output = loco::succs(tconv);
+ assert(tconv_output.size() == 1);
+ auto add = dynamic_cast<luci::CircleAdd *>(*tconv_output.begin());
+ if (not add)
+ return false;
+ if (add->dtype() != loco::DataType::FLOAT32)
+ return false;
+ if (add->fusedActivationFunction() != luci::FusedActFunc::NONE &&
+ add->fusedActivationFunction() != luci::FusedActFunc::RELU6)
+ return false;
+
+ // get addition
+ luci::CircleConst *addition = nullptr;
+ if (add->x() == tconv)
+ addition = dynamic_cast<luci::CircleConst *>(add->y());
+ else
+ addition = dynamic_cast<luci::CircleConst *>(add->x());
+
+ if (not addition)
+ return false;
+
+ // addition dim(0) == tconv filter channel dim
+ if (addition->rank() != 1)
+ return false;
+ auto addition_dim = addition->dim(0).value();
+ auto filter_channel_dim = filter->dim(0).value();
+ if (filter_channel_dim != addition_dim)
+ return false;
+
+ // fuse addition with transposed conv
+ tconv->bias(addition);
+
+ if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6)
+ {
+ // separate relu op from add op
+ auto relu = add->graph()->nodes()->create<luci::CircleRelu6>();
+ relu->features(tconv);
+
+ // remove add node
+ replace(add).with(relu);
+ }
+ else
+ {
+ replace(add).with(tconv);
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseAddWithTConvPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto tconv = dynamic_cast<luci::CircleTransposeConv *>(node);
+ if (not tconv)
+ continue;
+
+ if (fuse_add_with_tconv(tconv))
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseBCQPass.cpp b/compiler/luci/pass/src/FuseBCQPass.cpp
index 7aa2e3e80..ebf28779b 100644
--- a/compiler/luci/pass/src/FuseBCQPass.cpp
+++ b/compiler/luci/pass/src/FuseBCQPass.cpp
@@ -17,163 +17,139 @@
#include "luci/Pass/FuseBCQPass.h"
#include <luci/IR/CircleNodes.h>
+#include <luci/Log.h>
#include <cassert>
-#include <string>
#include <set>
namespace
{
-/**
- * @brief Circle nodes including BCQ information and a circle node to which BCQ will be applied
- * are connected with their name. And their names include common prefix.
- * However, after pb file is converted to tflite file, some nodes' name are changed.
- * Thus this function will return original common prefix.
- *
- * @note All the re-naming rule of TFLite converter is not figured out.
- * Therefore, if new naming rule is detected, this function should be updated.
- */
-const std::string node_name_prefix(luci::NodeName node_name)
-{
- std::string prefix = node_name;
-
- if (prefix.find("/ReadVariableOp/resource") != std::string::npos)
- {
- const auto start_index = prefix.find("/ReadVariableOp/resource");
-
- const auto left_prefix = prefix.substr(0, start_index);
- const auto right_prefix = prefix.substr(start_index + 24);
-
- prefix = left_prefix + right_prefix;
- }
-
- if (prefix.find("Tensordot/") != std::string::npos)
- {
- const auto index = prefix.find("Tensordot/");
- prefix = prefix.substr(0, index - 1);
- }
- else if (prefix.find("/MatMul") != std::string::npos)
- {
- const auto index = prefix.find("/MatMul");
- prefix = prefix.substr(0, index);
- }
- else if (prefix.find("kernel/") != std::string::npos)
- {
- const auto index = prefix.find("kernel/");
- prefix = prefix.substr(0, index - 1);
- }
- else if (prefix.find("/bcqinfo_") != std::string::npos)
- {
- const auto index = prefix.find("/bcqinfo_");
- prefix = prefix.substr(0, index);
- }
-
- return prefix;
-}
-
-/**
- * @brief Create CircleOutputExclude operation, which has same shape and dtype with
- * original circle_node.
- */
-luci::CircleOutputExclude *createNoOp(luci::CircleNode *circle_node)
-{
- auto graph = circle_node->graph();
- auto noOp = graph->nodes()->create<luci::CircleOutputExclude>();
-
- if (circle_node->shape_status() == luci::ShapeStatus::VALID)
- {
- noOp->dtype(circle_node->dtype());
- noOp->rank(circle_node->rank());
- for (uint32_t i = 0; i < circle_node->rank(); ++i)
- noOp->dim(i) = circle_node->dim(i);
- }
- else
- {
- // For type inference
- noOp->dtype(loco::DataType::FLOAT32);
- }
-
- return noOp;
-};
-
-} // namespace
-
-namespace
-{
-
// V means the version of BCQ.
template <int32_t V> class BCQFuser;
template <> class BCQFuser<1>
{
public:
+ BCQFuser<1>(int32_t original_output_cnt, int32_t bundle_cnt)
+ : _original_output_cnt{original_output_cnt}, _bundle_cnt{bundle_cnt}
+ {
+ // Do nothing
+ }
+
+public:
bool fuseBCQ(loco::Graph *g)
{
- bool changed = false;
- for (auto node : loco::all_nodes(g))
+ const auto output_nodes = loco::output_nodes(g);
+ for (auto node : output_nodes)
{
- if (auto circle_const = dynamic_cast<luci::CircleConst *>(node))
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+
+ /**
+ * First output of model is metadata for BCQ. Please refer to following example.
+ *
+ * When original_output_cnt is 2,
+ * BCQ_METADATA, original_output_1, original_output_2, BCQ_INFO_1, ...
+ */
+ if ((int)output_node->index() > _original_output_cnt)
{
- add_BCQ_info_node(circle_const);
+ const auto prefix = (output_node->index() - (_original_output_cnt + 1)) / (_bundle_cnt);
+ const MetadataType metadata_type = static_cast<MetadataType>(
+ (output_node->index() - (_original_output_cnt + 1)) % (_bundle_cnt));
+ const auto circle_node = loco::must_cast<luci::CircleNode *>(output_node->from());
+ add_BCQ_info_node(prefix, metadata_type, circle_node);
}
}
if (!is_bcqinfo_valid())
return false;
- for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ for (auto f : _fusable_op)
{
+ auto prefix = f.first;
+ luci::CircleNode *node = f.second;
+
+ if (!is_valid_prefix(prefix))
+ continue;
+
+ // Fuse Gather to BCQGather
if (auto gather = dynamic_cast<luci::CircleGather *>(node))
{
- auto params = dynamic_cast<luci::CircleConst *>(gather->params());
- if (params != nullptr && has_BCQ_info(params))
+ if (auto params = dynamic_cast<luci::CircleConst *>(gather->params()))
{
auto bcq_gather = g->nodes()->create<luci::CircleBCQGather>();
bcq_gather->op_version(1);
- bcq_gather->input_scales(get_alpha(params));
- bcq_gather->input_binary(get_packed_binary_code(params));
+ bcq_gather->input_scales(_alpha[prefix]);
+ bcq_gather->input_binary(_packed_binary_code[prefix]);
bcq_gather->indices(gather->indices());
- bcq_gather->input_clusters(packed_clusters(params));
+ bcq_gather->input_clusters(packed_clusters(g, prefix));
- // input_binary shape : [output_size, hidden_size]
- const auto binary_hidden_size =
- loco::must_cast<luci::CircleConst *>(bcq_gather->input_binary())->dim(1).value() * 32;
- bcq_gather->input_hidden_size(binary_hidden_size);
-
- if (do_w_x(params))
+ if (_do_w_x[prefix]->at<loco::DataType::BOOL>(0))
{
+ bcq_gather->input_hidden_size(params->dim(1).value());
bcq_gather->axis(gather->axis());
+ loco::replace(gather).with(bcq_gather);
}
else
{
+ bcq_gather->input_hidden_size(params->dim(0).value());
const auto axis_transpose = (gather->axis() == 0) ? 1 : 0;
bcq_gather->axis(axis_transpose);
+
+ const auto indices_rank =
+ loco::must_cast<luci::CircleNode *>(gather->indices())->rank();
+
+ auto perm = g->nodes()->create<luci::CircleConst>();
+ perm->dtype(loco::DataType::S32);
+ perm->size<loco::DataType::S32>(1 + indices_rank);
+ perm->rank(1);
+ perm->dim(0) = 1 + indices_rank;
+ for (uint32_t idx = 0; idx < indices_rank; ++idx)
+ perm->at<loco::DataType::S32>(idx) = idx + 1;
+ perm->at<loco::DataType::S32>(indices_rank) = 0;
+ perm->shape_status(luci::ShapeStatus::VALID);
+
+ auto output_transpose = g->nodes()->create<luci::CircleTranspose>();
+ output_transpose->a(bcq_gather);
+ output_transpose->perm(perm);
+
+ loco::replace(gather).with(output_transpose);
}
- loco::replace(gather).with(bcq_gather);
+ return true;
+ }
+ }
- changed = true;
+ // Einsum is unpacked to FullyConnected, Pack and Reshape
+ if (auto reshape = dynamic_cast<luci::CircleReshape *>(node))
+ {
+ node = dynamic_cast<luci::CircleNode *>(reshape->tensor());
+ }
+ if (auto pack = dynamic_cast<luci::CirclePack *>(node))
+ {
+ if (pack->values_count() == 1 && pack->rank() == 3)
+ {
+ node = dynamic_cast<luci::CircleNode *>(pack->values(0));
}
}
- else if (auto fully_connected = dynamic_cast<luci::CircleFullyConnected *>(node))
+
+ // Fuse FullyConnected to BCQFullyConnected
+ if (auto fully_connected = dynamic_cast<luci::CircleFullyConnected *>(node))
{
- auto weights = dynamic_cast<luci::CircleConst *>(fully_connected->weights());
- if (weights != nullptr && has_BCQ_info(weights))
+ if (auto weights = dynamic_cast<luci::CircleConst *>(fully_connected->weights()))
{
auto bcq_fc = g->nodes()->create<luci::CircleBCQFullyConnected>();
bcq_fc->op_version(1);
- bcq_fc->weights_scales(get_alpha(weights));
- bcq_fc->weights_binary(get_packed_binary_code(weights));
+ bcq_fc->weights_scales(_alpha[prefix]);
+ bcq_fc->weights_binary(_packed_binary_code[prefix]);
bcq_fc->bias(fully_connected->bias());
- bcq_fc->weights_clusters(packed_clusters(weights));
+ bcq_fc->weights_clusters(packed_clusters(g, prefix));
bcq_fc->fusedActivationFunction(fully_connected->fusedActivationFunction());
loco::Node *bcq_input = fully_connected->input();
- int32_t batch_rank = 0;
// If input of BCQFullyConnected has more than rank 2, we should reshape it as rank 2
const auto original_input = loco::must_cast<luci::CircleNode *>(fully_connected->input());
@@ -200,27 +176,18 @@ public:
reshape->shape(new_shape);
bcq_input = reshape;
- batch_rank = original_input->rank() - 2;
}
// If x_w formation, we should insert Transpose in front and back of BCQFullyConnected
- if (do_w_x(weights))
+ if (_do_w_x[prefix]->at<loco::DataType::BOOL>(0))
{
- const auto binary_hidden_size =
- loco::must_cast<luci::CircleNode *>(fully_connected->input())
- ->dim(batch_rank)
- .value();
- bcq_fc->weights_hidden_size(binary_hidden_size);
+ bcq_fc->weights_hidden_size(weights->dim(0).value());
bcq_fc->input(bcq_input);
loco::replace(fully_connected).with(bcq_fc);
}
else
{
- const auto binary_hidden_size =
- loco::must_cast<luci::CircleNode *>(fully_connected->input())
- ->dim(1 + batch_rank)
- .value();
- bcq_fc->weights_hidden_size(binary_hidden_size);
+ bcq_fc->weights_hidden_size(weights->dim(1).value());
auto perm = g->nodes()->create<luci::CircleConst>();
perm->dtype(loco::DataType::S32);
@@ -244,159 +211,183 @@ public:
loco::replace(fully_connected).with(output_transpose);
}
- changed = true;
+ return true;
+ }
+ else
+ {
+ // TODO Is there any case that input() is constant, instead of weights()?
}
}
}
- if (changed)
- clear_BCQ_nodes();
-
- return changed;
+ return false;
}
private:
- void add_BCQ_info_node(luci::CircleConst *node)
+ enum MetadataType
{
- const auto node_name = node->name();
- const auto prefix = node_name_prefix(node_name);
-
- // If bcqinfo_* nodes are held by Reshape operation,
- // shape of bcqinfo_* nodes are copied to `shape` input of Reshape operation.
- // Then the name becomes bcqinfo_*_copy_shape.
- // We should prevent this node not to added to bcq information.
- if (node_name.find("_copy_shape") != std::string::npos)
+ DO_W_X,
+ ALPHA,
+ BINARY_CODE,
+ NUM_OF_CLUSTERS,
+ SIZE_OF_CLUSTERS,
+ QBITS_OF_CLUSTERS,
+ FUSABLE_OP,
+ DEQUANT_WEIGHT,
+ };
+
+ void add_BCQ_info_node(int32_t prefix, MetadataType metadata_type, luci::CircleNode *node)
+ {
+ if (metadata_type == MetadataType::FUSABLE_OP)
+ {
+ _fusable_op[prefix] = node;
return;
+ }
- if (node_name.find("bcqinfo_do_w_x") != std::string::npos)
- _do_w_x[prefix] = node;
- else if (node_name.find("bcqinfo_alpha") != std::string::npos)
- _alpha[prefix] = node;
- else if (node_name.find("bcqinfo_packed_binary_code") != std::string::npos)
- _packed_binary_code[prefix] = node;
- else if (node_name.find("bcqinfo_number_of_clusters") != std::string::npos)
- _number_of_clusters[prefix] = node;
- else if (node_name.find("bcqinfo_size_of_clusters") != std::string::npos)
- _size_of_clusters[prefix] = node;
- else if (node_name.find("bcqinfo_qbits_of_clusters") != std::string::npos)
- _qbits_of_clusters[prefix] = node;
- else if (node_name.find("bcqinfo_dequant_weight") != std::string::npos)
- _dequant_weight[prefix] = node;
- }
+ luci::CircleConst *const_node;
- bool has_BCQ_info(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- bool has_info = true;
-
- has_info &= (_do_w_x.find(prefix) != _do_w_x.end());
- has_info &= (_alpha.find(prefix) != _alpha.end());
- has_info &= (_packed_binary_code.find(prefix) != _packed_binary_code.end());
- has_info &= (_number_of_clusters.find(prefix) != _number_of_clusters.end());
- has_info &= (_size_of_clusters.find(prefix) != _size_of_clusters.end());
- has_info &= (_qbits_of_clusters.find(prefix) != _qbits_of_clusters.end());
- // bcqinfo_dequant_weight is just for validation, so not always exists.
-
- return has_info;
+ // Converter in TensorFlow v1.x sometimes generate Reshape op
+ if (auto reshape = dynamic_cast<luci::CircleReshape *>(node))
+ const_node = loco::must_cast<luci::CircleConst *>(reshape->tensor());
+ else
+ const_node = loco::must_cast<luci::CircleConst *>(node);
+
+ if (metadata_type == MetadataType::DO_W_X)
+ _do_w_x[prefix] = const_node;
+ else if (metadata_type == MetadataType::ALPHA)
+ _alpha[prefix] = const_node;
+ else if (metadata_type == MetadataType::BINARY_CODE)
+ _packed_binary_code[prefix] = const_node;
+ else if (metadata_type == MetadataType::NUM_OF_CLUSTERS)
+ _number_of_clusters[prefix] = const_node;
+ else if (metadata_type == MetadataType::SIZE_OF_CLUSTERS)
+ _size_of_clusters[prefix] = const_node;
+ else if (metadata_type == MetadataType::QBITS_OF_CLUSTERS)
+ _qbits_of_clusters[prefix] = const_node;
+ else
+ _dequant_weight[prefix] = const_node;
}
- /**
- * @brief Exclude BCQ information nodes which are used for fusing BCQ operations
- * from graph output by using CircleOutputExclude
- */
- void clear_BCQ_nodes()
+ bool is_bcqinfo_valid()
{
- auto clear_nodes = [](std::map<std::string, luci::CircleConst *> &nodes) {
- for (auto &n : nodes)
+ LOGGER(l);
+
+ for (auto n : _do_w_x)
+ {
+ // do_w_x should be BOOL type
+ if (n.second->dtype() != loco::DataType::BOOL)
{
- auto node = n.second;
+ WARN(l) << "FuseBCQPass : do_w_x has wrong type" << std::endl;
+ return false;
+ }
+ }
- for (auto s : loco::succs(node))
- {
- if (auto outnode = dynamic_cast<luci::CircleOutput *>(s))
- {
- outnode->from(createNoOp(node));
- }
- else if (auto reshape_node = dynamic_cast<luci::CircleReshape *>(s))
- {
- for (auto o : loco::succs(reshape_node))
- {
- auto circle_output = loco::must_cast<luci::CircleOutput *>(o);
- circle_output->from(createNoOp(reshape_node));
- }
- }
- }
+ for (auto n : _alpha)
+ {
+ // alpha should be FLOAT32 type
+ if (n.second->dtype() != loco::DataType::FLOAT32)
+ {
+ WARN(l) << "FuseBCQPass : alpha has wrong type" << std::endl;
+ return false;
}
- };
-
- clear_nodes(_do_w_x);
- clear_nodes(_alpha);
- clear_nodes(_packed_binary_code);
- clear_nodes(_number_of_clusters);
- clear_nodes(_size_of_clusters);
- clear_nodes(_qbits_of_clusters);
- clear_nodes(_dequant_weight);
- }
+ }
- bool is_bcqinfo_valid()
- {
- // do_w_x should be int32 or bool type
- for (auto n : _do_w_x)
+ for (auto n : _packed_binary_code)
+ {
+ // packed_binary_code should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : packed_binary_code has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _number_of_clusters)
{
- if (n.second->dtype() != loco::DataType::BOOL && n.second->dtype() != loco::DataType::S32)
+ // number_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : number_of_clusters has wrong type" << std::endl;
return false;
+ }
}
+ for (auto n : _size_of_clusters)
+ {
+ // size_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : size_of_clusters has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _qbits_of_clusters)
+ {
+ // qbits_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : qbits_of_clusters has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ // As dequant_weight is not used for fusing, skip validation.
+
return true;
}
-private:
- bool do_w_x(luci::CircleConst *node)
+ bool is_valid_prefix(int32_t prefix)
{
- const auto prefix = node_name_prefix(node->name());
+ LOGGER(l);
- if (_do_w_x[prefix]->dtype() == loco::DataType::S32)
- return _do_w_x[prefix]->at<loco::DataType::S32>(0) == 1;
- else
- return _do_w_x[prefix]->at<loco::DataType::BOOL>(0);
- }
+ if (_do_w_x.find(prefix) == _do_w_x.end())
+ {
+ WARN(l) << "do_w_x is not found" << std::endl;
+ return false;
+ }
- luci::CircleConst *get_alpha(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- return _alpha[prefix];
- }
+ if (_alpha.find(prefix) == _alpha.end())
+ {
+ WARN(l) << "alpha is not found" << std::endl;
+ return false;
+ }
- luci::CircleConst *get_packed_binary_code(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- return _packed_binary_code[prefix];
- }
+ if (_packed_binary_code.find(prefix) == _packed_binary_code.end())
+ {
+ WARN(l) << "packed_binary_code is not found" << std::endl;
+ return false;
+ }
- luci::CircleConst *get_number_of_clusters(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- return _number_of_clusters[prefix];
- }
+ if (_number_of_clusters.find(prefix) == _number_of_clusters.end())
+ {
+ WARN(l) << "number_of_clusters is not found" << std::endl;
+ return false;
+ }
- luci::CircleConst *get_size_of_clusters(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- return _size_of_clusters[prefix];
- }
+ if (_size_of_clusters.find(prefix) == _size_of_clusters.end())
+ {
+ WARN(l) << "size_of_clusters is not found" << std::endl;
+ return false;
+ }
- luci::CircleConst *get_qbits_of_clusters(luci::CircleConst *node)
- {
- const auto prefix = node_name_prefix(node->name());
- return _qbits_of_clusters[prefix];
+ if (_qbits_of_clusters.find(prefix) == _qbits_of_clusters.end())
+ {
+ WARN(l) << "qbits_of_clusters is not found" << std::endl;
+ return false;
+ }
+
+ // As dequant_weight is not used for fusing, skip validation.
+
+ return true;
}
- luci::CircleConst *packed_clusters(luci::CircleConst *node)
+private:
+ luci::CircleConst *packed_clusters(loco::Graph *graph, int32_t prefix)
{
- auto graph = node->graph();
- auto qbits_of_clusters = get_qbits_of_clusters(node);
- auto size_of_clusters = get_size_of_clusters(node);
- const auto number_of_clusters = get_number_of_clusters(node)->at<loco::DataType::S32>(0);
+ auto qbits_of_clusters = _qbits_of_clusters[prefix];
+ auto size_of_clusters = _size_of_clusters[prefix];
+ const auto number_of_clusters = _number_of_clusters[prefix]->at<loco::DataType::S32>(0);
auto packed_clusters = graph->nodes()->create<luci::CircleConst>();
packed_clusters->dtype(loco::DataType::S32);
@@ -418,13 +409,18 @@ private:
}
private:
- std::map<std::string, luci::CircleConst *> _do_w_x;
- std::map<std::string, luci::CircleConst *> _alpha;
- std::map<std::string, luci::CircleConst *> _packed_binary_code;
- std::map<std::string, luci::CircleConst *> _number_of_clusters;
- std::map<std::string, luci::CircleConst *> _size_of_clusters;
- std::map<std::string, luci::CircleConst *> _qbits_of_clusters;
- std::map<std::string, luci::CircleConst *> _dequant_weight;
+ std::map<int32_t, luci::CircleConst *> _do_w_x;
+ std::map<int32_t, luci::CircleConst *> _alpha;
+ std::map<int32_t, luci::CircleConst *> _packed_binary_code;
+ std::map<int32_t, luci::CircleConst *> _number_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _size_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _qbits_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _dequant_weight;
+ std::map<int32_t, luci::CircleNode *> _fusable_op;
+
+private:
+ int32_t _original_output_cnt = 0;
+ int32_t _bundle_cnt = 0;
};
} // namespace
@@ -436,38 +432,72 @@ bool FuseBCQPass::run(loco::Graph *g)
{
bool changed = false;
- // Find BCQ version information and check validity.
- luci::CircleConst *version_node = nullptr;
- for (auto node : loco::all_nodes(g))
+ const int32_t start_magicnum = -2e9 + 27;
+ const int32_t end_magicnum = 2e9 - 27;
+
+ luci::CircleConst *metadata_node = nullptr;
+ for (auto node : loco::output_nodes(g))
{
- if (auto circle_const = dynamic_cast<luci::CircleConst *>(node))
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+
+ // Metadata node should be first output
+ if (output_node->index() != 0)
+ continue;
+
+ // Metadata should be constant and dtype should be S32
+ auto const_node = dynamic_cast<luci::CircleConst *>(output_node->from());
+ if (const_node == nullptr || const_node->dtype() != loco::DataType::S32)
+ continue;
+
+ // Metadata has at least four elements
+ const auto element_cnt = const_node->size<loco::DataType::S32>();
+ if (element_cnt < 4)
+ continue;
+
+ // Metadata has magic numbers at first and at last
+ const auto start_value = const_node->at<loco::DataType::S32>(0);
+ const auto end_value = const_node->at<loco::DataType::S32>(element_cnt - 1);
+ if (start_value == start_magicnum && end_value == end_magicnum)
{
- if (circle_const->name().find("/bcqinfo_version") != std::string::npos)
- {
- // There should be only one bcqinfo_version in the model
- if (version_node != nullptr)
- {
- assert(false && "Multiple version information found");
- return false;
- }
-
- version_node = circle_const;
- }
+ metadata_node = const_node;
+ break;
}
}
- // If version node is not found, regard it as version 1.
- int32_t bcq_version = (version_node != nullptr) ? version_node->at<loco::DataType::S32>(0) : 1;
+ if (metadata_node != nullptr)
+ {
+ const auto bcq_version = metadata_node->at<loco::DataType::S32>(1);
+ const auto original_output_cnt = metadata_node->at<loco::DataType::S32>(2);
- if (bcq_version == 1)
- changed = BCQFuser<1>().fuseBCQ(g);
- else
- assert(false && "Not supported BCQ version");
+ if (bcq_version == 1)
+ {
+ const auto bundle_cnt = metadata_node->at<loco::DataType::S32>(3);
- if (changed && version_node != nullptr)
- {
- // If BCQ is applied and version node was found, remove the node.
- loco::replace(version_node).with(createNoOp(version_node));
+ BCQFuser<1> fuser{original_output_cnt, bundle_cnt};
+ if (fuser.fuseBCQ(g))
+ changed = true;
+ }
+ else
+ {
+ LOGGER(l);
+ WARN(l) << "Not supported BCQ version is found." << std::endl;
+ }
+
+ // Remove all of BCQ information nodes iff there is no change
+ if (changed == false)
+ {
+ for (auto node : loco::output_nodes(g))
+ {
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+ if (output_node->index() == 0 || (int)output_node->index() > original_output_cnt)
+ {
+ auto noOp = g->nodes()->create<luci::CircleOutputExclude>();
+ noOp->dtype(loco::DataType::FLOAT32); // TODO Remove this setting
+ output_node->from(noOp);
+ changed = true;
+ }
+ }
+ }
}
return changed;
diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp
index e39455b1a..95ccd8176 100644
--- a/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp
+++ b/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp
@@ -77,11 +77,11 @@ bool fused_batch_norm_with_tconv(luci::CircleTransposeConv *tconv)
// scale dim(0) == tconv filter channel dim
if (filter->rank() != 4)
return false;
- auto filter_channel_dim = filter->dim(3).value();
+ auto filter_out_dim = filter->dim(0).value();
if (scale->rank() != 1)
return false;
auto scale_dim = scale->dim(0).value();
- if (filter_channel_dim != scale_dim)
+ if (filter_out_dim != scale_dim)
return false;
// get shift of batchnorm
@@ -93,23 +93,23 @@ bool fused_batch_norm_with_tconv(luci::CircleTransposeConv *tconv)
if (shift->rank() != 1)
return false;
auto shift_dim = shift->dim(0).value();
- if (filter_channel_dim != shift_dim)
+ if (filter_out_dim != shift_dim)
return false;
// filter weight = filter weight * mul(scale) + add(shift)
- uint32_t filter_batch_dim = filter->dim(0).value();
uint32_t filter_height_dim = filter->dim(1).value();
uint32_t filter_width_dim = filter->dim(2).value();
- for (uint32_t c = 0; c < filter_channel_dim; c++)
+ uint32_t filter_in_dim = filter->dim(3).value();
+ for (uint32_t c = 0; c < filter_out_dim; c++)
{
- for (uint32_t n = 0; n < filter_batch_dim; n++)
+ for (uint32_t h = 0; h < filter_height_dim; h++)
{
- for (uint32_t h = 0; h < filter_height_dim; h++)
+ for (uint32_t w = 0; w < filter_width_dim; w++)
{
- for (uint32_t w = 0; w < filter_width_dim; w++)
+ for (uint32_t b = 0; b < filter_in_dim; b++)
{
- uint32_t offset = n * filter_height_dim * filter_width_dim * filter_channel_dim +
- h * filter_width_dim * filter_channel_dim + w * filter_channel_dim + c;
+ uint32_t offset = c * filter_height_dim * filter_width_dim * filter_in_dim +
+ h * filter_width_dim * filter_in_dim + w * filter_in_dim + b;
filter->at<loco::DataType::FLOAT32>(offset) *= scale->at<loco::DataType::FLOAT32>(c);
}
}
diff --git a/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp
new file mode 100644
index 000000000..0f8d562e9
--- /dev/null
+++ b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp
@@ -0,0 +1,372 @@
+/*
+ * 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 "QuantizationUtils.h"
+
+#include <luci/IR/CircleQuantParam.h>
+
+#include <math.h>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+void addQuantParam(luci::CircleNode &node, const std::vector<float> &scale,
+ const std::vector<int64_t> &zp)
+{
+ assert(node.quantparam() == nullptr);
+
+ auto quantparam = std::make_unique<luci::CircleQuantParam>();
+ quantparam->scale = scale;
+ quantparam->zerop = zp;
+ node.quantparam(std::move(quantparam));
+}
+
+int32_t quantize(float f, luci::CircleQuantParam *qparam)
+{
+ float scale = qparam->scale[0];
+ int64_t zp = qparam->zerop[0];
+
+ return std::round(f / scale) + zp;
+}
+
+class SimpleConcatGraph
+{
+public:
+ SimpleConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(quant_type);
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {3.14}, {77});
+ addQuantParam(input_1, {1.0}, {1});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {3.14}, {0});
+ addQuantParam(input_1, {1.0}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~SimpleConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConv2D input_1;
+ luci::CircleConv2D input_2;
+};
+
+class SubsequentConcatGraph
+{
+public:
+ SubsequentConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(quant_type);
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {3.14}, {77});
+ addQuantParam(input_1, {1.0}, {1});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {3.14}, {0});
+ addQuantParam(input_1, {1.0}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~SubsequentConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConcatenation input_1{2};
+ luci::CircleConv2D input_2;
+};
+
+class ConstInputConcatGraph
+{
+public:
+ ConstInputConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(loco::DataType::FLOAT32);
+ input_1.size<loco::DataType::FLOAT32>(5);
+ for (int i = 0; i < 5; i++)
+ {
+ // Set data {-2, -1, 0, 1, 2}
+ input_1.at<loco::DataType::FLOAT32>(i) = i - 2.0;
+ }
+
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {0.1}, {10});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {0.1}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~ConstInputConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConst input_1;
+ luci::CircleConv2D input_2;
+};
+
+} // namespace
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8)
+{
+ // Check cases where qparam of concat_node is propagated
+ // (1) normal case: qparam is propagated to input_1 and input_2
+ // (2) input used by other Op: input_1 is an input of input_2. qparam is propagated only to
+ // input_2
+ // (3) subsequent concat: input_1 is concat. qparam is propagated only to input_2
+ // (4) const input: input_1 is const. constant values are quantized
+
+ // normal case: qparam of concat_node is propagated to input_1 and input_2
+ SimpleConcatGraph g(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.input_2.quantparam()->zerop[0]);
+
+ // input_1 is an input of input_2. qparam is propagated only to input_2
+ SimpleConcatGraph g2(loco::DataType::U8);
+ g2.input_2.input(&g2.input_1);
+ luci::propagate_concat_quantparam(&g2.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g2.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g2.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g2.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, g2.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g2.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, g2.input_2.quantparam()->zerop[0]);
+
+ // input_1 is concat. qparam is propagated only to input_2
+ SubsequentConcatGraph sg(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&sg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, sg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, sg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, sg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, sg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, sg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, sg.input_2.quantparam()->zerop[0]);
+
+ // input_1 is const. const values are quantized with the qparam of concat
+ ConstInputConcatGraph cg(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype());
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(0));
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(1));
+ EXPECT_EQ(10, cg.input_1.at<loco::DataType::U8>(2));
+ EXPECT_EQ(20, cg.input_1.at<loco::DataType::U8>(3));
+ EXPECT_EQ(30, cg.input_1.at<loco::DataType::U8>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8_NEG)
+{
+ // Check negative cases where qparam is not propagated
+ // (1) concat has fused activation function
+ // (2) concat has fused activation function and input is const
+
+ SimpleConcatGraph g(loco::DataType::U8);
+
+ // concat has fused activation function
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(2, g.input_2.quantparam()->zerop[0]);
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ // concat has fused activation function and input_1 is const.
+ // const values are quantized using its min/max
+ ConstInputConcatGraph cg(loco::DataType::U8);
+ cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.015686275, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(128, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(2, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype());
+ EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(0));
+ EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(1));
+ EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(2));
+ EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(3));
+ EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16)
+{
+ // Check cases where qparam of concat_node is propagated
+ // (1) normal case: qparam is propagated to input_1 and input_2
+ // (2) input used by other Op: input_1 is an input of input_2. qparam is propagated only to
+ // input_2
+ // (3) subsequent concat: input_1 is concat. qparam is propagated only to input_2
+ // (4) const input: input_1 is const. constant values are quantized
+
+ // normal case: qparam of concat_node is propagated to input_1 and input_2
+ SimpleConcatGraph g(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_2.quantparam()->zerop[0]);
+
+ // input_1 is an input of input_2. qparam is propagated only to input_2
+ SimpleConcatGraph g2(loco::DataType::S16);
+ g2.input_2.input(&g2.input_1);
+ luci::propagate_concat_quantparam(&g2.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g2.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g2.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g2.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.input_2.quantparam()->zerop[0]);
+
+ // input_1 is concat. qparam is propagated only to input_2
+ SubsequentConcatGraph sg(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&sg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, sg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, sg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, sg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.input_2.quantparam()->zerop[0]);
+
+ // input_1 is const. const values are quantized with the qparam of concat
+ ConstInputConcatGraph cg(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype());
+ EXPECT_EQ(-20, cg.input_1.at<loco::DataType::S16>(0));
+ EXPECT_EQ(-10, cg.input_1.at<loco::DataType::S16>(1));
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::S16>(2));
+ EXPECT_EQ(10, cg.input_1.at<loco::DataType::S16>(3));
+ EXPECT_EQ(20, cg.input_1.at<loco::DataType::S16>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16_NEG)
+{
+ // Check negative cases where qparam is not propagated
+ // (1) concat has fused activation function
+ // (2) concat has fused activation function and input is const
+
+ SimpleConcatGraph g(loco::DataType::S16);
+
+ // concat has fused activation function
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_2.quantparam()->zerop[0]);
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ // concat has fused activation function and input_1 is const.
+ // const values are quantized using its min/max
+ ConstInputConcatGraph cg(loco::DataType::S16);
+ cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.000061037, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype());
+ EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(0));
+ EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(1));
+ EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(2));
+ EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(3));
+ EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(4));
+}
diff --git a/compiler/luci/pass/src/QuantizationUtils.cpp b/compiler/luci/pass/src/QuantizationUtils.cpp
index e18690605..9af52a4c4 100644
--- a/compiler/luci/pass/src/QuantizationUtils.cpp
+++ b/compiler/luci/pass/src/QuantizationUtils.cpp
@@ -31,6 +31,66 @@ uint8_t fp32_to_uint8_cast(float f)
return static_cast<uint8_t>(f);
}
+// Per-layer quantization of weights (const tensor) using given min/max values
+void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max)
+{
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ // clipping
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ data = data < nudged_min ? nudged_min : data;
+ data = data > nudged_max ? nudged_max : data;
+ quantized_values[i] =
+ static_cast<int32_t>(std::round((data - nudged_min) * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+// Per-layer quantization of weights (const tensor) using given min/max values
+void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max)
+{
+ const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
+ const int32_t kMinScale = -kMaxScale;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ // clipping
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ data = data < nudged_min ? nudged_min : data;
+ data = data > nudged_max ? nudged_max : data;
+ quantized_values[i] = static_cast<int32_t>(std::round(data * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::S16); // change the type of tensor
+ node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S16>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
float &nudged_min, float &nudged_max)
{
diff --git a/compiler/luci/pass/src/QuantizationUtils.h b/compiler/luci/pass/src/QuantizationUtils.h
index ec0e86df8..f766bd66d 100644
--- a/compiler/luci/pass/src/QuantizationUtils.h
+++ b/compiler/luci/pass/src/QuantizationUtils.h
@@ -29,10 +29,20 @@ void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &
void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
float &nudged_min, float &nudged_max);
+void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max);
+
+void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max);
+
bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index);
uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices);
+void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataType quant_type);
+
} // namespace luci
#endif // __LUCI_QUANTIZATION_UTILS_H__
diff --git a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp
index c492234c7..e9925c7ff 100644
--- a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp
+++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp
@@ -284,36 +284,6 @@ void asymmetric_wdequant_per_channel(CircleConst *node, std::vector<float> &scal
}
}
-void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
- float &scaling_factor, int64_t &zp, float &nudged_min,
- float &nudged_max)
-{
-
- const int32_t kMinScale = 0;
- const int32_t kMaxScale = 255;
-
- uint32_t size = node->size<loco::DataType::FLOAT32>();
- compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
- const float scaling_factor_inv = 1.0 / scaling_factor;
- std::vector<int32_t> quantized_values(size);
- for (uint32_t i = 0; i < size; ++i)
- {
- // clipping
- auto data = node->at<loco::DataType::FLOAT32>(i);
- data = data < nudged_min ? nudged_min : data;
- data = data > nudged_max ? nudged_max : data;
- quantized_values[i] =
- static_cast<int32_t>(std::round((data - nudged_min) * scaling_factor_inv));
- }
-
- node->dtype(loco::DataType::U8); // change the type of tensor
- node->size<loco::DataType::U8>(size); // resize tensor
- for (uint32_t i = 0; i < size; ++i)
- {
- node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
- }
-}
-
void asymmetric_wdequant_with_minmax_per_layer(CircleConst *node, float scaling_factor,
float nudged_min)
{
diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
index 60c1cdd72..564e814f9 100644
--- a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
+++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
@@ -32,7 +32,99 @@ namespace luci
namespace
{
-// Check if the node is the bias of Conv2D, DepthwiseConv2D, or FullyConnected layer
+void overwrite_quantparam(luci::CircleConcatenation *concat, luci::CircleNode *target)
+{
+ auto concat_qparam = concat->quantparam();
+ if (concat_qparam == nullptr)
+ throw std::runtime_error("quantparam of concat is not found during overwrite");
+
+ auto target_qparam = target->quantparam();
+ if (target_qparam == nullptr)
+ {
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ target->quantparam(std::move(quantparam));
+ target_qparam = target->quantparam();
+ }
+ target_qparam->min = concat_qparam->min;
+ target_qparam->max = concat_qparam->max;
+ target_qparam->scale = concat_qparam->scale;
+ target_qparam->zerop = concat_qparam->zerop;
+ target_qparam->quantized_dimension = concat_qparam->quantized_dimension;
+}
+
+void quant_const_values(luci::CircleConst *const_node, float scaling_factor, float zerop,
+ loco::DataType quant_type)
+{
+ uint32_t size = const_node->size<loco::DataType::FLOAT32>();
+
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ auto data = const_node->at<loco::DataType::FLOAT32>(i);
+ quantized_values[i] = static_cast<int32_t>(std::round(data * scaling_factor_inv) + zerop);
+ }
+
+ switch (quant_type)
+ {
+ case loco::DataType::U8:
+ const_node->dtype(loco::DataType::U8); // change the type of tensor
+ const_node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ const_node->at<loco::DataType::U8>(i) = std::min(255, std::max(0, quantized_values[i]));
+ break;
+ case loco::DataType::S16:
+ assert(zerop == 0);
+ const_node->dtype(loco::DataType::S16); // change the type of tensor
+ const_node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ const_node->at<loco::DataType::S16>(i) =
+ std::min(32767, std::max(-32767, quantized_values[i]));
+ break;
+ default:
+ throw std::runtime_error("Unsupported data type");
+ }
+}
+
+void quant_const(CircleConst *node, loco::DataType quant_type)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ float min = std::numeric_limits<float>::max();
+ float max = std::numeric_limits<float>::lowest();
+ for (uint32_t i = 0; i < node->size<loco::DataType::FLOAT32>(); i++)
+ {
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ min = data < min ? data : min;
+ max = data > max ? data : max;
+ }
+
+ float scaling_factor{0.0};
+ int64_t zp{0};
+ float nudged_min{0.0};
+ float nudged_max{0.0};
+
+ switch (quant_type)
+ {
+ case loco::DataType::U8:
+ asymmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ break;
+ case loco::DataType::S16:
+ symmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ break;
+ default:
+ throw std::runtime_error("Unsupported data type");
+ }
+
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->scale.push_back(scaling_factor);
+ quantparam->zerop.push_back(zp);
+ node->quantparam(std::move(quantparam));
+}
+
+// Check if the node is the bias of Conv2D, DepthwiseConv2D, FullyConnected, or TransposeConv layer
// If true, return <input, weight> pair of the successor node (used to quantize bias)
// If flase, return <nullptr, nullptr>
std::pair<loco::Node *, loco::Node *> get_input_weight_of_bias(CircleNode *node)
@@ -68,6 +160,13 @@ std::pair<loco::Node *, loco::Node *> get_input_weight_of_bias(CircleNode *node)
assert(fc->weights() != nullptr);
return std::make_pair(fc->input(), fc->weights());
}
+ auto tconv = dynamic_cast<CircleTransposeConv *>(out);
+ if (tconv != nullptr && tconv->bias() == circle_const)
+ {
+ assert(tconv->outBackprop() != nullptr);
+ assert(tconv->filter() != nullptr);
+ return std::make_pair(tconv->outBackprop(), tconv->filter());
+ }
}
return std::make_pair(nullptr, nullptr);
}
@@ -514,8 +613,171 @@ struct QuantizeWeights final : public luci::CircleNodeMutableVisitor<bool>
}
};
+/**
+ * @brief Quantize const input tensors using min/max of const values
+ */
+void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type)
+{
+ auto opcode = node->opcode();
+ auto arity = node->arity();
+
+ loco::Node *input_node{nullptr};
+ luci::CircleConst *const_node{nullptr};
+
+ switch (opcode)
+ {
+ case luci::CircleOpcode::CONV_2D:
+ case luci::CircleOpcode::DEPTHWISE_CONV_2D:
+ case luci::CircleOpcode::FULLY_CONNECTED:
+ case luci::CircleOpcode::TRANSPOSE_CONV:
+ // Handled in QuantizeWeights and QuantizeBias
+ break;
+
+ case luci::CircleOpcode::CONCATENATION:
+ // Handled in propagate_concat_quantparam
+ break;
+
+ case luci::CircleOpcode::ARG_MAX:
+ case luci::CircleOpcode::ARG_MIN:
+ case luci::CircleOpcode::MEAN:
+ case luci::CircleOpcode::PAD:
+ case luci::CircleOpcode::REDUCE_ANY:
+ case luci::CircleOpcode::REDUCE_PROD:
+ case luci::CircleOpcode::REDUCE_MAX:
+ case luci::CircleOpcode::REDUCE_MIN:
+ case luci::CircleOpcode::RESHAPE:
+ case luci::CircleOpcode::SUM:
+ // The second input of these Ops should not be quantized
+ // Ex: axis, paddings
+ input_node = node->arg(0);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ quant_const(const_node, output_type);
+ break;
+
+ case luci::CircleOpcode::ADD:
+ case luci::CircleOpcode::ADD_N:
+ case luci::CircleOpcode::DIV:
+ case luci::CircleOpcode::EQUAL:
+ case luci::CircleOpcode::GREATER:
+ case luci::CircleOpcode::GREATER_EQUAL:
+ case luci::CircleOpcode::LESS:
+ case luci::CircleOpcode::LESS_EQUAL:
+ case luci::CircleOpcode::MAXIMUM:
+ case luci::CircleOpcode::MINIMUM:
+ case luci::CircleOpcode::MUL:
+ case luci::CircleOpcode::NOT_EQUAL:
+ case luci::CircleOpcode::PRELU:
+ case luci::CircleOpcode::SUB:
+ // Quantize all const inputs using their values
+ for (uint32_t i = 0; i < arity; i++)
+ {
+ input_node = node->arg(i);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ quant_const(const_node, output_type);
+ }
+ break;
+
+ default:
+ for (uint32_t i = 0; i < arity; i++)
+ {
+ input_node = node->arg(i);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ throw std::runtime_error("Unsupported Op for const inputs");
+ }
+ break;
+ }
+}
+
} // namespace
+/** BEFORE
+ *
+ * [CircleNode] [CircleConst]
+ * (U8 qparam1) (FP32)
+ * \ /
+ * \ /
+ * [CircleConcatenation]
+ * (U8 qparam2)
+ *
+ * AFTER
+ * [CircleNode] [CircleConst]
+ * (U8 qparam2) (U8 qparam2)
+ * \ /
+ * \ /
+ * [CircleConcatenation]
+ * (U8 qparam2)
+ */
+void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataType quant_type)
+{
+ assert(concat->quantparam() != nullptr);
+
+ const auto num_inputs = concat->numValues();
+
+ // Quantize const inputs using their values if concat has fused act function
+ if (concat->fusedActivationFunction() != luci::FusedActFunc::NONE)
+ {
+ for (uint32_t i = 0; i < num_inputs; i++)
+ {
+ auto node = concat->arg(i);
+ auto const_node = dynamic_cast<luci::CircleConst *>(node);
+ if (const_node != nullptr)
+ quant_const(const_node, quant_type);
+ }
+ return;
+ }
+
+ for (uint32_t i = 0; i < num_inputs; i++)
+ {
+ auto node = loco::must_cast<luci::CircleNode *>(concat->arg(i));
+
+ // Skip if this input is CONCAT Op
+ if (node->opcode() == luci::CircleOpcode::CONCATENATION)
+ continue;
+
+ // Skip if this input is used by other Ops
+ auto succs = loco::succs(node);
+ if (succs.size() != 1)
+ {
+ if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ luci::CircleConst *const_node = loco::must_cast<luci::CircleConst *>(node);
+ quant_const(const_node, quant_type);
+ }
+ continue;
+ }
+
+ assert(succs.find(concat) != succs.end());
+
+ // Quantize constant values
+ if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ luci::CircleConst *const_node = loco::must_cast<luci::CircleConst *>(node);
+ if (const_node->dtype() != loco::DataType::FLOAT32)
+ throw std::runtime_error("Unsupported data type for constant input of concatenation Op");
+
+ const auto concat_qparam = concat->quantparam();
+ if (concat_qparam == nullptr)
+ throw std::runtime_error("quantparam of concat is not found during propagation");
+
+ assert(concat_qparam->scale.size() == 1);
+ const auto scaling_factor = concat_qparam->scale[0];
+ const auto zerop = concat_qparam->zerop[0];
+
+ quant_const_values(const_node, scaling_factor, zerop, quant_type);
+ }
+ else
+ {
+ // Non-const input must have been quantized
+ assert(node->quantparam() != nullptr);
+ }
+
+ overwrite_quantparam(concat, node);
+ }
+}
+
bool QuantizeWithMinMaxPass::run(loco::Graph *g)
{
LOGGER(l);
@@ -538,11 +800,37 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g)
}
// Quantize bias
+ // (For int16 quantization, bias is not quantized)
+ if (_output_dtype == loco::DataType::U8)
+ {
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ QuantizeBias qb(_input_dtype, _output_dtype, _granularity);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&qb);
+ }
+ }
+
+ // Quantize const inputs other than weights and bias
for (auto node : loco::active_nodes(loco::output_nodes(g)))
{
- QuantizeBias qb(_input_dtype, _output_dtype, _granularity);
auto circle_node = loco::must_cast<luci::CircleNode *>(node);
- circle_node->accept(&qb);
+ quantize_const_inputs(circle_node, _output_dtype);
+ }
+
+ // Propagate quantization parameters of concat Op
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto concat = dynamic_cast<luci::CircleConcatenation *>(node);
+ if (not concat)
+ continue;
+
+ // Propagate qparam of concat to its inputs if
+ // (1) concat is uint8-quantized
+ // (2) concat has no fused activation function
+ // (3) the input is not concatenation Op
+ // (4) the input is not produced to Ops other than concat
+ propagate_concat_quantparam(concat, _output_dtype);
}
// Update output dtype
diff --git a/compiler/luci/pass/src/RequantizePass.cpp b/compiler/luci/pass/src/RequantizePass.cpp
index 49fbf76ec..fe84e3bc3 100644
--- a/compiler/luci/pass/src/RequantizePass.cpp
+++ b/compiler/luci/pass/src/RequantizePass.cpp
@@ -56,7 +56,9 @@ bool is_bias(CircleConst *node)
if (fc != nullptr && fc->bias() == node)
return true;
- // TODO: add TransposeConv when bias is supported in CircleTransposeConv
+ auto tconv = dynamic_cast<CircleTransposeConv *>(out);
+ if (tconv != nullptr && tconv->bias() == node)
+ return true;
}
return false;
}
diff --git a/compiler/luci/pass/src/Sparsifier.cpp b/compiler/luci/pass/src/Sparsifier.cpp
new file mode 100644
index 000000000..2aa542f15
--- /dev/null
+++ b/compiler/luci/pass/src/Sparsifier.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sparsifier.h"
+
+namespace luci
+{
+
+template <typename T>
+Sparsifier<T>::Sparsifier(const std::vector<int32_t> &shape,
+ const std::vector<int32_t> &traversal_order,
+ const std::vector<DimensionType> &format,
+ const std::vector<int32_t> &block_size,
+ const std::vector<int32_t> &block_map)
+ : _dense_shape(shape), _traversal_order(traversal_order), _block_size(block_size),
+ _block_map(block_map)
+{
+ _dense_size = 1;
+ int32_t block_dim = 0;
+ _blocked_shape.resize(shape.size());
+ _format.resize(shape.size() + block_map.size());
+ for (int32_t i = 0; i < static_cast<int32_t>(shape.size()); i++)
+ {
+ _format[i] = format[traversal_order[i]];
+ _dense_size *= shape[i];
+ if (block_dim < static_cast<int32_t>(block_map.size()) && block_map[block_dim] == i)
+ {
+ _blocked_shape[i] = shape[i] / block_size[block_dim];
+ block_dim++;
+ }
+ else
+ {
+ _blocked_shape[i] = shape[i];
+ }
+ }
+
+ // Only dense blocks are supported.
+ for (uint32_t i = 0; i < block_map.size(); i++)
+ {
+ _format[i + shape.size()] = DimensionType::DENSE;
+ }
+}
+
+template <typename T> void Sparsifier<T>::DenseToSparse(const T *src_data)
+{
+ int num_original_dims = _dense_shape.size();
+ int num_block_dims = _block_map.size();
+ int num_expanded_dims = num_original_dims + num_block_dims;
+ std::vector<int> expanded_shape(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; i++)
+ {
+ if (i < num_original_dims)
+ {
+ expanded_shape[i] = _blocked_shape[i];
+ }
+ else
+ {
+ expanded_shape[i] = _block_size[i - num_original_dims];
+ }
+ }
+
+ std::vector<int> shape_offset(num_original_dims);
+ shape_offset[shape_offset.size() - 1] = 1;
+ for (int i = num_original_dims - 1; i > 0; --i)
+ {
+ shape_offset[i - 1] = shape_offset[i] * _dense_shape[i];
+ }
+
+ std::vector<int> expanded_shape_offset(num_expanded_dims);
+ for (int i = 0; i < num_original_dims; ++i)
+ {
+ expanded_shape_offset[i] = shape_offset[i];
+ }
+ for (int i = 0; i < num_block_dims; ++i)
+ {
+ int mapped_dim = _block_map[i];
+ expanded_shape_offset[num_original_dims + i] = shape_offset[mapped_dim];
+ expanded_shape_offset[mapped_dim] *= _block_size[i];
+ }
+
+ std::vector<int> dst_ordered_offset(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; ++i)
+ {
+ dst_ordered_offset[i] = expanded_shape_offset[_traversal_order[i]];
+ }
+
+ std::vector<bool> dst_dim_has_nonzeroes(num_expanded_dims);
+ std::fill(dst_dim_has_nonzeroes.begin(), dst_dim_has_nonzeroes.end(), false);
+ std::vector<int> inner_compressed_dim(num_expanded_dims);
+ int most_recent_compressed_dim = -1;
+ std::vector<int> num_segments_of_next_compressed_dim(num_expanded_dims);
+ int segment_count = 1;
+ for (int i = num_expanded_dims - 1; i >= 0; --i)
+ {
+ inner_compressed_dim[i] = most_recent_compressed_dim;
+ if (_format[i] == DimensionType::SPARSE_CSR)
+ {
+ most_recent_compressed_dim = i;
+ num_segments_of_next_compressed_dim[i] = segment_count;
+ segment_count = 1;
+ }
+ else
+ {
+ num_segments_of_next_compressed_dim[i] = -1;
+ segment_count *= expanded_shape[_traversal_order[i]];
+ }
+ }
+
+ _dim_metadata.resize(num_expanded_dims * 2);
+ std::vector<int> dst_sparse_dims;
+ dst_sparse_dims.reserve(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; ++i)
+ {
+ _dim_metadata[i * 2].clear();
+ _dim_metadata[i * 2 + 1].clear();
+ if (_format[i] == DimensionType::DENSE)
+ {
+ // If dimension is dense, just store the shape.
+ _dim_metadata[i * 2].push_back(expanded_shape[_traversal_order[i]]);
+ }
+ else
+ {
+ _dim_metadata[i * 2].push_back(0); // Segment array always begins with 0.
+ dst_sparse_dims.push_back(i); // Add dimension to the sparse list.
+ }
+ }
+
+ // This algorithm assumes that the block size is small enough for all the
+ // elements to fit in cache, so the strided accesses from different traversal
+ // order and the write-first-erase-later strategy shouldn't be too slow
+ int dst_dim_idx = num_expanded_dims;
+ std::vector<int> coordinate(num_expanded_dims, 0);
+ int dense_tensor_idx = 0;
+ while (dst_dim_idx >= 0)
+ {
+ if (dst_dim_idx == num_expanded_dims)
+ {
+ // We have a complete coordinate. Add the element to the value array if it
+ // is not zero, or if the last dimension is dense.
+ if (!IsZero(src_data[dense_tensor_idx]))
+ {
+ _data.push_back(src_data[dense_tensor_idx]);
+ // Mark all sparse dimensions that their current indices have nonzeroes.
+ for (auto dst_dim : dst_sparse_dims)
+ {
+ if (!dst_dim_has_nonzeroes[dst_dim])
+ {
+ // Only add the index to the indices array if the current nonzero
+ // is the first nonzero of the block.
+ _dim_metadata[2 * dst_dim + 1].push_back(coordinate[dst_dim]);
+ dst_dim_has_nonzeroes[dst_dim] = true;
+ }
+ }
+ }
+ else if (_format[num_expanded_dims - 1] == DimensionType::DENSE)
+ {
+ _data.push_back(src_data[dense_tensor_idx]);
+ }
+ --dst_dim_idx;
+ }
+ else
+ {
+ int original_dim_idx = _traversal_order[dst_dim_idx];
+ int dim_size = expanded_shape[original_dim_idx];
+ if (dst_dim_has_nonzeroes[dst_dim_idx])
+ {
+ // If the previous block has nonzeroes, reset the flag to false since
+ // we have just moved to a new block.
+ dst_dim_has_nonzeroes[dst_dim_idx] = false;
+ }
+ else if (_format[dst_dim_idx] == DimensionType::SPARSE_CSR)
+ {
+ // This block is empty. Delete unnecessary values if compressed.
+ int next_compressed_dim = inner_compressed_dim[dst_dim_idx];
+ int erase_offset = _dim_metadata[2 * dst_dim_idx + 1].size() *
+ num_segments_of_next_compressed_dim[dst_dim_idx];
+ if (next_compressed_dim >= 0)
+ {
+ auto &segments = _dim_metadata[2 * inner_compressed_dim[dst_dim_idx]];
+ segments.erase(segments.begin() + 1 + erase_offset, segments.end());
+ }
+ else
+ {
+ _data.erase(_data.begin() + erase_offset, _data.end());
+ }
+ }
+ if (++coordinate[dst_dim_idx] < dim_size)
+ {
+ // The current dst_dim_idx is valid (not out of bound).
+ dense_tensor_idx += dst_ordered_offset[dst_dim_idx];
+ ++dst_dim_idx;
+ }
+ else
+ {
+ // dst_dim_idx has reached its dim size. Update segment array and go
+ // back to incrementing the previous dimension (dst_dim_idx - 1).
+ if (_format[dst_dim_idx] == DimensionType::SPARSE_CSR)
+ {
+ _dim_metadata[2 * dst_dim_idx].push_back(_dim_metadata[2 * dst_dim_idx + 1].size());
+ }
+ coordinate[dst_dim_idx] = -1;
+ dense_tensor_idx -= dst_ordered_offset[dst_dim_idx] * dim_size;
+ --dst_dim_idx;
+ }
+ }
+ }
+}
+
+template <typename T> bool Sparsifier<T>::IsZero(const T val) { return (val == 0); }
+
+template class Sparsifier<int32_t>;
+template class Sparsifier<int8_t>;
+template class Sparsifier<float>;
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/Sparsifier.h b/compiler/luci/pass/src/Sparsifier.h
new file mode 100644
index 000000000..71ea28da9
--- /dev/null
+++ b/compiler/luci/pass/src/Sparsifier.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SPARSIFIER_H__
+#define __LUCI_SPARSIFIER_H__
+
+#include <vector>
+
+#include <luci/IR/SparsityParam.h>
+
+namespace luci
+{
+
+template <typename T> class Sparsifier
+{
+public:
+ /*
+ * Creates a dense to sparse converter.
+ * @param shape Shape of the dense tensor.
+ * @param traversal_order In what order to traverse all dimensions,
+ * including block dimensions.
+ * @param format Whether each dimension in converted tensor is
+ * dense or sparse (not in the traversal order).
+ * @param block_size Size of each block dimension.
+ * @param block_map Map from block dimension to original tensor
+ * dimension.
+ */
+ Sparsifier(const std::vector<int> &shape, const std::vector<int> &traversal_order,
+ const std::vector<DimensionType> &format, const std::vector<int> &block_size = {},
+ const std::vector<int> &block_map = {});
+
+ std::vector<T> GetData() { return _data; }
+ std::vector<std::vector<int>> GetDimMetadata() { return _dim_metadata; }
+
+ void DenseToSparse(const T *src_data);
+
+private:
+ // Check if val is equal to zero.
+ bool IsZero(const T val);
+
+ // Shape of the conceptual dense tensor.
+ std::vector<int> _dense_shape;
+ // Shape of the dense tensor with inner blocks reduced. For example, a (4, 4)
+ // tensor with (2, 2) block has blocked_shape (2, 2).
+ std::vector<int> _blocked_shape;
+ // Total number of elements in the dense tensor.
+ uint64_t _dense_size;
+ // Has n(original dimension)+k(block_dimension) elements.
+ std::vector<int> _traversal_order;
+ // Format of each dimension in the traversal order.
+ std::vector<DimensionType> _format;
+ // Size of each block dimension, in the same order as block map.
+ std::vector<int> _block_size;
+ // Map from block dimension to the original tensor dimension.
+ std::vector<int> _block_map;
+ // Metadata of each dimension in the traversal order.
+ // Each dimension needs two vectors. For dense dimensions, the first vector
+ // stores the size of that dimension, and the second vector is empty. For
+ // sparse dimensions, the first vector stores the segments and the second one
+ // stores the indices.
+ std::vector<std::vector<int>> _dim_metadata;
+ // Actual buffer holding data after conversion. Could be sparse buffer or
+ // dense buffer.
+ std::vector<T> _data;
+};
+
+extern template class Sparsifier<int32_t>;
+extern template class Sparsifier<int8_t>;
+extern template class Sparsifier<float>;
+
+} // namespace luci
+
+#endif // __LUCI_SPARSIFIER_H__
diff --git a/compiler/luci/pass/src/SparsifyTensorPass.cpp b/compiler/luci/pass/src/SparsifyTensorPass.cpp
new file mode 100644
index 000000000..2f1a36e77
--- /dev/null
+++ b/compiler/luci/pass/src/SparsifyTensorPass.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/SparsifyTensorPass.h"
+
+#include "Sparsifier.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace luci
+{
+
+template <loco::DataType DT> void SparsifyTensorPass::sparsify_tensor(luci::CircleConst *cop)
+{
+ using PRIMITIVE_DTYPE = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<int32_t> dense_tensor_shape(cop->rank());
+ for (uint32_t d = 0; d < cop->rank(); d++)
+ {
+ dense_tensor_shape.at(d) = cop->dim(d).value();
+ }
+
+ Sparsifier<PRIMITIVE_DTYPE> sparsifier(dense_tensor_shape, _traversal_order, _format, _block_size,
+ _block_map);
+ // get dense tensor data
+ uint32_t dense_tensor_data_size = cop->size<DT>();
+ std::vector<PRIMITIVE_DTYPE> dense_tensor_data(dense_tensor_data_size);
+ for (uint32_t i = 0; i < dense_tensor_data_size; i++)
+ {
+ dense_tensor_data.at(i) = cop->at<DT>(i);
+ }
+ // sparsify
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+ // get sparse tensor data
+ std::vector<PRIMITIVE_DTYPE> sparse_tensor_data = sparsifier.GetData();
+ uint32_t sparse_tensor_data_size = sparse_tensor_data.size();
+ cop->size<DT>(sparse_tensor_data_size);
+ for (uint32_t i = 0; i < sparse_tensor_data_size; i++)
+ {
+ cop->at<DT>(i) = sparse_tensor_data.at(i);
+ }
+ // make sparsity parameter
+ auto sparsityparam = std::make_unique<SparsityParam>();
+ sparsityparam->traversal_order = _traversal_order;
+ sparsityparam->block_map = _block_map;
+ // get dimension meta data
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ for (uint32_t idx = 0; idx < _format.size(); idx++)
+ {
+ if (_format.at(idx) == DimensionType::DENSE)
+ {
+ sparsityparam->dim_metadata.emplace_back(DimensionType::DENSE,
+ dim_metadata.at(idx * 2).at(0));
+ }
+ // TODO Set SparseIndexVectorType according to its data range
+ else if (_format.at(idx) == DimensionType::SPARSE_CSR)
+ {
+ sparsityparam->dim_metadata.emplace_back(
+ DimensionType::SPARSE_CSR, /* dense size */ 0,
+ /* array_segments */ SparseIndexVector{SparseIndexVectorType::U16,
+ dim_metadata.at(idx * 2)},
+ /* array_indices */ SparseIndexVector{SparseIndexVectorType::U16,
+ dim_metadata.at(idx * 2 + 1)});
+ }
+ }
+ for (uint32_t i = 0; i < _block_size.size(); i++)
+ {
+ assert(_block_size.at(i) == dim_metadata.at((_format.size() + i) * 2).at(0));
+ sparsityparam->dim_metadata.emplace_back(DimensionType::DENSE, _block_size.at(i));
+ }
+ cop->sparsityparam(std::move(sparsityparam));
+}
+
+bool SparsifyTensorPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto cop = dynamic_cast<luci::CircleConst *>(node);
+ if (not cop)
+ continue;
+
+ if (cop->name() != _tensor_name)
+ continue;
+
+ switch (cop->dtype())
+ {
+ case loco::DataType::S32:
+ sparsify_tensor<loco::DataType::S32>(cop);
+ break;
+ case loco::DataType::S8:
+ sparsify_tensor<loco::DataType::S8>(cop);
+ break;
+ case loco::DataType::FLOAT32:
+ sparsify_tensor<loco::DataType::FLOAT32>(cop);
+ break;
+ default:
+ throw std::runtime_error("SparsifyTensorPass: Unsupported dtype.");
+ }
+ changed = true;
+ }
+
+ return changed;
+}
+
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::S32>(luci::CircleConst *cop);
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::S8>(luci::CircleConst *cop);
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::FLOAT32>(luci::CircleConst *cop);
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
index db25186b1..a55f50b19 100644
--- a/compiler/luci/service/src/CircleShapeInferenceRule.cpp
+++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
@@ -1608,6 +1608,22 @@ loco::NodeShape infer_unpack(const luci::CircleUnpack *node)
return loco::NodeShape{output_shape};
}
+loco::NodeShape infer_unidirectionalsequencelstm(const luci::CircleUnidirectionalSequenceLSTM *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto recurrent_to_output_weights =
+ loco::shape_get(node->recurrent_to_output_weights()).as<loco::TensorShape>();
+ auto rank = input_shape.rank();
+ loco::TensorShape output_shape;
+ output_shape.rank(rank);
+ for (uint32_t i = 0; i < rank - 1; i++)
+ {
+ output_shape.dim(i) = input_shape.dim(i);
+ }
+ output_shape.dim(rank - 1) = recurrent_to_output_weights.dim(1);
+ return loco::NodeShape{output_shape};
+}
+
loco::NodeShape infer_unique(const luci::CircleUnique *node)
{
auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
@@ -2047,6 +2063,12 @@ public:
return infer_depthwise_conv2d(node);
}
+ loco::NodeShape visit(const luci::CircleDequantize *node) final
+ {
+ const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
loco::NodeShape visit(const luci::CircleDiv *node) final { return broadcast_xy(node); }
loco::NodeShape visit(const luci::CircleElu *node) final
@@ -2373,6 +2395,11 @@ public:
loco::NodeShape visit(const luci::CircleUnpack *node) final { return infer_unpack(node); }
+ loco::NodeShape visit(const luci::CircleUnidirectionalSequenceLSTM *node) final
+ {
+ return infer_unidirectionalsequencelstm(node);
+ }
+
loco::NodeShape visit(const luci::CircleUnique *node) final { return infer_unique(node); }
loco::NodeShape visit(const luci::CircleWhere *node) final { return use_own(node); }
diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
index d28d8ac99..f738ab5a8 100644
--- a/compiler/luci/service/src/CircleTypeInferenceRule.cpp
+++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
@@ -111,6 +111,8 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT
return loco::dtype_get(node->input());
}
+ loco::DataType visit(const luci::CircleDequantize *) final { return loco::DataType::FLOAT32; }
+
loco::DataType visit(const luci::CircleDiv *node) final { return loco::dtype_get(node->x()); }
loco::DataType visit(const luci::CircleElu *node) final
@@ -490,6 +492,11 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT
return loco::dtype_get(node->outBackprop());
}
+ loco::DataType visit(const luci::CircleUnidirectionalSequenceLSTM *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
loco::DataType visit(const luci::CircleUnique *node) final
{
return loco::dtype_get(node->input());
diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp
index 282a068e0..d224fd172 100644
--- a/compiler/luci/service/src/Validate.cpp
+++ b/compiler/luci/service/src/Validate.cpp
@@ -75,6 +75,11 @@ bool validate_shape_dtype(loco::Graph *g)
assert(circle_output != nullptr);
assert(circle_output->from() != nullptr);
auto circle_node = loco::must_cast<luci::CircleNode *>(circle_output->from());
+
+ // Shape and dtype validation for CiecleOutputExclude is not needed
+ if (dynamic_cast<luci::CircleOutputExclude *>(circle_node))
+ continue;
+
assert(loco::shape_known(circle_node));
// check if output node shape is same as graph output shape
diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst
index 12dd7ff5b..897d41983 100644
--- a/compiler/luci/tests/test.lst
+++ b/compiler/luci/tests/test.lst
@@ -42,6 +42,7 @@ addread(DepthwiseConv2D_000)
addread(DepthwiseConv2D_U8_000)
addread(DepthwiseConv2D_U8_001)
addread(DepthwiseConv2D_001)
+addread(Dequantize_000)
addread(Div_000)
addread(ELU_000)
addread(Equal_000)
@@ -180,6 +181,8 @@ addread(TopKV2_000)
addread(TopKV2_001)
addread(Transpose_000)
addread(TransposeConv_000)
+addread(UnidirectionalSequenceLSTM_000)
+addread(UnidirectionalSequenceLSTM_001)
addread(Unique_000)
addread(Unique_001)
addread(Unique_002)
@@ -256,6 +259,7 @@ addwrite(DepthwiseConv2D_000)
addwrite(DepthwiseConv2D_U8_000)
addwrite(DepthwiseConv2D_U8_001)
addwrite(DepthwiseConv2D_001)
+addwrite(Dequantize_000)
addwrite(Div_000)
addwrite(ELU_000)
addwrite(Equal_000)
@@ -393,6 +397,8 @@ addwrite(TopKV2_000)
addwrite(TopKV2_001)
addwrite(Transpose_000)
addwrite(TransposeConv_000)
+addwrite(UnidirectionalSequenceLSTM_000)
+addwrite(UnidirectionalSequenceLSTM_001)
addwrite(Unique_000)
addwrite(Unique_001)
addwrite(Unique_002)
diff --git a/compiler/mio-tf/CMakeLists.txt b/compiler/mio-tf/CMakeLists.txt
index d670f6bab..133d4684a 100644
--- a/compiler/mio-tf/CMakeLists.txt
+++ b/compiler/mio-tf/CMakeLists.txt
@@ -1,6 +1,6 @@
nnas_find_package(Protobuf QUIET)
# TensorFlowSource package is used to use ~.proto files
-nnas_find_package(TensorFlowSource EXACT 1.12 QUIET)
+nnas_find_package(TensorFlowSource EXACT 2.3 QUIET)
if(NOT Protobuf_FOUND)
return()
diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt
index 173b8b476..a7135d64b 100644
--- a/compiler/one-cmds/CMakeLists.txt
+++ b/compiler/one-cmds/CMakeLists.txt
@@ -43,3 +43,9 @@ foreach(ONE_DOCUMENT IN ITEMS ${ONE_DOCUMENT_FILES})
install(FILES ${ONE_DOCUMENT} DESTINATION doc)
endforeach(ONE_DOCUMENT)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+add_subdirectory(tests)
diff --git a/compiler/one-cmds/how-to-prepare-virtualenv.txt b/compiler/one-cmds/how-to-prepare-virtualenv.txt
index 62a94968b..f3dcf704b 100644
--- a/compiler/one-cmds/how-to-prepare-virtualenv.txt
+++ b/compiler/one-cmds/how-to-prepare-virtualenv.txt
@@ -1,7 +1,7 @@
About
-----
-Last update: 2020-08-03
+Last update: 2020-09-15
This document explains about 'one-prepare-venv' command.
@@ -20,8 +20,8 @@ $ sudo apt-get upgrade
$ sudo apt-get install python3-pip python3-venv
-How to run
-----------
+How to run for Ubuntu
+---------------------
Just run 'one-prepare-venv' command
@@ -30,6 +30,23 @@ $ one-prepare-venv
There will be venv folder as of result.
+How to run for Windows
+----------------------
+
+1. First, please prepare Python 3.5-3.7
+2. Open the Command Prompt as an administrator
+3. cd(change directory) to the directory where one-compiler is installed
+4. run below command
+```
+$ ONE\install\bin> python -m venv venv
+$ ONE\install\bin> cd venv/Scripts
+$ ONE\install\bin\venv/Scripts> pip.exe install -U pip
+$ ONE\install\bin\venv/Scripts> pip.exe install -U tensorflow-cpu==2.3.0
+```
+
+After running the above command, go back to MinGW and run one-compiler.
+
+
Trouble shooting
----------------
diff --git a/compiler/one-cmds/how-to-use-one-commands.txt b/compiler/one-cmds/how-to-use-one-commands.txt
index 0ee69e077..2b903c973 100644
--- a/compiler/one-cmds/how-to-use-one-commands.txt
+++ b/compiler/one-cmds/how-to-use-one-commands.txt
@@ -37,7 +37,7 @@ This will convert Tensorflow model file (.pb) to our circle model file with appl
To execute this command, original Tensorflow model file must include BCQ information.
This command invokes following scripts internally.
-- preserve_bcq_info : Prevent BCQ information vanishing problem
+- generate_bcq_metadata : Generate BCQ metadata in the model
- generate_bcq_info : Designate BCQ information nodes as model output automatically
- tf2tfliteV2 : Convert Tensorflow model to tflite model
- tflite2circle : Convert Tensorflow Lite model to circle model
@@ -81,6 +81,8 @@ one-optimize
one-optimize provides network or operator transformation shown below.
Current transformation options are
+- fold_dequantize : This removes Dequantize operation which can be folded
+- fuse_add_with_tconv: This fuses Add operator with the preceding TConv operator if possible
- fuse_bcq: This enables Binary-Coded-bases Quantized DNNs
- read https://arxiv.org/abs/2005.09904 for detailed information
- fuse_instnorm: This will convert instance normalization related operators to
diff --git a/compiler/one-cmds/one-import-bcq b/compiler/one-cmds/one-import-bcq
index 98dd1efed..7c583071f 100644
--- a/compiler/one-cmds/one-import-bcq
+++ b/compiler/one-cmds/one-import-bcq
@@ -38,6 +38,41 @@ version()
exit 255
}
+input_not_set()
+{
+ echo "Error: input_path not set"
+ echo ""
+ usage
+}
+
+output_not_set()
+{
+ echo "Error: output_path not set"
+ echo ""
+ usage
+}
+
+input_arrays_not_set()
+{
+ echo "Error: input_arrays not set"
+ echo ""
+ usage
+}
+
+input_shapes_not_set()
+{
+ echo "Error: input_shapes not set"
+ echo ""
+ usage
+}
+
+output_arrays_not_set()
+{
+ echo "Error: output_arrays not set"
+ echo ""
+ usage
+}
+
TF_INTERFACE="--v1"
# Parse command-line arguments
@@ -54,22 +89,37 @@ while [ "$#" -ne 0 ]; do
;;
'--input_path')
export INPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ input_not_set
+ fi
shift 2
;;
'--output_path')
export OUTPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ output_not_set
+ fi
shift 2
;;
'--input_arrays')
export INPUT_ARRAYS="$2"
+ if [ $# -lt 2 ]; then
+ input_arrays_not_set
+ fi
shift 2
;;
'--input_shapes')
export INPUT_SHAPES="$2"
+ if [ $# -lt 2 ]; then
+ input_shapes_not_set
+ fi
shift 2
;;
'--output_arrays')
export OUTPUT_ARRAYS="$2"
+ if [ $# -lt 2 ]; then
+ output_arrays_not_set
+ fi
shift 2
;;
'--v2')
@@ -90,6 +140,20 @@ if [ -z ${INPUT_PATH} ] || [ ! -e ${INPUT_PATH} ]; then
exit 2
fi
+if [ -z ${INPUT_ARRAYS} ]; then
+ input_arrays_not_set
+fi
+
+# INPUT_SHAPES is optional
+
+if [ -z ${OUTPUT_PATH} ]; then
+ output_not_set
+fi
+
+if [ -z ${OUTPUT_ARRAYS} ]; then
+ output_arrays_not_set
+fi
+
FILE_BASE=$(basename ${OUTPUT_PATH})
MODEL_NAME="${FILE_BASE%.*}"
@@ -104,40 +168,58 @@ if [ -e ${VIRTUALENV_LINUX} ]; then
source ${VIRTUALENV_LINUX}
elif [ -e ${VIRTUALENV_WINDOWS} ]; then
source ${VIRTUALENV_WINDOWS}
+else
+ echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command."
+ echo "If encounter any problems, please follow provided document in 'doc' folder."
+ exit 255
fi
# remove previous log
rm -rf "${OUTPUT_PATH}.log"
-# generate temporary preserved pb file
-echo "${DRIVER_PATH}/preserve_bcq_info" --input_path ${INPUT_PATH} \
---output_path "${TMPDIR}/${MODEL_NAME}_preserved.pb" > "${OUTPUT_PATH}.log"
-echo " " >> "${OUTPUT_PATH}.log"
+show_err_onexit()
+{
+ cat "${OUTPUT_PATH}.log"
+}
-"${DRIVER_PATH}/preserve_bcq_info" --input_path ${INPUT_PATH} \
---output_path "${TMPDIR}/${MODEL_NAME}_preserved.pb" >> "${OUTPUT_PATH}.log" 2>&1
+trap show_err_onexit ERR
-# generate output_arrays automatically
-echo "${DRIVER_PATH}/generate_bcq_output_arrays" \
---input_path "${TMPDIR}/${MODEL_NAME}_preserved.pb" \
---output_path "${TMPDIR}/${MODEL_NAME}_output_arrays.txt" > "${OUTPUT_PATH}.log"
-echo " " >> "${OUTPUT_PATH}.log"
+# Generate BCQ information metadata
+# If model has no BCQ information or invalid information, pb file is not changed.
+METAGEN_SCRIPT="${DRIVER_PATH}/generate_bcq_metadata "
+METAGEN_SCRIPT+="--input_path ${INPUT_PATH} "
+METAGEN_SCRIPT+="--output_path ${TMPDIR}/${MODEL_NAME}_withmeta.pb "
+METAGEN_SCRIPT+="--output_arrays ${OUTPUT_ARRAYS} "
+
+echo ${METAGEN_SCRIPT} > "${OUTPUT_PATH}.log"
+echo "" >> "${OUTPUT_PATH}.log"
+$METAGEN_SCRIPT >> "${OUTPUT_PATH}.log" 2>&1
+
+# Generate BCQ information nodes as output_arrays
+# If model has no BCQ information, output_arrays would be empty.
+OUTARR_GEN_SCRIPT="${DRIVER_PATH}/generate_bcq_output_arrays "
+OUTARR_GEN_SCRIPT+="--input_path ${TMPDIR}/${MODEL_NAME}_withmeta.pb "
+OUTARR_GEN_SCRIPT+="--metadata_path ${TMPDIR}/${MODEL_NAME}_metadata_arrays.txt "
+OUTARR_GEN_SCRIPT+="--output_arrays_path ${TMPDIR}/${MODEL_NAME}_output_arrays.txt "
-"${DRIVER_PATH}/generate_bcq_output_arrays" \
---input_path "${TMPDIR}/${MODEL_NAME}_preserved.pb" \
---output_path "${TMPDIR}/${MODEL_NAME}_output_arrays.txt" >> "${OUTPUT_PATH}.log" 2>&1
+echo ${OUTARR_GEN_SCRIPT} >> "${OUTPUT_PATH}.log"
+echo "" >> "${OUTPUT_PATH}.log"
+$OUTARR_GEN_SCRIPT >> "${OUTPUT_PATH}.log" 2>&1
-# generate temporary tflite file
+# generate tflite file
CONVERT_SCRIPT="python ${DRIVER_PATH}/tf2tfliteV2.py ${TF_INTERFACE} "
-CONVERT_SCRIPT+="--input_path ${TMPDIR}/${MODEL_NAME}_preserved.pb "
+CONVERT_SCRIPT+="--input_path ${TMPDIR}/${MODEL_NAME}_withmeta.pb "
CONVERT_SCRIPT+="--input_arrays ${INPUT_ARRAYS} "
CONVERT_SCRIPT+="--output_path ${TMPDIR}/${MODEL_NAME}.tflite "
-CONVERT_SCRIPT+="--output_arrays ${OUTPUT_ARRAYS}$(cat ${TMPDIR}/${MODEL_NAME}_output_arrays.txt) "
+CONVERT_SCRIPT+="--output_arrays "
+CONVERT_SCRIPT+="$(cat ${TMPDIR}/${MODEL_NAME}_metadata_arrays.txt)"
+CONVERT_SCRIPT+="${OUTPUT_ARRAYS}"
+CONVERT_SCRIPT+="$(cat ${TMPDIR}/${MODEL_NAME}_output_arrays.txt) "
if [ ! -z ${INPUT_SHAPES} ]; then
CONVERT_SCRIPT+="--input_shapes ${INPUT_SHAPES} "
fi
-echo ${CONVERT_SCRIPT} > "${OUTPUT_PATH}.log"
+echo ${CONVERT_SCRIPT} >> "${OUTPUT_PATH}.log"
$CONVERT_SCRIPT >> "${OUTPUT_PATH}.log" 2>&1
# convert .tflite to .circle
diff --git a/compiler/one-cmds/one-import-tf b/compiler/one-cmds/one-import-tf
index 58c686882..3b2b976e1 100644
--- a/compiler/one-cmds/one-import-tf
+++ b/compiler/one-cmds/one-import-tf
@@ -38,6 +38,41 @@ version()
exit 255
}
+input_not_set()
+{
+ echo "Error: input_path not set"
+ echo ""
+ usage
+}
+
+output_not_set()
+{
+ echo "Error: output_path not set"
+ echo ""
+ usage
+}
+
+input_arrays_not_set()
+{
+ echo "Error: input_arrays not set"
+ echo ""
+ usage
+}
+
+input_shapes_not_set()
+{
+ echo "Error: input_shapes not set"
+ echo ""
+ usage
+}
+
+output_arrays_not_set()
+{
+ echo "Error: output_arrays not set"
+ echo ""
+ usage
+}
+
TF_INTERFACE="--v1"
# Parse command-line arguments
@@ -54,22 +89,37 @@ while [ "$#" -ne 0 ]; do
;;
'--input_path')
export INPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ input_not_set
+ fi
shift 2
;;
'--output_path')
export OUTPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ output_not_set
+ fi
shift 2
;;
'--input_arrays')
export INPUT_ARRAYS="$2"
+ if [ $# -lt 2 ]; then
+ input_arrays_not_set
+ fi
shift 2
;;
'--input_shapes')
export INPUT_SHAPES="$2"
+ if [ $# -lt 2 ]; then
+ input_shapes_not_set
+ fi
shift 2
;;
'--output_arrays')
export OUTPUT_ARRAYS="$2"
+ if [ $# -lt 2 ]; then
+ output_arrays_not_set
+ fi
shift 2
;;
'--v2')
@@ -94,6 +144,20 @@ if [ -z ${INPUT_PATH} ] || [ ! -e ${INPUT_PATH} ]; then
exit 2
fi
+if [ -z ${INPUT_ARRAYS} ]; then
+ input_arrays_not_set
+fi
+
+# INPUT_SHAPES is optional
+
+if [ -z ${OUTPUT_PATH} ]; then
+ output_not_set
+fi
+
+if [ -z ${OUTPUT_ARRAYS} ]; then
+ output_arrays_not_set
+fi
+
FILE_BASE=$(basename ${OUTPUT_PATH})
MODEL_NAME="${FILE_BASE%.*}"
@@ -108,6 +172,10 @@ if [ -e ${VIRTUALENV_LINUX} ]; then
source ${VIRTUALENV_LINUX}
elif [ -e ${VIRTUALENV_WINDOWS} ]; then
source ${VIRTUALENV_WINDOWS}
+else
+ echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command."
+ echo "If encounter any problems, please follow provided document in 'doc' folder."
+ exit 255
fi
# remove previous log
diff --git a/compiler/one-cmds/one-import-tflite b/compiler/one-cmds/one-import-tflite
index 053489c92..0d5cef101 100644
--- a/compiler/one-cmds/one-import-tflite
+++ b/compiler/one-cmds/one-import-tflite
@@ -34,6 +34,20 @@ version()
exit 255
}
+input_not_set()
+{
+ echo "Error: input_path not set"
+ echo ""
+ usage
+}
+
+output_not_set()
+{
+ echo "Error: output_path not set"
+ echo ""
+ usage
+}
+
# Parse command-line arguments
#
while [ "$#" -ne 0 ]; do
@@ -48,10 +62,16 @@ while [ "$#" -ne 0 ]; do
;;
'--input_path')
export INPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ input_not_set
+ fi
shift 2
;;
'--output_path')
export OUTPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ output_not_set
+ fi
shift 2
;;
*)
@@ -67,6 +87,10 @@ if [ -z ${INPUT_PATH} ] || [ ! -e ${INPUT_PATH} ]; then
usage
fi
+if [ -z ${OUTPUT_PATH} ]; then
+ output_not_set
+fi
+
# remove previous log
rm -rf "${OUTPUT_PATH}.log"
diff --git a/compiler/one-cmds/one-optimize b/compiler/one-cmds/one-optimize
index 17b6b980e..e2427a9ef 100644
--- a/compiler/one-cmds/one-optimize
+++ b/compiler/one-cmds/one-optimize
@@ -24,6 +24,10 @@ usage()
echo "Usage: one-optimize"
echo " --version Show version information and exit"
echo " --all Enable all optimization algorithms"
+ echo " --fold_dequantize"
+ echo " Enable FoldDequantize Pass"
+ echo " --fuse_add_with_tconv"
+ echo " Enable FuseAddWithTConv Pass"
echo " --fuse_bcq Enable FuseBCQ Pass"
echo " --fuse_instnorm Enable FuseInstanceNormalization Pass"
echo " --resolve_customop_add"
@@ -44,6 +48,8 @@ version()
}
OPTIMIZE_all=0
+OPTIMIZE_fold_dequantize=0
+OPTIMIZE_fuse_add_with_tconv=0
OPTIMIZE_fuse_bcq=0
OPTIMIZE_fuse_instnorm=0
OPTIMIZE_resolve_customop_add=0
@@ -66,6 +72,14 @@ while [ "$#" -ne 0 ]; do
OPTIMIZE_all=1
shift
;;
+ '--fold_dequantize')
+ OPTIMIZE_fold_dequantize=1
+ shift
+ ;;
+ '--fuse_add_with_tconv')
+ OPTIMIZE_fuse_add_with_tconv=1
+ shift
+ ;;
'--fuse_bcq')
OPTIMIZE_fuse_bcq=1
shift
@@ -113,6 +127,12 @@ OPTIMIZE_OPTIONS=""
if [ $OPTIMIZE_all == 1 ]; then
OPTIMIZE_OPTIONS+="--all "
fi
+if [ $OPTIMIZE_fold_dequantize == 1 ]; then
+ OPTIMIZE_OPTIONS+="--fold_dequantize "
+fi
+if [ $OPTIMIZE_fuse_add_with_tconv == 1 ]; then
+ OPTIMIZE_OPTIONS+="--fuse_add_with_tconv "
+fi
if [ $OPTIMIZE_fuse_bcq == 1 ]; then
OPTIMIZE_OPTIONS+="--fuse_bcq "
fi
diff --git a/compiler/one-cmds/one-pack b/compiler/one-cmds/one-pack
index 023b0a85f..fe9f1bc37 100644
--- a/compiler/one-cmds/one-pack
+++ b/compiler/one-cmds/one-pack
@@ -34,6 +34,20 @@ version()
exit 255
}
+input_not_set()
+{
+ echo "Error: input path not set"
+ echo ""
+ usage
+}
+
+output_not_set()
+{
+ echo "Error: output path not set"
+ echo ""
+ usage
+}
+
# Parse command-line arguments
#
while [ "$#" -ne 0 ]; do
@@ -51,10 +65,16 @@ while [ "$#" -ne 0 ]; do
;;
'-i')
export INPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ input_not_set
+ fi
shift 2
;;
'-o')
export OUTPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ output_not_set
+ fi
shift 2
;;
*)
@@ -70,6 +90,10 @@ if [ -z ${INPUT_PATH} ] || [ ! -e ${INPUT_PATH} ]; then
usage
fi
+if [ -z ${OUTPUT_PATH} ]; then
+ output_not_set
+fi
+
INPUT_FILE=$(basename "${INPUT_PATH}")
LOG_FILE="${INPUT_FILE%.*}.pack.log"
diff --git a/compiler/one-cmds/one-prepare-venv b/compiler/one-cmds/one-prepare-venv
index 0b11e7f0b..4fa6f519e 100644
--- a/compiler/one-cmds/one-prepare-venv
+++ b/compiler/one-cmds/one-prepare-venv
@@ -52,3 +52,7 @@ python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host file
install -U pip==20.2.1 setuptools==49.3.0
python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \
install tensorflow-cpu==2.3.0
+
+# Create python symoblic link
+rm -f ${DRIVER_PATH}/python
+ln -s venv/bin/python ${DRIVER_PATH}/python
diff --git a/compiler/one-cmds/one-quantize b/compiler/one-cmds/one-quantize
index c74b2c2d2..112bff337 100644
--- a/compiler/one-cmds/one-quantize
+++ b/compiler/one-cmds/one-quantize
@@ -24,7 +24,7 @@ usage()
echo "Usage: one-quantize"
echo " --version Show version information and exit"
echo " --input_dtype Input data type (supported: float32, default=float32)"
- echo " --quantized_dtype Output quantized data type (supported: uint8, default=uint8)"
+ echo " --quantized_dtype Output quantized data type (supported: uint8, int16, default=uint8)"
echo " --granularity Quantize granularity (supported: layer, channel, default=layer)"
echo " --min_percentile Minimum percentile (0.0~100.0, default=1.0)"
echo " --max_percentile Maximum percentile (0.0~100.0, default=99.0)"
@@ -41,6 +41,69 @@ version()
exit 255
}
+input_not_set()
+{
+ echo "Error: input_path not set"
+ echo ""
+ usage
+}
+
+output_not_set()
+{
+ echo "Error: output_path not set"
+ echo ""
+ usage
+}
+
+input_data_not_set()
+{
+ echo "Error: input_data not set"
+ echo ""
+ usage
+}
+
+input_dtype_not_set()
+{
+ echo "Error: input_dtype not set"
+ echo ""
+ usage
+}
+
+quantized_dtype_not_set()
+{
+ echo "Error: quantized_dtype not set"
+ echo ""
+ usage
+}
+
+granularity_not_set()
+{
+ echo "Error: granularity not set"
+ echo ""
+ usage
+}
+
+min_percentile_not_set()
+{
+ echo "Error: min_percentile not set"
+ echo ""
+ usage
+}
+
+max_percentile_not_set()
+{
+ echo "Error: max_percentile not set"
+ echo ""
+ usage
+}
+
+mode_not_set()
+{
+ echo "Error: mode not set"
+ echo ""
+ usage
+}
+
INPUT_DTYPE=float32
QUANTIZED_DTYPE=uint8
GRANULARITY=layer
@@ -63,39 +126,66 @@ while [ "$#" -ne 0 ]; do
'--input_dtype')
INPUT_DTYPE="$2"
+ if [ $# -lt 2 ]; then
+ input_dtype_not_set
+ fi
shift 2
;;
'--quantized_dtype')
QUANTIZED_DTYPE="$2"
+ if [ $# -lt 2 ]; then
+ quantized_dtype_not_set
+ fi
shift 2
;;
'--granularity')
GRANULARITY="$2"
+ if [ $# -lt 2 ]; then
+ granularity_not_set
+ fi
shift 2
;;
'--min_percentile')
MIN_PERCENTILE="$2"
+ if [ $# -lt 2 ]; then
+ min_percentile_not_set
+ fi
shift 2
;;
'--max_percentile')
MAX_PERCENTILE="$2"
+ if [ $# -lt 2 ]; then
+ max_percentile_not_set
+ fi
shift 2
;;
'--mode')
MODE="$2"
+ if [ $# -lt 2 ]; then
+ mode_not_set
+ fi
shift 2
;;
'--input_path')
INPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ input_not_set
+ fi
shift 2
;;
'--input_data')
INPUT_DATA="$2"
+ if [ $# -lt 2 ]; then
+ input_data_not_set
+ fi
shift 2
;;
'--output_path')
OUTPUT_PATH="$2"
+ if [ $# -lt 2 ]; then
+ output_not_set
+ fi
shift 2
;;
@@ -116,6 +206,9 @@ if [ -z ${INPUT_DATA} ] || [ ! -e ${INPUT_DATA} ]; then
echo ""
usage
fi
+if [ -z ${OUTPUT_PATH} ]; then
+ output_not_set
+fi
FILE_BASE=$(basename ${OUTPUT_PATH})
MODEL_NAME="${FILE_BASE%.*}"
diff --git a/compiler/one-cmds/tests/CMakeLists.txt b/compiler/one-cmds/tests/CMakeLists.txt
new file mode 100644
index 000000000..cb1081d28
--- /dev/null
+++ b/compiler/one-cmds/tests/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Install one-cmds test scripts
+
+# Gather test scripts
+file(GLOB TESTITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.test")
+
+# Create a script to run the tests at installation folder
+set(DRIVER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runtestall.sh")
+
+file(WRITE "${DRIVER_SCRIPT}" "#!/bin/bash\n\n")
+file(APPEND "${DRIVER_SCRIPT}" "SCRIPT_PATH=$(cd $(dirname $\{BASH_SOURCE\[0\]\}) && pwd)\n")
+file(APPEND "${DRIVER_SCRIPT}" "pushd $SCRIPT_PATH > /dev/null\n")
+file(APPEND "${DRIVER_SCRIPT}" "rm -rf runtestall.log\n")
+file(APPEND "${DRIVER_SCRIPT}" "export PATH=$SCRIPT_PATH/../bin:$PATH\n")
+file(APPEND "${DRIVER_SCRIPT}" "if [[ $# -ge 1 ]]; then\n")
+file(APPEND "${DRIVER_SCRIPT}" " USER_PATH=$1\n")
+file(APPEND "${DRIVER_SCRIPT}" " export PATH=$USER_PATH:$PATH\n")
+file(APPEND "${DRIVER_SCRIPT}" "fi\n")
+file(APPEND "${DRIVER_SCRIPT}" "\n")
+
+foreach(TESTITEM IN ITEMS ${TESTITEMS})
+ get_filename_component(ITEM_PREFIX ${TESTITEM} NAME_WE)
+
+ set(TESTITEM_SCRIPT_FILE "${ITEM_PREFIX}.test")
+ set(TESTITEM_SCRIPT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${TESTITEM_SCRIPT_FILE}")
+
+ file(APPEND "${DRIVER_SCRIPT}" "/bin/bash ${TESTITEM_SCRIPT_FILE} | tee -a runtestall.log\n")
+
+ install(FILES ${TESTITEM} DESTINATION test)
+
+endforeach(TESTITEM)
+
+file(APPEND "${DRIVER_SCRIPT}" "popd> /dev/null")
+
+set(PREPARE_TEST_MATERIALS_SH "${CMAKE_CURRENT_SOURCE_DIR}/prepare_test_materials.sh")
+
+install(FILES ${DRIVER_SCRIPT}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION test)
+
+install(FILES ${PREPARE_TEST_MATERIALS_SH}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION test)
+
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.txt
+ DESTINATION test)
diff --git a/compiler/one-cmds/tests/README.txt b/compiler/one-cmds/tests/README.txt
new file mode 100644
index 000000000..0d4d0ecbe
--- /dev/null
+++ b/compiler/one-cmds/tests/README.txt
@@ -0,0 +1,27 @@
+one-cmds testing
+================
+
+Run 'runtestall.sh' program to test ONE command line programs, all at once.
+
+Steps:
+1) run 'one-prepare-venv' in bin folder to prepare python virtual-env with TensorFlow
+ - you need to run this only once
+ - read 'doc/how-to-prepare-virtualenv.txt' for more information
+----------------------------------------------
+bin/one-prepare-venv
+----------------------------------------------
+
+2) run 'test/prepare_test_materials.sh' to download test material models
+ - you need to run this only once
+ - you need internet connection to download files
+ - you may need to install 'wget' and 'unzip' packages
+----------------------------------------------
+test/prepare_test_materials.sh
+----------------------------------------------
+
+3) run 'test/runtestall.sh' to run the test
+----------------------------------------------
+test/runtestall.sh
+----------------------------------------------
+
+End.
diff --git a/compiler/one-cmds/tests/one-import-bcq_001.test b/compiler/one-cmds/tests/one-import-bcq_001.test
new file mode 100644
index 000000000..74b8bab32
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_001.test
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# 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.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf $outputfile
+rm -rf $outputfile.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_001.test b/compiler/one-cmds/tests/one-import-bcq_neg_001.test
new file mode 100644
index 000000000..9a2b455ef
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_001.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder_null \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_002.test b/compiler/one-cmds/tests/one-import-bcq_neg_002.test
new file mode 100644
index 000000000..5779aa03e
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_002.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid output array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul_null > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_003.test b/compiler/one-cmds/tests/one-import-bcq_neg_003.test
new file mode 100644
index 000000000..3a8e6368b
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_003.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "input model not found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq_null.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_004.test b/compiler/one-cmds/tests/one-import-bcq_neg_004.test
new file mode 100644
index 000000000..dc83b98c8
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_004.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Error parsing message" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_005.test b/compiler/one-cmds/tests/one-import-bcq_neg_005.test
new file mode 100644
index 000000000..ad1196a67
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_005.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to write circle" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_006.test b/compiler/one-cmds/tests/one-import-bcq_neg_006.test
new file mode 100644
index 000000000..f761aa7c0
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_006.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "1,32,32" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_007.test b/compiler/one-cmds/tests/one-import-bcq_neg_007.test
new file mode 100644
index 000000000..5013254f5
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_007.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "30,30" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_008.test b/compiler/one-cmds/tests/one-import-bcq_neg_008.test
new file mode 100644
index 000000000..e7d5d2e03
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_008.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: invalid literal for" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "32,O" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_009.test b/compiler/one-cmds/tests/one-import-bcq_neg_009.test
new file mode 100644
index 000000000..ef990438a
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_009.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "must have the same number of items" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "32,32:1" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_001.test b/compiler/one-cmds/tests/one-import_001.test
new file mode 100644
index 000000000..165f49193
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_001.test
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# 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.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+# Note: Do not remove output circle file as it's used for quantize tests
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-import_neg_001.test b/compiler/one-cmds/tests/one-import_neg_001.test
new file mode 100644
index 000000000..5a233d2f3
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_001.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage improper input model
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors 'input' were found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.tflite"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_002.test b/compiler/one-cmds/tests/one-import_neg_002.test
new file mode 100644
index 000000000..78b77511a
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_002.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with unsupported dynamic tensor
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "is incompatible with result type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+outputfile="./while_3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Hole,Hole_2 --input_shapes "1,1:1,1" \
+--output_arrays Output > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_003.test b/compiler/one-cmds/tests/one-import_neg_003.test
new file mode 100644
index 000000000..b0e95b6d1
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_003.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid output array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_2 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_004.test b/compiler/one-cmds/tests/one-import_neg_004.test
new file mode 100644
index 000000000..afb206ad9
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_004.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,1" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_005.test b/compiler/one-cmds/tests/one-import_neg_005.test
new file mode 100644
index 000000000..045fb89b7
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_005.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_006.test b/compiler/one-cmds/tests/one-import_neg_006.test
new file mode 100644
index 000000000..bb3e2344c
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_006.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ConverterError: <unknown>:0: error:" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "0,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_007.test b/compiler/one-cmds/tests/one-import_neg_007.test
new file mode 100644
index 000000000..bcfc4bcc9
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_007.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: invalid literal" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "None,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_008.test b/compiler/one-cmds/tests/one-import_neg_008.test
new file mode 100644
index 000000000..2f48fd708
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_008.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "must have the same number of items" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input,InceptionV3/Predictions/Shape --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_009.test b/compiler/one-cmds/tests/one-import_neg_009.test
new file mode 100644
index 000000000..79e463d64
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_009.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to write circle" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_010.test b/compiler/one-cmds/tests/one-import_neg_010.test
new file mode 100644
index 000000000..05677a6d4
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_010.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input_arrays
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input2 --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_001.test b/compiler/one-cmds/tests/one-optimize_001.test
new file mode 100644
index 000000000..240a62506
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_001.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# 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.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-optimize_neg_001.test b/compiler/one-cmds/tests/one-optimize_neg_001.test
new file mode 100644
index 000000000..4ee509697
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_001.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# 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.
+
+# this test should fail
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Invalid input file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_neg_002.test b/compiler/one-cmds/tests/one-optimize_neg_002.test
new file mode 100644
index 000000000..811b4d814
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_002.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "input model not found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circletxt"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_neg_003.test b/compiler/one-cmds/tests/one-optimize_neg_003.test
new file mode 100644
index 000000000..17f53b9a4
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_003.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage without output folder path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to export" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_001.test b/compiler/one-cmds/tests/one-pack_001.test
new file mode 100644
index 000000000..3d746dffd
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_001.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# 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.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfolder="nnpack"
+
+rm -rf ${outputfolder}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-pack \
+-i ${inputfile} \
+-o ${outputfolder} >> /dev/null
+
+if [[ ! -d "${outputfolder}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-pack_neg_001.test b/compiler/one-cmds/tests/one-pack_neg_001.test
new file mode 100644
index 000000000..de1eab9c7
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_001.test
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "input model not found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+
+# run test
+one-pack \
+-i ./inception_v2.circle \
+-o nnpack > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_neg_002.test b/compiler/one-cmds/tests/one-pack_neg_002.test
new file mode 100644
index 000000000..6427c260e
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_002.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with filename without extension
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "modelfile does not have extension" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+rm -rf nnpack
+
+# prepare dummy file
+touch ./sample
+
+# run test
+one-pack \
+-i ./sample \
+-o nnpack > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_neg_003.test b/compiler/one-cmds/tests/one-pack_neg_003.test
new file mode 100644
index 000000000..bf91a5226
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_003.test
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage without output folder path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "output path not set" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+
+# prepare dummy file
+touch ./sample.circle
+
+# run test
+one-pack \
+-i ./sample.circle > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_001.test b/compiler/one-cmds/tests/one-quantize_001.test
new file mode 100644
index 000000000..ef25ac800
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_001.test
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# 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.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ./inception_v3.circle \
+--input_data ./inception_v3_test_data.h5 \
+--output_path ./inception_v3.quantized.circle >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-quantize_neg_001.test b/compiler/one-cmds/tests/one-quantize_neg_001.test
new file mode 100644
index 000000000..ccf16fbf8
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_001.test
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with unsupported input dtype
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported input type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# test begin
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+one-quantize \
+--input_dtype float64 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_002.test b/compiler/one-cmds/tests/one-quantize_neg_002.test
new file mode 100644
index 000000000..387e53637
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_002.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with unsupported input dtype
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported output type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint16 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_003.test b/compiler/one-cmds/tests/one-quantize_neg_003.test
new file mode 100644
index 000000000..47cf0691f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_003.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with wrong representative dataset
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Input shape mismatch" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ./mobilenet_test_data.h5 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_004.test b/compiler/one-cmds/tests/one-quantize_neg_004.test
new file mode 100644
index 000000000..8d53ca1d1
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_004.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to export" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_005.test b/compiler/one-cmds/tests/one-quantize_neg_005.test
new file mode 100644
index 000000000..4504730d0
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_005.test
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Invalid input file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_006.test b/compiler/one-cmds/tests/one-quantize_neg_006.test
new file mode 100644
index 000000000..2ecbc0413
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_006.test
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "input model not found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v2.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_007.test b/compiler/one-cmds/tests/one-quantize_neg_007.test
new file mode 100644
index 000000000..4796f9e89
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_007.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid input_data
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Given data file is not HDF5" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_008.test b/compiler/one-cmds/tests/one-quantize_neg_008.test
new file mode 100644
index 000000000..1656c6ba2
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_008.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid mode
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported mode" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--mode average \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_009.test b/compiler/one-cmds/tests/one-quantize_neg_009.test
new file mode 100644
index 000000000..6ecc5c32f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_009.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid max_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--max_percentile 101 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_010.test b/compiler/one-cmds/tests/one-quantize_neg_010.test
new file mode 100644
index 000000000..209645990
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_010.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid max_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--max_percentile -1 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_011.test b/compiler/one-cmds/tests/one-quantize_neg_011.test
new file mode 100644
index 000000000..ea44c2ffe
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_011.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--min_percentile 101 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_012.test b/compiler/one-cmds/tests/one-quantize_neg_012.test
new file mode 100644
index 000000000..b744051ae
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_012.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--min_percentile -1 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_013.test b/compiler/one-cmds/tests/one-quantize_neg_013.test
new file mode 100644
index 000000000..dec1cbd06
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_013.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# 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.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported granularity" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--granularity layered \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/prepare_test_materials.sh b/compiler/one-cmds/tests/prepare_test_materials.sh
new file mode 100644
index 000000000..e241f44b5
--- /dev/null
+++ b/compiler/one-cmds/tests/prepare_test_materials.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+# 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.
+
+# See https://github.com/Samsung/ONE/issues/4155 for information
+
+SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+pushd $SCRIPT_PATH > /dev/null
+
+if [[ ! -s "inception_v3.pb" ]]; then
+ rm -rf inception_v3_2018_04_27.tgz
+ wget https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/inception_v3_2018_04_27.tgz
+ tar zxvf inception_v3_2018_04_27.tgz
+fi
+
+if [[ ! -s "while_3.pbtxt" ]]; then
+ rm -rf while_3.zip
+ wget https://github.com/Samsung/ONE/files/5095630/while_3.zip
+ unzip while_3.zip
+fi
+
+if [[ ! -s "inception_v3_test_data.h5" ]]; then
+ rm -rf inception_v3_test_data.zip
+ wget https://github.com/Samsung/ONE/files/5139370/inception_v3_test_data.zip
+ unzip inception_v3_test_data.zip
+fi
+
+if [[ ! -s "mobilenet_test_data.h5" ]]; then
+ rm -rf mobilenet_test_data.zip
+ wget https://github.com/Samsung/ONE/files/5139460/mobilenet_test_data.zip
+ unzip mobilenet_test_data.zip
+fi
+
+if [[ ! -s "bcq.pb" ]]; then
+ rm -rf bcq.pb.zip
+ wget https://github.com/Samsung/ONE/files/5153842/bcq.pb.zip
+ unzip bcq.pb.zip
+fi
+
+# prepare 'inception_v3.circle' file used for quantizatio test
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+if [[ ! -s ${outputfile} ]]; then
+ ../bin/one-import-tf \
+ --input_path ${inputfile} \
+ --output_path ${outputfile} \
+ --input_arrays input --input_shapes "1,299,299,3" \
+ --output_arrays InceptionV3/Predictions/Reshape_1
+fi
+
+popd > /dev/null
diff --git a/compiler/oneco/CMakeLists.txt b/compiler/oneco/CMakeLists.txt
index 73bc57d43..10f466948 100644
--- a/compiler/oneco/CMakeLists.txt
+++ b/compiler/oneco/CMakeLists.txt
@@ -1,5 +1,5 @@
nnas_find_package(Protobuf QUIET)
-nnas_find_package(ONNXSource EXACT 1.4.1 QUIET)
+nnas_find_package(ONNXSource EXACT 1.6.0 QUIET)
if(NOT Protobuf_FOUND)
return()
diff --git a/compiler/onnx-tools/CMakeLists.txt b/compiler/onnx-tools/CMakeLists.txt
new file mode 100644
index 000000000..ac4500e0e
--- /dev/null
+++ b/compiler/onnx-tools/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(ONNX_TOOL_FILES
+ onnx-dump.py
+ onnx-ops.py
+)
+
+foreach(ONNX_TOOL IN ITEMS ${ONNX_TOOL_FILES})
+
+ set(ONNX_TOOL_FILE ${ONNX_TOOL})
+ set(ONNX_TOOL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${ONNX_TOOL_FILE}")
+ set(ONNX_TOOL_BIN "${CMAKE_CURRENT_BINARY_DIR}/${ONNX_TOOL_FILE}")
+ set(ONNX_TOOL_TARGET "${ONNX_TOOL}_target")
+
+ add_custom_command(OUTPUT ${ONNX_TOOL_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${ONNX_TOOL_SRC}" "${ONNX_TOOL_BIN}"
+ DEPENDS ${ONNX_TOOL_SRC}
+ COMMENT "Generate ${ONNX_TOOL_BIN}"
+ )
+
+ add_custom_target(${ONNX_TOOL_TARGET} ALL DEPENDS ${ONNX_TOOL_BIN})
+
+endforeach(ONNX_TOOL)
diff --git a/compiler/onnx-tools/README.md b/compiler/onnx-tools/README.md
new file mode 100644
index 000000000..f1b886132
--- /dev/null
+++ b/compiler/onnx-tools/README.md
@@ -0,0 +1,65 @@
+# onnx-tools
+
+_onnx-tools_ provides developer tools to support ONNX format in compiler frontend.
+
+## onnx-dump.py
+
+Use `onnx-dump.py` to dump ONNX model graph in human readable text format.
+
+For example,
+
+```
+[General] -----------------------------
+IR version = 6
+Producer = pytorch 1.6
+
+[Operators] ---------------------------
+ 3 Conv
+ 3 Relu
+...
+
+[Initializers] ------------------------
+"0.bias" FLOAT [16]
+"0.weight" FLOAT [16, 1, 3, 3]
+...
+
+[Nodes] -------------------------------
+Conv("Conv_0")
+ A dilations: [1, 1], group: 1, kernel_shape: [3, 3], pads: [1, 1, 1, 1], strides: [2, 2]
+ I "input.1"
+ I "0.weight"
+ I "0.bias"
+ O "7"
+Relu("Relu_1")
+ I "7"
+ O "8"
+...
+
+[Graph Input/Output]-------------------
+ I: "input.1" FLOAT [1, 1, 28, 28]
+ O: "21" FLOAT [1, 10]
+```
+
+In `[Nodes]` section, `A` is for attributes for the node, `I` for input name and `O` for output name.
+
+`I` and `O` also applies to `[Graph Input/Output]` section.
+
+## onnx-ops.py
+
+Use `onnx-ops.py` to dump ONNX model operators.
+
+You can use with other command line tools to analyze operators in the model file.
+
+For example,
+```bash
+$ python onnx-ops.py mymodel.onnx | sort | uniq -c
+ 1 Concat
+ 1 Constant
+ 3 Conv
+ 1 Gather
+ 1 GlobalAveragePool
+ 3 Relu
+ 1 Reshape
+ 1 Shape
+ 1 Unsqueeze
+```
diff --git a/compiler/onnx-tools/onnx-dump.py b/compiler/onnx-tools/onnx-dump.py
new file mode 100644
index 000000000..4f169cbe9
--- /dev/null
+++ b/compiler/onnx-tools/onnx-dump.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+
+# 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.
+
+import onnx
+import os
+import sys
+
+from onnx import AttributeProto, TensorProto
+from onnx import numpy_helper
+from onnx import helper
+
+
+def _data_type_str(data_type):
+ return TensorProto.DataType.Name(data_type)
+
+
+def _get_attribute_value(attr):
+ if attr.type == AttributeProto.TENSOR:
+ return "{}, {}".format(
+ _data_type_str(attr.t.data_type), numpy_helper.to_array(attr.t))
+ if attr.type == AttributeProto.GRAPH:
+ # TODO revise when graph node is available
+ return "<graph>"
+ if attr.type == AttributeProto.TENSORS:
+ # TODO revise to see contents
+ return "<tensors>..."
+ if attr.type == AttributeProto.GRAPHS:
+ # TODO revise when graph node is available
+ return "<graphs>..."
+ return helper.get_attribute_value(attr)
+
+
+def _dump_header(onnx_model):
+ print("[General] -----------------------------")
+ print("IR version =", onnx_model.ir_version)
+ print("Producer =", onnx_model.producer_name, onnx_model.producer_version)
+ print("")
+
+
+def _dump_operators(onnx_model):
+ opcodes_dict = dict()
+ for node in onnx_model.graph.node:
+ if node.op_type in opcodes_dict:
+ opcodes_dict[node.op_type] = opcodes_dict[node.op_type] + 1
+ else:
+ opcodes_dict[node.op_type] = 1
+
+ print("[Operators] ---------------------------")
+ for opcode_key in opcodes_dict:
+ print("{:>5} {}".format(opcodes_dict[opcode_key], opcode_key))
+
+ print("")
+
+
+def _dump_initializers(onnx_model):
+ print("[Initializers] ------------------------")
+ for initializer in onnx_model.graph.initializer:
+ init_name = '"{}"'.format(initializer.name)
+ dtstr = _data_type_str(initializer.data_type)
+ print('{:<15} {} {}'.format(init_name, dtstr, initializer.dims))
+
+ print("")
+
+
+def _dump_nodes(onnx_model):
+ print("[Nodes] -------------------------------")
+
+ for node in onnx_model.graph.node:
+ print('{0}("{1}")'.format(node.op_type, node.name))
+
+ attribute = ''
+ for attr in node.attribute:
+ if attribute != '':
+ attribute += ', '
+ attribute += "{}: {}".format(attr.name, _get_attribute_value(attr))
+
+ if attribute != '':
+ print(' A {0}'.format(attribute))
+
+ for inp in node.input:
+ print(' I "{0}"'.format(inp))
+ for out in node.output:
+ print(' O "{0}"'.format(out))
+
+ print("")
+
+
+def _dump_inputoutputs(onnx_model):
+ print("[Graph Input/Output]-------------------")
+ for mod_input in onnx_model.graph.input:
+ io_name = '"{}"'.format(mod_input.name)
+ dtstr = _data_type_str(mod_input.type.tensor_type.elem_type)
+ shape = mod_input.type.tensor_type.shape
+ input_shape = [dim.dim_value for dim in shape.dim]
+ print(' I: {:<15} {} {}'.format(io_name, dtstr, input_shape))
+
+ for mod_output in onnx_model.graph.output:
+ io_name = '"{}"'.format(mod_output.name)
+ dtstr = _data_type_str(mod_output.type.tensor_type.elem_type)
+ shape = mod_output.type.tensor_type.shape
+ output_shape = [dim.dim_value for dim in shape.dim]
+ print(' O: {:<15} {} {}'.format(io_name, dtstr, output_shape))
+
+ print("")
+
+
+def _dump_graph(onnx_model):
+ _dump_header(onnx_model)
+ _dump_operators(onnx_model)
+ _dump_initializers(onnx_model)
+ _dump_nodes(onnx_model)
+ _dump_inputoutputs(onnx_model)
+
+
+def _help_exit(cmd_name):
+ print('Dump ONNX model file Graph')
+ print('Usage: {0} [onnx_path]'.format(cmd_name))
+ print('')
+ exit()
+
+
+def main():
+ if len(sys.argv) < 2:
+ _help_exit(os.path.basename(sys.argv[0]))
+
+ onnx_model = onnx.load(sys.argv[1])
+ onnx.checker.check_model(onnx_model)
+
+ _dump_graph(onnx_model)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/onnx-tools/onnx-ops.py b/compiler/onnx-tools/onnx-ops.py
new file mode 100644
index 000000000..5292dc70e
--- /dev/null
+++ b/compiler/onnx-tools/onnx-ops.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# 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.
+
+import onnx
+import os
+import sys
+
+
+def _dump_operators(onnx_model):
+ for node in onnx_model.graph.node:
+ print(node.op_type)
+
+
+def _help_exit(cmd_name):
+ print('Dump ONNX model file Operators')
+ print('Usage: {0} [onnx_path]'.format(cmd_name))
+ print('')
+ exit()
+
+
+def main():
+ if len(sys.argv) < 2:
+ _help_exit(os.path.basename(sys.argv[0]))
+
+ onnx_model = onnx.load(sys.argv[1])
+ onnx.checker.check_model(onnx_model)
+
+ _dump_operators(onnx_model)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/onnxkit/CMakeLists.txt b/compiler/onnxkit/CMakeLists.txt
index 18f1ed423..81c3622c9 100644
--- a/compiler/onnxkit/CMakeLists.txt
+++ b/compiler/onnxkit/CMakeLists.txt
@@ -1,5 +1,5 @@
nnas_find_package(Protobuf QUIET)
-nnas_find_package(ONNXSource EXACT 1.4.1 QUIET)
+nnas_find_package(ONNXSource EXACT 1.6.0 QUIET)
if(NOT Protobuf_FOUND)
return()
diff --git a/compiler/pota-quantization-value-test/README.md b/compiler/pota-quantization-value-test/README.md
index e3359ae4f..d6d003b4b 100644
--- a/compiler/pota-quantization-value-test/README.md
+++ b/compiler/pota-quantization-value-test/README.md
@@ -39,3 +39,17 @@ The expected output should include
(1) scale, zero point of activations
(2) scale, zero point, values of weights
(3) scale, values (weights) of bias
+
+### Golden data
+
+Golden data was generated as follows.
+
+(1) Generate random h5 input for a target model (using gen_h5_random_inputs.py in `record-minmax-conversion-test`)
+
+(2) Run `dalgona` with the target model, input data, and analysis code named GenGoldenWeights.py for uint8 (GenGoldenWeightsSym.py for int16) (https://github.com/Samsung/ONE/pull/3501)
+
+(3) Do fake quantization using circle-quantizer
+
+(4) Run `dalgona` with the fake-quantized model, input data, and analysis code named GenGoldenActBias.py for uint8 (GenGoldenActBiasSym.py for int16) (https://github.com/Samsung/ONE/pull/3501)
+
+(5) Edit generated data for some operators (concat: scale propagation, mean: axis data)
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..3ae32419f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015018323028925806,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..ab968c9fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192,
+ -12288
+ ],
+ [
+ -16384,
+ -20479,
+ 24575
+ ]
+ ],
+ [
+ [
+ -28671,
+ 32767,
+ 16384
+ ],
+ [
+ -8192,
+ 12288,
+ -4096
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..4a1297410
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00037582992808893323,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..4f17e12de
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.92105390548706,
+ "max": 4.809383983612061
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..6d2169066
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -11.397277755737305,
+ "max": 12.314819450378417
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..a223fa4aa
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.038489170372486115,
+ "zero_point": 129.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..ec6082d55
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153,
+ 68
+ ],
+ [
+ 51,
+ 34,
+ 221
+ ]
+ ],
+ [
+ [
+ 0,
+ 255,
+ 187
+ ],
+ [
+ 85,
+ 170,
+ 102
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..afa9b1a8e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0892433300614357,
+ "zero_point": 134.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..0138d54cf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9815891456604,
+ "max": 4.833149127960205
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..8edbed5b6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -11.962269973754882,
+ "max": 10.79477970123291
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..6a5fc3e88
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001524870313005522,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..b1fee1e89
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00013096666953060776,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..905be6038
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.996542510986329,
+ "max": 4.979214477539063
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..a35199a6f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.9937237644195553,
+ "max": 4.291385040283203
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..0528cc9cc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03911808878183365,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..ac5da0bda
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.027372928336262703,
+ "zero_point": 141.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..8701c51ff
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9830295753479,
+ "max": 4.992084045410156
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..b2bb2d227
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.863597021102905,
+ "max": 3.1164999485015867
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..71265a270
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..53d7cdba3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json
@@ -0,0 +1,28 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192
+ ],
+ [
+ -12288,
+ -16384
+ ]
+ ],
+ [
+ [
+ -20479,
+ 24575
+ ],
+ [
+ -28671,
+ 32767
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..71265a270
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..1a3f56eb0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.83315715789795,
+ "max": 4.561212120056152
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..700674c7c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -7.0,
+ "max": 8.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..522880618
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..17ba25363
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,28 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153
+ ],
+ [
+ 68,
+ 51
+ ]
+ ],
+ [
+ [
+ 34,
+ 221
+ ],
+ [
+ 0,
+ 255
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..522880618
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..dc8d1db1e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.8125765800476072,
+ "max": 4.720572299957276
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..700674c7c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -7.0,
+ "max": 8.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..8817cbef7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.000030517578125,
+ 2.00006103515625
+ ],
+ [
+ -3.000091552734375,
+ -4.0001220703125
+ ]
+ ],
+ [
+ [
+ -4.999908447265625,
+ 5.99993896484375
+ ],
+ [
+ -6.999969482421875,
+ 8.0
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 4.0001220703125,
+ -2.00006103515625
+ ],
+ [
+ 3.000091552734375,
+ -1.000030517578125
+ ]
+ ],
+ [
+ [
+ -8.0,
+ -5.99993896484375
+ ],
+ [
+ 6.999969482421875,
+ 4.999908447265625
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..1b12a4d8d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json
@@ -0,0 +1,6 @@
+{
+ "weights": [
+ 1.0,
+ 2.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..b4b2e2136
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015194647130556405,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..94c794fbb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json
@@ -0,0 +1,61 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192
+ ],
+ [
+ -12288,
+ -16384
+ ]
+ ],
+ [
+ [
+ -20479,
+ 24575
+ ],
+ [
+ -28671,
+ 32767
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 16384,
+ -8192
+ ],
+ [
+ 12288,
+ -4096
+ ]
+ ],
+ [
+ [
+ -32767,
+ -24575
+ ],
+ [
+ 28671,
+ 20479
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.00024414807580797754,
+ 0.00024414807580797754
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -8.0,
+ -8.0
+ ],
+ "max": [
+ 8.0,
+ 8.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..5df65ebd4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0018470257055014372,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..4136cdffe
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9788299179077145,
+ "max": 4.917050857543946
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..0a35b161b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 60.521490783691405
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..20c1f6759
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,34 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.00018310546875,
+ 2.0,
+ 2.99981689453125,
+ 4.0001220703125
+ ],
+ [
+ -9.00006103515625,
+ 10.0,
+ -10.99993896484375,
+ 11.9998779296875
+ ]
+ ],
+ [
+ [
+ 5.0001220703125,
+ 6.0,
+ 6.9998779296875,
+ 8.000244140625
+ ],
+ [
+ 13.0,
+ -14.0,
+ 15.0,
+ -16.0
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..e0573e4f9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json
@@ -0,0 +1,8 @@
+{
+ "weights": [
+ 1.0,
+ 2.0,
+ 3.0,
+ 4.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..2d4178372
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015058962162584066,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..d465a7c17
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json
@@ -0,0 +1,53 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 2521,
+ 4681,
+ 6553,
+ 8192
+ ],
+ [
+ -22685,
+ 23405,
+ -24029,
+ 24575
+ ]
+ ],
+ [
+ [
+ 12603,
+ 14043,
+ 15291,
+ 16384
+ ],
+ [
+ 32767,
+ -32767,
+ 32767,
+ -32767
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.0003967406231879635,
+ 0.0004272591326639607,
+ 0.0004577776421399579,
+ 0.0004882961516159551
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -13.0,
+ -14.0,
+ -15.0,
+ -16.0
+ ],
+ "max": [
+ 13.0,
+ 14.0,
+ 15.0,
+ 16.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..5f6a88ce8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0034479827154427767,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..991c8d6d9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.733428707122803,
+ "max": 4.9343701171875
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..e6ec29252
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 112.98004760742187
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json
new file mode 100644
index 000000000..559e537fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json
@@ -0,0 +1,76 @@
+{
+ "weights": [
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..1c3479fa3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json
@@ -0,0 +1,8 @@
+{
+ "weights": [
+ 1.0,
+ -2.0,
+ -3.0,
+ 4.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json
new file mode 100644
index 000000000..ad24004c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015171234554145485,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json
new file mode 100644
index 000000000..42dc0edee
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0028422886971384287,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json
new file mode 100644
index 000000000..69254d12b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json
@@ -0,0 +1,95 @@
+{
+ "weights": [
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ]
+ ],
+ "scale": [
+ 0.00024414807580797754,
+ 0.00024414807580797754,
+ 0.00024414807580797754,
+ 0.00024414807580797754
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -8.0,
+ -8.0,
+ -8.0,
+ -8.0
+ ],
+ "max": [
+ 8.0,
+ 8.0,
+ 8.0,
+ 8.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json
new file mode 100644
index 000000000..dd4e1cb03
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.931026897430419,
+ "max": 4.971158237457275
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json
new file mode 100644
index 000000000..763dce164
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json
@@ -0,0 +1,4 @@
+{
+ "min": -91.51926612854004,
+ "max": 93.13327117919921
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..7bc9dff6d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015248177805915475,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..9fd574932
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015124233323149383,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..318cd008b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.99637056350708,
+ "max": 4.955757389068604
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..e52196f1a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.257103805541992,
+ "max": 4.955757389068604
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..9bf6c9bff
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03876218944787979,
+ "zero_point": 126.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..87de1116e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.029836513102054596,
+ "zero_point": 88.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..bb42bdf8e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.901860733032226,
+ "max": 4.982497882843018
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..bb3a52516
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.6258130359649656,
+ "max": 4.982497882843018
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..6671787bc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015251722652465105,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..31d974626
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00013075214519631118,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..4f1bc7595
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.997532138824463,
+ "max": 4.995666198730469
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..fc5074dbe
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.224256687164306,
+ "max": 4.284355401992798
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..ede36c6ad
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.039086975157260895,
+ "zero_point": 128.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..bd2fc7f62
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.028692100197076797,
+ "zero_point": 131.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..ae1dc5e90
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.993542575836181,
+ "max": 4.97363561630249
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..527ed8d46
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.766610870361328,
+ "max": 3.5498746299743655
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..802c38bea
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015054467075970024,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..ab968c9fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192,
+ -12288
+ ],
+ [
+ -16384,
+ -20479,
+ 24575
+ ]
+ ],
+ [
+ [
+ -28671,
+ 32767,
+ 16384
+ ],
+ [
+ -8192,
+ 12288,
+ -4096
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..0c497ea3f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0008732788846828043,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..c8a1be941
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.82299165725708,
+ "max": 4.932897224426269
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..12791d92c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -28.49001350402832,
+ "max": 28.614729080200195
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..bbff8952d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03780897706747055,
+ "zero_point": 131.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..ec6082d55
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153,
+ 68
+ ],
+ [
+ 51,
+ 34,
+ 221
+ ]
+ ],
+ [
+ [
+ 0,
+ 255,
+ 187
+ ],
+ [
+ 85,
+ 170,
+ 102
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..cec0bdf9a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.232084259390831,
+ "zero_point": 111.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..7cdb53424
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.954726142883301,
+ "max": 4.686561832427978
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..5f63577ea
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -25.874579315185546,
+ "max": 33.30691329956055
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json
new file mode 100644
index 000000000..5f6db8d72
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json
@@ -0,0 +1,13 @@
+{
+ "weights": [
+ [
+ [
+ 6553,
+ 19660,
+ 32767
+ ]
+ ]
+ ],
+ "scale": 1.5259254723787308e-05,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..b43bac4d7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001517884520580992,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..b43bac4d7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001517884520580992,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..321af6680
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.966492671966552,
+ "max": 4.97365219116211
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..2c18c21cb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.44303900718689,
+ "max": 4.97365219116211
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json
new file mode 100644
index 000000000..7c001602f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json
@@ -0,0 +1,13 @@
+{
+ "weights": [
+ [
+ [
+ 51,
+ 153,
+ 255
+ ]
+ ]
+ ],
+ "scale": 0.0019607844296842813,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..05ce9dd2c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03849203139543533,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..8f883094a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.02848827838897705,
+ "zero_point": 82.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..76e719001
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.899785652160644,
+ "max": 4.915681838989258
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..2aa27ca64
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.348829574584961,
+ "max": 4.915681838989258
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..a9a676169
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015181511116679758,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..29096744a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00014686971553601325,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..99fc6992c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.974525604248047,
+ "max": 4.812480030059814
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..dd17def11
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 4.812480030059814
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..3b97773ce
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03907399624586105,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..698a8a7ee
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.01955186203122139,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..fee2d92c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.978144645690918,
+ "max": 4.985724964141846
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..bd6199fc0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 4.985724964141846
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..6df24eb42
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 0.999786376953125,
+ 2.0001220703125
+ ],
+ [
+ -2.999908447265625,
+ -4.000244140625
+ ],
+ [
+ 5.000030517578125,
+ -5.99981689453125
+ ]
+ ],
+ [
+ [
+ 7.000152587890625,
+ 7.99993896484375
+ ],
+ [
+ -9.000274658203125,
+ -10.00006103515625
+ ],
+ [
+ 10.999847412109375,
+ -12.00018310546875
+ ]
+ ],
+ [
+ [
+ 12.999969482421875,
+ 13.999755859375
+ ],
+ [
+ -15.000091552734375,
+ -15.9998779296875
+ ],
+ [
+ 17.000213623046875,
+ -18.0
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json
new file mode 100644
index 000000000..2fc53a0ff
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json
@@ -0,0 +1,3 @@
+{
+ "weights": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..27afd4f6a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015109812375158072,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..8d0ceb1c6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json
@@ -0,0 +1,58 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1820,
+ 3641
+ ],
+ [
+ -5461,
+ -7282
+ ],
+ [
+ 9102,
+ -10922
+ ]
+ ],
+ [
+ [
+ 12743,
+ 14563
+ ],
+ [
+ -16384,
+ -18204
+ ],
+ [
+ 20024,
+ -21845
+ ]
+ ],
+ [
+ [
+ 23665,
+ 25485
+ ],
+ [
+ -27306,
+ -29126
+ ],
+ [
+ 30947,
+ -32767
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.0005493331705679495
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -18.0
+ ],
+ "max": [
+ 18.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..b39fcd7a4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.008887303993105888,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..ae3f30db7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.951032066345215,
+ "max": 4.942168235778809
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..348831b9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -260.74616638183596,
+ "max": 291.21028076171876
+}
diff --git a/compiler/pota-quantization-value-test/test.lst b/compiler/pota-quantization-value-test/test.lst
index d9fd91761..15606b8e4 100644
--- a/compiler/pota-quantization-value-test/test.lst
+++ b/compiler/pota-quantization-value-test/test.lst
@@ -1,8 +1,28 @@
+addTest(Add_002 layer uint8)
+addTest(Add_002 channel int16)
+addTest(AveragePool2D_000 layer uint8)
+addTest(AveragePool2D_000 channel int16)
+addTest(Concatenation_001 layer uint8)
+addTest(Concatenation_001 channel int16)
addTest(Conv2D_004 channel uint8)
+addTest(Conv2D_004 channel int16)
addTest(Conv2D_004 layer uint8)
addTest(DepthwiseConv2D_002 channel uint8)
+addTest(DepthwiseConv2D_002 channel int16)
addTest(DepthwiseConv2D_002 layer uint8)
addTest(FullyConnected_003 channel uint8)
+addTest(FullyConnected_003 channel int16)
addTest(FullyConnected_003 layer uint8)
+addTest(Mean_000 layer uint8)
+addTest(Mean_000 channel int16)
+addTest(MaxPool2D_000 layer uint8)
+addTest(MaxPool2D_000 channel int16)
+addTest(Mul_001 layer uint8)
+addTest(Mul_001 channel int16)
+addTest(PRelu_001 layer uint8)
+addTest(PRelu_001 channel int16)
+addTest(ReLU_000 layer uint8)
+addTest(ReLU_000 channel int16)
addTest(TransposeConv_001 channel uint8)
+addTest(TransposeConv_001 channel int16)
addTest(TransposeConv_001 layer uint8)
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt
new file mode 100644
index 000000000..1fce3b67d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt
@@ -0,0 +1 @@
+ 2.9230394,-3.3165355, 4.3210225,-3.323507 , 1.5080413,-2.4125786,-2.1971512, 4.227092 ,-4.6573114, 3.6270325,-1.9319664, 3.9428957
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt
new file mode 100644
index 000000000..0425e1f25
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt
@@ -0,0 +1 @@
+-2.9018474,-0.9047734, 1.2572454,-3.90714 , 1.4757215, 3.2261674,-4.431676 , 4.318475 , 2.305025 , 3.5344698,-3.9441512,-1.9244975
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt
new file mode 100644
index 000000000..a3e5e910e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt
@@ -0,0 +1 @@
+ 0.42610177, 3.3458166 , 2.3041742 ,-1.8122445 , 0.48324248,-2.813166 ,-2.951401 ,-4.1868343 , 0.0872704 , 4.8097663 , 3.0866373 ,-4.744804
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt
new file mode 100644
index 000000000..b129666a3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt
@@ -0,0 +1 @@
+ 3.8244517 , 2.7528017 , 0.9391686 ,-2.3091493 ,-0.8263007 ,-3.7062955 , 1.0708941 ,-0.14978653,-2.7624338 , 1.8035483 ,-1.8124399 ,-3.5439892
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt
new file mode 100644
index 000000000..3efd52ea6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt
@@ -0,0 +1 @@
+ 4.8002086,-2.8242967, 1.2763925,-2.814267 , 2.3917456,-4.9283977,-3.571722 , 3.9308782, 0.1240977,-2.529994 ,-4.2461286,-1.8420148
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt
new file mode 100644
index 000000000..b6e2efa3d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt
@@ -0,0 +1 @@
+-0.8596993, 4.8127713,-3.4127183, 4.2323627,-2.2201376,-1.5362649,-4.9921966, 0.9565166, 3.2879171,-1.3590081,-3.771852 ,-4.1042285
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt
new file mode 100644
index 000000000..bcf2807ba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 0.14624089, 4.7304125 , 4.833998 , 4.2321773 ,-2.0582533 ,-2.3694758 , 1.4213978 , 2.2444596 , 3.3630798 ,-0.70257574, 3.586656 ,-2.513805
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt
new file mode 100644
index 000000000..c3e32d2c5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 2.175218 , 0.02776978,-2.6291077 , 3.5350094 ,-1.2364857 ,-3.3151364 ,-0.92507887, 2.8038094 ,-1.8781518 , 3.6221995 , 2.4015775 ,-2.9217577
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt
new file mode 100644
index 000000000..a92abd4f6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt
@@ -0,0 +1 @@
+-1.0345451,-1.5055941,-4.144375 ,-4.727011 , 1.5841546, 4.5780725,-4.24402 ,-2.3966947,-3.0370803,-1.0234503,-0.2750057, 3.2965126
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt
new file mode 100644
index 000000000..2f2937fcb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt
@@ -0,0 +1 @@
+-2.4460397 , 2.6090143 , 4.1773095 , 0.11204174,-3.3053472 , 2.5160108 ,-3.0612547 , 1.0667087 , 2.8952355 , 3.842513 , 0.6790793 ,-0.33375
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt
new file mode 100644
index 000000000..d81e9c221
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt
@@ -0,0 +1 @@
+-0.510907 , 1.4089959 ,-2.1241407 , 2.4592288 ,-2.9779427 , 0.34636852, 2.2236073 ,-1.8599606 ,-2.5068367 ,-4.527614 ,-1.6316494 ,-3.9199774 ,-0.7226009 ,-2.4377425 , 2.1216922 , 3.505039 ,-3.7432935 ,-3.955653 , 3.6486173 ,-1.4690397 , 2.6959121 , 1.0808113 , 1.2353466 ,-0.22913602,-4.3915443 , 1.1245769 , 1.4626362 , 1.798673 , 1.7990736 , 0.89201635,-4.9303627 , 3.3033402 ,-4.865378 ,-4.7560496 ,-2.6321218 ,-2.4497926 ,-1.79407 ,-0.5770113 , 2.245869 , 3.885302 , 3.028755 ,-3.7664125 ,-3.6485636 ,-1.9558538 , 2.132231 ,-4.438173 ,-2.1077657 , 0.43832883, 2.14264 ,-4.680402 , 0.08914744,-2.1209 ,-4.8455005 ,-4.648564 ,-1.7070053 , 3.2453303 , 2.7448945 ,-0.36615932,-0.26296365, 2.1842651 , 0.6767488 ,-2.9756927 , 2.2691672 , 2.1817193
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt
new file mode 100644
index 000000000..c8064ca4e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 4.4700613 , 2.578246 ,-3.0349965 ,-4.3796597 , 0.7688315 , 3.1931324 ,-1.9384179 , 1.7633927 , 4.016731 , 0.82653385,-1.3487217 , 0.5983474 , 4.9796753 , 1.2518785 ,-1.5897057 , 1.3097483 ,-3.5301888 , 4.7911434 ,-3.5171971 ,-1.9102467 ,-3.3005824 , 4.6697702 ,-2.2170236 ,-3.560176 ,-0.22950156, 1.2118694 ,-2.664568 , 1.37606 ,-3.2944875 , 0.6496278 ,-2.3036664 , 0.2789595 , 4.543674 , 2.1632304 , 1.707507 , 2.5155134 , 3.8962896 , 2.404059 , 4.56469 ,-0.10736128,-1.5589205 ,-2.0748043 , 0.6820406 ,-0.18399024, 2.8496518 ,-3.3359828 , 1.9705079 ,-3.4824357 ,-1.4309742 ,-4.7938485 , 1.5998274 , 1.5979563 , 1.3703158 ,-3.112683 ,-3.5520315 ,-0.04035192, 1.5651652 , 4.972494 , 0.8159539 ,-3.4126616 ,-3.4475358 , 0.65332323,-3.6707017 , 2.280633
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt
new file mode 100644
index 000000000..1fdec4017
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 3.5354848e+00, 9.1399390e-01, 3.9115832e+00, 4.5195384e+00,-4.1488175e+00,-4.0608268e+00, 1.4575723e+00,-4.4007349e+00, 4.9681549e+00, 6.9878888e-01,-4.4128213e+00,-3.8517938e+00,-4.7306256e+00, 2.2618176e-01,-3.6951845e+00, 2.9969633e+00, 3.0128233e+00, 5.6477183e-01,-1.0598251e+00, 1.2899172e+00, 9.6092409e-01,-3.6659038e+00,-1.3825614e+00, 3.7959399e+00,-4.9993000e+00,-4.9668365e+00, 2.6209738e+00, 1.7419627e+00, 3.4296618e+00, 1.0272391e+00,-4.6021142e+00,-6.6580313e-01, 4.7928743e+00, 3.7836730e+00, 4.7099791e+00,-3.1748291e-03, 4.2090578e+00,-3.8970560e-01, 2.1450028e+00, 1.7832998e+00, 3.9223313e+00, 4.6870313e+00, 1.2538388e+00,-3.9964933e+00, 1.4849350e+00, 1.8416238e+00, 2.6485243e+00,-1.5326008e+00, 7.2043061e-01, 2.9865074e+00, 4.3521776e+00, 1.5164815e+00,-2.6253865e+00, 4.3517418e+00, 4.3981061e+00, 1.7968456e+00,-4.8398142e+00,-1.7621598e+00,-8.1946427e-01, 2.7901583e+00, 4.8448319e+00, 1.2321786e+00,-1.1610132e+00, 9.6679872e-01
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt
new file mode 100644
index 000000000..152f1eba1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt
@@ -0,0 +1 @@
+-0.20464787, 2.5710201 , 0.59214157, 4.259379 ,-3.540491 ,-4.372994 , 2.3727858 , 0.69051546,-4.4903455 ,-0.9209778 ,-4.8972993 ,-4.2514396 ,-0.5489476 ,-4.057988 ,-2.1644015 ,-3.0984209 , 0.03034865,-0.04239147, 1.883851 ,-1.9254268 , 1.2777244 , 4.0522346 , 1.5753645 ,-4.6625195 , 4.15404 , 4.426107 ,-3.6130836 , 2.3792834 , 1.5132596 ,-1.441894 , 4.829869 , 3.089136 , 0.72886735, 1.7921836 ,-1.6057556 ,-3.0386446 ,-2.1475565 ,-2.0181272 , 4.7128267 , 4.0448503 ,-2.7346356 , 4.034245 , 4.6702056 ,-4.1446853 , 3.8138201 ,-1.426579 ,-1.9305505 ,-1.629861 , 4.905659 ,-1.3258263 ,-3.6109068 ,-1.8653567 , 1.6524535 , 2.7292273 , 2.7259579 ,-0.07184646,-4.0560045 ,-1.4042319 , 2.0582678 ,-3.1527088 ,-1.2431176 , 4.3459873 , 3.0906582 , 0.15360738
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt
new file mode 100644
index 000000000..572ed4eae
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt
@@ -0,0 +1 @@
+ 2.0034928 ,-3.0825787 ,-2.5465937 , 0.08314594,-1.1672878 ,-4.022598 , 1.6787933 , 1.3834887 , 4.716868 ,-4.367728 ,-4.5804863 ,-4.5252604 , 1.3848665 ,-0.12535067, 0.46983087,-4.486054 , 4.1792865 ,-2.1861317 ,-4.8893723 , 1.5433223 , 0.93831426, 3.021679 , 1.6269827 ,-1.6597769 ,-4.2329807 ,-2.364968 ,-0.7685641 , 1.7036824 , 4.3565035 ,-0.6187156 , 2.735659 , 3.8990567 , 1.31432 , 3.974897 , 2.3083837 ,-3.358307 , 0.5860206 , 3.1305172 , 2.6267734 ,-4.908361 ,-3.1134105 , 1.256104 , 0.43383893, 3.2501204 ,-4.7082167 ,-4.090534 , 0.5735267 ,-2.4570494 ,-0.96135706,-0.11614823,-0.68806463, 4.2216 , 1.9002053 ,-1.1091975 , 2.3109403 , 1.8851153 ,-0.82943046, 2.5827515 , 2.711629 ,-4.0693617 ,-2.2042627 , 3.8568714 , 2.7913945 ,-2.9618587
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt
new file mode 100644
index 000000000..e42cbf88b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.1358833e+00, 1.7854472e+00, 4.1751757e+00, 5.5915713e-01,-2.6459083e-01,-1.7176826e+00,-1.8155930e+00, 2.8710868e+00,-2.7043006e+00, 1.0959731e+00,-2.0176995e+00,-6.5950048e-01,-3.6413522e+00,-4.1966043e+00,-2.6820884e+00,-3.6055098e+00, 3.6852844e+00, 8.9128174e-02, 1.3107824e+00,-3.6425626e+00,-3.2318896e-01, 3.6238370e+00,-4.9837337e+00,-4.0550299e+00,-1.4882606e+00, 1.5547658e+00,-1.1696080e+00, 2.1651111e+00, 4.9318314e+00,-3.5928023e+00,-1.2348548e+00,-1.7002642e+00, 1.7365140e+00,-8.8151926e-01,-4.1655774e+00,-1.0166957e+00,-3.7440193e+00, 2.8588972e+00, 4.1286149e+00,-4.9504828e+00, 4.8477168e+00,-2.2587967e+00, 2.8542519e+00,-7.9565448e-01, 6.8252671e-01, 2.5875571e-01,-6.3935977e-01,-4.8547015e+00, 4.1373856e-03,-1.3893708e+00, 8.8775367e-01, 2.1222150e-01, 3.1871333e+00, 1.3869151e+00,-3.8274391e+00, 3.2623324e+00, 7.2669631e-01, 1.0303619e+00, 8.1438148e-01, 8.1272924e-01,-2.7527118e+00, 1.8215455e+00,-1.6416427e-01, 4.9103169e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt
new file mode 100644
index 000000000..7caf8ce9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+-4.250757 , 1.4186406 , 0.63726735,-0.35924944, 1.9436699 , 3.2695885 , 3.6638293 , 4.5166173 , 1.3807241 ,-1.9112543 ,-1.9026492 ,-0.4800549 , 2.818216 ,-4.6390033 ,-3.8570547 , 3.6634028 ,-1.2112037 ,-1.3335027 , 1.3524677 , 2.7240725 ,-3.8335826 , 1.1397903 ,-3.1570992 ,-4.802078 , 3.8334577 , 0.23457901, 0.7132307 , 2.9887354 , 2.9702394 ,-1.4113717 ,-0.66712093, 0.77366674, 1.9308351 ,-0.45465755, 4.925366 , 2.4214447 , 2.8401468 , 0.49789894, 0.53141665,-2.7466767 , 0.2059374 ,-4.9661317 ,-4.1334467 , 1.6928389 ,-0.42529574, 1.1033608 , 4.275776 , 1.5063075 , 2.3528252 , 0.79505247, 3.9829993 ,-4.8472476 ,-1.2752185 , 3.7365675 , 1.976164 ,-4.742636 ,-2.7199092 ,-2.9191706 ,-3.181069 ,-4.489485 , 4.0847454 , 2.2164 , 0.9725334 ,-0.72566307
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt
new file mode 100644
index 000000000..7facffa57
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-3.8293874 ,-0.13678598,-2.5444264 , 1.654611 ,-4.3037786 ,-3.4240584 ,-4.5642533 , 4.1250315 , 1.0469195 , 4.2802887 , 3.1617825 ,-3.1706758 ,-0.99622065, 2.7707603 , 3.7494645 ,-1.4548893 , 2.328633 , 1.7976477 ,-1.2107176 ,-2.0178459 ,-0.6488357 ,-2.9393644 , 2.8918762 , 3.6192262 ,-4.1777225 , 1.3264071 , 0.32620123, 0.7890992 ,-3.304334 , 3.4893208 , 2.5354576 ,-4.7718143 , 3.8602633 , 0.4927564 , 2.2971296 ,-0.3296792 , 2.8115997 ,-0.75152504, 0.558675 ,-2.343631 , 4.650826 ,-3.0893488 , 0.8726873 , 0.24922371, 2.7634025 , 1.0358421 ,-3.862506 ,-3.169402 ,-2.5373347 , 0.9484093 , 4.1409917 ,-4.0408096 ,-2.7231216 ,-2.548547 ,-2.6315095 , 0.8164778 ,-3.017436 , 1.1860138 ,-1.8634807 , 1.8684052 , 1.8657844 , 1.7747321 ,-3.1472425 ,-1.3989028
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt
new file mode 100644
index 000000000..0be8fdd19
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-2.0492268 ,-2.2555764 ,-1.3543441 ,-3.7278662 ,-4.8601675 , 3.1095552 , 4.6319957 , 3.0211062 , 1.7870535 , 4.8839574 ,-1.3494394 , 2.635408 ,-0.24201432, 1.312397 , 0.16790341, 2.42507 ,-3.101355 , 3.1760497 ,-4.500736 ,-2.53691 , 1.064206 , 0.62096214, 2.803344 ,-4.6166744 ,-4.624786 , 3.667064 ,-1.484021 , 4.9401817 ,-3.763283 , 3.4351027 ,-2.906393 , 4.9945946 ,-3.2997096 , 3.6325612 ,-0.47211674, 0.28783202, 1.8703817 ,-4.042374 ,-3.3353784 , 4.9085765 ,-1.6753131 ,-3.4926984 ,-4.8663344 ,-4.495712 , 2.3402312 ,-1.0722051 , 0.28559962, 2.1208072 , 1.3024254 , 3.4810693 , 0.09860361, 1.695624 , 1.3901931 , 1.6858819 , 3.8231227 , 4.5972557 ,-4.6835494 , 0.5753765 ,-2.2377403 , 0.13013013,-2.1165738 ,-0.26044115,-0.653468 , 1.1010929
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt
new file mode 100644
index 000000000..7e2d618f9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+ 4.397323 ,-0.51448834, 2.5729322 ,-4.3229046 , 1.149113 ,-3.8652143 ,-1.7352968 ,-0.7575065 ,-0.41720778, 4.327346 ,-4.2363043 , 0.8653738 ,-1.7511971 ,-0.7874244 ,-4.0734816 , 2.5622475 ,-3.1229742 ,-1.1783633 , 0.4017013 ,-0.76175183,-1.058416 , 1.128772 ,-3.0143378 ,-2.6688366 ,-2.575279 ,-4.326955 , 4.175434 , 4.791393 ,-1.10654 ,-4.4417224 , 3.5057635 , 1.5339037 ,-4.0297494 ,-3.7187057 ,-0.6645762 , 4.215642 , 1.6742749 , 2.5468905 , 1.73195 ,-3.3100636 ,-4.4818826 ,-2.5627983 ,-1.4624406 , 1.2433167 ,-4.005364 ,-4.3450556 ,-1.0652863 ,-1.0240986 , 3.989825 ,-4.1690702 ,-4.595108 ,-1.1154945 , 0.65749156, 2.5127344 , 2.509761 ,-4.3936505 , 3.6513395 ,-2.3340352 ,-4.3615093 , 3.5973237 , 0.9316653 , 1.9391845 , 3.6356397 , 0.8133118
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt
new file mode 100644
index 000000000..f6444c365
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt
@@ -0,0 +1 @@
+-1.9946578,-2.9780228, 4.1516557,-3.4539075
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt
new file mode 100644
index 000000000..3bb45b23a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt
@@ -0,0 +1 @@
+-1.3113928, 2.2005532, 3.6652274, 4.487159
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt
new file mode 100644
index 000000000..735651cb2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt
@@ -0,0 +1 @@
+ 4.5642977,-1.9772011, 0.4170904,-4.8775287
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt
new file mode 100644
index 000000000..6ebe4022a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt
@@ -0,0 +1 @@
+ 0.7775621, 3.3111284, 3.5688167,-2.8435721
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt
new file mode 100644
index 000000000..cbe6a935f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.1520452, 3.960813 , 2.9887378,-3.768241
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt
new file mode 100644
index 000000000..9def1c2eb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+0.24671102,3.271825 ,3.979895 ,1.3334678
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt
new file mode 100644
index 000000000..eaec2409f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 1.9181111, 2.2396102,-2.8641696,-1.9045062
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt
new file mode 100644
index 000000000..3e05181cc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+4.751434 ,2.8798263 ,0.15149078,2.9485583
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt
new file mode 100644
index 000000000..19d95b267
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+-1.5743442 , 0.6716824 , 0.75737774,-0.27396253
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt
new file mode 100644
index 000000000..d302e07a9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-1.0539489 , 1.9595883 , 0.19975437, 2.526178
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt
new file mode 100644
index 000000000..7343be940
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt
@@ -0,0 +1 @@
+ 4.7784176 ,-3.664888 ,-3.661973 , 4.151922 ,-1.6849446 ,-3.5729008 ,-4.4134717 ,-4.4660335 ,-0.4282645 ,-3.3516212 , 4.3843856 ,-0.9578603 , 0.26670414, 3.1411405 ,-1.0374211 ,-2.5923402 ,-2.9806767 ,-0.8796363 , 2.035723 ,-2.8266547 , 2.2503827 , 1.0205121 ,-0.43321547, 1.2068397
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt
new file mode 100644
index 000000000..4d061522c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt
@@ -0,0 +1 @@
+-4.993156 , 2.1743555 ,-2.1204185 ,-4.686441 , 1.4717784 ,-3.7628727 , 2.2680001 , 0.5471788 ,-2.9423294 ,-3.7934408 ,-1.7061723 , 3.0367396 ,-3.9785733 , 3.5727022 ,-1.5245439 ,-1.426896 ,-1.4908481 ,-1.6985036 , 3.69504 ,-1.9351982 , 2.1623316 ,-4.7080107 , 4.9228272 , 0.65127385
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt
new file mode 100644
index 000000000..43cdf4782
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt
@@ -0,0 +1 @@
+-0.50205654,-2.502941 ,-3.8719609 ,-4.4488025 , 3.5040143 , 4.151995 ,-0.29484403,-0.12409137, 0.96817803, 2.830527 , 2.7696652 ,-4.286391 , 3.1336517 ,-4.016251 ,-3.8219085 , 0.12036578, 0.17878295, 1.2638323 , 1.6834463 , 0.14874293,-0.38150313, 0.8128281 ,-0.3667917 , 2.9356427
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt
new file mode 100644
index 000000000..dd4bef433
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt
@@ -0,0 +1 @@
+-0.34789565, 1.0015719 ,-0.06971588,-1.8463258 , 0.38877836, 3.7922597 ,-4.635005 , 0.09295294, 2.1624413 , 0.6197247 , 2.6534898 ,-1.1711022 , 0.2883494 , 1.5565678 , 4.760776 ,-2.0096483 ,-3.2912285 , 2.5289662 , 0.01655606, 3.8149574 ,-3.1482995 ,-0.20109762,-2.0927203 , 3.4672668
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt
new file mode 100644
index 000000000..e24f918c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.5574467 ,-3.2333457 , 0.78457445,-0.90771025,-2.122042 , 0.11078442,-4.327074 , 0.19774374, 3.3724785 ,-4.603591 , 0.577071 , 1.7609557 ,-4.16058 ,-3.5114238 ,-1.63763 , 1.1950372 , 1.5514423 , 2.041781 , 2.5683656 , 4.7605743 , 1.9907378 ,-2.6334338 , 2.421719 ,-0.28131783
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt
new file mode 100644
index 000000000..9b8e65bdb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt
@@ -0,0 +1 @@
+-0.47067887, 1.6245431 ,-3.827272 ,-0.8795752 , 4.1726565 , 0.8228656 ,-2.4443243 , 3.43631 ,-3.1509953 ,-2.5931532 ,-4.7419844 , 1.2288777 , 3.2510107 , 4.9364767 ,-4.497389 , 0.08496564
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt
new file mode 100644
index 000000000..1556a7661
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt
@@ -0,0 +1 @@
+ 3.9474714 ,-2.6625853 ,-2.283004 ,-0.02414002,-4.392698 , 4.240785 ,-2.4344695 ,-2.2256756 ,-3.9908662 ,-2.2188916 ,-0.11010418, 2.3360708 , 3.3392577 , 2.3586333 ,-0.8532365 ,-4.500675
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt
new file mode 100644
index 000000000..e655c07cf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt
@@ -0,0 +1 @@
+ 0.10326682, 2.1289074 , 3.5456502 , 0.35302642,-4.4911804 ,-2.134726 ,-2.0466588 ,-2.3322723 ,-1.3623004 ,-1.9854587 , 0.1923696 , 3.6274686 ,-1.5344565 ,-4.15489 ,-2.5438864 , 3.8615432
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt
new file mode 100644
index 000000000..80f93444f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt
@@ -0,0 +1 @@
+ 3.5030484 , 1.4227937 ,-2.9866784 ,-2.2149732 , 1.9371717 , 4.883812 , 1.8504329 ,-4.089092 , 3.9873357 ,-0.856626 , 2.1024404 ,-2.6687756 ,-3.3716505 , 0.7224773 , 3.8326802 , 0.00597815
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt
new file mode 100644
index 000000000..62f006bc3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.540293 , 0.9187398 ,-1.7232021 , 4.2609916 ,-0.36517945,-4.528093 , 2.0078566 ,-2.8224776 ,-0.52138734, 2.083479 , 4.13081 , 1.6346717 ,-1.786156 ,-1.868778 ,-2.003354 , 4.861706
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt
new file mode 100644
index 000000000..0f8cc18e3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt
@@ -0,0 +1 @@
+-2.096356 ,-2.8830805, 3.2734153, 4.75676 ,-1.4854555,-3.761438 , 4.9110246, 2.0026941,-0.5639237,-0.8497009, 0.2410297,-0.5871555, 3.2145824,-3.0885973,-3.6895127, 2.1789384
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt
new file mode 100644
index 000000000..c50bf39a9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt
@@ -0,0 +1 @@
+ 1.1109806 , 0.7826781 ,-0.74992985,-1.7466942 , 1.580436 ,-2.9916067 ,-3.5476816 , 3.8817286 , 1.6322273 ,-2.4337368 , 2.7259283 ,-3.9455266 ,-0.49857467,-0.08708442, 1.8806074 , 1.2781891
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt
new file mode 100644
index 000000000..593319fae
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt
@@ -0,0 +1 @@
+ 0.00969718,-1.6622301 ,-4.5473747 , 2.6495044 , 2.09744 ,-3.0735466 ,-1.4153806 ,-0.5514852 , 2.0267386 , 2.7606184 , 0.65523773,-4.741409 , 3.9757168 ,-1.4596124 , 0.16563408, 4.203863
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt
new file mode 100644
index 000000000..b78a8b931
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt
@@ -0,0 +1 @@
+ 0.9968256, 3.1801567, 4.973664 ,-4.5165515,-1.3277559, 2.102693 ,-4.122782 ,-4.2825947, 3.6787858,-2.7863328, 2.5955915,-3.6259568, 3.8748455,-2.6964746, 4.419685 , 1.4595481
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt
new file mode 100644
index 000000000..2ca9c88c5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt
@@ -0,0 +1 @@
+ 0.38209972,-1.798579 , 0.30720893,-2.2759287 , 2.5577447 ,-2.3818388 ,-4.9389277 ,-1.6131732 , 4.463589 , 0.99052995,-3.0197868 ,-4.8446875 ,-0.9983996 , 1.3928102 , 4.7655883 ,-4.643676
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt
new file mode 100644
index 000000000..34c8d9efa
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt
@@ -0,0 +1 @@
+ 4.8284526 ,-2.691639 ,-4.6848307 ,-2.197698 ,-1.6452677 ,-4.8613544 , 1.5453953 ,-0.39886513, 4.0380425 ,-0.04039171, 2.301464 ,-3.714465 ,-4.421984 ,-4.1743374 , 3.4737499 , 2.588904 , 3.1158638 ,-1.1720241 ,-3.5342755 ,-3.751013 , 0.01882128,-2.8355627 ,-1.4244885 , 2.297474 ,-3.1879032 ,-0.44498882,-4.940659 , 2.730317 , 2.8211846 ,-2.5893457 , 2.2638 ,-2.6992602 , 2.401231 , 1.2854915 , 2.346975 , 0.5765137 ,-3.2890596 , 2.063855 ,-4.731475 ,-0.557063 , 0.54349065,-1.5450648 ,-0.5341154 ,-3.4168484 ,-2.0516255 , 4.346786 ,-4.3981385 , 4.674038 ,-2.3534555 ,-4.851739 , 3.9623237 ,-3.9519038 ,-3.5602655 ,-2.0898452 ,-1.3825879 , 4.720879 , 4.5703683 , 1.9761374 , 3.492937 ,-2.2800248 ,-3.1707876 ,-2.384202 , 1.2742764 ,-0.76953995
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt
new file mode 100644
index 000000000..8f4f6af7f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 0.48372003,-4.9410214 ,-1.1078873 , 1.4024726 , 0.8605548 ,-3.7719595 ,-4.6056633 ,-1.6772615 , 4.8427873 , 3.5070498 , 0.9382978 , 3.5805914 ,-4.9232273 , 3.5987895 , 1.9776107 ,-2.147376 ,-0.17856362, 1.4812931 , 4.740502 , 2.6861246 ,-3.5170615 , 3.935952 ,-0.6979904 ,-4.5876417 ,-4.137302 ,-0.10453341, 1.5544755 ,-3.1888318 ,-3.6421032 , 2.1766756 ,-2.4349477 , 0.5977967 ,-0.47258878,-3.3868475 ,-2.2278457 , 4.69384 ,-2.4523015 , 1.7757872 , 1.4693154 ,-0.50499505, 3.0612497 , 2.0326838 , 3.9193847 ,-2.5797045 ,-3.61123 ,-1.6678554 ,-1.3377333 ,-4.931451 , 4.662463 ,-1.4456365 , 0.61296666, 1.3043345 , 4.7175946 , 1.1552657 , 2.8492255 , 3.9504783 , 1.7083023 , 0.593394 ,-0.42887765, 0.8986678 , 0.61605704, 0.49256468,-0.20637548,-0.72854817
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt
new file mode 100644
index 000000000..c666b1db5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt
@@ -0,0 +1 @@
+-1.2767923 , 4.635175 , 4.634353 , 2.2961338 ,-4.525523 , 4.7173486 ,-1.118744 ,-2.0162437 ,-4.089979 , 4.2400355 ,-2.121403 , 1.3392338 ,-0.14014104,-2.2286527 ,-4.3265066 ,-2.9784994 ,-0.9419822 ,-3.659519 , 3.0981843 , 0.95809066, 1.6028512 ,-2.7508621 ,-1.1240566 , 0.6601117 ,-3.3664095 , 3.5669692 ,-4.5485654 ,-4.025274 , 4.5913305 ,-4.5493083 ,-3.7018857 , 4.950167 , 4.0111384 ,-2.1159806 ,-4.297305 ,-0.74918365,-3.2612495 , 2.8310492 ,-0.7887121 , 3.533797 , 0.29970253, 0.80299735, 4.7283497 , 4.5975323 ,-0.5759047 , 1.4421924 , 1.6174877 ,-3.5346878 ,-0.3994039 , 1.3907117 , 4.46784 , 0.61307186,-2.4161408 ,-0.78354657, 0.8080768 , 2.6124284 ,-4.398268 ,-3.96591 , 0.7457658 , 0.17488185,-4.315872 , 2.2258658 ,-4.692843 , 2.3056333
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt
new file mode 100644
index 000000000..89c21282d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt
@@ -0,0 +1 @@
+ 4.4875917 , 0.81472397, 3.6098514 ,-1.0605507 , 0.16224344,-0.02751762, 4.3322067 , 1.3057764 , 4.8877854 ,-3.1575646 ,-0.27372208,-2.8751788 , 1.5704893 , 1.7208904 ,-0.90212977, 3.367112 , 1.1692609 ,-3.2632163 ,-0.9717545 , 3.848303 ,-3.8752236 , 0.07510394,-3.4816995 , 2.816553 ,-0.92604953, 3.4131455 , 1.5475662 , 0.9335745 ,-1.9984822 ,-0.88401717, 1.7502712 , 1.6837802 , 3.7524889 ,-4.0362206 , 0.10621884, 1.3092963 , 3.9410071 , 4.8334627 ,-0.5977371 ,-3.9537277 , 2.6244955 , 3.84642 , 3.866643 , 2.263317 , 4.471196 ,-2.5969682 ,-4.848496 , 3.8593578 ,-2.3364575 , 0.93461806,-4.9278994 ,-3.981684 , 0.79419816, 3.9157522 ,-1.6737558 ,-2.6708112 , 1.8820635 ,-3.9298456 , 0.36201027,-4.8502846 ,-4.7925324 , 4.528682 , 3.6746473 ,-2.756068
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt
new file mode 100644
index 000000000..b99983b43
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.157378 , 0.18785916,-2.142268 ,-3.3184152 , 4.1268454 ,-1.8841478 ,-1.2312942 ,-1.8732002 , 4.8144236 , 3.3052166 , 1.6794424 ,-4.1501184 ,-3.8177824 , 0.4225169 , 4.045759 ,-4.03065 , 0.5957578 ,-3.2322567 ,-4.649256 ,-4.227309 ,-1.7070007 , 3.4748268 , 4.4875193 ,-4.104651 ,-2.1271632 , 4.7759695 , 4.427194 , 4.3794928 , 4.3293757 ,-0.7616468 ,-2.7183442 ,-2.5630903 , 3.8545947 , 0.7578416 , 3.865139 ,-0.60673547,-4.998677 , 1.6777763 ,-3.0278416 , 4.9559903 , 2.2378786 ,-4.918824 , 2.6796322 ,-0.575707 ,-4.9119215 ,-0.26456386, 2.9720273 ,-4.655656 , 1.5877472 , 3.5130315 ,-4.509794 ,-3.7033517 ,-0.7976036 ,-0.17557316, 2.7904704 ,-4.195402 , 1.6284761 , 1.9448873 , 3.0018816 ,-2.6292274 ,-2.475803 , 1.2488724 ,-1.0502579 , 1.1644874
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt
new file mode 100644
index 000000000..31a2db03e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.1984134 , 3.7565446 , 1.3521377 ,-4.0263743 ,-1.929471 ,-3.7523155 , 1.3858393 , 4.1565247 ,-2.4681342 , 0.3598748 ,-2.0044599 , 3.7168603 , 3.6330557 , 3.0176272 ,-4.4643235 ,-0.1893698 , 3.8839848 ,-4.5703125 , 3.365731 , 4.5556674 , 4.954971 , 1.7591819 ,-0.9497736 ,-0.8527185 ,-1.1863561 ,-4.522639 ,-4.3187394 ,-3.702939 , 0.15341021, 0.8564923 , 1.9076811 , 4.2765 ,-3.7695112 ,-1.6033245 , 2.3159432 ,-1.6656336 , 1.4186145 , 4.334284 , 4.0654674 ,-4.518256 , 0.72815216, 2.5133176 ,-4.238172 , 1.0198449 ,-0.9638457 , 2.5847483 , 4.0381308 , 4.472872 , 0.11794223, 1.3358012 , 1.7975981 , 2.168553 ,-3.5131238 , 3.8412008 , 3.851232 ,-2.130775 , 3.556102 , 0.69062364,-4.668594 ,-4.619906 ,-2.87768 ,-1.0679495 ,-4.523185 , 4.184176
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt
new file mode 100644
index 000000000..2bdd62b24
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 2.9193265 , 4.315574 ,-3.7834768 , 3.4352486 , 4.1452866 ,-4.0322523 , 1.8039155 ,-4.080042 ,-1.1999705 , 4.9018297 ,-0.27180746, 1.709373 , 4.3322196 , 4.9179945 ,-3.977508 , 2.3486571 ,-0.11026379,-0.24730131, 2.3269305 , 2.1862001 , 0.92486495, 3.5822759 , 2.8370361 , 3.915398 ,-0.6385275 ,-0.02720119,-1.408676 ,-4.4472733 , 1.2901759 ,-4.60209 ,-2.9502335 ,-2.650517 ,-1.4038593 ,-2.967456 ,-2.0060933 ,-1.9603083 ,-0.4727794 ,-1.7877682 ,-3.9565926 , 1.4452418 , 2.5925353 ,-4.5134907 ,-4.195412 , 2.4681656 , 0.7140492 , 3.0753498 , 0.269442 ,-4.768041 ,-3.5370746 , 1.0272335 ,-0.7654047 ,-1.977087 , 3.1920779 , 0.37378865, 4.016262 ,-3.3201067 ,-4.7767315 ,-3.5074112 ,-4.094166 , 1.6035818 , 1.6506963 ,-3.2142932 , 4.7714067 ,-1.7164946
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt
new file mode 100644
index 000000000..8c770f61d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-1.8028042 , 1.7280815 ,-3.0464594 ,-2.810487 , 0.582805 ,-1.786865 ,-1.7263526 ,-0.36871073, 3.3955328 ,-3.9523299 ,-1.880003 , 4.9068613 , 4.6292953 , 3.9778202 ,-1.859954 , 2.8149757 , 4.5020967 ,-4.160163 , 1.9295161 ,-1.2508658 , 0.5669804 , 0.99246883,-2.4829247 , 0.88920474,-3.7942843 , 2.4626305 , 4.3087935 , 3.0680852 , 3.0893688 , 3.1640174 ,-0.41890725, 0.5377459 ,-4.0344224 ,-4.5812287 , 0.5720303 , 1.802316 ,-0.31413126, 2.9586952 , 1.1723012 ,-4.696369 ,-3.7047153 ,-1.8109767 ,-3.6122723 , 1.2727392 , 4.4057164 , 3.8347735 ,-4.739083 , 2.4655118 , 0.45258832, 4.0693913 ,-3.3486447 ,-0.64714307, 1.4990507 , 2.771129 ,-0.6109979 ,-1.0617865 , 2.0837703 ,-1.633663 , 1.8431798 ,-4.3942385 , 4.8523426 , 1.1941985 , 3.0366988 , 4.7991366
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt
new file mode 100644
index 000000000..8a4c9ebb5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-2.2375767 ,-1.1274278 , 0.18025301,-4.598087 , 1.1042122 , 3.1241179 , 1.9084688 ,-1.214722 , 4.596646 , 4.1969523 , 4.658112 , 3.143779 ,-2.6940444 ,-1.5482163 , 1.542811 ,-1.1338089 , 3.721594 , 0.24673286, 4.71102 , 2.7811737 , 1.171089 , 4.145586 ,-2.6335135 , 1.1190183 ,-3.7932637 ,-4.6548123 ,-3.10302 ,-3.392706 ,-3.856141 , 0.6618614 , 0.9668614 , 4.4293485 , 1.3193 , 4.983464 , 1.659716 ,-3.185926 , 4.8983006 , 1.6323217 , 0.18800464,-1.9328839 , 4.6031475 , 3.459718 , 4.128766 ,-3.4701612 ,-2.3796144 , 1.6752707 ,-3.6569223 , 2.922704 , 3.642789 ,-1.6817225 , 3.151759 ,-1.5401909 ,-3.8259532 , 2.4556105 ,-4.4989905 , 1.2779988 ,-0.62634754, 3.5827441 ,-0.82541114, 2.1539748 , 4.583461 , 1.2231985 ,-1.4457659 ,-2.9194565
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt
new file mode 100644
index 000000000..5110f86aa
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+-4.011289 , 0.9077414 ,-2.8109396 ,-4.33598 ,-2.6516347 ,-3.917852 , 3.2461808 , 1.7588768 ,-1.9439132 , 2.190185 , 1.5180751 , 0.3587409 ,-4.3434815 ,-4.1376143 , 3.750847 , 1.5820616 , 0.03843357, 4.71235 , 1.0592757 ,-1.7640393 , 0.44547582, 2.8698466 , 4.5816092 , 4.6638517 , 1.4207541 , 1.863644 , 3.6007912 , 0.6800818 ,-2.4884489 , 3.0707197 , 3.3961668 ,-4.331953 , 2.7828538 ,-0.16146964,-4.9070745 ,-2.9787786 , 0.3337284 ,-3.935533 ,-3.303555 , 2.376896 ,-4.7058997 ,-2.2409894 , 0.07352693,-2.6024988 , 4.9593167 ,-4.7717366 , 1.6590588 , 4.063875 ,-3.8855767 , 2.6274624 , 4.901856 , 4.157007 ,-3.292969 , 3.579326 , 3.9860668 ,-3.0936542 ,-4.7793274 , 0.71697485,-2.0354068 ,-2.1414943 , 3.6339438 , 0.10732502,-0.86129206, 4.4152017
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt
new file mode 100644
index 000000000..0a60ff544
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt
@@ -0,0 +1 @@
+-2.2074962 , 2.34192 ,-2.709864 , 1.9128946 ,-2.2117736 , 3.7492905 ,-3.846714 ,-0.14091022,-0.05969257,-0.7129768 , 3.6450825 , 2.5212145 , 0.7989227 ,-2.5739667 , 3.970536 ,-2.6660712 ,-1.8958896 , 2.300999 , 3.1438015 , 1.2797564 , 0.08288309,-4.293988 ,-2.2450752 ,-3.3478742 , 0.15746279, 4.5632443 ,-2.6077847 , 2.8849685 ,-4.3597856 , 3.5858352 ,-1.0896981 ,-2.922654 , 1.7538116 , 0.95464027,-3.7287776 ,-2.7478595 , 3.6285002 ,-0.41975695,-0.35182676, 3.6266248 , 1.3659422 , 3.8589127 ,-4.233086 , 0.8218454 ,-3.4972434 , 4.615624 , 1.9660673 ,-0.70521665,-2.0720336 , 2.7834241 ,-2.358451 , 3.1219418 , 0.5374302 , 0.93982154, 0.55175847, 1.659779 , 1.5089085 ,-3.2819571 ,-0.1463864 ,-4.5264926 , 3.4349985 , 4.2010264 ,-1.7860808 , 1.8264372 ,-1.4440862 , 1.1329567 , 1.9254624 , 1.7995164 ,-2.663578 ,-4.799549 ,-1.6716652 ,-4.423277 ,-0.26002383, 3.1433315 , 2.2171266 , 4.1119127 ,-3.1298876 , 0.6021499 , 1.9682903 ,-2.8893127 , 3.4758754 ,-4.870843 ,-4.800037 ,-4.1607656 , 0.47251 ,-3.0049233 ,-2.155954 , 2.175629 ,-2.0159044 , 4.8229475 ,-4.4880743 ,-0.984097 , 4.52838 ,-4.676001 ,-0.35018834, 3.1078057 ,-3.6997106 ,-3.1540651 , 0.45590773,-1.3007423 , 4.9675007 ,-4.5856795 , 4.144678 , 2.9743934 ,-1.6183054 ,-0.5026187 ,-0.45754507,-3.0440507 ,-4.2907186 ,-1.5810571 ,-3.3099668 ,-2.9118912 ,-2.1923656 , 2.0955439 , 2.3726344 ,-4.7950087 , 3.3958588 ,-0.55981565,-1.808636 ,-4.7620907 , 4.050268 ,-2.427531 ,-1.1281991 , 2.8483155 , 1.5577518 ,-1.1400249 , 1.5824546 ,-4.822372 ,-4.9407682 , 4.3104033 ,-0.07276537, 2.0490766 ,-2.0670223 ,-2.3535848 , 0.5239419 , 0.37653625,-3.6111376 ,-4.8165507 ,-3.231536 , 1.7372189 , 4.8433757 ,-0.72669244, 4.395995 ,-0.5532281 ,-2.7518723 ,-0.46534494, 1.229831 , 2.8872015 , 1.0584512 ,-0.83641016, 3.9595466 , 3.253532 ,-3.605762 , 0.78982335, 4.0811715 , 3.3635073 ,-1.9074551 , 1.7357349 ,-1.8057578 , 2.2644591 ,-0.91638184,-1.9505771 , 1.9017309 , 1.9173802 ,-1.2468227 , 2.0470798 ,-4.1916666 , 1.6746832 ,-3.0363586 , 4.785653 ,-0.53253794, 4.34697 , 0.61329865, 4.6044106 ,-4.347125 , 3.0415568 , 0.85138226,-3.42608 ,-3.600788 , 2.4055347 ,-0.6169718 , 4.00248 , 0.25979143,-0.88910925,-0.22139451, 4.357685 ,-3.0035346 , 3.5880077 ,-1.8679962 ,-4.3341603 ,-4.9402742 , 1.428447 ,-2.847086 , 2.1966681 ,-4.905821 ,-1.7971262 , 1.8915637 , 2.2758172 ,-1.3374038 ,-3.0997677 , 1.736807 , 4.50119 ,-2.6054833 ,-3.0651853 , 4.685231 , 0.893667 ,-1.1920239 , 4.8786464 , 2.7724717 ,-1.4378481 , 4.3327556 , 1.2405481 , 1.8002224 , 2.0331264 , 1.1052294 ,-1.3300043 , 2.8293777 , 0.7841656 , 0.47411916, 3.7697432 , 1.7962953 ,-2.545056 , 3.43001 , 4.9719415 ,-4.8534617 , 3.4633894 ,-2.566673 ,-0.80052173, 1.4536021 ,-1.5736219 ,-3.9482996 ,-1.5102588 , 1.1547778 ,-4.6677794 , 3.2731333 ,-4.059292 ,-1.1536314 , 0.84613454,-0.4756105 , 2.231564 , 2.0913293 ,-2.983188 , 3.1336026 , 1.9971098 , 4.7132673 , 0.2544511 ,-1.7493942 ,-4.0819116 , 4.5831046 , 4.983917 ,-1.4336734 , 2.770158 , 1.7522675 , 1.7341546 ,-3.7084382 , 2.820421
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt
new file mode 100644
index 000000000..243ae7ac5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 4.647416 , 4.9679117 , 3.1563618 , 2.097811 ,-4.791759 ,-0.7455675 ,-4.6186633 ,-1.3497456 ,-0.3574563 , 2.7634604 ,-4.788801 , 4.08388 , 4.967569 ,-0.91977555, 2.4698513 ,-3.888301 , 4.3066487 , 0.9423939 , 0.05353882,-3.3108146 ,-4.990351 ,-4.979936 ,-3.8345113 , 3.8786337 ,-2.8743896 , 3.924127 , 3.0873044 , 2.4747493 , 1.9881026 , 4.2850347 , 4.7974505 ,-4.713631 , 4.4727798 , 4.3543534 ,-0.58807266, 2.8979537 , 4.9961557 ,-2.088324 ,-0.11115739, 1.3920798 , 0.73489606,-0.26524037,-3.0612137 ,-2.2441452 , 2.4489026 ,-1.1380061 , 3.3269863 , 0.39811748, 0.06907854, 3.5682824 ,-3.195291 , 3.2613728 ,-4.175812 ,-1.1097213 , 0.01470342,-3.9216485 , 2.005782 ,-4.5967135 , 0.2766234 , 0.50879586, 0.9087977 ,-4.582055 , 3.9930925 ,-3.2334676 , 4.063303 , 3.280631 ,-4.08185 , 1.6128619 , 3.684287 ,-2.5079417 , 1.29454 ,-0.8991583 ,-4.7053046 , 2.5193672 , 1.4263965 , 4.2287545 , 3.2037172 , 0.24987306,-1.4175392 ,-2.7125013 ,-1.5511144 ,-2.6464562 ,-4.910153 , 4.2622967 ,-1.2177622 ,-0.2950588 , 3.0605023 , 4.2451105 ,-0.4860682 , 0.0829033 ,-1.8873469 ,-0.12465403, 3.1823132 ,-4.1410875 ,-0.57973766,-1.9339868 , 2.4527252 ,-4.1042285 , 1.2916591 ,-0.43612963,-4.5488896 ,-3.3105433 ,-4.9393787 ,-4.237338 , 3.408978 ,-3.0267413 , 0.44544792,-1.9766037 ,-1.5030789 ,-2.8587856 , 3.3850634 ,-3.9410233 ,-4.052784 , 0.5824722 ,-0.07491566, 0.57859915, 0.72390985,-1.8118609 , 0.3350256 ,-1.5373514 ,-0.03153525,-2.8978603 ,-0.86536926,-2.2109356 , 2.7301579 , 4.0579762 ,-1.4266934 , 2.51175 ,-0.6414894 ,-1.3042144 ,-2.4148684 ,-0.11735582, 4.4275966 ,-4.1753044 , 0.222479 , 0.57152927,-1.3205781 ,-1.6346083 ,-2.0534148 ,-1.5933816 , 2.4688685 , 1.7039679 , 4.0698404 ,-1.6570914 ,-4.4231663 , 1.6994585 ,-4.159936 , 1.8877546 , 2.5192266 , 2.1880152 , 0.1552987 , 2.8667293 ,-0.24308844,-2.5318801 ,-0.9884318 , 4.88736 ,-3.9448256 ,-3.54328 , 0.9494098 ,-1.4893662 , 4.6291637 ,-4.2713094 ,-1.2336831 ,-3.3976934 ,-4.9367647 , 1.7812147 ,-0.44779322, 1.8611335 , 1.6815461 , 3.4643054 ,-3.7002919 ,-2.6072085 ,-0.8358446 , 4.5414486 ,-1.4070685 ,-3.1732821 ,-1.147933 , 3.8136475 , 4.4463906 , 4.713708 ,-2.2642767 , 1.165452 , 2.0734186 , 4.6640544 , 4.5748796 ,-1.13552 , 0.0123144 , 1.2279074 , 4.5093293 ,-4.8872313 , 2.9312396 , 3.077511 ,-3.3208303 ,-3.5604925 ,-0.8427212 , 2.5253384 , 2.4704914 ,-3.271838 , 0.01163167,-0.75937307, 4.766512 , 3.2750878 , 3.757167 ,-4.989937 , 2.4368346 , 0.7778476 , 2.5371192 , 3.3019354 , 1.1787901 ,-4.6297603 ,-0.24127986,-2.6508133 , 3.2619736 , 1.6687889 ,-2.3318424 , 4.952497 ,-4.0604258 , 0.26896703,-4.83261 , 0.1513177 ,-3.3763385 , 3.9463341 ,-0.34016863,-1.4536736 ,-3.4208305 , 4.289349 , 0.5334734 ,-1.3894193 , 4.3037252 , 1.5239613 ,-0.43751678,-4.979746 , 1.5469617 ,-4.5540833 , 2.0537195 , 0.70013916,-0.1546976 , 0.7859695 , 4.67385 , 2.8416243 ,-4.6232533 , 2.8257215 ,-3.9204762 , 0.26588315,-1.0345049 , 2.9870927 ,-0.8294203 , 2.4436429 , 3.861819 ,-2.6891387 ,-0.09799351,-4.276479 , 2.288106 ,-4.5913887 ,-2.4511378 ,-2.5102944
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt
new file mode 100644
index 000000000..4a983f972
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 4.8798766e+00, 4.9243131e+00, 4.8819971e+00, 2.5457311e+00, 2.3260918e+00,-3.6621268e+00, 4.9664865e+00,-2.5198615e-01, 4.4377279e+00,-8.5797942e-01, 4.5949574e+00,-2.7489040e+00, 4.1843333e+00,-2.6608632e+00,-1.3712088e+00,-4.2638259e+00,-1.4219848e+00,-4.5535688e+00, 2.1673117e+00, 4.9565008e-01, 4.2422485e+00,-1.7685415e+00, 3.0243170e+00,-1.6031052e+00,-2.7372043e+00,-3.1805620e+00,-5.3593642e-01, 1.4621491e+00,-2.6961849e+00, 2.0458367e+00,-3.1595535e+00,-4.0961289e+00, 1.1753587e+00, 9.3906957e-01,-4.3159337e+00,-1.3725852e+00, 1.3072140e+00, 1.5345490e-01,-3.6739161e+00,-4.8221606e-01,-4.6497369e+00, 4.3760514e+00,-1.9013669e+00,-1.4918685e+00,-8.0158120e-01, 4.4854193e+00, 1.2181104e+00, 3.5900481e-02, 3.0020311e+00, 5.5376965e-01,-6.3398939e-01, 4.8934984e+00,-3.2580850e+00,-3.7261260e+00, 3.9005661e-04, 1.4454360e+00,-4.2321515e+00,-2.7544975e+00,-1.7314111e+00, 1.5144792e+00,-3.2667000e+00,-5.9683466e-01, 2.1015060e+00,-2.3534837e+00,-5.7822615e-01, 3.0378773e+00, 1.0944736e+00,-4.0058913e+00,-2.0754468e+00,-4.5949697e+00, 4.4344592e+00,-1.3073648e+00,-6.3490713e-01, 1.8540500e-01,-4.5599942e+00, 2.8582293e-01,-1.7934854e+00,-7.0607591e-01, 2.0355430e+00,-4.6090999e+00, 1.7777715e+00, 4.8398571e+00,-1.4936711e+00, 1.7854555e-01,-9.4804019e-01, 4.1062727e+00, 4.7332139e+00, 2.9031954e+00, 4.8915653e+00, 1.9979690e+00,-8.1200880e-01,-9.2868048e-01,-4.3415589e+00,-3.7189934e+00, 1.6964570e+00, 2.0999928e+00,-8.4056407e-01,-4.4467325e+00,-3.9956238e+00,-2.0207644e+00, 6.6059715e-01,-2.0528836e+00, 4.6259356e+00,-4.9360566e+00,-3.8701682e+00, 4.5166662e-01,-4.4485717e+00, 3.5869598e+00,-3.6782477e+00,-4.8435564e+00, 2.4834676e+00,-4.3192039e+00,-1.8272166e+00,-1.0558940e-01,-6.3537258e-01,-3.1022735e+00,-4.1558928e+00,-4.8120623e+00, 2.7207766e+00,-2.2537978e+00,-4.5614452e+00,-3.2558560e+00, 4.6432371e+00, 1.7005105e+00, 3.1943071e+00,-4.9606085e+00, 4.6081147e+00, 2.0349250e+00,-3.7596638e+00,-2.1179686e+00,-1.2983947e+00,-3.3367488e+00, 2.8882802e-01,-2.5551078e+00,-7.5433016e-01,-9.0093839e-01,-1.8420401e-01, 2.8669918e+00, 4.5551214e+00,-9.2204064e-02, 4.0048879e-01, 4.7526321e+00, 4.6743426e+00, 3.9137602e+00, 4.0956023e-01, 4.0974464e+00, 4.2964678e+00, 4.8832207e+00, 9.2255503e-01,-4.7051301e+00, 2.1469953e+00,-3.7833872e+00,-3.3037670e+00,-2.9071469e+00,-1.1760893e+00,-4.9690862e+00,-2.1627474e+00,-9.6528506e-01,-1.1314354e+00,-4.3561492e+00,-4.4200878e+00, 3.3768594e+00,-3.6639380e+00,-3.9702466e+00,-3.3278401e+00,-4.9813142e+00,-1.6705159e+00,-5.9130019e-01, 3.0274973e+00,-1.7484089e+00,-3.5171264e-01, 3.0469453e+00,-3.2046111e+00, 4.8803782e+00, 3.1684818e+00,-4.3790922e+00, 7.5879174e-01, 4.5218272e+00,-2.5914080e+00,-3.0454910e+00, 3.4617710e-01,-2.2997477e+00, 9.6962333e-02,-8.6270535e-01, 2.7255342e+00,-1.0079044e+00,-3.9956221e-01, 1.5535468e+00,-1.7760757e+00, 3.6640015e+00, 2.6955497e+00,-2.9474480e+00, 1.8736525e+00, 2.2837176e+00,-2.0603726e+00, 4.2487226e+00, 2.9422033e+00, 4.2111969e+00,-2.6478791e+00, 7.1064776e-01, 3.2437108e-02,-2.9048746e+00,-2.6201909e+00, 3.8928044e+00,-1.3717099e+00, 3.5247872e+00,-2.6093180e+00, 4.8967223e+00, 1.6301818e+00,-4.7146568e+00,-3.0934784e-01,-1.1863232e+00,-8.2591486e-01,-4.5406181e-02, 2.0123808e+00,-2.5105116e+00, 5.1519338e-02,-2.2054148e+00, 3.3810835e+00,-1.6602491e+00,-4.0132251e+00,-8.6186725e-01, 1.5905821e-01,-3.9856031e+00,-3.4374268e+00,-7.7546448e-01, 2.1148517e+00, 9.7378927e-01,-9.8872207e-02, 6.4961392e-01,-2.2246380e+00,-2.7060149e+00, 4.4127951e+00, 2.0425823e+00,-1.5409564e+00,-4.3303204e+00,-1.5762631e+00, 3.9626603e+00, 4.0119057e+00,-3.5879347e+00,-8.1219703e-01, 3.5089598e+00,-4.6350203e+00, 4.2187424e+00,-3.4364567e+00,-4.1966414e+00,-1.3043808e+00,-3.6387622e+00, 8.4374899e-01,-2.0959642e+00,-4.9362640e+00,-2.3911943e+00, 3.4672189e+00,-1.7254879e+00,-4.1084757e+00, 3.5413663e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt
new file mode 100644
index 000000000..eb263afcc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt
@@ -0,0 +1 @@
+-2.8310468 , 1.7082282 , 1.0476835 , 4.861815 , 0.17206787, 2.6601248 , 3.7602353 ,-2.226125 , 2.1263433 , 4.114946 ,-4.196935 , 2.1586251 ,-4.8116245 ,-0.99921423, 4.3145165 ,-0.06300683, 0.9726341 ,-2.1688504 ,-3.915935 , 1.7099698 , 4.4308853 , 4.7915993 , 1.4655458 , 1.7440084 , 0.06360465,-3.3773756 ,-1.7748362 , 0.17255747, 4.977531 , 1.8433566 ,-1.667572 , 3.150487 , 4.2519865 , 0.25772935,-3.1341906 , 4.38715 , 1.1157507 , 3.988992 ,-1.0793406 ,-3.1062634 , 1.0461776 , 2.581822 , 0.07701357, 1.7032046 ,-1.719688 ,-1.7490327 , 3.2383342 , 2.3423147 , 2.0320241 , 4.7218304 , 4.2650065 ,-4.26582 , 3.6375117 ,-2.9219685 , 0.60033566, 4.8128977 , 4.293248 ,-1.03351 , 3.0293162 ,-4.0676146 , 1.5307257 , 3.0569024 , 4.741497 ,-1.6664437 , 0.94484675, 3.4286795 ,-3.0304806 ,-2.1725938 , 3.0652328 , 3.4135303 ,-0.5874611 ,-0.0110481 , 1.0779312 ,-2.2292843 , 0.45149755, 1.6158468 ,-4.477534 , 4.4338675 ,-3.5888722 ,-2.2577443 , 2.648855 , 4.4507165 ,-4.6514072 ,-0.74817985,-3.182327 ,-1.3589416 , 0.93689245,-2.5330613 ,-3.114144 , 0.40391582, 1.1717435 , 2.394531 ,-3.4209871 ,-1.3696115 ,-3.8207295 , 2.0000634 , 2.693987 , 3.086585 , 2.903521 ,-2.149884 , 0.44542593, 1.4966886 , 0.26367816, 3.4363923 ,-2.7479713 , 0.79156274,-1.642051 , 1.0039544 ,-3.848295 , 4.3903728 , 0.33243847,-3.2833455 , 2.353057 ,-3.1497164 ,-0.14659806, 4.085301 ,-2.2828193 ,-2.6476057 , 3.6578662 , 4.410953 , 0.24965906, 4.6081295 , 0.87406915, 1.6971318 , 4.4883027 , 2.3990798 ,-2.9472117 ,-0.1569035 , 0.75272334,-1.586017 ,-3.9895067 ,-1.9349185 , 2.3048878 , 0.46985063, 2.3396938 ,-1.79147 , 0.46238607, 3.7024033 , 0.15210733, 1.5247787 ,-2.6879177 ,-3.0745072 ,-3.1346188 , 1.622853 ,-4.6427855 ,-0.68014264,-3.4689806 ,-3.458772 , 4.605259 ,-3.3465903 , 4.0176997 , 0.62666076,-0.16934091, 4.0533366 , 2.9058437 ,-2.1753752 , 2.0530415 , 1.7414057 , 1.9485176 ,-4.6453176 , 0.5048718 ,-3.9436147 ,-3.154428 ,-1.4952016 ,-1.8423259 ,-2.7371721 ,-0.37776777, 2.222526 , 0.4938752 , 4.7000623 , 4.055448 , 1.7984202 , 3.8540864 ,-4.020227 , 3.6239922 ,-0.4930054 ,-3.1983838 ,-0.59420776, 0.6061396 , 4.241923 ,-2.3298786 , 2.1898527 ,-2.1658192 ,-1.9973361 ,-3.4951637 , 0.22367272,-1.0662649 , 4.90254 , 0.9198921 ,-2.7692912 , 0.11930244, 4.3874326 , 1.6891963 ,-3.9006917 ,-4.7475104 ,-3.1887932 ,-4.247136 ,-1.0733032 ,-3.4617836 , 2.7277462 , 2.8156383 , 1.7267603 ,-2.2093835 ,-2.3587527 ,-3.2521384 ,-4.2703094 ,-1.1650047 ,-2.5386038 ,-3.226933 ,-4.110442 , 4.928302 ,-1.9791938 , 1.951922 ,-0.05581883, 1.9885951 , 1.724876 ,-1.5001607 ,-2.4702833 ,-4.046387 , 4.598048 , 2.0017822 , 2.4606009 ,-3.0076113 ,-3.630138 ,-0.58256227,-1.9988314 , 1.7240179 , 2.5316033 , 4.7843785 ,-0.27961534, 0.45785254, 3.4268925 , 2.2096367 , 2.6645563 ,-1.963684 ,-4.9165344 , 2.946002 , 1.0085366 , 3.7448997 , 4.444165 , 0.20047304, 0.72959673,-3.989426 , 4.233162 ,-2.6953456 , 1.3067822 ,-3.7083473 , 3.799648 ,-3.0094013 ,-2.4605138 ,-4.863343 , 1.2970219 ,-1.4700948 ,-4.939425 ,-0.4917321 , 0.6425007
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt
new file mode 100644
index 000000000..ff3c5ace3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt
@@ -0,0 +1 @@
+ 0.77628356, 0.8742178 , 4.3841376 , 3.9455464 , 4.8370442 ,-4.5891595 , 2.8366191 , 2.6603813 , 3.2389634 ,-1.2136962 , 0.79639715, 0.25305036, 2.9633057 ,-1.2732202 ,-0.9941141 ,-4.6739984 , 1.2970364 ,-3.9004529 ,-2.495188 , 4.454323 ,-0.41007832, 2.0026298 , 2.1573522 ,-3.6922464 ,-1.0944914 , 3.94396 , 1.9214427 ,-0.44390342,-3.8700597 ,-0.43640822, 4.6144395 ,-0.5723506 ,-3.3060803 , 0.7521421 , 1.7805135 ,-1.3054347 , 4.5463753 ,-4.731837 , 3.0060315 ,-1.1449385 ,-2.6159992 , 4.3935895 , 1.2105472 ,-0.7257971 , 0.8602565 , 4.083583 ,-3.8691707 ,-3.131461 , 4.156716 ,-1.1995463 ,-3.1585817 , 0.82309705, 2.0970995 , 3.378476 ,-2.8005354 ,-0.7037894 ,-2.3273225 ,-4.640461 ,-1.9123144 ,-2.944773 ,-2.1782146 , 2.056767 , 1.7063855 ,-4.1916785 ,-1.4816947 , 1.5674059 , 1.7946502 ,-0.04801524, 3.684821 ,-1.2795266 ,-4.2824235 , 2.3105388 ,-1.7977144 , 0.598917 ,-0.13459285, 4.3482127 ,-0.39270914, 4.3593087 , 2.331621 , 1.5738527 , 1.365369 , 2.1118524 ,-3.1903272 ,-2.6296768 ,-1.7748582 , 4.4265466 ,-0.22214267, 4.2687926 ,-4.9356604 ,-4.735486 ,-1.2183895 , 4.505148 ,-0.51000804, 1.0697384 ,-4.4798536 , 3.481103 , 4.337101 , 2.3028882 ,-0.14744945,-0.09266943,-3.966115 ,-1.8844036 , 2.3948793 , 4.349734 ,-4.433698 ,-4.424103 , 0.5152364 ,-0.5438365 , 1.8132337 ,-2.065906 , 1.0651636 , 0.08070637,-1.3664498 , 0.8790827 ,-2.8489985 , 4.6403775 ,-1.7058657 , 4.744852 , 4.629411 ,-3.8179498 , 1.7875512 , 4.804663 ,-3.7588627 , 4.5770745 , 4.234062 , 4.4133935 ,-3.8471553 , 0.28418094, 3.103688 , 0.11558467,-4.9503083 ,-0.26066843,-4.038079 ,-2.5637898 , 0.13195343,-0.48607835, 0.49593657,-3.877949 , 4.033323 , 1.6440424 ,-3.3776283 , 4.429419 ,-1.5363382 , 0.96985877, 1.8553139 ,-1.0163096 ,-4.9978313 , 4.4859114 ,-1.1065072 , 4.84827 , 1.3259351 , 1.4972471 ,-2.8903277 ,-4.209779 , 4.445652 , 1.5648273 , 2.1782582 ,-2.53759 ,-0.41737884, 4.5770307 ,-0.4921347 , 3.4396465 , 1.9875554 ,-0.740242 , 3.6160038 , 1.9656229 , 0.4855264 , 0.35450923,-3.8319254 , 1.664986 ,-4.091312 , 4.332086 ,-0.59232306, 1.4464446 ,-3.7363563 , 2.524187 ,-2.8357584 ,-2.317436 , 3.5311913 ,-4.1268234 , 1.6923355 , 1.3935364 , 4.1926684 , 1.7765065 , 1.0287559 ,-4.145092 , 1.8842214 ,-0.39877102, 1.9611474 , 2.4198146 , 0.5241378 ,-1.844806 ,-1.5277894 , 0.22524206, 2.6451344 , 4.9797215 , 3.2711246 ,-2.1342309 , 3.568039 , 0.2390882 ,-0.553521 ,-4.588598 ,-1.620724 , 4.576343 ,-2.2504685 , 3.687249 ,-4.0313754 ,-4.729034 ,-1.0298516 ,-4.223112 , 1.8052602 ,-2.5888221 ,-4.5665293 , 3.9396172 ,-4.5181932 , 4.7747293 ,-3.143026 ,-3.6682124 ,-3.2871885 , 4.826698 , 2.5052974 ,-3.8402681 , 0.15495667, 1.5239644 ,-1.3660222 , 3.5605366 , 2.8165507 ,-0.57148635,-3.5490298 ,-1.4684753 , 3.8387637 ,-3.533492 , 1.1032137 ,-3.3146498 , 2.770136 ,-1.4327627 , 2.215508 , 0.08182216, 4.6495414 ,-0.83671314,-4.074062 ,-2.9050274 , 3.668077 ,-0.64422435, 4.610212 , 3.9310832 ,-2.5132718 , 3.3636646 , 4.2034388 ,-3.6327288 ,-1.4320643 , 1.3307538 ,-3.9999893 ,-1.4370643 , 0.24813278,-3.6330073
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt
new file mode 100644
index 000000000..182eb5290
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+ 3.4251418 , 1.8884782 ,-4.061519 ,-2.1329548 , 3.851976 , 3.668601 ,-0.7418167 , 2.379966 , 0.87259316,-3.96981 ,-4.627804 ,-3.3958297 , 3.025158 ,-1.299777 ,-4.322816 , 3.9173064 ,-0.55214256, 1.9224825 ,-4.8571157 ,-4.778045 , 3.3015614 , 0.56785774, 4.7985554 ,-0.4355816 , 4.9478025 , 1.7909397 ,-0.7620663 ,-0.09947702,-3.0230513 , 1.3817457 ,-4.5706887 ,-3.4097836 ,-4.7086477 ,-3.4651487 , 1.4401027 , 4.7513933 ,-1.0788624 ,-3.4946275 , 4.607974 ,-3.1215246 ,-1.4637078 ,-3.5266285 , 2.1268125 , 0.19458893, 4.058288 , 2.2452407 , 0.7575343 , 0.12213306, 4.885321 ,-1.2482406 ,-1.1034219 ,-4.054173 ,-3.6471267 , 4.774012 , 0.9450243 ,-2.5827825 ,-2.3991685 ,-2.8482654 , 0.9294943 ,-3.1165063 ,-1.6113516 , 0.04260086, 2.0987031 , 2.1601508 , 4.9740996 , 3.7719023 , 2.6817482 , 0.42131838,-1.4525859 ,-0.5124655 , 2.6313434 , 4.5606523 ,-4.6180778 , 4.788594 ,-0.8446551 ,-1.5460813 , 1.4288356 ,-1.9648911 ,-4.9766145 ,-2.405665 ,-0.30327383, 3.5204673 ,-3.848158 ,-2.6913974 ,-2.76141 , 4.336643 , 1.4205143 , 4.5898 ,-0.93183124, 4.2199287 ,-4.216924 ,-1.0979122 ,-2.3032405 ,-3.4457245 , 2.944412 , 2.137278 , 1.0326933 , 2.3116126 , 4.2138443 , 1.8283377 , 0.28901085,-1.8877143 , 0.50673705, 1.4360197 ,-2.924691 , 0.9819095 , 3.4656513 ,-2.541582 ,-1.9102442 , 3.3629627 ,-0.9675056 , 0.5937253 ,-2.4236617 ,-1.4193813 ,-0.7552614 ,-1.7121441 , 4.39647 ,-2.2712908 ,-4.3387337 , 1.5912663 , 0.8397044 , 0.17277755, 1.5272428 , 3.571715 ,-1.4471695 , 1.8623346 ,-4.3603377 , 1.2116091 , 4.960487 , 2.3681397 , 1.2925869 ,-4.3249073 , 2.4402251 ,-1.4506928 , 3.023616 ,-3.232099 ,-4.0106025 , 3.5774167 ,-0.6024932 , 1.0183483 ,-2.8215308 , 3.7395437 , 1.9100485 , 3.892712 , 4.6569633 ,-3.251774 ,-3.6923678 ,-4.8891983 ,-3.8605282 ,-4.0293036 ,-2.8199108 , 4.1668954 , 2.1569817 ,-2.9700332 ,-0.7035824 ,-0.5176811 ,-3.1826456 ,-3.334556 , 4.9103675 , 3.8513231 , 2.8609774 , 1.1845547 ,-1.4094447 ,-2.0445833 , 0.9833705 , 4.481276 , 3.83006 , 4.6240997 ,-4.268881 ,-0.85518706,-2.2650888 , 4.032545 , 0.9495817 , 1.1353155 ,-4.6551876 ,-2.2839146 , 2.6291692 ,-3.0398533 , 0.52652216,-1.8323399 ,-0.12300313, 0.46178594, 1.120684 , 1.4657134 ,-1.9794375 , 0.08941289,-4.4573083 , 2.7112565 , 4.9227715 , 2.4938288 ,-0.37153494,-4.1604757 , 4.7694197 ,-1.3021677 , 2.454714 ,-2.4902875 ,-2.760436 , 0.05183195,-2.6723208 ,-1.1471758 ,-2.2565122 , 0.20876396,-0.7288584 , 0.4386669 , 0.7846054 , 2.7294593 ,-3.836883 , 2.7501638 ,-4.775067 ,-3.2403855 ,-2.0307286 ,-1.6403166 , 4.9471517 , 1.0428456 , 2.5126355 , 3.0090203 ,-2.3476288 ,-2.9215205 , 3.8079188 , 0.83959275, 4.2670302 , 1.2338712 , 2.7329903 , 2.2549257 , 4.882931 , 0.12783106,-2.4392028 ,-2.4590807 , 4.2874207 ,-0.08333418,-3.4244132 ,-0.2235516 ,-4.23632 ,-1.3970895 , 2.1245553 ,-2.513883 ,-2.8092728 ,-1.9194845 ,-4.1932216 ,-3.7431748 ,-1.1063433 ,-3.714845 , 1.7230242 ,-0.19162221, 1.1123114 , 3.937181 , 2.6165597 ,-0.61531806, 0.44309503,-2.9260228 ,-3.1617007 , 0.0663496 , 2.4541974 ,-2.714474 , 4.2564497 , 1.2300675
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt
new file mode 100644
index 000000000..dd8037244
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+-4.8834 ,-4.6238756 , 2.020674 ,-2.3068821 , 3.7487323 ,-0.36079448, 0.08661745, 3.423143 , 3.3073757 ,-2.709357 , 4.4810205 , 3.4159606 , 4.1597505 ,-4.249789 , 2.3782206 ,-2.02848 , 0.90137833,-0.6249625 ,-3.5300052 ,-4.1113796 ,-3.768913 ,-3.59854 , 2.0896666 , 1.7677166 ,-2.3101497 ,-1.0116942 ,-3.7846713 , 2.4777756 , 3.413987 ,-2.1964507 , 0.08637846, 0.02552292,-1.9918599 , 0.7785565 ,-4.065995 , 0.8808776 ,-2.0446506 ,-1.8421272 , 0.42566776, 3.8834689 , 4.900111 ,-3.0617309 , 4.0613194 ,-3.3601153 , 3.678536 ,-4.1136184 ,-4.2903633 ,-2.6918027 , 3.4335177 ,-3.9272869 ,-1.6882807 ,-1.9629028 , 4.2125826 , 1.6536059 ,-1.1801353 , 4.8443203 , 2.9393198 , 0.4306524 , 4.390743 ,-4.6322317 , 2.932263 , 4.140538 , 2.7385068 , 2.620753 , 2.0725663 ,-1.3642436 ,-0.48539641,-4.2409816 ,-1.5950899 ,-1.688442 , 4.4769464 ,-1.25038 , 3.462903 , 0.5011836 , 0.981037 , 0.63532305,-3.4727957 , 4.6721544 ,-3.481392 , 2.8904114 ,-1.7057139 , 1.0501702 , 3.0799537 , 1.6698593 ,-1.3895478 , 4.487443 , 2.5352533 ,-0.19357985, 0.78166926, 3.5892236 ,-4.3259463 , 2.8381345 , 1.3652785 ,-0.40142608,-0.62102544,-3.088937 ,-4.0266094 , 4.7095647 , 2.0513067 ,-1.8115149 , 0.11062156,-4.5980725 , 2.809295 , 4.2042894 ,-3.4689455 ,-1.3418434 , 2.9026117 ,-1.6125411 , 2.153075 ,-3.4445221 , 3.4869678 , 1.8746428 , 0.8482056 , 3.0525062 , 1.715966 , 1.7684505 ,-2.0022326 ,-4.3427444 ,-3.1659825 , 1.6855526 , 3.1612136 , 2.0646648 ,-3.972224 ,-2.91726 ,-3.5450957 ,-2.7226381 ,-0.3273488 ,-2.5905557 , 3.6621993 ,-4.3285728 ,-0.6200474 , 0.08522832,-2.1981175 ,-3.4179437 , 2.5989106 ,-0.8503352 ,-3.3723786 , 3.9595454 ,-0.5431398 ,-2.6962373 , 1.9689399 ,-2.8925 ,-1.2064192 , 1.606632 , 2.2728612 ,-0.1403075 ,-4.8031726 , 0.1549256 ,-1.3698703 , 0.78889227,-2.286554 , 0.96417916,-0.10438658,-3.8131578 , 2.9322996 , 2.4103441 , 4.4864798 , 0.02176606,-1.1966147 ,-3.6921146 , 4.943659 ,-1.0050472 ,-1.2238564 ,-4.5758605 ,-2.6865735 , 1.7294792 , 4.180183 , 3.157911 ,-3.581904 ,-2.9112866 , 4.1674094 , 3.2326035 ,-2.7883985 ,-0.09154221, 0.8667318 ,-4.532571 , 0.816668 , 3.1307516 ,-4.1993947 ,-1.0503744 , 0.123965 , 0.17691068,-3.1465137 ,-1.4964765 , 3.4077635 ,-0.35415363, 1.9092371 ,-4.709203 , 1.148622 , 4.4766874 ,-2.193539 ,-3.7959206 , 1.4420112 ,-2.5300896 , 4.107192 , 3.4666913 ,-2.1158516 ,-3.182484 ,-2.8406513 ,-1.9396024 ,-2.3695247 , 3.8301885 ,-1.5032169 ,-0.48879272, 0.41695955,-1.1829228 , 4.822825 ,-2.9244933 ,-3.8178608 , 2.7742817 , 2.6998327 ,-3.1187122 , 2.508593 , 1.2989064 , 2.3436947 ,-0.39074868,-3.034766 ,-1.8690065 , 4.850296 ,-2.4549792 , 4.839528 , 2.2758777 , 2.6689568 , 3.2014422 , 3.6975234 ,-3.2566156 , 3.546554 , 1.9570364 ,-2.753807 , 2.3366053 ,-4.357898 , 4.9184504 ,-1.0057111 ,-3.8582199 , 1.2416974 , 4.355522 ,-2.7863925 , 0.4679685 , 2.6850772 , 2.9984746 , 2.434312 , 2.9931593 , 2.2637212 ,-0.18371914,-4.07688 ,-2.0402577 , 0.5173147 , 0.19596666, 4.71653 , 4.291663 ,-3.3575501 ,-1.0857964 ,-0.16504912, 3.6683955 , 2.9581416 ,-1.354989
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt
new file mode 100644
index 000000000..1295bfdba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 1.2340723 ,-1.7371651 , 4.271641 ,-2.3332376 , 0.82301813,-3.4199295 ,-0.75806665,-2.2647665 , 2.613749 , 2.2658496 ,-2.1277714 ,-0.465433 ,-0.1323059 ,-1.9658507 ,-4.7780223 ,-4.392719 ,-0.81063855,-3.639001 ,-3.6398284 , 4.6309023 ,-0.17483327, 1.7921627 ,-1.1493484 ,-3.8145075 , 2.2367268 ,-0.40209827,-1.4159911 , 2.3032134 ,-4.154446 , 1.6760192 , 2.3430173 ,-1.386683 , 3.3363335 ,-2.976934 , 3.3983 ,-0.0069695 , 3.7025425 ,-1.8683758 , 0.72029626, 2.7558882 ,-4.4060984 , 2.553126 ,-3.5888321 , 1.8549582 ,-0.52258795, 4.6549897 , 0.8886988 ,-3.0400214 ,-3.6890693 , 3.6663766 ,-4.8026586 , 1.0636287 ,-2.9774907 , 0.39021772,-4.2414255 , 2.914968 ,-0.24334456,-4.0344954 ,-1.1011956 ,-3.8205252 , 0.05693521,-4.1379023 , 1.0584197 ,-4.0404034 , 4.841462 ,-1.2727845 , 2.6974225 ,-4.2507453 ,-2.7101111 ,-2.9800036 , 0.3082796 , 3.6763537 , 2.3277721 ,-4.9667864 ,-2.4498677 , 0.2704629 , 3.006634 ,-1.1129389 , 4.373073 ,-1.2066779 ,-3.1575904 ,-2.721046 ,-0.861226 , 1.7315729 , 2.255666 , 2.5448847 , 3.1268334 , 1.5189171 ,-3.1992466 , 0.607633 , 4.0749955 , 1.2546133 ,-1.5335796 ,-1.6200712 ,-3.9392874 , 1.053699 ,-0.87970537,-3.9218261 ,-2.2724128 , 0.82235074,-2.3400521 , 3.6467028 , 1.6891364 ,-1.6333519 , 2.2639709 ,-0.08272895,-3.076964 , 3.731091 , 3.7932968 , 2.496441 ,-4.12142 ,-2.0908666 ,-4.994248 ,-0.0429902 ,-4.6083336 ,-4.522535 , 4.717733 , 1.6715643 ,-4.779822 , 1.2919815 ,-4.6121325 ,-0.6206874 ,-2.6633883 ,-1.9632595 ,-3.2203329 ,-0.6556523 , 1.3083993 , 0.13287744, 4.599294 ,-1.1777852 ,-2.9159715 ,-0.25669238, 0.48217958,-3.9736347 ,-0.774503 ,-0.7264863 ,-3.0058725 ,-2.1682055 , 2.6579158 ,-4.4020653 , 3.0450368 , 1.3798735 ,-4.9858127 ,-4.5812607 ,-3.7349749 ,-4.4158583 , 1.631093 ,-3.0769646 ,-3.8406906 , 1.6544044 , 0.36895755,-1.8196682 ,-2.0880237 ,-3.708266 ,-2.0277069 , 1.0536597 ,-3.6726243 , 1.1704421 , 2.3201573 , 1.4994124 , 4.0197086 , 2.1001272 ,-0.39845964, 4.879206 ,-4.6042013 , 4.367211 , 2.2712052 , 2.7754369 ,-3.156667 , 4.349216 ,-4.111492 , 1.0267047 ,-2.3381946 , 4.8876834 , 4.876814 ,-0.28538027, 4.8861 ,-0.95963717, 0.46279734,-4.5789995 , 0.26168647,-0.8879058 , 2.4468584 , 1.3030591 , 3.7261188 , 3.9933589 , 2.4964094 ,-1.3851117 , 0.7147012 ,-3.8367457 , 0.79737735,-0.5907085 , 4.317288 , 0.7659837 ,-4.821792 ,-1.466433 ,-1.147227 ,-1.8638811 , 2.5115767 , 1.9449657 ,-2.4122007 ,-2.4968379 , 0.7738737 ,-1.4761454 , 4.131583 , 0.4211128 ,-2.4312468 ,-1.9722428 , 2.2810268 , 4.950381 ,-0.0406047 , 4.67312 , 0.66613483,-0.28880936, 3.2917845 , 1.6225572 , 4.809879 , 0.48241946,-3.654634 , 0.68542016, 1.3973923 , 3.479005 ,-1.4296091 , 0.64391786,-4.0887494 ,-2.186845 ,-4.5834355 ,-0.67726034, 2.4158256 ,-2.4787726 , 0.4353257 , 2.9205139 , 0.10488439, 2.0790074 ,-4.5518365 ,-3.3856661 , 3.940736 ,-1.7141095 ,-4.8946457 , 1.1085542 , 3.785141 ,-2.4175835 , 3.7720537 , 4.623048 , 2.2239215 , 0.11616404, 0.09229392,-3.637964 ,-2.334849 ,-0.95000714,-2.1338253 , 3.2281857 ,-4.0220475 , 4.7304025 ,-1.8075961 , 0.2428817
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt
new file mode 100644
index 000000000..378b5fea5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+ 2.4605505 ,-2.7001262 ,-4.3874917 ,-2.9867616 ,-3.4332 , 0.76675916, 3.4377892 ,-0.6712793 , 1.8018581 , 1.8148962 , 2.0353577 ,-4.766427 , 3.2487285 , 3.886249 ,-2.8867183 ,-0.7906634 ,-4.376028 ,-4.2085958 ,-0.36025277, 0.6360799 ,-4.687723 , 4.8313313 , 3.3582768 , 2.1117954 , 0.9821817 , 3.3697798 ,-1.1784939 ,-3.1590316 ,-0.24019621, 0.20640443, 1.2808957 , 2.3346424 , 2.13951 , 0.61864626, 2.4020443 ,-1.9671458 ,-1.6852348 , 0.32225233,-2.3928862 ,-4.173372 ,-2.282281 ,-1.271318 , 3.0839682 ,-4.4726086 ,-0.635177 , 3.2710915 , 3.08071 ,-0.7311931 , 2.1444874 , 0.4102332 ,-3.332888 ,-4.8965516 , 3.903695 , 1.4920163 ,-4.041926 ,-0.3941788 , 3.6352818 ,-2.098405 ,-0.9248165 , 2.6277795 , 3.225142 ,-1.4461963 ,-4.2050753 ,-0.2213572 , 1.9704323 , 3.298732 ,-4.710403 , 3.6876736 , 2.0771818 , 1.3559113 , 1.328373 ,-4.4079022 ,-3.28067 , 3.8852313 , 2.322237 , 2.3243637 ,-1.9126451 , 4.6277676 , 1.7031307 , 0.74861574,-4.688967 , 3.9351206 ,-1.8054084 , 1.5824287 , 3.5381088 , 2.4798677 ,-3.3099444 ,-3.8518245 , 1.5562242 ,-1.9466928 , 0.08375791,-0.16754703, 2.9265418 ,-1.6599798 , 2.766202 ,-2.8269696 ,-0.19389874, 2.0869334 ,-1.5073173 ,-3.2024453 ,-3.6522708 ,-4.588111 ,-2.3425827 , 4.8709297 ,-1.4231887 , 1.0590451 ,-1.6406479 , 0.37192422, 0.7313186 , 0.3865313 ,-4.2832613 , 3.9712496 , 0.07653506, 0.2593589 ,-2.6036396 ,-0.45185068, 3.6537335 ,-0.6341783 ,-0.6381408 ,-1.0992868 , 2.766365 , 4.666631 , 4.416099 ,-3.6654727 ,-4.0626607 ,-3.4928396 ,-0.6944366 , 4.869798 , 4.2240977 , 0.9655519 ,-2.5654511 , 1.3396966 ,-3.7639391 ,-1.2369057 ,-3.7242758 ,-0.5189227 , 1.6548159 ,-2.6197302 , 4.2732763 , 2.239486 ,-4.316255 , 3.2419755 ,-1.9283817 , 0.22489135, 2.6034477 , 0.15818155, 2.0811818 , 0.836994 , 2.7832468 ,-0.68581384, 0.89475006,-3.1455147 ,-4.818614 ,-4.1738377 , 0.4281551 ,-2.935886 ,-3.7582467 , 0.58168256, 0.2854076 , 1.0492616 , 2.2415884 ,-4.4923434 ,-3.2479804 , 3.8439462 , 3.9802108 ,-0.9027783 , 1.7783072 ,-2.2782066 , 4.4638705 , 4.28735 , 4.291463 , 1.1685107 , 1.2765578 ,-3.7954235 ,-3.494621 , 4.4340134 ,-3.5995178 ,-4.3025713 , 3.3037348 ,-3.6675146 ,-1.7871013 ,-1.2922373 , 0.72924066,-4.7065907 , 2.1388702 , 2.3570008 , 3.9203117 , 0.07483537,-2.8389792 ,-1.795164 ,-4.380931 , 1.3189598 , 2.4404252 , 4.4774084 ,-1.2798066 ,-4.95842 , 1.8095461 , 4.2692375 ,-2.0918155 , 0.33083543,-3.794544 , 1.4940621 ,-3.9446015 ,-0.38208306, 0.30863285,-0.6832849 ,-2.5675633 ,-4.948772 , 1.5904989 , 3.0415509 ,-4.899339 , 0.9415345 ,-0.91124976, 4.4849253 ,-3.4605968 , 1.6737833 , 1.9091597 , 1.3111106 , 2.0829957 ,-2.1308084 ,-2.912219 , 1.1306196 , 2.231948 , 4.7522073 ,-2.1438766 ,-2.1000512 ,-0.2984778 ,-1.2093959 , 2.6259391 , 1.8113437 ,-4.137133 , 2.716111 , 3.4318748 ,-0.89123845,-3.70718 , 2.453927 ,-0.22418758,-3.098459 ,-4.4986243 , 0.85048616, 2.8023102 , 3.743153 , 0.9931644 , 3.8588202 , 1.7585737 ,-4.2855363 ,-2.5475764 ,-0.83141845,-1.9358089 , 3.1711586 , 2.4221613 ,-1.881327 ,-3.7230873 ,-4.55259 ,-0.42294836, 4.64625
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt
new file mode 100644
index 000000000..339435425
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+-3.37344313e+00, 2.78325319e+00,-7.30300546e-01, 1.33456266e+00, 3.96648932e+00, 4.33421373e+00,-3.11558557e+00,-3.64659280e-02,-1.73589993e+00, 4.81018400e+00,-8.32905114e-01, 2.33330703e+00, 1.85830116e+00,-4.60395622e+00, 5.26070774e-01,-4.71355534e+00,-2.97202754e+00, 3.57638383e+00, 4.50985909e+00, 2.08423686e+00,-1.85349309e+00,-2.18306184e+00,-4.65403509e+00, 4.31280661e+00, 1.16069472e+00,-4.85344124e+00, 8.40563923e-02,-1.98723459e+00,-4.29561710e+00,-2.57372570e+00,-4.22641230e+00,-4.00811911e+00,-9.61861551e-01,-2.14665198e+00, 4.18120289e+00,-3.87826174e-01,-2.86187083e-01,-4.84979200e+00,-1.34733701e+00, 1.27489030e+00, 1.98844969e+00,-4.11230135e+00,-1.61191213e+00, 2.63515592e+00, 4.35539484e+00,-1.56582773e+00,-2.45283508e+00, 1.44556177e+00,-8.56053472e-01, 3.25111747e+00, 3.58699083e+00,-2.47732449e+00, 3.64130282e+00,-4.91288567e+00, 8.97059917e-01,-2.26010180e+00, 4.91831064e+00, 4.45047706e-01, 1.88655663e+00, 3.20642543e+00, 1.38243341e+00, 9.06112790e-01, 1.15262544e+00,-2.39862514e+00,-2.87477684e+00, 7.36831248e-01, 3.18799114e+00, 1.22698748e+00, 5.63625395e-01, 1.29130912e+00,-4.89572334e+00, 2.11258578e+00,-4.55420208e+00, 4.94569272e-01,-7.08617330e-01,-1.84863120e-01,-4.81965256e+00,-1.06512284e+00, 4.79633398e-02, 2.70429182e+00, 4.78289175e+00,-2.11806059e+00, 4.23046875e+00, 3.18022132e+00,-8.39496255e-01, 3.13150501e+00,-3.24103773e-01,-7.48505890e-01,-2.45754886e+00, 4.16639376e+00, 3.25864077e+00, 3.40006447e+00,-3.77217412e+00, 2.93266010e+00, 3.33685803e+00, 1.02347994e+00,-2.22839618e+00,-1.90375733e+00, 3.24283957e+00,-4.01684284e-01,-4.45417643e+00, 3.74440104e-01, 3.33520865e+00, 6.64106190e-01, 3.84395885e+00, 2.38586918e-01,-1.51634857e-01,-2.64977455e+00,-3.45786500e+00, 4.89002228e+00,-1.07323432e+00,-2.92749858e+00,-1.76510501e+00,-3.44604325e+00,-1.89681911e+00, 4.20239258e+00,-1.75864971e+00, 2.13181686e+00, 3.90355319e-01,-4.11911535e+00, 6.61891177e-02,-4.32988214e+00,-1.42876351e+00, 3.12163901e+00,-4.56227779e+00, 4.17938662e+00, 9.63881195e-01, 4.35952139e+00, 1.61931109e+00, 4.11196423e+00, 2.25612569e+00,-4.77538586e+00,-1.72600198e+00,-4.39411783e+00,-8.98730099e-01,-1.04562032e+00,-2.81517529e+00, 3.57167959e+00, 1.90318239e+00, 2.17302442e+00,-3.79942179e+00, 2.19838643e+00,-4.16209459e+00, 4.45025682e+00, 1.68786839e-01,-2.56879544e+00, 3.60925221e+00, 1.06542781e-01,-3.48755455e+00,-6.77028894e-01,-3.51582170e+00, 3.90697241e+00, 4.49116230e+00,-1.56180394e+00, 4.96249914e+00, 9.63374436e-01, 2.72304177e+00, 8.38046610e-01,-2.91993833e+00,-9.41783428e-01, 8.00800502e-01, 3.89176035e+00, 6.70560122e-01, 2.76782703e+00,-1.37075472e+00,-3.25303817e+00,-4.41226482e+00,-8.38777184e-01, 1.73568249e+00,-1.09438455e+00,-1.08815920e+00, 1.06787062e+00, 2.04415274e+00,-2.93027782e+00,-6.86941504e-01, 3.83109421e-01,-3.49270535e+00,-2.13225913e+00,-3.61786675e+00, 1.32213378e+00,-2.89654016e+00, 4.23944092e+00, 4.53665400e+00, 4.26081800e+00,-1.95718706e+00, 4.72295076e-01,-3.08592963e+00, 2.53354859e+00, 3.80069661e+00,-1.14408419e-01, 2.39438844e+00,-4.73618507e+00, 2.35079074e+00,-1.43686843e+00, 1.32946157e+00, 1.10381134e-01,-3.49878430e+00, 2.83181930e+00, 4.57872486e+00, 2.29953095e-01, 7.19881415e-01,-2.97208834e+00, 4.11286211e+00,-3.89149117e+00, 3.83631349e+00, 4.14627981e+00,-1.14082299e-01,-6.89825296e-01,-2.55468488e+00,-4.04466152e+00, 9.95541453e-01,-2.59181118e+00,-4.60567427e+00,-4.77339029e+00,-7.36041367e-02, 1.85957468e+00,-3.42530179e+00, 4.55782986e+00,-3.29603004e+00, 3.55632234e+00, 2.40858841e+00,-2.07399082e+00,-3.96705031e+00, 4.41718817e+00, 3.19581985e+00,-3.72379017e+00,-3.76826024e+00, 6.79764748e-01,-4.43838930e+00, 2.29627752e+00, 2.34923697e+00,-4.23308420e+00, 3.80186272e+00, 8.65862250e-01, 8.44927967e-01,-1.05974531e+00, 4.70531940e+00, 1.25060010e+00, 4.82314730e+00,-4.53093815e+00, 4.51410580e+00, 4.95166332e-01,-3.45584202e+00, 1.82002666e-03,-3.27616286e+00,-2.68104935e+00, 2.39554620e+00, 2.99364328e+00,-2.57998848e+00,-4.35891914e+00, 4.64737415e+00,-5.74958742e-01, 6.47293210e-01, 1.85961032e+00, 4.49567413e+00,-4.36166048e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt
new file mode 100644
index 000000000..2f14c1e05
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt
@@ -0,0 +1 @@
+-1.6401564 , 1.5341219 ,-1.6809373 ,-0.23482142, 1.0358882 , 4.1831236 ,-1.1044886 ,-3.6126914 , 3.4088228 , 0.7250639 , 4.935399 ,-1.0157012
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt
new file mode 100644
index 000000000..6d7e58528
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt
@@ -0,0 +1 @@
+-0.8402546,-1.1927719,-1.0746883,-4.480075 , 2.5831218, 3.5992115,-4.108747 ,-1.5075738,-2.004373 , 4.8728533,-4.5553994,-2.8098056
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt
new file mode 100644
index 000000000..b4e7cda03
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt
@@ -0,0 +1 @@
+-3.056717 , 4.2170177, 1.4303325,-4.606602 , 2.3446174, 3.184377 , 2.3996902,-2.0212016,-4.653397 , 4.8631716, 1.8641028,-2.1063576
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt
new file mode 100644
index 000000000..0bf886f56
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt
@@ -0,0 +1 @@
+ 1.9526653 ,-4.830058 , 1.2988665 , 2.9302614 ,-1.628051 , 3.9963005 , 2.2121394 ,-0.54135066, 2.6000595 , 4.6699815 , 1.7348015 ,-2.545231
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt
new file mode 100644
index 000000000..944e41e36
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.865225 , 2.0706809 , 2.0491793 , 2.2364922 ,-2.5974188 ,-1.8711575 ,-2.917702 ,-0.9958814 ,-0.18635549,-2.7875533 , 1.4581085 ,-2.5568976
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt
new file mode 100644
index 000000000..e580d6f85
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.024665 , 3.0544488,-4.5645285,-3.2134292,-2.1543078, 4.039755 ,-4.613908 , 4.2014904, 3.8222141,-4.4992657,-4.02681 ,-3.2933445
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt
new file mode 100644
index 000000000..c593dfbb6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+-2.669042 , 2.479217 , 4.691815 , 1.8187722 ,-3.7656548 ,-2.0555806 ,-2.4494352 ,-3.2394514 ,-0.38215363,-1.543695 ,-0.6927158 , 2.3534324
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt
new file mode 100644
index 000000000..14520a177
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 4.036224 ,-1.2903051 , 1.2116423 , 3.92255 ,-0.48049024,-1.0290806 ,-0.9644837 , 1.3379688 ,-1.0027533 ,-1.9611529 , 3.7190473 , 0.45794436
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt
new file mode 100644
index 000000000..2238d5e9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+ 4.560488 ,-1.2475324, 1.8892838,-2.0155866,-4.968927 , 0.3717404,-0.6095849, 3.2483344,-1.2499679, 1.4237018,-3.1225715, 3.0611598
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt
new file mode 100644
index 000000000..14a91ccc9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-1.7167594, 2.116633 ,-1.3816848,-1.7106141,-3.273076 ,-4.148302 ,-2.1654181, 0.4368236, 3.4279666, 1.2954224, 1.3004405,-4.3022
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt
new file mode 100644
index 000000000..bffcf1477
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt
@@ -0,0 +1 @@
+-2.6855478 ,-1.0063782 ,-0.05736925,-3.5160403 ,-1.1558081 , 3.615521 ,-2.8372238 ,-0.6269437 , 0.49917883,-4.913136 , 1.5265877 , 4.974778 , 3.3098187 ,-0.09241077, 3.5081398 , 0.0505144 ,-0.6729551 ,-4.4237547 ,-4.966356 , 4.141383 ,-1.2096795 ,-1.4394493 , 1.5831724 ,-2.8024888 , 2.545578 , 2.6052134 ,-0.22428347, 4.1437554 , 2.4520326 ,-3.2729409 ,-1.5977669 , 3.436161 , 1.9117191 , 2.0326712 ,-1.8667631 , 1.0792333 , 3.1582525 , 0.6821356 , 4.374157 ,-0.5893812 , 1.6538872 , 1.6130942 ,-3.498988 , 3.2384932 , 0.07478751, 3.957244 , 2.799743 ,-3.4798396
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt
new file mode 100644
index 000000000..48a56eb27
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt
@@ -0,0 +1 @@
+ 1.2590754, 4.442343 ,-4.0991793, 2.0638733,-3.4404948,-2.646043 ,-1.7430832, 4.3756185, 2.2659435, 4.9466286,-1.80044 ,-2.531546 ,-3.7237234, 4.3929725, 4.8750644, 2.4617221, 4.887943 , 1.5641859,-2.610336 ,-4.410633 ,-2.5240743,-4.5204134, 4.1221995,-2.2482948, 4.2502975, 3.3698628, 1.0758704, 4.9336023, 1.1999178,-2.875248 , 3.1566763,-0.626414 ,-4.6683826, 4.4540606,-4.2442794,-1.7926724,-2.242487 ,-0.5315291,-0.0130378,-3.6322727, 2.6875575,-2.4005275, 2.9970996, 1.0781384,-3.0705473, 4.303309 , 4.6578 ,-1.7002223
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt
new file mode 100644
index 000000000..f93010fe9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt
@@ -0,0 +1 @@
+-4.368311 , 0.13439946, 0.24416327, 1.5211624 , 1.7509546 ,-4.778414 ,-1.5761154 ,-0.8055587 , 4.025158 , 2.5964646 , 1.4061503 , 4.0578322 , 1.1699337 , 4.7441864 ,-1.3572135 ,-4.225252 , 2.578213 ,-1.1911626 ,-3.538451 , 0.177016 ,-4.8895907 ,-0.39637035,-1.802691 , 1.7761022 , 1.2734097 , 0.31743827, 2.686107 , 0.7306774 ,-4.460011 , 4.1779523 , 3.7888825 ,-1.4841243 ,-2.8362072 , 1.2245483 , 1.7972459 ,-3.4700866 , 4.320854 ,-4.5425787 ,-3.7104082 , 3.0125594 ,-2.0490885 , 2.795659 ,-1.84546 , 2.135244 ,-0.15280259, 1.4062694 ,-3.8837774 ,-0.75812423
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt
new file mode 100644
index 000000000..db2b6edd8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt
@@ -0,0 +1 @@
+-2.8243983 ,-4.4524055 ,-1.0317615 ,-1.1988745 ,-3.2368467 , 4.609625 , 4.2858357 ,-2.6587856 , 0.38659486,-3.3237243 , 3.0585716 ,-3.260465 ,-4.9664984 ,-2.1594415 ,-3.934371 , 2.1108305 ,-4.30752 , 1.8228706 ,-1.2307801 ,-2.1066875 , 2.4061344 ,-3.1168818 , 1.6208204 ,-4.468139 ,-0.02087302,-2.7939155 , 2.927627 ,-0.41147447, 0.14678593,-3.6301854 ,-3.0083933 , 1.9953763 , 2.6338673 , 1.8736236 , 2.3656332 ,-3.8076937 ,-0.29208612,-4.7813087 ,-3.6228116 , 4.764327 ,-3.261239 , 3.5934968 , 0.93079615,-0.8640369 , 3.0465791 ,-2.7058053 ,-3.1439428 , 1.197273
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt
new file mode 100644
index 000000000..fc7ec3c12
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.8762918 ,-0.23988086, 4.05123 , 4.471294 ,-1.2950295 , 4.0446515 , 1.4410477 , 4.087779 ,-4.366653 ,-1.1168864 , 0.6057049 , 2.1632779 ,-3.588556 ,-3.8958907 , 4.7070394 ,-2.9224374 ,-3.914126 , 1.7182319 ,-1.897104 ,-3.5838506 , 4.097744 ,-4.6861534 , 4.3335524 , 3.4057853 , 1.0159183 ,-2.026307 ,-3.1870387 , 2.874219 ,-4.3282895 , 3.3507135 ,-2.3833654 ,-3.4183152 , 0.15211865,-0.482133 , 1.6552299 , 2.4949796 ,-4.837721 , 3.7088242 , 1.3266064 ,-1.3987536 , 3.7350404 , 0.80048573,-4.469703 ,-1.0222132 ,-1.3030492 ,-2.8426054 , 1.4573859 ,-4.8017726
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt
new file mode 100644
index 000000000..1f2993269
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+-3.3436873 ,-0.79453826, 2.2211137 , 2.6420908 ,-1.3191302 , 1.2973647 ,-4.506594 , 4.867371 ,-4.318404 , 1.6957753 ,-4.3091793 ,-3.2230556 , 4.9175825 ,-3.1527104 ,-2.6669753 ,-2.1135337 ,-3.7701926 ,-3.358504 ,-4.419803 , 3.2045574 ,-0.5828494 ,-3.5796826 ,-4.0088696 ,-4.7178082 , 2.2726505 , 2.1860175 , 3.7198956 ,-0.5788681 ,-3.7766652 ,-0.65016747, 3.707159 ,-2.240267 , 4.5772953 ,-0.54754776, 4.7143884 ,-3.196982 ,-3.6356654 , 3.7157805 , 3.1312432 , 0.58816016, 2.1710336 ,-1.600533 ,-3.689763 , 4.322089 , 0.4816874 , 2.2769346 ,-3.9072733 ,-0.58615017
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt
new file mode 100644
index 000000000..a19ea6696
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+-1.275483 ,-3.6622071 ,-0.87433696, 0.60946655, 1.4415421 , 3.3705983 , 2.2635043 , 3.3926573 ,-0.2936643 ,-0.5169573 , 3.2535644 , 2.1269164 ,-3.4180303 , 1.0427854 ,-1.3514856 , 3.6084783 , 4.569944 ,-0.79272085, 2.9771423 ,-1.6668562 , 4.8700657 , 0.3355385 , 0.76509756, 3.5142152 ,-1.6743544 , 4.794434 ,-2.958765 ,-0.23857778, 2.4555902 , 2.459867 , 3.3922994 ,-4.350212 , 0.6286153 , 0.8139546 , 4.1676807 ,-3.3461437 , 0.69633776,-4.6548877 , 0.98267466,-4.508397 ,-1.4581255 ,-1.2289628 , 3.8701873 , 3.334336 ,-3.5611253 , 2.6133575 ,-1.0554558 ,-3.3291767
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt
new file mode 100644
index 000000000..7113eb52e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+-0.6250365 ,-4.798417 ,-4.214081 ,-3.625409 , 2.4391694 , 4.1856265 , 3.2472587 ,-3.20996 ,-2.3537548 , 1.3749354 , 2.5947835 ,-1.8891864 ,-3.612735 , 2.246563 , 1.2701501 ,-2.8927476 ,-0.71078295,-3.6037376 ,-4.5916877 , 2.0044398 , 3.4437728 ,-1.0695096 , 4.3483944 ,-3.3387017 ,-0.9384242 , 1.4229002 ,-0.6568144 , 1.1164346 , 1.7145283 ,-2.596518 , 4.6728883 , 3.4737296 , 1.7935314 , 3.1263895 , 1.3614839 ,-3.824968 ,-3.0405738 , 3.1729462 ,-4.1985774 ,-2.9489865 ,-4.2080064 , 2.0368521 ,-2.858539 ,-0.03206728,-1.1123812 , 0.2994737 , 1.6906137 ,-0.8665008
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt
new file mode 100644
index 000000000..afeb2c0e6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+-4.5279946 ,-3.4497826 ,-2.058617 ,-0.39549035,-0.26672208, 3.0173857 , 3.2430282 , 1.9996022 , 1.3895315 , 1.7620904 ,-4.9040093 ,-3.2858686 ,-2.2823575 ,-1.4176623 ,-0.537347 , 0.68219584,-3.193989 ,-3.1675165 , 0.47214374,-4.390378 ,-1.8730192 , 1.4416525 ,-3.0460286 ,-0.73547626, 1.8686327 ,-0.8146671 ,-2.0906649 , 0.01226121,-0.06992937, 0.9302521 ,-2.1858516 , 4.8370657 ,-4.1847024 , 4.4963436 ,-1.3834711 ,-1.1244944 , 0.4290957 ,-4.2681174 , 1.2978764 , 3.4149706 ,-2.7011304 ,-3.1285405 ,-3.8857136 ,-0.18625297,-0.13618916, 2.427405 ,-1.7979074 ,-1.4174187
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt
new file mode 100644
index 000000000..99c6284d6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-0.40635094,-2.485209 ,-2.9641154 , 4.09174 ,-1.9137962 ,-2.0860991 , 1.6594787 , 0.53744185, 1.7737653 ,-1.7054961 , 2.5611186 ,-1.1456238 , 2.741241 ,-2.283051 ,-4.2111306 ,-0.8722772 , 1.6465468 ,-0.61518955, 0.08495517, 3.6847656 , 3.7826371 , 2.0023444 ,-3.5326133 , 2.3723035 , 3.7383325 ,-3.3514297 , 2.031452 ,-0.7364658 ,-4.3347225 ,-2.8146286 ,-1.37377 ,-3.518721 ,-0.19657679,-1.6831368 , 1.2457223 , 0.25099897,-4.4722757 ,-4.135197 ,-0.6378818 , 3.8833187 , 1.9291897 , 2.5969315 , 2.146067 ,-2.846719 ,-2.2562532 ,-2.6856182 , 2.824374 , 2.3662992
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt
new file mode 100644
index 000000000..43cf02238
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt
@@ -0,0 +1 @@
+-3.7802553 ,-1.9713228 , 2.5057538 , 2.0533051 ,-0.4372419 , 4.400527 ,-2.53668 ,-3.636254 ,-3.6546571 , 2.1307952 , 0.81209564,-2.4923725 ,-0.32200927, 4.5844235 , 1.4608113 , 1.5916203 , 1.1521534 ,-1.6989231
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt
new file mode 100644
index 000000000..0eacf50b6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt
@@ -0,0 +1 @@
+-1.4361136 ,-4.975774 , 4.1094494 , 1.032158 ,-0.04814574,-2.422229 , 4.4187384 , 2.1794112 , 2.4763412 ,-1.2492582 ,-4.5920935 ,-4.0279813 ,-4.1276155 , 2.1920047 , 1.3363422 , 1.711838 , 1.1330673 ,-4.929005
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt
new file mode 100644
index 000000000..1cf684299
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt
@@ -0,0 +1 @@
+-1.0732957,-1.0392259, 2.789581 , 0.8985455, 3.40924 ,-4.615564 ,-2.5503623, 1.6059098,-4.5399165, 2.4623697, 4.5237975,-0.9411976, 4.4180136,-4.4564066, 3.2571127, 2.2749462,-4.565914 ,-3.9453325
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt
new file mode 100644
index 000000000..c3cfb5569
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt
@@ -0,0 +1 @@
+-4.9445686 ,-0.12239262,-2.5453374 ,-1.2110028 ,-0.79905856, 3.981249 , 2.694105 ,-3.174401 ,-0.9479676 ,-3.8547504 ,-3.9985576 ,-0.4692157 , 1.1693578 ,-3.0193985 , 3.1290145 , 3.1759324 , 2.9510665 ,-3.6862066
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt
new file mode 100644
index 000000000..82609cfc9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt
@@ -0,0 +1 @@
+ 3.335564 ,-1.8178905 ,-4.7109857 , 3.3468652 , 3.6460853 ,-0.03393465, 3.7895339 ,-4.4296284 , 1.8252304 ,-2.2677863 ,-2.6210299 , 0.12556647,-1.0245817 , 1.2620107 , 4.8219824 ,-3.124949 , 0.3166363 ,-3.4773922
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt
new file mode 100644
index 000000000..eb058a1c3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-0.55411166,-4.1992335 , 1.4317423 ,-3.7261302 , 1.151971 ,-2.117022 ,-0.7386241 , 4.654951 , 1.4869142 ,-4.6252975 ,-3.305923 , 3.632628 ,-2.6403873 ,-4.862389 , 3.477561 ,-4.9842925 ,-3.6267536 , 4.9950438
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt
new file mode 100644
index 000000000..ff15f032d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 0.18094282,-0.58095986, 1.2765085 ,-0.534363 , 4.5564513 ,-0.28305855, 0.80606604,-3.3217795 ,-0.08041744,-3.7558215 ,-0.5370528 , 1.8984528 ,-0.09462419,-0.28595117, 4.6817894 ,-4.6653147 ,-4.127137 ,-2.3407753
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt
new file mode 100644
index 000000000..e564168bf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-0.62747055, 1.4133646 ,-0.9954612 ,-4.687624 ,-2.5390003 ,-4.534569 ,-1.1943612 ,-4.830596 , 4.3214984 ,-2.4795794 , 4.166298 ,-1.4772589 ,-4.074577 , 3.2332711 ,-1.5221404 ,-1.7308865 , 0.06814837, 2.944668
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt
new file mode 100644
index 000000000..c763b6311
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-3.2136867 , 0.6229863 , 0.02772082,-0.00820862,-2.4893622 ,-0.6757174 ,-2.2024722 ,-2.0893583 , 0.33953062,-3.5438979 , 0.7000838 , 1.3219849 ,-0.02302017, 2.3125873 ,-1.5376673 ,-4.0330076 , 4.755884 , 2.729685
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt
new file mode 100644
index 000000000..12e13272d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+ 0.82922786, 4.762074 ,-3.5043278 , 2.4521468 , 2.6450796 ,-2.8606322 , 0.8321993 ,-1.4020495 ,-0.25749585, 1.0287803 ,-3.911455 ,-1.8311876 , 2.763438 , 3.8604703 ,-3.5478592 ,-4.2335987 ,-3.6402035 ,-1.8485361
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt
new file mode 100644
index 000000000..a0462c946
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt
@@ -0,0 +1 @@
+ 2.4324646 ,-0.72821015, 2.7450123 , 1.1036391 ,-0.43891 ,-4.863224 ,-3.2931955 ,-2.0340252 , 4.284326 , 0.27745003, 3.761442 ,-4.3499503 ,-4.7881107 , 4.5609903 , 4.6533604 ,-4.5404058 , 2.5523062 , 0.04669883,-0.86533225,-2.082092 , 0.83845824, 2.774215 ,-2.0169477 ,-0.49454054, 3.1055443 ,-4.750468 ,-1.055414 ,-4.213197 , 3.6063917 ,-3.1573813 ,-0.776909 ,-3.001087
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt
new file mode 100644
index 000000000..af7247264
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt
@@ -0,0 +1 @@
+-3.572596 , 3.6327808 , 4.4630327 ,-3.3366838 , 4.207919 , 2.4960644 ,-2.7017984 ,-3.7505877 ,-1.618726 , 0.26884264,-4.879002 ,-4.262543 , 3.6506212 ,-4.016184 ,-4.777153 ,-2.1727114 ,-3.4873834 ,-4.6051025 ,-4.858286 , 2.0302868 ,-2.410233 , 0.10019613, 0.63945997, 3.9416385 ,-3.2295997 , 0.905913 , 0.41592363, 2.6739645 , 2.8928993 ,-3.4646466 , 3.2302775 , 4.776366
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt
new file mode 100644
index 000000000..8eeedd0d8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt
@@ -0,0 +1 @@
+-2.461527 , 4.5492992 ,-1.7411181 ,-4.339588 ,-4.57271 ,-4.8995566 , 1.8722419 ,-1.8143005 ,-0.349418 ,-1.1475405 ,-1.5390202 ,-2.6817439 ,-2.1467986 ,-3.6956887 ,-0.28721192,-2.7982469 , 2.455128 ,-4.1546254 ,-0.569284 , 2.2394757 , 1.713712 ,-0.05896076, 4.192996 , 3.8417945 ,-1.4612933 , 2.6798608 , 1.8344553 ,-2.7644687 , 3.4822197 , 1.2855778 ,-0.9130815 , 2.66463
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt
new file mode 100644
index 000000000..b3eb330e8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt
@@ -0,0 +1 @@
+-1.4890275 ,-1.4528996 ,-2.8753834 ,-2.1400492 ,-0.05646766,-2.416613 ,-1.0417156 ,-3.1061885 , 4.8631783 ,-2.29921 ,-2.4092016 , 4.5070224 , 4.3909883 ,-1.765903 , 2.0950131 , 0.7523961 , 2.3408594 ,-0.34509352,-4.7058167 ,-4.941943 ,-2.2534447 ,-0.46839336,-1.2519743 , 1.6062143 , 4.3456235 ,-4.428032 ,-2.0671709 , 0.8194458 ,-2.7333214 , 4.9490767 ,-4.497053 , 2.786572
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt
new file mode 100644
index 000000000..8070e7337
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-1.8261101e+00, 4.3587184e+00,-2.2425966e+00, 3.2468474e+00,-4.8007107e+00, 1.8991641e+00, 2.9119995e+00,-4.8224683e+00, 1.3924009e+00, 2.0646741e+00,-3.7295690e-03, 2.2643164e-01,-1.5079597e+00, 3.5466003e+00,-4.3877802e+00, 4.6155982e+00, 1.4900422e+00,-4.9514108e+00, 3.7944238e+00,-3.1528413e+00,-4.6058831e+00, 2.6000957e+00,-1.2902383e+00, 3.6344111e+00, 3.8714981e+00,-3.8451505e-01,-4.9935651e-01,-2.1561024e+00,-1.9556242e-01, 4.7684064e+00, 8.2646644e-01, 3.4120755e+00
diff --git a/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py
index b7709812c..bdf86fe29 100755
--- a/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py
+++ b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py
@@ -39,8 +39,9 @@ for i in range(num_data):
for j in range(len(input_details)):
input_detail = input_details[j]
- input_data = np.array(
- np.random.random_sample(input_detail["shape"]), input_detail["dtype"])
+ # Generate random input [-5, 5)
+ input_data = np.array(10 * np.random.random_sample(input_detail["shape"]) - 5,
+ input_detail["dtype"])
sample.create_dataset(str(j), data=input_data)
h5_file.close()
diff --git a/compiler/record-minmax/src/HDF5Importer.h b/compiler/record-minmax/src/HDF5Importer.h
index cf6526685..9e98c7752 100644
--- a/compiler/record-minmax/src/HDF5Importer.h
+++ b/compiler/record-minmax/src/HDF5Importer.h
@@ -21,6 +21,8 @@
#include <H5Cpp.h>
+#include <stdexcept>
+
using Shape = luci_interpreter::Shape;
using DataType = luci_interpreter::DataType;
@@ -39,9 +41,12 @@ namespace record_minmax
class HDF5Importer
{
public:
- explicit HDF5Importer(const std::string &path) : _file{path, H5F_ACC_RDONLY}
+ explicit HDF5Importer(const std::string &path)
{
- // Do nothing
+ if (_file.isHdf5(path) == false)
+ throw std::runtime_error("Given data file is not HDF5");
+
+ _file = H5::H5File(path, H5F_ACC_RDONLY);
}
public:
diff --git a/compiler/souschef/include/souschef/Dims.h b/compiler/souschef/include/souschef/Dims.h
index 52c64dd47..fabbf3f95 100644
--- a/compiler/souschef/include/souschef/Dims.h
+++ b/compiler/souschef/include/souschef/Dims.h
@@ -17,6 +17,7 @@
#ifndef __SOUSCHEF_DIMS_H__
#define __SOUSCHEF_DIMS_H__
+#include <cstdint>
#include <functional>
#include <numeric>
#include <vector>
diff --git a/compiler/tf2tfliteV2/tf2tfliteV2.py b/compiler/tf2tfliteV2/tf2tfliteV2.py
index c51dabde0..3fb988102 100755
--- a/compiler/tf2tfliteV2/tf2tfliteV2.py
+++ b/compiler/tf2tfliteV2/tf2tfliteV2.py
@@ -88,8 +88,7 @@ def _get_parser():
"-I",
"--input_arrays",
type=str,
- help="Names of the input arrays, comma-separated.",
- required=True)
+ help="Names of the input arrays, comma-separated.")
parser.add_argument(
"-s",
"--input_shapes",
@@ -101,8 +100,7 @@ def _get_parser():
"-O",
"--output_arrays",
type=str,
- help="Names of the output arrays, comma-separated.",
- required=True)
+ help="Names of the output arrays, comma-separated.")
# Set default value
parser.set_defaults(model_format="graph_def")
@@ -146,6 +144,10 @@ def _parse_array(arrays, type_fn=str):
def _v1_convert(flags):
if flags.model_format == "graph_def":
+ if not flags.input_arrays:
+ raise ValueError("--input_arrays must be provided")
+ if not flags.output_arrays:
+ raise ValueError("--output_arrays must be provided")
input_shapes = None
if flags.input_shapes:
input_arrays = _parse_array(flags.input_arrays)
@@ -174,6 +176,10 @@ def _v1_convert(flags):
def _v2_convert(flags):
if flags.model_format == "graph_def":
+ if not flags.input_arrays:
+ raise ValueError("--input_arrays must be provided")
+ if not flags.output_arrays:
+ raise ValueError("--output_arrays must be provided")
file_content = open(flags.input_path, 'rb').read()
try:
graph_def = tf.compat.v1.GraphDef()
diff --git a/compiler/tflchef/core/src/Convert.cpp b/compiler/tflchef/core/src/Convert.cpp
index dc8e31db0..9602faa96 100644
--- a/compiler/tflchef/core/src/Convert.cpp
+++ b/compiler/tflchef/core/src/Convert.cpp
@@ -45,6 +45,10 @@ tflite::ActivationFunctionType as_tflite_activation(const tflchef::Activation &v
return tflite::ActivationFunctionType_RELU_N1_TO_1;
case tflchef::RELU6:
return tflite::ActivationFunctionType_RELU6;
+ case tflchef::TANH:
+ return tflite::ActivationFunctionType_TANH;
+ case tflchef::SIGN_BIT:
+ return tflite::ActivationFunctionType_SIGN_BIT;
default:
break;
}
@@ -87,3 +91,72 @@ tflite::MirrorPadMode as_tflite_mirrorpadmode(const tflchef::MirrorPadMode &valu
throw std::runtime_error{"Unknown mirrorpad mode"};
}
+
+tflite::DimensionType as_tflite_dimensiontype(const tflchef::DimensionType &value)
+{
+ switch (value)
+ {
+ case tflchef::DimensionType::DENSE:
+ return tflite::DimensionType_DENSE;
+ case tflchef::DimensionType::SPARSE_CSR:
+ return tflite::DimensionType_SPARSE_CSR;
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown dimension type");
+}
+
+tflite::SparseIndexVector as_tflite_sparse_idx_vec_type(const tflchef::SparseIndexVecType &value)
+{
+ switch (value)
+ {
+ case tflchef::SparseIndexVecType::SparseIdxVecType_NONE:
+ return tflite::SparseIndexVector_NONE;
+ case tflchef::SparseIndexVecType::INT32VEC:
+ return tflite::SparseIndexVector_Int32Vector;
+ case tflchef::SparseIndexVecType::UINT16VEC:
+ return tflite::SparseIndexVector_Uint16Vector;
+ case tflchef::SparseIndexVecType::UINT8VEC:
+ return tflite::SparseIndexVector_Uint8Vector;
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown SparseIndexVector type");
+}
+
+flatbuffers::Offset<void>
+as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb,
+ const ::tflchef::TensorSparsity_IndexVec &value)
+{
+ auto sparse_idx_type = value.type();
+
+ switch (sparse_idx_type)
+ {
+ case tflchef::SparseIndexVecType::SparseIdxVecType_NONE:
+ return flatbuffers::Offset<void>();
+ case tflchef::SparseIndexVecType::INT32VEC:
+ {
+ auto values_vec_int32 = std::vector<int32_t>{value.dim().begin(), value.dim().end()};
+ auto values_int32 = fb.CreateVector(values_vec_int32);
+ return tflite::CreateInt32Vector(fb, values_int32).Union();
+ }
+ case tflchef::SparseIndexVecType::UINT16VEC:
+ {
+ auto values_vec_uint16 = std::vector<uint16_t>{value.dim().begin(), value.dim().end()};
+ auto values_uint16 = fb.CreateVector(values_vec_uint16);
+ return tflite::CreateUint16Vector(fb, values_uint16).Union();
+ }
+ case tflchef::SparseIndexVecType::UINT8VEC:
+ {
+ auto values_vec_uint8 = std::vector<uint8_t>{value.dim().begin(), value.dim().end()};
+ auto values_uint8 = fb.CreateVector(values_vec_uint8);
+ return tflite::CreateUint8Vector(fb, values_uint8).Union();
+ }
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown SparseIndexVector type");
+}
diff --git a/compiler/tflchef/core/src/Convert.h b/compiler/tflchef/core/src/Convert.h
index b56e6ef69..45c93d229 100644
--- a/compiler/tflchef/core/src/Convert.h
+++ b/compiler/tflchef/core/src/Convert.h
@@ -28,5 +28,10 @@ tflite::Padding as_tflite_padding(const tflchef::Padding &value);
tflite::ActivationFunctionType as_tflite_activation(const tflchef::Activation &value);
tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value);
tflite::MirrorPadMode as_tflite_mirrorpadmode(const tflchef::MirrorPadMode &value);
+tflite::DimensionType as_tflite_dimensiontype(const tflchef::DimensionType &value);
+tflite::SparseIndexVector as_tflite_sparse_idx_vec_type(const tflchef::SparseIndexVecType &value);
+flatbuffers::Offset<void>
+as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb,
+ const ::tflchef::TensorSparsity_IndexVec &value);
#endif // __CONVERT_H__
diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp
index a4b435dfa..164011d68 100644
--- a/compiler/tflchef/core/src/ModelChef.cpp
+++ b/compiler/tflchef/core/src/ModelChef.cpp
@@ -376,6 +376,53 @@ template <typename T> void cook_graph(const T &graph, CookParams &cp)
quant_index = quant_builder.Finish();
}
+ flatbuffers::Offset<tflite::SparsityParameters> sparsity_index;
+
+ if (operand.has_sparsity())
+ {
+ const auto &sparsity = operand.sparsity();
+
+ // Create traversal order
+ std::vector<int> traversal_order_vec{sparsity.traversal_order().dim().begin(),
+ sparsity.traversal_order().dim().end()};
+ auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec);
+
+ // Create block map
+ std::vector<int> block_map_vec{sparsity.block_map().dim().begin(),
+ sparsity.block_map().dim().end()};
+ auto block_map = flatbuffer_builder->CreateVector(block_map_vec);
+
+ // Create dimension metadata
+ std::vector<flatbuffers::Offset<tflite::DimensionMetadata>> dim_metadata_vec;
+ auto recipe_dim_metadata = sparsity.dim_metadata();
+ for (const auto &dm : recipe_dim_metadata)
+ {
+ // Create array segments
+ auto tflite_array_segments =
+ as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_segments());
+
+ // Create array indices
+ auto tflite_array_indices =
+ as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_indices());
+
+ auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*flatbuffer_builder};
+ tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format()));
+ tflite_dim_metadata_builder.add_dense_size(dm.dense_size());
+ tflite_dim_metadata_builder.add_array_segments(tflite_array_segments);
+ tflite_dim_metadata_builder.add_array_segments_type(
+ as_tflite_sparse_idx_vec_type(dm.array_segments().type()));
+ tflite_dim_metadata_builder.add_array_indices(tflite_array_indices);
+ tflite_dim_metadata_builder.add_array_indices_type(
+ as_tflite_sparse_idx_vec_type(dm.array_indices().type()));
+ auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish();
+ dim_metadata_vec.emplace_back(tflite_dim_metadata);
+ }
+ auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec);
+
+ sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order,
+ block_map, dim_metadata);
+ }
+
// Create Tensor
tflite::TensorBuilder tensor_builder{*flatbuffer_builder};
@@ -383,8 +430,10 @@ template <typename T> void cook_graph(const T &graph, CookParams &cp)
tensor_builder.add_type(as_tflite_tensortype(operand.type()));
tensor_builder.add_buffer(buffer_index);
tensor_builder.add_name(name);
+ tensor_builder.add_is_variable(operand.is_variable());
if (operand.has_quant())
tensor_builder.add_quantization(quant_index);
+ tensor_builder.add_sparsity(sparsity_index);
// Append!
tensor_vec.emplace_back(tensor_builder.Finish());
diff --git a/compiler/tflchef/core/src/Op/Dequantize.cpp b/compiler/tflchef/core/src/Op/Dequantize.cpp
new file mode 100644
index 000000000..761d7f99e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Dequantize.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "Dequantize.h"
+
+flatbuffers::Offset<void> DequantizeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> DequantizeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new DequantizeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Dequantize.h b/compiler/tflchef/core/src/Op/Dequantize.h
new file mode 100644
index 000000000..82580560d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Dequantize.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_DEQUANTIZE_H__
+#define __OP_DEQUANTIZE_H__
+
+#include "OpChef.h"
+
+class DequantizeChef final : public OpChef
+{
+public:
+ explicit DequantizeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_DEQUANTIZE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct DequantizeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_DEQUANTIZE_H__
diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..ceabfc13c
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnidirectionalSequenceLSTM.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void>
+UnidirectionalSequenceLSTMChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_unidirectional_sequence_lstm_options());
+
+ tflite::UnidirectionalSequenceLSTMOptionsBuilder options_builder(fbb);
+ options_builder.add_fused_activation_function(
+ as_tflite_activation(operation.unidirectional_sequence_lstm_options().activation()));
+ options_builder.add_cell_clip(operation.unidirectional_sequence_lstm_options().cell_clip());
+ options_builder.add_proj_clip(operation.unidirectional_sequence_lstm_options().proj_clip());
+ options_builder.add_time_major(operation.unidirectional_sequence_lstm_options().time_major());
+ options_builder.add_asymmetric_quantize_inputs(
+ operation.unidirectional_sequence_lstm_options().asymmetric_quantize_inputs());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+UnidirectionalSequenceLSTMChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new UnidirectionalSequenceLSTMChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..6811ad378
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __OP_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "OpChef.h"
+
+class UnidirectionalSequenceLSTMChef final : public OpChef
+{
+public:
+ explicit UnidirectionalSequenceLSTMChef(const tflchef::Operation *operation)
+ : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_UnidirectionalSequenceLSTMOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct UnidirectionalSequenceLSTMChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def
index 6b242e811..f2755fc29 100644
--- a/compiler/tflchef/core/src/OpChef.def
+++ b/compiler/tflchef/core/src/OpChef.def
@@ -19,6 +19,7 @@ OP_CHEF(Conv2D, Conv2DChefFactory)
OP_CHEF(Cos, CosChefFactory)
OP_CHEF(DepthToSpace, DepthToSpaceChefFactory)
OP_CHEF(DepthwiseConv2D, DepthwiseConv2DChefFactory)
+OP_CHEF(Dequantize, DequantizeChefFactory)
OP_CHEF(Div, DivChefFactory)
OP_CHEF(ELU, ELUChefFactory)
OP_CHEF(Equal, EqualChefFactory)
@@ -105,6 +106,7 @@ OP_CHEF(Tile, TileChefFactory)
OP_CHEF(TopKV2, TopKV2ChefFactory)
OP_CHEF(Transpose, TransposeChefFactory)
OP_CHEF(TransposeConv, TransposeConvChefFactory)
+OP_CHEF(UnidirectionalSequenceLSTM, UnidirectionalSequenceLSTMChefFactory)
OP_CHEF(Unique, UniqueChefFactory)
OP_CHEF(Unpack, UnpackChefFactory)
OP_CHEF(Where, WhereChefFactory)
diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h
index 7637b1c69..36b9bdb76 100644
--- a/compiler/tflchef/core/src/OpChefs.h
+++ b/compiler/tflchef/core/src/OpChefs.h
@@ -32,6 +32,7 @@
#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"
@@ -118,6 +119,7 @@
#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"
diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto
index 9909d517a..2efb54c39 100644
--- a/compiler/tflchef/proto/tflchef.proto
+++ b/compiler/tflchef/proto/tflchef.proto
@@ -21,6 +21,18 @@ enum TensorType {
BOOL = 6;
}
+enum DimensionType {
+ DENSE = 0;
+ SPARSE_CSR = 1;
+}
+
+enum SparseIndexVecType {
+ SparseIdxVecType_NONE = 0;
+ INT32VEC = 1;
+ UINT16VEC = 2;
+ UINT8VEC = 3;
+}
+
message TensorShape {
repeated uint32 dim = 3;
}
@@ -38,12 +50,37 @@ message TensorQuantization {
optional int32 quantized_dimension = 5 [default = 0];
}
+message TensorSparsity {
+ message TraversalOrder {
+ repeated int32 dim = 1;
+ }
+ message BlockMap {
+ repeated int32 dim = 1;
+ }
+ message IndexVec {
+ repeated int32 dim = 1;
+ optional SparseIndexVecType type = 2;
+ }
+ message DimMetaData {
+ optional DimensionType format = 1;
+ optional int32 dense_size = 2;
+ optional IndexVec array_segments = 3;
+ optional IndexVec array_indices = 4;
+ }
+
+ optional TraversalOrder traversal_order = 1;
+ optional BlockMap block_map = 2;
+ repeated DimMetaData dim_metadata = 3;
+}
+
message Operand {
optional string name = 1;
optional TensorType type = 2;
optional TensorShape shape = 3;
optional TensorFiller filler = 4;
optional TensorQuantization quant = 5;
+ optional TensorSparsity sparsity = 6;
+ optional bool is_variable = 7 [default = false];
}
// This enum value corresponds to Padding in TensorFlow Lite schema
@@ -58,6 +95,8 @@ enum Activation {
RELU = 1;
RELU_N1_TO_1 = 2;
RELU6 = 3;
+ TANH = 4;
+ SIGN_BIT = 5;
}
// This enum value corresponds to MirrorPadMode in TensorFlow Lite schema
@@ -419,6 +458,14 @@ message SegmentSumOptions {
// NONE
}
+message UnidirectionalSequenceLSTMOptions {
+ optional Activation activation = 1 [default = NONE];
+ optional float cell_clip = 2 [default = 0.0];
+ optional float proj_clip = 3 [default = 0.0];
+ optional bool time_major = 4 [default = false];
+ optional bool asymmetric_quantize_inputs = 5 [default = false];
+}
+
message UniqueOptions {
optional TensorType idx_out_type = 1 [default = INT32];
}
@@ -443,6 +490,10 @@ message MatrixSetDiagOptions {
// NONE
}
+message DequantizeOptions {
+ // NONE
+}
+
message Operation {
optional string type = 1;
repeated string input = 2;
@@ -518,7 +569,7 @@ message Operation {
// SequenceRNNOptions 166
optional TopKV2Options topk_v2_options = 167;
optional LogSoftmaxOptions log_softmax_options = 168;
- // DequantizeOptions 169
+ optional DequantizeOptions dequantize_options = 169;
optional NegOptions neg_options = 170;
optional PadV2Options padv2_options = 171;
optional LessEqualOptions lessequal_options = 172;
@@ -530,7 +581,7 @@ message Operation {
// FakeQuantOptions 178
// BidirectionalSequenceLSTMOptions 179
// BidirectionalSequenceRNNOptions 180
- // UnidirectionalSequenceLSTMOptions 181
+ optional UnidirectionalSequenceLSTMOptions unidirectional_sequence_lstm_options = 181;
optional RangeOptions range_options = 182;
optional ResizeNearestNeighborOptions resize_nearest_neighbor_options = 183;
optional LeakyReluOptions leaky_relu_options = 184;
diff --git a/compiler/tflchef/tflite/src/Convert.cpp b/compiler/tflchef/tflite/src/Convert.cpp
index 3cc1c9238..29276ff94 100644
--- a/compiler/tflchef/tflite/src/Convert.cpp
+++ b/compiler/tflchef/tflite/src/Convert.cpp
@@ -55,9 +55,10 @@ tflchef::Activation as_tflchef_activation(const tflite::ActivationFunctionType t
return tflchef::RELU_N1_TO_1;
case tflite::ActivationFunctionType_RELU6:
return tflchef::RELU6;
- // TODO handle other types
- // ActivationFunctionType_TANH
- // ActivationFunctionType_SIGN_BIT
+ case tflite::ActivationFunctionType_TANH:
+ return tflchef::TANH;
+ case tflite::ActivationFunctionType_SIGN_BIT:
+ return tflchef::SIGN_BIT;
default:
throw std::runtime_error{"unsupported activation type"};
}
@@ -89,4 +90,34 @@ tflchef::MirrorPadMode as_tflchef_mirrorpadmode(const tflite::MirrorPadMode mode
}
}
+tflchef::DimensionType as_tflchef_sparse_dim_type(const tflite::DimensionType type)
+{
+ switch (type)
+ {
+ case tflite::DimensionType_DENSE:
+ return tflchef::DimensionType::DENSE;
+ case tflite::DimensionType_SPARSE_CSR:
+ return tflchef::DimensionType::SPARSE_CSR;
+ default:
+ throw std::runtime_error("unsupported sparse dimension type");
+ }
+}
+
+tflchef::SparseIndexVecType as_tflchef_sparse_idx_vec_type(const tflite::SparseIndexVector type)
+{
+ switch (type)
+ {
+ case tflite::SparseIndexVector_NONE:
+ return tflchef::SparseIndexVecType::SparseIdxVecType_NONE;
+ case tflite::SparseIndexVector_Int32Vector:
+ return tflchef::SparseIndexVecType::INT32VEC;
+ case tflite::SparseIndexVector_Uint16Vector:
+ return tflchef::SparseIndexVecType::UINT16VEC;
+ case tflite::SparseIndexVector_Uint8Vector:
+ return tflchef::SparseIndexVecType::UINT8VEC;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+}
+
} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Convert.h b/compiler/tflchef/tflite/src/Convert.h
index 770bffa4d..cf0c61550 100644
--- a/compiler/tflchef/tflite/src/Convert.h
+++ b/compiler/tflchef/tflite/src/Convert.h
@@ -28,6 +28,8 @@ tflchef::TensorType as_tflchef_type(const tflite::TensorType type);
tflchef::Activation as_tflchef_activation(const tflite::ActivationFunctionType type);
tflchef::Padding as_tflchef_padding(const tflite::Padding padding);
tflchef::MirrorPadMode as_tflchef_mirrorpadmode(const tflite::MirrorPadMode mode);
+tflchef::DimensionType as_tflchef_sparse_dim_type(const tflite::DimensionType type);
+tflchef::SparseIndexVecType as_tflchef_sparse_idx_vec_type(const tflite::SparseIndexVector type);
/**
* @brief extract buffer data to std::vector<DT>
diff --git a/compiler/tflchef/tflite/src/Op/Dequantize.cpp b/compiler/tflchef/tflite/src/Op/Dequantize.cpp
new file mode 100644
index 000000000..436a0db19
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Dequantize.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "Dequantize.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpDequantize::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpDequantize::build(const tflite::Operator *, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Dequantize");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Dequantize.h b/compiler/tflchef/tflite/src/Op/Dequantize.h
new file mode 100644
index 000000000..df1c7bbdb
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Dequantize.h
@@ -0,0 +1,39 @@
+/*
+ * 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 __TFLITE_OP_DEQUANTIZE_H__
+#define __TFLITE_OP_DEQUANTIZE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Dequantize
+ */
+class TFliteOpDequantize : public TFliteOpChef
+{
+public:
+ void filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+ tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace tflchef
+
+#endif // __TFLITE_OP_DEQUANTIZE_H__
diff --git a/compiler/tflchef/tflite/src/Op/FullyConnected.cpp b/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
index 4291c844b..1f6e73aa6 100644
--- a/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
+++ b/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
@@ -17,6 +17,7 @@
#include "FullyConnected.h"
#include "Convert.h"
+#include "FillerHelper.h"
namespace tflchef
{
@@ -24,7 +25,14 @@ namespace tflchef
void TFliteOpFullyConnected::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- // Nothing to do with filler
+ const auto &inputs = *op->inputs();
+
+ for (uint32_t idx = 1; idx < inputs.size(); idx++)
+ {
+ // optional input tensor idx has minus value.
+ if (inputs[idx] >= 0)
+ fill_tensor_to_import(inputs[idx], import);
+ }
}
tflchef::Operation *TFliteOpFullyConnected::build(const tflite::Operator *op, TFliteImport *import,
diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..c2c79285b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnidirectionalSequenceLSTM.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpUnidirectionalSequenceLSTM::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() == 24);
+
+ for (int32_t i = 0; i < inputs.size(); i++)
+ {
+ // Except for Input 0, 17 and 18.
+ // Each Input mean Input[0](=Input Tensor), Input[17](=OutputState Tensor) and
+ // Input[18](=CellState Tensor).
+ // This could be updated from previous input or User Given data, so This could not be Const
+ if (i == 0 || i == 17 || i == 18)
+ continue;
+ if (inputs[i] != -1)
+ fill_tensor_to_import(inputs[i], import);
+ }
+}
+
+tflchef::Operation *
+TFliteOpUnidirectionalSequenceLSTM::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("UnidirectionalSequenceLSTM");
+
+ auto op_options = operation->mutable_unidirectional_sequence_lstm_options();
+
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+ op_options->set_cell_clip(op_params->cell_clip());
+ op_options->set_proj_clip(op_params->proj_clip());
+ op_options->set_time_major(op_params->time_major());
+ op_options->set_asymmetric_quantize_inputs(op_params->asymmetric_quantize_inputs());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..cc4e5fb0f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h
@@ -0,0 +1,39 @@
+/*
+ * 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 __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for UnidirectionalSequenceLSTM
+ */
+class TFliteOpUnidirectionalSequenceLSTM : public TFliteOpChef
+{
+public:
+ void filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+ tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace tflchef
+
+#endif // __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/tflchef/tflite/src/RecipeChef.cpp b/compiler/tflchef/tflite/src/RecipeChef.cpp
index 088961c1c..32ac9c3f3 100644
--- a/compiler/tflchef/tflite/src/RecipeChef.cpp
+++ b/compiler/tflchef/tflite/src/RecipeChef.cpp
@@ -110,6 +110,7 @@ std::unique_ptr<ModelRecipe> generate_recipe(const tflite::Model *model)
operand->set_name(tensor_name(tensor));
operand->set_type(as_tflchef_type(tensor->type()));
+ operand->set_is_variable(tensor->is_variable());
if (tensor->shape())
{
@@ -187,6 +188,89 @@ std::unique_ptr<ModelRecipe> generate_recipe(const tflite::Model *model)
tflchef::TensorQuantization *chef_quant = operand->mutable_quant();
chef_quant->set_quantized_dimension(quant->quantized_dimension());
}
+
+ auto sparsity = tensor->sparsity();
+ if (sparsity != nullptr)
+ {
+ tflchef::TensorSparsity *chef_sparsity = operand->mutable_sparsity();
+ // traversal_order
+ auto chef_traversal_order = chef_sparsity->mutable_traversal_order();
+ for (const auto &to : *(sparsity->traversal_order()))
+ {
+ chef_traversal_order->add_dim(to);
+ }
+ // block_map
+ auto chef_block_map = chef_sparsity->mutable_block_map();
+ for (const auto &bm : *(sparsity->block_map()))
+ {
+ chef_block_map->add_dim(bm);
+ }
+ // dim_metadata
+ for (const auto &dm : *(sparsity->dim_metadata()))
+ {
+ auto chef_dm = chef_sparsity->add_dim_metadata();
+ // format
+ chef_dm->set_format(as_tflchef_sparse_dim_type(dm->format()));
+ // dense_size
+ chef_dm->set_dense_size(dm->dense_size());
+ // array_segments
+ auto chef_array_segments = chef_dm->mutable_array_segments();
+ switch (dm->array_segments_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ for (const auto &as : *(dm->array_segments_as_Int32Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ for (const auto &as : *(dm->array_segments_as_Uint16Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ for (const auto &as : *(dm->array_segments_as_Uint8Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+ // array_indices
+ auto chef_array_indices = chef_dm->mutable_array_indices();
+ switch (dm->array_indices_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ for (const auto &as : *(dm->array_indices_as_Int32Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ for (const auto &as : *(dm->array_indices_as_Uint16Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ for (const auto &as : *(dm->array_indices_as_Uint8Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+ }
+ }
}
// add all operators
diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h
index 36a010957..2e4d28051 100644
--- a/compiler/tflchef/tflite/src/TFliteOpChefs.h
+++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h
@@ -33,6 +33,7 @@
#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"
@@ -118,6 +119,7 @@
#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"
diff --git a/compiler/tflchef/tflite/src/TFliteOpRegistry.h b/compiler/tflchef/tflite/src/TFliteOpRegistry.h
index a454e98b6..9cc630a97 100644
--- a/compiler/tflchef/tflite/src/TFliteOpRegistry.h
+++ b/compiler/tflchef/tflite/src/TFliteOpRegistry.h
@@ -70,6 +70,7 @@ private:
REG_TFL_OP(COS, TFliteOpCos);
REG_TFL_OP(DEPTH_TO_SPACE, TFliteOpDepthToSpace);
REG_TFL_OP(DEPTHWISE_CONV_2D, TFliteOpDepthwiseConv2D);
+ REG_TFL_OP(DEQUANTIZE, TFliteOpDequantize);
REG_TFL_OP(DIV, TFliteOpDiv);
REG_TFL_OP(ELU, TFliteOpELU);
REG_TFL_OP(EQUAL, TFliteOpEqual);
@@ -155,6 +156,7 @@ private:
REG_TFL_OP(TOPK_V2, TFliteOpTopKV2);
REG_TFL_OP(TRANSPOSE, TFliteOpTranspose);
REG_TFL_OP(TRANSPOSE_CONV, TFliteOpTransposeConv);
+ REG_TFL_OP(UNIDIRECTIONAL_SEQUENCE_LSTM, TFliteOpUnidirectionalSequenceLSTM);
REG_TFL_OP(UNIQUE, TFliteOpUnique);
REG_TFL_OP(UNPACK, TFliteOpUnpack);
REG_TFL_OP(WHERE, TFliteOpWhere);
diff --git a/compiler/tfldump/src/Dump.cpp b/compiler/tfldump/src/Dump.cpp
index e1562d42f..8c8178f93 100644
--- a/compiler/tfldump/src/Dump.cpp
+++ b/compiler/tfldump/src/Dump.cpp
@@ -73,10 +73,34 @@ std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
return os;
}
-template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
+template <typename T>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect, uint32_t size)
+{
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << fbvect->Get(q);
+ }
+}
+
+template <>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<uint8_t> *fbvect, uint32_t size)
+{
+ assert(fbvect);
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << static_cast<uint32_t>(fbvect->Get(q));
+ }
+}
+
+template <typename T>
+std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
{
if (fbvect == nullptr)
- return;
+ return os;
bool ellipsis = (fbvect->size() > 4);
auto limit_size = ellipsis ? 4 : fbvect->size();
@@ -85,22 +109,14 @@ template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vect
{
os << "(" << fbvect->size() << ") ";
}
- for (uint32_t q = 0; q < limit_size; q++)
- {
- if (q)
- os << ", ";
- os << fbvect->Get(q);
- }
+
+ dump_fbvect(os, fbvect, limit_size);
+
if (ellipsis)
{
os << " ... ";
}
-}
-template <typename T>
-std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
-{
- dump_fbvect(os, fbvect);
return os;
}
@@ -169,8 +185,90 @@ void dump_sub_graph(std::ostream &os, tflread::Reader &reader)
os << std::endl;
}
}
+
+ if (const auto &s_params = tensor->sparsity())
+ {
+ std::string strsparsity = " Sparsity: ";
+ std::string strsindent(strsparsity.size(), ' ');
+ os << strsparsity;
+
+ if (s_params->traversal_order())
+ {
+ os << "traversal_order(" << s_params->traversal_order() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (s_params->block_map())
+ {
+ os << "block_map(" << s_params->block_map() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (const auto &dim_metadata = s_params->dim_metadata())
+ {
+ uint32_t idx = 0;
+ for (const auto &dm : *dim_metadata)
+ {
+ std::string strdm = "dim_metadata[" + std::to_string(idx++) + "]: ";
+ std::string strdm_indent = strsindent + std::string(strdm.size(), ' ');
+ os << strdm;
+
+ os << "format(" << tflite::EnumNameDimensionType(dm->format()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "dense_size(" << dm->dense_size() << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments_type("
+ << tflite::EnumNameSparseIndexVector(dm->array_segments_type()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments(";
+ switch (dm->array_segments_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ os << dm->array_segments_as_Int32Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ os << dm->array_segments_as_Uint16Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ os << dm->array_segments_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_segments");
+ }
+ os << ")" << std::endl << strdm_indent;
+
+ os << "array_indices_type(" << tflite::EnumNameSparseIndexVector(dm->array_indices_type())
+ << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_indices(";
+ switch (dm->array_indices_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ os << dm->array_indices_as_Int32Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ os << dm->array_indices_as_Uint16Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ os << dm->array_indices_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_indices");
+ }
+ os << ")" << std::endl << strsindent;
+ }
+ }
+ }
+ os << std::endl;
}
- os << std::endl;
// dump operators
os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl;
diff --git a/compiler/tfldump/src/OpPrinter.cpp b/compiler/tfldump/src/OpPrinter.cpp
index 24b9264ff..5d279632c 100644
--- a/compiler/tfldump/src/OpPrinter.cpp
+++ b/compiler/tfldump/src/OpPrinter.cpp
@@ -592,6 +592,25 @@ public:
}
};
+class UnidirectionalSequenceLSTMPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << "cell_clip(" << params->cell_clip() << ") ";
+ os << "proj_clip(" << params->proj_clip() << ") ";
+ os << "time_major(" << params->time_major() << ") ";
+ os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
class UniquePrinter : public OpPrinter
{
public:
@@ -659,6 +678,7 @@ OpPrinterRegistry::OpPrinterRegistry()
_op_map[tflite::BuiltinOperator_CONV_2D] = make_unique<Conv2DPrinter>();
_op_map[tflite::BuiltinOperator_DEPTH_TO_SPACE] = make_unique<DepthToSpacePrinter>();
_op_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>();
+ // There is no Option for DEQUANTIZE
_op_map[tflite::BuiltinOperator_DIV] = make_unique<DivPrinter>();
// There is no Option for FLOOR
// There is no Option for FLOOR_MOD
@@ -713,6 +733,8 @@ OpPrinterRegistry::OpPrinterRegistry()
_op_map[tflite::BuiltinOperator_SUM] = make_unique<ReducerPrinter>();
_op_map[tflite::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>();
// There is no Option for TOPK_V2
+ _op_map[tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] =
+ make_unique<UnidirectionalSequenceLSTMPrinter>();
_op_map[tflite::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>();
_op_map[tflite::BuiltinOperator_WHILE] = make_unique<WhilePrinter>();
_op_map[tflite::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>();
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions.h
index 680118618..56a16d4e0 100644
--- a/compiler/tflite2circle/src/BuildBuiltinOptions.h
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions.h
@@ -104,6 +104,7 @@
#include "BuildBuiltinOptions/TopKV2Options.h"
#include "BuildBuiltinOptions/TransposeOptions.h"
#include "BuildBuiltinOptions/TransposeConvOptions.h"
+#include "BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h"
#include "BuildBuiltinOptions/UniqueOptions.h"
#include "BuildBuiltinOptions/UnpackOptions.h"
#include "BuildBuiltinOptions/WhereOptions.h"
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp
new file mode 100644
index 000000000..64ceb5a74
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "UnidirectionalSequenceLSTMOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnidirectionalSequenceLSTMOptions>
+build_circle_UnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_UnidirectionalSequenceLSTMOptions();
+ circle::UnidirectionalSequenceLSTMOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ builtin_options_builder.add_cell_clip(tflite_builtin_options->cell_clip());
+ builtin_options_builder.add_proj_clip(tflite_builtin_options->proj_clip());
+ builtin_options_builder.add_time_major(tflite_builtin_options->time_major());
+ builtin_options_builder.add_asymmetric_quantize_inputs(
+ tflite_builtin_options->asymmetric_quantize_inputs());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h
new file mode 100644
index 000000000..2be0efbc2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h
@@ -0,0 +1,32 @@
+/*
+ * 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 __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
+#define __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnidirectionalSequenceLSTMOptions>
+build_circle_UnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp
index 14c44cb36..a95c37089 100644
--- a/compiler/tflite2circle/src/CircleModel.cpp
+++ b/compiler/tflite2circle/src/CircleModel.cpp
@@ -152,14 +152,14 @@ Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_fla
// array_segments
auto tflite_array_segments_type = it->array_segments_type();
auto circle_array_segments =
- get_circle_sparse_index_vector(*fb, it, tflite_array_segments_type);
+ get_circle_sparse_index_vector(*fb, it->array_segments(), tflite_array_segments_type);
auto circle_array_segments_type =
get_circle_sparse_index_vector_type(tflite_array_segments_type);
// array_indices
auto tflite_array_indices_type = it->array_indices_type();
auto circle_array_indices =
- get_circle_sparse_index_vector(*fb, it, tflite_array_indices_type);
+ get_circle_sparse_index_vector(*fb, it->array_indices(), tflite_array_indices_type);
auto circle_array_indices_type =
get_circle_sparse_index_vector_type(tflite_array_indices_type);
@@ -218,41 +218,44 @@ Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_fla
std::vector<flatbuffers::Offset<circle::Operator>> operator_vec;
auto tflite_operators = it_sg->operators();
- for (auto it : *tflite_operators)
+ if (tflite_operators != nullptr)
{
- // inputs
- std::vector<int32_t> input_vec{it->inputs()->begin(), it->inputs()->end()};
- auto circle_inputs = fb->CreateVector(input_vec);
- // outputs
- std::vector<int32_t> output_vec{it->outputs()->begin(), it->outputs()->end()};
- auto circle_outputs = fb->CreateVector(output_vec);
- // builtin options
- auto circle_builtin_options = get_circle_builtin_options(*fb, it);
- auto circle_builtin_options_type = get_circle_builtin_options_type(it);
- // custom options
- flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
- if (it->custom_options())
+ for (auto it : *tflite_operators)
{
- std::vector<uint8_t> custom_options_vec{it->custom_options()->begin(),
- it->custom_options()->end()};
- circle_custom_options = fb->CreateVector(custom_options_vec);
+ // inputs
+ std::vector<int32_t> input_vec{it->inputs()->begin(), it->inputs()->end()};
+ auto circle_inputs = fb->CreateVector(input_vec);
+ // outputs
+ std::vector<int32_t> output_vec{it->outputs()->begin(), it->outputs()->end()};
+ auto circle_outputs = fb->CreateVector(output_vec);
+ // builtin options
+ auto circle_builtin_options = get_circle_builtin_options(*fb, it);
+ auto circle_builtin_options_type = get_circle_builtin_options_type(it);
+ // custom options
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
+ if (it->custom_options())
+ {
+ std::vector<uint8_t> custom_options_vec{it->custom_options()->begin(),
+ it->custom_options()->end()};
+ circle_custom_options = fb->CreateVector(custom_options_vec);
+ }
+ // custom options format
+ // TODO Make get_circle_custom_options_format
+ assert(it->custom_options_format() == tflite::CustomOptionsFormat_FLEXBUFFERS);
+ auto circle_custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS;
+
+ circle::OperatorBuilder operator_builder{*fb};
+ operator_builder.add_opcode_index(it->opcode_index());
+ operator_builder.add_inputs(circle_inputs);
+ operator_builder.add_outputs(circle_outputs);
+ operator_builder.add_builtin_options(circle_builtin_options);
+ operator_builder.add_builtin_options_type(circle_builtin_options_type);
+ operator_builder.add_custom_options(circle_custom_options);
+ operator_builder.add_custom_options_format(circle_custom_options_format);
+ // TODO mutating_variable_inputs
+ auto opeartor = operator_builder.Finish();
+ operator_vec.emplace_back(opeartor);
}
- // custom options format
- // TODO Make get_circle_custom_options_format
- assert(it->custom_options_format() == tflite::CustomOptionsFormat_FLEXBUFFERS);
- auto circle_custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS;
-
- circle::OperatorBuilder operator_builder{*fb};
- operator_builder.add_opcode_index(it->opcode_index());
- operator_builder.add_inputs(circle_inputs);
- operator_builder.add_outputs(circle_outputs);
- operator_builder.add_builtin_options(circle_builtin_options);
- operator_builder.add_builtin_options_type(circle_builtin_options_type);
- operator_builder.add_custom_options(circle_custom_options);
- operator_builder.add_custom_options_format(circle_custom_options_format);
- // TODO mutating_variable_inputs
- auto opeartor = operator_builder.Finish();
- operator_vec.emplace_back(opeartor);
}
auto circle_operators = fb->CreateVector(operator_vec);
diff --git a/compiler/tflite2circle/src/DataLookup.cpp b/compiler/tflite2circle/src/DataLookup.cpp
index 75504b062..f8dd75f4c 100644
--- a/compiler/tflite2circle/src/DataLookup.cpp
+++ b/compiler/tflite2circle/src/DataLookup.cpp
@@ -137,8 +137,7 @@ circle::DimensionType get_circle_dimension_type(tflite::DimensionType tfl_dim_ty
}
flatbuffers::Offset<void>
-get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
- const tflite::DimensionMetadata *dm,
+get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *v_array,
const tflite::SparseIndexVector &tfl_sparse_index_vector_type)
{
switch (tfl_sparse_index_vector_type)
@@ -147,9 +146,9 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
return flatbuffers::Offset<void>();
case tflite::SparseIndexVector_Int32Vector:
{
+ const tflite::Int32Vector *i32_array = static_cast<const tflite::Int32Vector *>(v_array);
auto values_vec_int32 =
- std::vector<int32_t>{dm->array_segments_as_Int32Vector()->values()->begin(),
- dm->array_segments_as_Int32Vector()->values()->end()};
+ std::vector<int32_t>{i32_array->values()->begin(), i32_array->values()->end()};
auto values_int32 = fb.CreateVector(values_vec_int32);
circle::Int32VectorBuilder int32_vector_builder{fb};
int32_vector_builder.add_values(values_int32);
@@ -157,9 +156,9 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
}
case tflite::SparseIndexVector_Uint16Vector:
{
+ const tflite::Uint16Vector *u16_array = static_cast<const tflite::Uint16Vector *>(v_array);
auto values_vec_uint16 =
- std::vector<uint16_t>{dm->array_segments_as_Uint16Vector()->values()->begin(),
- dm->array_segments_as_Uint16Vector()->values()->end()};
+ std::vector<uint16_t>{u16_array->values()->begin(), u16_array->values()->end()};
auto values_uint16 = fb.CreateVector(values_vec_uint16);
circle::Uint16VectorBuilder uint16_vector_builder{fb};
uint16_vector_builder.add_values(values_uint16);
@@ -167,9 +166,9 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
}
case tflite::SparseIndexVector_Uint8Vector:
{
+ const tflite::Uint8Vector *u8_array = static_cast<const tflite::Uint8Vector *>(v_array);
auto values_vec_uint8 =
- std::vector<uint8_t>{dm->array_segments_as_Uint8Vector()->values()->begin(),
- dm->array_segments_as_Uint8Vector()->values()->end()};
+ std::vector<uint8_t>{u8_array->values()->begin(), u8_array->values()->end()};
auto values_uint8 = fb.CreateVector(values_vec_uint8);
circle::Uint8VectorBuilder uint8_vector_builder{fb};
uint8_vector_builder.add_values(values_uint8);
diff --git a/compiler/tflite2circle/src/DataLookup.h b/compiler/tflite2circle/src/DataLookup.h
index 26ad74666..58a357703 100644
--- a/compiler/tflite2circle/src/DataLookup.h
+++ b/compiler/tflite2circle/src/DataLookup.h
@@ -85,8 +85,7 @@ circle::DimensionType get_circle_dimension_type(tflite::DimensionType tfl_dim_ty
* @brief Returns circle SparseIndexVector according to tflite.
*/
flatbuffers::Offset<void>
-get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
- const tflite::DimensionMetadata *dm,
+get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *values,
const tflite::SparseIndexVector &tfl_sparse_index_vector_type);
/**
diff --git a/compiler/tflite2circle/src/TFLBuiltinOptions.lst b/compiler/tflite2circle/src/TFLBuiltinOptions.lst
index 22b59863b..4bc101f8e 100644
--- a/compiler/tflite2circle/src/TFLBuiltinOptions.lst
+++ b/compiler/tflite2circle/src/TFLBuiltinOptions.lst
@@ -76,7 +76,7 @@ TFL_BUILTIN_OPTIONS(ZerosLikeOptions)
TFL_BUILTIN_OPTIONS(FillOptions)
//TFL_BUILTIN_OPTIONS(BidirectionalSequenceLSTMOptions)
//TFL_BUILTIN_OPTIONS(BidirectionalSequenceRNNOptions)
-//TFL_BUILTIN_OPTIONS(UnidirectionalSequenceLSTMOptions)
+TFL_BUILTIN_OPTIONS(UnidirectionalSequenceLSTMOptions)
TFL_BUILTIN_OPTIONS(FloorModOptions)
TFL_BUILTIN_OPTIONS(RangeOptions)
TFL_BUILTIN_OPTIONS(ResizeNearestNeighborOptions)
diff --git a/compiler/vconone/CMakeLists.txt b/compiler/vconone/CMakeLists.txt
index be4398996..ffca275db 100644
--- a/compiler/vconone/CMakeLists.txt
+++ b/compiler/vconone/CMakeLists.txt
@@ -1,5 +1,5 @@
if (NOT VCONONE_VERSION)
- set(VCONONE_VERSION 0x0000000000090001)
+ set(VCONONE_VERSION 0x00000000000a0001)
# NOTE order is [build patch minor major]
# if VCONONE_VERSION is set with -D option, it will be cached
# you may have to remove cache file if you remove -D option